1// Copyright 2013 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/compiler/code-generator.h" 6 7#include "src/compilation-info.h" 8#include "src/compiler/code-generator-impl.h" 9#include "src/compiler/gap-resolver.h" 10#include "src/compiler/node-matchers.h" 11#include "src/compiler/osr.h" 12#include "src/frames.h" 13#include "src/ia32/assembler-ia32.h" 14#include "src/ia32/frames-ia32.h" 15#include "src/ia32/macro-assembler-ia32.h" 16 17namespace v8 { 18namespace internal { 19namespace compiler { 20 21#define __ masm()-> 22 23 24#define kScratchDoubleReg xmm0 25 26 27// Adds IA-32 specific methods for decoding operands. 28class IA32OperandConverter : public InstructionOperandConverter { 29 public: 30 IA32OperandConverter(CodeGenerator* gen, Instruction* instr) 31 : InstructionOperandConverter(gen, instr) {} 32 33 Operand InputOperand(size_t index, int extra = 0) { 34 return ToOperand(instr_->InputAt(index), extra); 35 } 36 37 Immediate InputImmediate(size_t index) { 38 return ToImmediate(instr_->InputAt(index)); 39 } 40 41 Operand OutputOperand() { return ToOperand(instr_->Output()); } 42 43 Operand ToOperand(InstructionOperand* op, int extra = 0) { 44 if (op->IsRegister()) { 45 DCHECK(extra == 0); 46 return Operand(ToRegister(op)); 47 } else if (op->IsFPRegister()) { 48 DCHECK(extra == 0); 49 return Operand(ToDoubleRegister(op)); 50 } 51 DCHECK(op->IsStackSlot() || op->IsFPStackSlot()); 52 return SlotToOperand(AllocatedOperand::cast(op)->index(), extra); 53 } 54 55 Operand SlotToOperand(int slot, int extra = 0) { 56 FrameOffset offset = frame_access_state()->GetFrameOffset(slot); 57 return Operand(offset.from_stack_pointer() ? esp : ebp, 58 offset.offset() + extra); 59 } 60 61 Operand HighOperand(InstructionOperand* op) { 62 DCHECK(op->IsFPStackSlot()); 63 return ToOperand(op, kPointerSize); 64 } 65 66 Immediate ToImmediate(InstructionOperand* operand) { 67 Constant constant = ToConstant(operand); 68 if (constant.type() == Constant::kInt32 && 69 RelocInfo::IsWasmReference(constant.rmode())) { 70 return Immediate(reinterpret_cast<Address>(constant.ToInt32()), 71 constant.rmode()); 72 } 73 switch (constant.type()) { 74 case Constant::kInt32: 75 return Immediate(constant.ToInt32()); 76 case Constant::kFloat32: 77 return Immediate( 78 isolate()->factory()->NewNumber(constant.ToFloat32(), TENURED)); 79 case Constant::kFloat64: 80 return Immediate( 81 isolate()->factory()->NewNumber(constant.ToFloat64(), TENURED)); 82 case Constant::kExternalReference: 83 return Immediate(constant.ToExternalReference()); 84 case Constant::kHeapObject: 85 return Immediate(constant.ToHeapObject()); 86 case Constant::kInt64: 87 break; 88 case Constant::kRpoNumber: 89 return Immediate::CodeRelativeOffset(ToLabel(operand)); 90 } 91 UNREACHABLE(); 92 return Immediate(-1); 93 } 94 95 static size_t NextOffset(size_t* offset) { 96 size_t i = *offset; 97 (*offset)++; 98 return i; 99 } 100 101 static ScaleFactor ScaleFor(AddressingMode one, AddressingMode mode) { 102 STATIC_ASSERT(0 == static_cast<int>(times_1)); 103 STATIC_ASSERT(1 == static_cast<int>(times_2)); 104 STATIC_ASSERT(2 == static_cast<int>(times_4)); 105 STATIC_ASSERT(3 == static_cast<int>(times_8)); 106 int scale = static_cast<int>(mode - one); 107 DCHECK(scale >= 0 && scale < 4); 108 return static_cast<ScaleFactor>(scale); 109 } 110 111 Operand MemoryOperand(size_t* offset) { 112 AddressingMode mode = AddressingModeField::decode(instr_->opcode()); 113 switch (mode) { 114 case kMode_MR: { 115 Register base = InputRegister(NextOffset(offset)); 116 int32_t disp = 0; 117 return Operand(base, disp); 118 } 119 case kMode_MRI: { 120 Register base = InputRegister(NextOffset(offset)); 121 Constant ctant = ToConstant(instr_->InputAt(NextOffset(offset))); 122 return Operand(base, ctant.ToInt32(), ctant.rmode()); 123 } 124 case kMode_MR1: 125 case kMode_MR2: 126 case kMode_MR4: 127 case kMode_MR8: { 128 Register base = InputRegister(NextOffset(offset)); 129 Register index = InputRegister(NextOffset(offset)); 130 ScaleFactor scale = ScaleFor(kMode_MR1, mode); 131 int32_t disp = 0; 132 return Operand(base, index, scale, disp); 133 } 134 case kMode_MR1I: 135 case kMode_MR2I: 136 case kMode_MR4I: 137 case kMode_MR8I: { 138 Register base = InputRegister(NextOffset(offset)); 139 Register index = InputRegister(NextOffset(offset)); 140 ScaleFactor scale = ScaleFor(kMode_MR1I, mode); 141 Constant ctant = ToConstant(instr_->InputAt(NextOffset(offset))); 142 return Operand(base, index, scale, ctant.ToInt32(), ctant.rmode()); 143 } 144 case kMode_M1: 145 case kMode_M2: 146 case kMode_M4: 147 case kMode_M8: { 148 Register index = InputRegister(NextOffset(offset)); 149 ScaleFactor scale = ScaleFor(kMode_M1, mode); 150 int32_t disp = 0; 151 return Operand(index, scale, disp); 152 } 153 case kMode_M1I: 154 case kMode_M2I: 155 case kMode_M4I: 156 case kMode_M8I: { 157 Register index = InputRegister(NextOffset(offset)); 158 ScaleFactor scale = ScaleFor(kMode_M1I, mode); 159 Constant ctant = ToConstant(instr_->InputAt(NextOffset(offset))); 160 return Operand(index, scale, ctant.ToInt32(), ctant.rmode()); 161 } 162 case kMode_MI: { 163 Constant ctant = ToConstant(instr_->InputAt(NextOffset(offset))); 164 return Operand(ctant.ToInt32(), ctant.rmode()); 165 } 166 case kMode_None: 167 UNREACHABLE(); 168 return Operand(no_reg, 0); 169 } 170 UNREACHABLE(); 171 return Operand(no_reg, 0); 172 } 173 174 Operand MemoryOperand(size_t first_input = 0) { 175 return MemoryOperand(&first_input); 176 } 177}; 178 179 180namespace { 181 182bool HasImmediateInput(Instruction* instr, size_t index) { 183 return instr->InputAt(index)->IsImmediate(); 184} 185 186class OutOfLineLoadZero final : public OutOfLineCode { 187 public: 188 OutOfLineLoadZero(CodeGenerator* gen, Register result) 189 : OutOfLineCode(gen), result_(result) {} 190 191 void Generate() final { __ xor_(result_, result_); } 192 193 private: 194 Register const result_; 195}; 196 197class OutOfLineLoadFloat32NaN final : public OutOfLineCode { 198 public: 199 OutOfLineLoadFloat32NaN(CodeGenerator* gen, XMMRegister result) 200 : OutOfLineCode(gen), result_(result) {} 201 202 void Generate() final { 203 __ xorps(result_, result_); 204 __ divss(result_, result_); 205 } 206 207 private: 208 XMMRegister const result_; 209}; 210 211class OutOfLineLoadFloat64NaN final : public OutOfLineCode { 212 public: 213 OutOfLineLoadFloat64NaN(CodeGenerator* gen, XMMRegister result) 214 : OutOfLineCode(gen), result_(result) {} 215 216 void Generate() final { 217 __ xorpd(result_, result_); 218 __ divsd(result_, result_); 219 } 220 221 private: 222 XMMRegister const result_; 223}; 224 225class OutOfLineTruncateDoubleToI final : public OutOfLineCode { 226 public: 227 OutOfLineTruncateDoubleToI(CodeGenerator* gen, Register result, 228 XMMRegister input) 229 : OutOfLineCode(gen), result_(result), input_(input) {} 230 231 void Generate() final { 232 __ sub(esp, Immediate(kDoubleSize)); 233 __ movsd(MemOperand(esp, 0), input_); 234 __ SlowTruncateToI(result_, esp, 0); 235 __ add(esp, Immediate(kDoubleSize)); 236 } 237 238 private: 239 Register const result_; 240 XMMRegister const input_; 241}; 242 243 244class OutOfLineRecordWrite final : public OutOfLineCode { 245 public: 246 OutOfLineRecordWrite(CodeGenerator* gen, Register object, Operand operand, 247 Register value, Register scratch0, Register scratch1, 248 RecordWriteMode mode) 249 : OutOfLineCode(gen), 250 object_(object), 251 operand_(operand), 252 value_(value), 253 scratch0_(scratch0), 254 scratch1_(scratch1), 255 mode_(mode) {} 256 257 void Generate() final { 258 if (mode_ > RecordWriteMode::kValueIsPointer) { 259 __ JumpIfSmi(value_, exit()); 260 } 261 __ CheckPageFlag(value_, scratch0_, 262 MemoryChunk::kPointersToHereAreInterestingMask, zero, 263 exit()); 264 RememberedSetAction const remembered_set_action = 265 mode_ > RecordWriteMode::kValueIsMap ? EMIT_REMEMBERED_SET 266 : OMIT_REMEMBERED_SET; 267 SaveFPRegsMode const save_fp_mode = 268 frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs; 269 RecordWriteStub stub(isolate(), object_, scratch0_, scratch1_, 270 remembered_set_action, save_fp_mode); 271 __ lea(scratch1_, operand_); 272 __ CallStub(&stub); 273 } 274 275 private: 276 Register const object_; 277 Operand const operand_; 278 Register const value_; 279 Register const scratch0_; 280 Register const scratch1_; 281 RecordWriteMode const mode_; 282}; 283 284} // namespace 285 286#define ASSEMBLE_CHECKED_LOAD_FLOAT(asm_instr, OutOfLineLoadNaN, \ 287 SingleOrDouble) \ 288 do { \ 289 auto result = i.OutputDoubleRegister(); \ 290 if (instr->InputAt(0)->IsRegister()) { \ 291 auto offset = i.InputRegister(0); \ 292 if (instr->InputAt(1)->IsRegister()) { \ 293 __ cmp(offset, i.InputRegister(1)); \ 294 } else { \ 295 __ cmp(offset, i.InputImmediate(1)); \ 296 } \ 297 OutOfLineCode* ool = new (zone()) OutOfLineLoadNaN(this, result); \ 298 __ j(above_equal, ool->entry()); \ 299 __ asm_instr(result, i.MemoryOperand(2)); \ 300 __ bind(ool->exit()); \ 301 } else { \ 302 auto index2 = i.InputInt32(0); \ 303 auto length = i.InputInt32(1); \ 304 auto index1 = i.InputRegister(2); \ 305 RelocInfo::Mode rmode_length = i.ToConstant(instr->InputAt(1)).rmode(); \ 306 RelocInfo::Mode rmode_buffer = i.ToConstant(instr->InputAt(3)).rmode(); \ 307 DCHECK_LE(index2, length); \ 308 __ cmp(index1, Immediate(reinterpret_cast<Address>(length - index2), \ 309 rmode_length)); \ 310 class OutOfLineLoadFloat final : public OutOfLineCode { \ 311 public: \ 312 OutOfLineLoadFloat(CodeGenerator* gen, XMMRegister result, \ 313 Register buffer, Register index1, int32_t index2, \ 314 int32_t length, RelocInfo::Mode rmode_length, \ 315 RelocInfo::Mode rmode_buffer) \ 316 : OutOfLineCode(gen), \ 317 result_(result), \ 318 buffer_reg_(buffer), \ 319 buffer_int_(0), \ 320 index1_(index1), \ 321 index2_(index2), \ 322 length_(length), \ 323 rmode_length_(rmode_length), \ 324 rmode_buffer_(rmode_buffer) {} \ 325 \ 326 OutOfLineLoadFloat(CodeGenerator* gen, XMMRegister result, \ 327 int32_t buffer, Register index1, int32_t index2, \ 328 int32_t length, RelocInfo::Mode rmode_length, \ 329 RelocInfo::Mode rmode_buffer) \ 330 : OutOfLineCode(gen), \ 331 result_(result), \ 332 buffer_reg_({-1}), \ 333 buffer_int_(buffer), \ 334 index1_(index1), \ 335 index2_(index2), \ 336 length_(length), \ 337 rmode_length_(rmode_length), \ 338 rmode_buffer_(rmode_buffer) {} \ 339 \ 340 void Generate() final { \ 341 Label oob; \ 342 __ push(index1_); \ 343 __ lea(index1_, Operand(index1_, index2_)); \ 344 __ cmp(index1_, Immediate(reinterpret_cast<Address>(length_), \ 345 rmode_length_)); \ 346 __ j(above_equal, &oob, Label::kNear); \ 347 if (buffer_reg_.is_valid()) { \ 348 __ asm_instr(result_, Operand(buffer_reg_, index1_, times_1, 0)); \ 349 } else { \ 350 __ asm_instr(result_, \ 351 Operand(index1_, buffer_int_, rmode_buffer_)); \ 352 } \ 353 __ pop(index1_); \ 354 __ jmp(exit()); \ 355 __ bind(&oob); \ 356 __ pop(index1_); \ 357 __ xorp##SingleOrDouble(result_, result_); \ 358 __ divs##SingleOrDouble(result_, result_); \ 359 } \ 360 \ 361 private: \ 362 XMMRegister const result_; \ 363 Register const buffer_reg_; \ 364 int32_t const buffer_int_; \ 365 Register const index1_; \ 366 int32_t const index2_; \ 367 int32_t const length_; \ 368 RelocInfo::Mode rmode_length_; \ 369 RelocInfo::Mode rmode_buffer_; \ 370 }; \ 371 if (instr->InputAt(3)->IsRegister()) { \ 372 auto buffer = i.InputRegister(3); \ 373 OutOfLineCode* ool = new (zone()) \ 374 OutOfLineLoadFloat(this, result, buffer, index1, index2, length, \ 375 rmode_length, rmode_buffer); \ 376 __ j(above_equal, ool->entry()); \ 377 __ asm_instr(result, Operand(buffer, index1, times_1, index2)); \ 378 __ bind(ool->exit()); \ 379 } else { \ 380 auto buffer = i.InputInt32(3); \ 381 OutOfLineCode* ool = new (zone()) \ 382 OutOfLineLoadFloat(this, result, buffer, index1, index2, length, \ 383 rmode_length, rmode_buffer); \ 384 __ j(above_equal, ool->entry()); \ 385 __ asm_instr(result, Operand(index1, buffer + index2, rmode_buffer)); \ 386 __ bind(ool->exit()); \ 387 } \ 388 } \ 389 } while (false) 390 391#define ASSEMBLE_CHECKED_LOAD_INTEGER(asm_instr) \ 392 do { \ 393 auto result = i.OutputRegister(); \ 394 if (instr->InputAt(0)->IsRegister()) { \ 395 auto offset = i.InputRegister(0); \ 396 if (instr->InputAt(1)->IsRegister()) { \ 397 __ cmp(offset, i.InputRegister(1)); \ 398 } else { \ 399 __ cmp(offset, i.InputImmediate(1)); \ 400 } \ 401 OutOfLineCode* ool = new (zone()) OutOfLineLoadZero(this, result); \ 402 __ j(above_equal, ool->entry()); \ 403 __ asm_instr(result, i.MemoryOperand(2)); \ 404 __ bind(ool->exit()); \ 405 } else { \ 406 auto index2 = i.InputInt32(0); \ 407 auto length = i.InputInt32(1); \ 408 auto index1 = i.InputRegister(2); \ 409 RelocInfo::Mode rmode_length = i.ToConstant(instr->InputAt(1)).rmode(); \ 410 RelocInfo::Mode rmode_buffer = i.ToConstant(instr->InputAt(3)).rmode(); \ 411 DCHECK_LE(index2, length); \ 412 __ cmp(index1, Immediate(reinterpret_cast<Address>(length - index2), \ 413 rmode_length)); \ 414 class OutOfLineLoadInteger final : public OutOfLineCode { \ 415 public: \ 416 OutOfLineLoadInteger(CodeGenerator* gen, Register result, \ 417 Register buffer, Register index1, int32_t index2, \ 418 int32_t length, RelocInfo::Mode rmode_length, \ 419 RelocInfo::Mode rmode_buffer) \ 420 : OutOfLineCode(gen), \ 421 result_(result), \ 422 buffer_reg_(buffer), \ 423 buffer_int_(0), \ 424 index1_(index1), \ 425 index2_(index2), \ 426 length_(length), \ 427 rmode_length_(rmode_length), \ 428 rmode_buffer_(rmode_buffer) {} \ 429 \ 430 OutOfLineLoadInteger(CodeGenerator* gen, Register result, \ 431 int32_t buffer, Register index1, int32_t index2, \ 432 int32_t length, RelocInfo::Mode rmode_length, \ 433 RelocInfo::Mode rmode_buffer) \ 434 : OutOfLineCode(gen), \ 435 result_(result), \ 436 buffer_reg_({-1}), \ 437 buffer_int_(buffer), \ 438 index1_(index1), \ 439 index2_(index2), \ 440 length_(length), \ 441 rmode_length_(rmode_length), \ 442 rmode_buffer_(rmode_buffer) {} \ 443 \ 444 void Generate() final { \ 445 Label oob; \ 446 bool need_cache = !result_.is(index1_); \ 447 if (need_cache) __ push(index1_); \ 448 __ lea(index1_, Operand(index1_, index2_)); \ 449 __ cmp(index1_, Immediate(reinterpret_cast<Address>(length_), \ 450 rmode_length_)); \ 451 __ j(above_equal, &oob, Label::kNear); \ 452 if (buffer_reg_.is_valid()) { \ 453 __ asm_instr(result_, Operand(buffer_reg_, index1_, times_1, 0)); \ 454 } else { \ 455 __ asm_instr(result_, \ 456 Operand(index1_, buffer_int_, rmode_buffer_)); \ 457 } \ 458 if (need_cache) __ pop(index1_); \ 459 __ jmp(exit()); \ 460 __ bind(&oob); \ 461 if (need_cache) __ pop(index1_); \ 462 __ xor_(result_, result_); \ 463 } \ 464 \ 465 private: \ 466 Register const result_; \ 467 Register const buffer_reg_; \ 468 int32_t const buffer_int_; \ 469 Register const index1_; \ 470 int32_t const index2_; \ 471 int32_t const length_; \ 472 RelocInfo::Mode rmode_length_; \ 473 RelocInfo::Mode rmode_buffer_; \ 474 }; \ 475 if (instr->InputAt(3)->IsRegister()) { \ 476 auto buffer = i.InputRegister(3); \ 477 OutOfLineCode* ool = new (zone()) \ 478 OutOfLineLoadInteger(this, result, buffer, index1, index2, length, \ 479 rmode_length, rmode_buffer); \ 480 __ j(above_equal, ool->entry()); \ 481 __ asm_instr(result, Operand(buffer, index1, times_1, index2)); \ 482 __ bind(ool->exit()); \ 483 } else { \ 484 auto buffer = i.InputInt32(3); \ 485 OutOfLineCode* ool = new (zone()) \ 486 OutOfLineLoadInteger(this, result, buffer, index1, index2, length, \ 487 rmode_length, rmode_buffer); \ 488 __ j(above_equal, ool->entry()); \ 489 __ asm_instr(result, Operand(index1, buffer + index2, rmode_buffer)); \ 490 __ bind(ool->exit()); \ 491 } \ 492 } \ 493 } while (false) 494 495#define ASSEMBLE_CHECKED_STORE_FLOAT(asm_instr) \ 496 do { \ 497 auto value = i.InputDoubleRegister(2); \ 498 if (instr->InputAt(0)->IsRegister()) { \ 499 auto offset = i.InputRegister(0); \ 500 if (instr->InputAt(1)->IsRegister()) { \ 501 __ cmp(offset, i.InputRegister(1)); \ 502 } else { \ 503 __ cmp(offset, i.InputImmediate(1)); \ 504 } \ 505 Label done; \ 506 __ j(above_equal, &done, Label::kNear); \ 507 __ asm_instr(i.MemoryOperand(3), value); \ 508 __ bind(&done); \ 509 } else { \ 510 auto index2 = i.InputInt32(0); \ 511 auto length = i.InputInt32(1); \ 512 auto index1 = i.InputRegister(3); \ 513 RelocInfo::Mode rmode_length = i.ToConstant(instr->InputAt(1)).rmode(); \ 514 RelocInfo::Mode rmode_buffer = i.ToConstant(instr->InputAt(4)).rmode(); \ 515 DCHECK_LE(index2, length); \ 516 __ cmp(index1, Immediate(reinterpret_cast<Address>(length - index2), \ 517 rmode_length)); \ 518 class OutOfLineStoreFloat final : public OutOfLineCode { \ 519 public: \ 520 OutOfLineStoreFloat(CodeGenerator* gen, Register buffer, \ 521 Register index1, int32_t index2, int32_t length, \ 522 XMMRegister value, RelocInfo::Mode rmode_length, \ 523 RelocInfo::Mode rmode_buffer) \ 524 : OutOfLineCode(gen), \ 525 buffer_reg_(buffer), \ 526 buffer_int_(0), \ 527 index1_(index1), \ 528 index2_(index2), \ 529 length_(length), \ 530 value_(value), \ 531 rmode_length_(rmode_length), \ 532 rmode_buffer_(rmode_buffer) {} \ 533 \ 534 OutOfLineStoreFloat(CodeGenerator* gen, int32_t buffer, \ 535 Register index1, int32_t index2, int32_t length, \ 536 XMMRegister value, RelocInfo::Mode rmode_length, \ 537 RelocInfo::Mode rmode_buffer) \ 538 : OutOfLineCode(gen), \ 539 buffer_reg_({-1}), \ 540 buffer_int_(buffer), \ 541 index1_(index1), \ 542 index2_(index2), \ 543 length_(length), \ 544 value_(value), \ 545 rmode_length_(rmode_length), \ 546 rmode_buffer_(rmode_buffer) {} \ 547 \ 548 void Generate() final { \ 549 Label oob; \ 550 __ push(index1_); \ 551 __ lea(index1_, Operand(index1_, index2_)); \ 552 __ cmp(index1_, Immediate(reinterpret_cast<Address>(length_), \ 553 rmode_length_)); \ 554 __ j(above_equal, &oob, Label::kNear); \ 555 if (buffer_reg_.is_valid()) { \ 556 __ asm_instr(Operand(buffer_reg_, index1_, times_1, 0), value_); \ 557 } else { \ 558 __ asm_instr(Operand(index1_, buffer_int_, rmode_buffer_), \ 559 value_); \ 560 } \ 561 __ bind(&oob); \ 562 __ pop(index1_); \ 563 } \ 564 \ 565 private: \ 566 Register const buffer_reg_; \ 567 int32_t const buffer_int_; \ 568 Register const index1_; \ 569 int32_t const index2_; \ 570 int32_t const length_; \ 571 XMMRegister const value_; \ 572 RelocInfo::Mode rmode_length_; \ 573 RelocInfo::Mode rmode_buffer_; \ 574 }; \ 575 if (instr->InputAt(4)->IsRegister()) { \ 576 auto buffer = i.InputRegister(4); \ 577 OutOfLineCode* ool = new (zone()) \ 578 OutOfLineStoreFloat(this, buffer, index1, index2, length, value, \ 579 rmode_length, rmode_buffer); \ 580 __ j(above_equal, ool->entry()); \ 581 __ asm_instr(Operand(buffer, index1, times_1, index2), value); \ 582 __ bind(ool->exit()); \ 583 } else { \ 584 auto buffer = i.InputInt32(4); \ 585 OutOfLineCode* ool = new (zone()) \ 586 OutOfLineStoreFloat(this, buffer, index1, index2, length, value, \ 587 rmode_length, rmode_buffer); \ 588 __ j(above_equal, ool->entry()); \ 589 __ asm_instr(Operand(index1, buffer + index2, rmode_buffer), value); \ 590 __ bind(ool->exit()); \ 591 } \ 592 } \ 593 } while (false) 594 595#define ASSEMBLE_CHECKED_STORE_INTEGER_IMPL(asm_instr, Value) \ 596 do { \ 597 if (instr->InputAt(0)->IsRegister()) { \ 598 auto offset = i.InputRegister(0); \ 599 if (instr->InputAt(1)->IsRegister()) { \ 600 __ cmp(offset, i.InputRegister(1)); \ 601 } else { \ 602 __ cmp(offset, i.InputImmediate(1)); \ 603 } \ 604 Label done; \ 605 __ j(above_equal, &done, Label::kNear); \ 606 __ asm_instr(i.MemoryOperand(3), value); \ 607 __ bind(&done); \ 608 } else { \ 609 auto index2 = i.InputInt32(0); \ 610 auto length = i.InputInt32(1); \ 611 auto index1 = i.InputRegister(3); \ 612 RelocInfo::Mode rmode_length = i.ToConstant(instr->InputAt(1)).rmode(); \ 613 RelocInfo::Mode rmode_buffer = i.ToConstant(instr->InputAt(4)).rmode(); \ 614 DCHECK_LE(index2, length); \ 615 __ cmp(index1, Immediate(reinterpret_cast<Address>(length - index2), \ 616 rmode_length)); \ 617 class OutOfLineStoreInteger final : public OutOfLineCode { \ 618 public: \ 619 OutOfLineStoreInteger(CodeGenerator* gen, Register buffer, \ 620 Register index1, int32_t index2, int32_t length, \ 621 Value value, RelocInfo::Mode rmode_length, \ 622 RelocInfo::Mode rmode_buffer) \ 623 : OutOfLineCode(gen), \ 624 buffer_reg_(buffer), \ 625 buffer_int_(0), \ 626 index1_(index1), \ 627 index2_(index2), \ 628 length_(length), \ 629 value_(value), \ 630 rmode_length_(rmode_length), \ 631 rmode_buffer_(rmode_buffer) {} \ 632 \ 633 OutOfLineStoreInteger(CodeGenerator* gen, int32_t buffer, \ 634 Register index1, int32_t index2, int32_t length, \ 635 Value value, RelocInfo::Mode rmode_length, \ 636 RelocInfo::Mode rmode_buffer) \ 637 : OutOfLineCode(gen), \ 638 buffer_reg_({-1}), \ 639 buffer_int_(buffer), \ 640 index1_(index1), \ 641 index2_(index2), \ 642 length_(length), \ 643 value_(value), \ 644 rmode_length_(rmode_length), \ 645 rmode_buffer_(rmode_buffer) {} \ 646 \ 647 void Generate() final { \ 648 Label oob; \ 649 __ push(index1_); \ 650 __ lea(index1_, Operand(index1_, index2_)); \ 651 __ cmp(index1_, Immediate(reinterpret_cast<Address>(length_), \ 652 rmode_length_)); \ 653 __ j(above_equal, &oob, Label::kNear); \ 654 if (buffer_reg_.is_valid()) { \ 655 __ asm_instr(Operand(buffer_reg_, index1_, times_1, 0), value_); \ 656 } else { \ 657 __ asm_instr(Operand(index1_, buffer_int_, rmode_buffer_), \ 658 value_); \ 659 } \ 660 __ bind(&oob); \ 661 __ pop(index1_); \ 662 } \ 663 \ 664 private: \ 665 Register const buffer_reg_; \ 666 int32_t const buffer_int_; \ 667 Register const index1_; \ 668 int32_t const index2_; \ 669 int32_t const length_; \ 670 Value const value_; \ 671 RelocInfo::Mode rmode_length_; \ 672 RelocInfo::Mode rmode_buffer_; \ 673 }; \ 674 if (instr->InputAt(4)->IsRegister()) { \ 675 auto buffer = i.InputRegister(4); \ 676 OutOfLineCode* ool = new (zone()) \ 677 OutOfLineStoreInteger(this, buffer, index1, index2, length, value, \ 678 rmode_length, rmode_buffer); \ 679 __ j(above_equal, ool->entry()); \ 680 __ asm_instr(Operand(buffer, index1, times_1, index2), value); \ 681 __ bind(ool->exit()); \ 682 } else { \ 683 auto buffer = i.InputInt32(4); \ 684 OutOfLineCode* ool = new (zone()) \ 685 OutOfLineStoreInteger(this, buffer, index1, index2, length, value, \ 686 rmode_length, rmode_buffer); \ 687 __ j(above_equal, ool->entry()); \ 688 __ asm_instr(Operand(index1, buffer + index2, rmode_buffer), value); \ 689 __ bind(ool->exit()); \ 690 } \ 691 } \ 692 } while (false) 693 694#define ASSEMBLE_CHECKED_STORE_INTEGER(asm_instr) \ 695 do { \ 696 if (instr->InputAt(2)->IsRegister()) { \ 697 Register value = i.InputRegister(2); \ 698 ASSEMBLE_CHECKED_STORE_INTEGER_IMPL(asm_instr, Register); \ 699 } else { \ 700 Immediate value = i.InputImmediate(2); \ 701 ASSEMBLE_CHECKED_STORE_INTEGER_IMPL(asm_instr, Immediate); \ 702 } \ 703 } while (false) 704 705#define ASSEMBLE_COMPARE(asm_instr) \ 706 do { \ 707 if (AddressingModeField::decode(instr->opcode()) != kMode_None) { \ 708 size_t index = 0; \ 709 Operand left = i.MemoryOperand(&index); \ 710 if (HasImmediateInput(instr, index)) { \ 711 __ asm_instr(left, i.InputImmediate(index)); \ 712 } else { \ 713 __ asm_instr(left, i.InputRegister(index)); \ 714 } \ 715 } else { \ 716 if (HasImmediateInput(instr, 1)) { \ 717 if (instr->InputAt(0)->IsRegister()) { \ 718 __ asm_instr(i.InputRegister(0), i.InputImmediate(1)); \ 719 } else { \ 720 __ asm_instr(i.InputOperand(0), i.InputImmediate(1)); \ 721 } \ 722 } else { \ 723 if (instr->InputAt(1)->IsRegister()) { \ 724 __ asm_instr(i.InputRegister(0), i.InputRegister(1)); \ 725 } else { \ 726 __ asm_instr(i.InputRegister(0), i.InputOperand(1)); \ 727 } \ 728 } \ 729 } \ 730 } while (0) 731 732#define ASSEMBLE_IEEE754_BINOP(name) \ 733 do { \ 734 /* Pass two doubles as arguments on the stack. */ \ 735 __ PrepareCallCFunction(4, eax); \ 736 __ movsd(Operand(esp, 0 * kDoubleSize), i.InputDoubleRegister(0)); \ 737 __ movsd(Operand(esp, 1 * kDoubleSize), i.InputDoubleRegister(1)); \ 738 __ CallCFunction(ExternalReference::ieee754_##name##_function(isolate()), \ 739 4); \ 740 /* Return value is in st(0) on ia32. */ \ 741 /* Store it into the result register. */ \ 742 __ sub(esp, Immediate(kDoubleSize)); \ 743 __ fstp_d(Operand(esp, 0)); \ 744 __ movsd(i.OutputDoubleRegister(), Operand(esp, 0)); \ 745 __ add(esp, Immediate(kDoubleSize)); \ 746 } while (false) 747 748#define ASSEMBLE_IEEE754_UNOP(name) \ 749 do { \ 750 /* Pass one double as argument on the stack. */ \ 751 __ PrepareCallCFunction(2, eax); \ 752 __ movsd(Operand(esp, 0 * kDoubleSize), i.InputDoubleRegister(0)); \ 753 __ CallCFunction(ExternalReference::ieee754_##name##_function(isolate()), \ 754 2); \ 755 /* Return value is in st(0) on ia32. */ \ 756 /* Store it into the result register. */ \ 757 __ sub(esp, Immediate(kDoubleSize)); \ 758 __ fstp_d(Operand(esp, 0)); \ 759 __ movsd(i.OutputDoubleRegister(), Operand(esp, 0)); \ 760 __ add(esp, Immediate(kDoubleSize)); \ 761 } while (false) 762 763void CodeGenerator::AssembleDeconstructFrame() { 764 __ mov(esp, ebp); 765 __ pop(ebp); 766} 767 768void CodeGenerator::AssemblePrepareTailCall() { 769 if (frame_access_state()->has_frame()) { 770 __ mov(ebp, MemOperand(ebp, 0)); 771 } 772 frame_access_state()->SetFrameAccessToSP(); 773} 774 775void CodeGenerator::AssemblePopArgumentsAdaptorFrame(Register args_reg, 776 Register, Register, 777 Register) { 778 // There are not enough temp registers left on ia32 for a call instruction 779 // so we pick some scratch registers and save/restore them manually here. 780 int scratch_count = 3; 781 Register scratch1 = ebx; 782 Register scratch2 = ecx; 783 Register scratch3 = edx; 784 DCHECK(!AreAliased(args_reg, scratch1, scratch2, scratch3)); 785 Label done; 786 787 // Check if current frame is an arguments adaptor frame. 788 __ cmp(Operand(ebp, StandardFrameConstants::kContextOffset), 789 Immediate(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR))); 790 __ j(not_equal, &done, Label::kNear); 791 792 __ push(scratch1); 793 __ push(scratch2); 794 __ push(scratch3); 795 796 // Load arguments count from current arguments adaptor frame (note, it 797 // does not include receiver). 798 Register caller_args_count_reg = scratch1; 799 __ mov(caller_args_count_reg, 800 Operand(ebp, ArgumentsAdaptorFrameConstants::kLengthOffset)); 801 __ SmiUntag(caller_args_count_reg); 802 803 ParameterCount callee_args_count(args_reg); 804 __ PrepareForTailCall(callee_args_count, caller_args_count_reg, scratch2, 805 scratch3, ReturnAddressState::kOnStack, scratch_count); 806 __ pop(scratch3); 807 __ pop(scratch2); 808 __ pop(scratch1); 809 810 __ bind(&done); 811} 812 813namespace { 814 815void AdjustStackPointerForTailCall(MacroAssembler* masm, 816 FrameAccessState* state, 817 int new_slot_above_sp, 818 bool allow_shrinkage = true) { 819 int current_sp_offset = state->GetSPToFPSlotCount() + 820 StandardFrameConstants::kFixedSlotCountAboveFp; 821 int stack_slot_delta = new_slot_above_sp - current_sp_offset; 822 if (stack_slot_delta > 0) { 823 masm->sub(esp, Immediate(stack_slot_delta * kPointerSize)); 824 state->IncreaseSPDelta(stack_slot_delta); 825 } else if (allow_shrinkage && stack_slot_delta < 0) { 826 masm->add(esp, Immediate(-stack_slot_delta * kPointerSize)); 827 state->IncreaseSPDelta(stack_slot_delta); 828 } 829} 830 831} // namespace 832 833void CodeGenerator::AssembleTailCallBeforeGap(Instruction* instr, 834 int first_unused_stack_slot) { 835 CodeGenerator::PushTypeFlags flags(kImmediatePush | kScalarPush); 836 ZoneVector<MoveOperands*> pushes(zone()); 837 GetPushCompatibleMoves(instr, flags, &pushes); 838 839 if (!pushes.empty() && 840 (LocationOperand::cast(pushes.back()->destination()).index() + 1 == 841 first_unused_stack_slot)) { 842 IA32OperandConverter g(this, instr); 843 for (auto move : pushes) { 844 LocationOperand destination_location( 845 LocationOperand::cast(move->destination())); 846 InstructionOperand source(move->source()); 847 AdjustStackPointerForTailCall(masm(), frame_access_state(), 848 destination_location.index()); 849 if (source.IsStackSlot()) { 850 LocationOperand source_location(LocationOperand::cast(source)); 851 __ push(g.SlotToOperand(source_location.index())); 852 } else if (source.IsRegister()) { 853 LocationOperand source_location(LocationOperand::cast(source)); 854 __ push(source_location.GetRegister()); 855 } else if (source.IsImmediate()) { 856 __ push(Immediate(ImmediateOperand::cast(source).inline_value())); 857 } else { 858 // Pushes of non-scalar data types is not supported. 859 UNIMPLEMENTED(); 860 } 861 frame_access_state()->IncreaseSPDelta(1); 862 move->Eliminate(); 863 } 864 } 865 AdjustStackPointerForTailCall(masm(), frame_access_state(), 866 first_unused_stack_slot, false); 867} 868 869void CodeGenerator::AssembleTailCallAfterGap(Instruction* instr, 870 int first_unused_stack_slot) { 871 AdjustStackPointerForTailCall(masm(), frame_access_state(), 872 first_unused_stack_slot); 873} 874 875// Assembles an instruction after register allocation, producing machine code. 876CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( 877 Instruction* instr) { 878 IA32OperandConverter i(this, instr); 879 InstructionCode opcode = instr->opcode(); 880 ArchOpcode arch_opcode = ArchOpcodeField::decode(opcode); 881 switch (arch_opcode) { 882 case kArchCallCodeObject: { 883 EnsureSpaceForLazyDeopt(); 884 if (HasImmediateInput(instr, 0)) { 885 Handle<Code> code = Handle<Code>::cast(i.InputHeapObject(0)); 886 __ call(code, RelocInfo::CODE_TARGET); 887 } else { 888 Register reg = i.InputRegister(0); 889 __ add(reg, Immediate(Code::kHeaderSize - kHeapObjectTag)); 890 __ call(reg); 891 } 892 RecordCallPosition(instr); 893 frame_access_state()->ClearSPDelta(); 894 break; 895 } 896 case kArchTailCallCodeObjectFromJSFunction: 897 case kArchTailCallCodeObject: { 898 if (arch_opcode == kArchTailCallCodeObjectFromJSFunction) { 899 AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister, 900 no_reg, no_reg, no_reg); 901 } 902 if (HasImmediateInput(instr, 0)) { 903 Handle<Code> code = Handle<Code>::cast(i.InputHeapObject(0)); 904 __ jmp(code, RelocInfo::CODE_TARGET); 905 } else { 906 Register reg = i.InputRegister(0); 907 __ add(reg, Immediate(Code::kHeaderSize - kHeapObjectTag)); 908 __ jmp(reg); 909 } 910 frame_access_state()->ClearSPDelta(); 911 frame_access_state()->SetFrameAccessToDefault(); 912 break; 913 } 914 case kArchTailCallAddress: { 915 CHECK(!HasImmediateInput(instr, 0)); 916 Register reg = i.InputRegister(0); 917 __ jmp(reg); 918 frame_access_state()->ClearSPDelta(); 919 frame_access_state()->SetFrameAccessToDefault(); 920 break; 921 } 922 case kArchCallJSFunction: { 923 EnsureSpaceForLazyDeopt(); 924 Register func = i.InputRegister(0); 925 if (FLAG_debug_code) { 926 // Check the function's context matches the context argument. 927 __ cmp(esi, FieldOperand(func, JSFunction::kContextOffset)); 928 __ Assert(equal, kWrongFunctionContext); 929 } 930 __ call(FieldOperand(func, JSFunction::kCodeEntryOffset)); 931 RecordCallPosition(instr); 932 frame_access_state()->ClearSPDelta(); 933 break; 934 } 935 case kArchTailCallJSFunctionFromJSFunction: { 936 Register func = i.InputRegister(0); 937 if (FLAG_debug_code) { 938 // Check the function's context matches the context argument. 939 __ cmp(esi, FieldOperand(func, JSFunction::kContextOffset)); 940 __ Assert(equal, kWrongFunctionContext); 941 } 942 AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister, no_reg, 943 no_reg, no_reg); 944 __ jmp(FieldOperand(func, JSFunction::kCodeEntryOffset)); 945 frame_access_state()->ClearSPDelta(); 946 frame_access_state()->SetFrameAccessToDefault(); 947 break; 948 } 949 case kArchPrepareCallCFunction: { 950 // Frame alignment requires using FP-relative frame addressing. 951 frame_access_state()->SetFrameAccessToFP(); 952 int const num_parameters = MiscField::decode(instr->opcode()); 953 __ PrepareCallCFunction(num_parameters, i.TempRegister(0)); 954 break; 955 } 956 case kArchPrepareTailCall: 957 AssemblePrepareTailCall(); 958 break; 959 case kArchCallCFunction: { 960 int const num_parameters = MiscField::decode(instr->opcode()); 961 if (HasImmediateInput(instr, 0)) { 962 ExternalReference ref = i.InputExternalReference(0); 963 __ CallCFunction(ref, num_parameters); 964 } else { 965 Register func = i.InputRegister(0); 966 __ CallCFunction(func, num_parameters); 967 } 968 frame_access_state()->SetFrameAccessToDefault(); 969 frame_access_state()->ClearSPDelta(); 970 break; 971 } 972 case kArchJmp: 973 AssembleArchJump(i.InputRpo(0)); 974 break; 975 case kArchLookupSwitch: 976 AssembleArchLookupSwitch(instr); 977 break; 978 case kArchTableSwitch: 979 AssembleArchTableSwitch(instr); 980 break; 981 case kArchComment: { 982 Address comment_string = i.InputExternalReference(0).address(); 983 __ RecordComment(reinterpret_cast<const char*>(comment_string)); 984 break; 985 } 986 case kArchDebugBreak: 987 __ int3(); 988 break; 989 case kArchNop: 990 case kArchThrowTerminator: 991 // don't emit code for nops. 992 break; 993 case kArchDeoptimize: { 994 int deopt_state_id = 995 BuildTranslation(instr, -1, 0, OutputFrameStateCombine::Ignore()); 996 CodeGenResult result = 997 AssembleDeoptimizerCall(deopt_state_id, current_source_position_); 998 if (result != kSuccess) return result; 999 break; 1000 } 1001 case kArchRet: 1002 AssembleReturn(instr->InputAt(0)); 1003 break; 1004 case kArchStackPointer: 1005 __ mov(i.OutputRegister(), esp); 1006 break; 1007 case kArchFramePointer: 1008 __ mov(i.OutputRegister(), ebp); 1009 break; 1010 case kArchParentFramePointer: 1011 if (frame_access_state()->has_frame()) { 1012 __ mov(i.OutputRegister(), Operand(ebp, 0)); 1013 } else { 1014 __ mov(i.OutputRegister(), ebp); 1015 } 1016 break; 1017 case kArchTruncateDoubleToI: { 1018 auto result = i.OutputRegister(); 1019 auto input = i.InputDoubleRegister(0); 1020 auto ool = new (zone()) OutOfLineTruncateDoubleToI(this, result, input); 1021 __ cvttsd2si(result, Operand(input)); 1022 __ cmp(result, 1); 1023 __ j(overflow, ool->entry()); 1024 __ bind(ool->exit()); 1025 break; 1026 } 1027 case kArchStoreWithWriteBarrier: { 1028 RecordWriteMode mode = 1029 static_cast<RecordWriteMode>(MiscField::decode(instr->opcode())); 1030 Register object = i.InputRegister(0); 1031 size_t index = 0; 1032 Operand operand = i.MemoryOperand(&index); 1033 Register value = i.InputRegister(index); 1034 Register scratch0 = i.TempRegister(0); 1035 Register scratch1 = i.TempRegister(1); 1036 auto ool = new (zone()) OutOfLineRecordWrite(this, object, operand, value, 1037 scratch0, scratch1, mode); 1038 __ mov(operand, value); 1039 __ CheckPageFlag(object, scratch0, 1040 MemoryChunk::kPointersFromHereAreInterestingMask, 1041 not_zero, ool->entry()); 1042 __ bind(ool->exit()); 1043 break; 1044 } 1045 case kArchStackSlot: { 1046 FrameOffset offset = 1047 frame_access_state()->GetFrameOffset(i.InputInt32(0)); 1048 Register base; 1049 if (offset.from_stack_pointer()) { 1050 base = esp; 1051 } else { 1052 base = ebp; 1053 } 1054 __ lea(i.OutputRegister(), Operand(base, offset.offset())); 1055 break; 1056 } 1057 case kIeee754Float64Acos: 1058 ASSEMBLE_IEEE754_UNOP(acos); 1059 break; 1060 case kIeee754Float64Acosh: 1061 ASSEMBLE_IEEE754_UNOP(acosh); 1062 break; 1063 case kIeee754Float64Asin: 1064 ASSEMBLE_IEEE754_UNOP(asin); 1065 break; 1066 case kIeee754Float64Asinh: 1067 ASSEMBLE_IEEE754_UNOP(asinh); 1068 break; 1069 case kIeee754Float64Atan: 1070 ASSEMBLE_IEEE754_UNOP(atan); 1071 break; 1072 case kIeee754Float64Atanh: 1073 ASSEMBLE_IEEE754_UNOP(atanh); 1074 break; 1075 case kIeee754Float64Atan2: 1076 ASSEMBLE_IEEE754_BINOP(atan2); 1077 break; 1078 case kIeee754Float64Cbrt: 1079 ASSEMBLE_IEEE754_UNOP(cbrt); 1080 break; 1081 case kIeee754Float64Cos: 1082 ASSEMBLE_IEEE754_UNOP(cos); 1083 break; 1084 case kIeee754Float64Cosh: 1085 ASSEMBLE_IEEE754_UNOP(cosh); 1086 break; 1087 case kIeee754Float64Expm1: 1088 ASSEMBLE_IEEE754_UNOP(expm1); 1089 break; 1090 case kIeee754Float64Exp: 1091 ASSEMBLE_IEEE754_UNOP(exp); 1092 break; 1093 case kIeee754Float64Log: 1094 ASSEMBLE_IEEE754_UNOP(log); 1095 break; 1096 case kIeee754Float64Log1p: 1097 ASSEMBLE_IEEE754_UNOP(log1p); 1098 break; 1099 case kIeee754Float64Log2: 1100 ASSEMBLE_IEEE754_UNOP(log2); 1101 break; 1102 case kIeee754Float64Log10: 1103 ASSEMBLE_IEEE754_UNOP(log10); 1104 break; 1105 case kIeee754Float64Pow: { 1106 // TODO(bmeurer): Improve integration of the stub. 1107 if (!i.InputDoubleRegister(1).is(xmm2)) { 1108 __ movaps(xmm2, i.InputDoubleRegister(0)); 1109 __ movaps(xmm1, i.InputDoubleRegister(1)); 1110 } else { 1111 __ movaps(xmm0, i.InputDoubleRegister(0)); 1112 __ movaps(xmm1, xmm2); 1113 __ movaps(xmm2, xmm0); 1114 } 1115 MathPowStub stub(isolate(), MathPowStub::DOUBLE); 1116 __ CallStub(&stub); 1117 __ movaps(i.OutputDoubleRegister(), xmm3); 1118 break; 1119 } 1120 case kIeee754Float64Sin: 1121 ASSEMBLE_IEEE754_UNOP(sin); 1122 break; 1123 case kIeee754Float64Sinh: 1124 ASSEMBLE_IEEE754_UNOP(sinh); 1125 break; 1126 case kIeee754Float64Tan: 1127 ASSEMBLE_IEEE754_UNOP(tan); 1128 break; 1129 case kIeee754Float64Tanh: 1130 ASSEMBLE_IEEE754_UNOP(tanh); 1131 break; 1132 case kIA32Add: 1133 if (HasImmediateInput(instr, 1)) { 1134 __ add(i.InputOperand(0), i.InputImmediate(1)); 1135 } else { 1136 __ add(i.InputRegister(0), i.InputOperand(1)); 1137 } 1138 break; 1139 case kIA32And: 1140 if (HasImmediateInput(instr, 1)) { 1141 __ and_(i.InputOperand(0), i.InputImmediate(1)); 1142 } else { 1143 __ and_(i.InputRegister(0), i.InputOperand(1)); 1144 } 1145 break; 1146 case kIA32Cmp: 1147 ASSEMBLE_COMPARE(cmp); 1148 break; 1149 case kIA32Cmp16: 1150 ASSEMBLE_COMPARE(cmpw); 1151 break; 1152 case kIA32Cmp8: 1153 ASSEMBLE_COMPARE(cmpb); 1154 break; 1155 case kIA32Test: 1156 ASSEMBLE_COMPARE(test); 1157 break; 1158 case kIA32Test16: 1159 ASSEMBLE_COMPARE(test_w); 1160 break; 1161 case kIA32Test8: 1162 ASSEMBLE_COMPARE(test_b); 1163 break; 1164 case kIA32Imul: 1165 if (HasImmediateInput(instr, 1)) { 1166 __ imul(i.OutputRegister(), i.InputOperand(0), i.InputInt32(1)); 1167 } else { 1168 __ imul(i.OutputRegister(), i.InputOperand(1)); 1169 } 1170 break; 1171 case kIA32ImulHigh: 1172 __ imul(i.InputRegister(1)); 1173 break; 1174 case kIA32UmulHigh: 1175 __ mul(i.InputRegister(1)); 1176 break; 1177 case kIA32Idiv: 1178 __ cdq(); 1179 __ idiv(i.InputOperand(1)); 1180 break; 1181 case kIA32Udiv: 1182 __ Move(edx, Immediate(0)); 1183 __ div(i.InputOperand(1)); 1184 break; 1185 case kIA32Not: 1186 __ not_(i.OutputOperand()); 1187 break; 1188 case kIA32Neg: 1189 __ neg(i.OutputOperand()); 1190 break; 1191 case kIA32Or: 1192 if (HasImmediateInput(instr, 1)) { 1193 __ or_(i.InputOperand(0), i.InputImmediate(1)); 1194 } else { 1195 __ or_(i.InputRegister(0), i.InputOperand(1)); 1196 } 1197 break; 1198 case kIA32Xor: 1199 if (HasImmediateInput(instr, 1)) { 1200 __ xor_(i.InputOperand(0), i.InputImmediate(1)); 1201 } else { 1202 __ xor_(i.InputRegister(0), i.InputOperand(1)); 1203 } 1204 break; 1205 case kIA32Sub: 1206 if (HasImmediateInput(instr, 1)) { 1207 __ sub(i.InputOperand(0), i.InputImmediate(1)); 1208 } else { 1209 __ sub(i.InputRegister(0), i.InputOperand(1)); 1210 } 1211 break; 1212 case kIA32Shl: 1213 if (HasImmediateInput(instr, 1)) { 1214 __ shl(i.OutputOperand(), i.InputInt5(1)); 1215 } else { 1216 __ shl_cl(i.OutputOperand()); 1217 } 1218 break; 1219 case kIA32Shr: 1220 if (HasImmediateInput(instr, 1)) { 1221 __ shr(i.OutputOperand(), i.InputInt5(1)); 1222 } else { 1223 __ shr_cl(i.OutputOperand()); 1224 } 1225 break; 1226 case kIA32Sar: 1227 if (HasImmediateInput(instr, 1)) { 1228 __ sar(i.OutputOperand(), i.InputInt5(1)); 1229 } else { 1230 __ sar_cl(i.OutputOperand()); 1231 } 1232 break; 1233 case kIA32AddPair: { 1234 // i.OutputRegister(0) == i.InputRegister(0) ... left low word. 1235 // i.InputRegister(1) ... left high word. 1236 // i.InputRegister(2) ... right low word. 1237 // i.InputRegister(3) ... right high word. 1238 bool use_temp = false; 1239 if (i.OutputRegister(0).code() == i.InputRegister(1).code() || 1240 i.OutputRegister(0).code() == i.InputRegister(3).code()) { 1241 // We cannot write to the output register directly, because it would 1242 // overwrite an input for adc. We have to use the temp register. 1243 use_temp = true; 1244 __ Move(i.TempRegister(0), i.InputRegister(0)); 1245 __ add(i.TempRegister(0), i.InputRegister(2)); 1246 } else { 1247 __ add(i.OutputRegister(0), i.InputRegister(2)); 1248 } 1249 if (i.OutputRegister(1).code() != i.InputRegister(1).code()) { 1250 __ Move(i.OutputRegister(1), i.InputRegister(1)); 1251 } 1252 __ adc(i.OutputRegister(1), Operand(i.InputRegister(3))); 1253 if (use_temp) { 1254 __ Move(i.OutputRegister(0), i.TempRegister(0)); 1255 } 1256 break; 1257 } 1258 case kIA32SubPair: { 1259 // i.OutputRegister(0) == i.InputRegister(0) ... left low word. 1260 // i.InputRegister(1) ... left high word. 1261 // i.InputRegister(2) ... right low word. 1262 // i.InputRegister(3) ... right high word. 1263 bool use_temp = false; 1264 if (i.OutputRegister(0).code() == i.InputRegister(1).code() || 1265 i.OutputRegister(0).code() == i.InputRegister(3).code()) { 1266 // We cannot write to the output register directly, because it would 1267 // overwrite an input for adc. We have to use the temp register. 1268 use_temp = true; 1269 __ Move(i.TempRegister(0), i.InputRegister(0)); 1270 __ sub(i.TempRegister(0), i.InputRegister(2)); 1271 } else { 1272 __ sub(i.OutputRegister(0), i.InputRegister(2)); 1273 } 1274 if (i.OutputRegister(1).code() != i.InputRegister(1).code()) { 1275 __ Move(i.OutputRegister(1), i.InputRegister(1)); 1276 } 1277 __ sbb(i.OutputRegister(1), Operand(i.InputRegister(3))); 1278 if (use_temp) { 1279 __ Move(i.OutputRegister(0), i.TempRegister(0)); 1280 } 1281 break; 1282 } 1283 case kIA32MulPair: { 1284 __ imul(i.OutputRegister(1), i.InputOperand(0)); 1285 __ mov(i.TempRegister(0), i.InputOperand(1)); 1286 __ imul(i.TempRegister(0), i.InputOperand(2)); 1287 __ add(i.OutputRegister(1), i.TempRegister(0)); 1288 __ mov(i.OutputRegister(0), i.InputOperand(0)); 1289 // Multiplies the low words and stores them in eax and edx. 1290 __ mul(i.InputRegister(2)); 1291 __ add(i.OutputRegister(1), i.TempRegister(0)); 1292 1293 break; 1294 } 1295 case kIA32ShlPair: 1296 if (HasImmediateInput(instr, 2)) { 1297 __ ShlPair(i.InputRegister(1), i.InputRegister(0), i.InputInt6(2)); 1298 } else { 1299 // Shift has been loaded into CL by the register allocator. 1300 __ ShlPair_cl(i.InputRegister(1), i.InputRegister(0)); 1301 } 1302 break; 1303 case kIA32ShrPair: 1304 if (HasImmediateInput(instr, 2)) { 1305 __ ShrPair(i.InputRegister(1), i.InputRegister(0), i.InputInt6(2)); 1306 } else { 1307 // Shift has been loaded into CL by the register allocator. 1308 __ ShrPair_cl(i.InputRegister(1), i.InputRegister(0)); 1309 } 1310 break; 1311 case kIA32SarPair: 1312 if (HasImmediateInput(instr, 2)) { 1313 __ SarPair(i.InputRegister(1), i.InputRegister(0), i.InputInt6(2)); 1314 } else { 1315 // Shift has been loaded into CL by the register allocator. 1316 __ SarPair_cl(i.InputRegister(1), i.InputRegister(0)); 1317 } 1318 break; 1319 case kIA32Ror: 1320 if (HasImmediateInput(instr, 1)) { 1321 __ ror(i.OutputOperand(), i.InputInt5(1)); 1322 } else { 1323 __ ror_cl(i.OutputOperand()); 1324 } 1325 break; 1326 case kIA32Lzcnt: 1327 __ Lzcnt(i.OutputRegister(), i.InputOperand(0)); 1328 break; 1329 case kIA32Tzcnt: 1330 __ Tzcnt(i.OutputRegister(), i.InputOperand(0)); 1331 break; 1332 case kIA32Popcnt: 1333 __ Popcnt(i.OutputRegister(), i.InputOperand(0)); 1334 break; 1335 case kSSEFloat32Cmp: 1336 __ ucomiss(i.InputDoubleRegister(0), i.InputOperand(1)); 1337 break; 1338 case kSSEFloat32Add: 1339 __ addss(i.InputDoubleRegister(0), i.InputOperand(1)); 1340 break; 1341 case kSSEFloat32Sub: 1342 __ subss(i.InputDoubleRegister(0), i.InputOperand(1)); 1343 break; 1344 case kSSEFloat32Mul: 1345 __ mulss(i.InputDoubleRegister(0), i.InputOperand(1)); 1346 break; 1347 case kSSEFloat32Div: 1348 __ divss(i.InputDoubleRegister(0), i.InputOperand(1)); 1349 // Don't delete this mov. It may improve performance on some CPUs, 1350 // when there is a (v)mulss depending on the result. 1351 __ movaps(i.OutputDoubleRegister(), i.OutputDoubleRegister()); 1352 break; 1353 case kSSEFloat32Sqrt: 1354 __ sqrtss(i.OutputDoubleRegister(), i.InputOperand(0)); 1355 break; 1356 case kSSEFloat32Abs: { 1357 // TODO(bmeurer): Use 128-bit constants. 1358 __ pcmpeqd(kScratchDoubleReg, kScratchDoubleReg); 1359 __ psrlq(kScratchDoubleReg, 33); 1360 __ andps(i.OutputDoubleRegister(), kScratchDoubleReg); 1361 break; 1362 } 1363 case kSSEFloat32Neg: { 1364 // TODO(bmeurer): Use 128-bit constants. 1365 __ pcmpeqd(kScratchDoubleReg, kScratchDoubleReg); 1366 __ psllq(kScratchDoubleReg, 31); 1367 __ xorps(i.OutputDoubleRegister(), kScratchDoubleReg); 1368 break; 1369 } 1370 case kSSEFloat32Round: { 1371 CpuFeatureScope sse_scope(masm(), SSE4_1); 1372 RoundingMode const mode = 1373 static_cast<RoundingMode>(MiscField::decode(instr->opcode())); 1374 __ roundss(i.OutputDoubleRegister(), i.InputDoubleRegister(0), mode); 1375 break; 1376 } 1377 case kSSEFloat64Cmp: 1378 __ ucomisd(i.InputDoubleRegister(0), i.InputOperand(1)); 1379 break; 1380 case kSSEFloat64Add: 1381 __ addsd(i.InputDoubleRegister(0), i.InputOperand(1)); 1382 break; 1383 case kSSEFloat64Sub: 1384 __ subsd(i.InputDoubleRegister(0), i.InputOperand(1)); 1385 break; 1386 case kSSEFloat64Mul: 1387 __ mulsd(i.InputDoubleRegister(0), i.InputOperand(1)); 1388 break; 1389 case kSSEFloat64Div: 1390 __ divsd(i.InputDoubleRegister(0), i.InputOperand(1)); 1391 // Don't delete this mov. It may improve performance on some CPUs, 1392 // when there is a (v)mulsd depending on the result. 1393 __ movaps(i.OutputDoubleRegister(), i.OutputDoubleRegister()); 1394 break; 1395 case kSSEFloat32Max: { 1396 Label compare_nan, compare_swap, done_compare; 1397 if (instr->InputAt(1)->IsFPRegister()) { 1398 __ ucomiss(i.InputDoubleRegister(0), i.InputDoubleRegister(1)); 1399 } else { 1400 __ ucomiss(i.InputDoubleRegister(0), i.InputOperand(1)); 1401 } 1402 auto ool = 1403 new (zone()) OutOfLineLoadFloat32NaN(this, i.OutputDoubleRegister()); 1404 __ j(parity_even, ool->entry()); 1405 __ j(above, &done_compare, Label::kNear); 1406 __ j(below, &compare_swap, Label::kNear); 1407 __ movmskps(i.TempRegister(0), i.InputDoubleRegister(0)); 1408 __ test(i.TempRegister(0), Immediate(1)); 1409 __ j(zero, &done_compare, Label::kNear); 1410 __ bind(&compare_swap); 1411 if (instr->InputAt(1)->IsFPRegister()) { 1412 __ movss(i.InputDoubleRegister(0), i.InputDoubleRegister(1)); 1413 } else { 1414 __ movss(i.InputDoubleRegister(0), i.InputOperand(1)); 1415 } 1416 __ bind(&done_compare); 1417 __ bind(ool->exit()); 1418 break; 1419 } 1420 1421 case kSSEFloat64Max: { 1422 Label compare_nan, compare_swap, done_compare; 1423 if (instr->InputAt(1)->IsFPRegister()) { 1424 __ ucomisd(i.InputDoubleRegister(0), i.InputDoubleRegister(1)); 1425 } else { 1426 __ ucomisd(i.InputDoubleRegister(0), i.InputOperand(1)); 1427 } 1428 auto ool = 1429 new (zone()) OutOfLineLoadFloat64NaN(this, i.OutputDoubleRegister()); 1430 __ j(parity_even, ool->entry()); 1431 __ j(above, &done_compare, Label::kNear); 1432 __ j(below, &compare_swap, Label::kNear); 1433 __ movmskpd(i.TempRegister(0), i.InputDoubleRegister(0)); 1434 __ test(i.TempRegister(0), Immediate(1)); 1435 __ j(zero, &done_compare, Label::kNear); 1436 __ bind(&compare_swap); 1437 if (instr->InputAt(1)->IsFPRegister()) { 1438 __ movsd(i.InputDoubleRegister(0), i.InputDoubleRegister(1)); 1439 } else { 1440 __ movsd(i.InputDoubleRegister(0), i.InputOperand(1)); 1441 } 1442 __ bind(&done_compare); 1443 __ bind(ool->exit()); 1444 break; 1445 } 1446 case kSSEFloat32Min: { 1447 Label compare_swap, done_compare; 1448 if (instr->InputAt(1)->IsFPRegister()) { 1449 __ ucomiss(i.InputDoubleRegister(0), i.InputDoubleRegister(1)); 1450 } else { 1451 __ ucomiss(i.InputDoubleRegister(0), i.InputOperand(1)); 1452 } 1453 auto ool = 1454 new (zone()) OutOfLineLoadFloat32NaN(this, i.OutputDoubleRegister()); 1455 __ j(parity_even, ool->entry()); 1456 __ j(below, &done_compare, Label::kNear); 1457 __ j(above, &compare_swap, Label::kNear); 1458 if (instr->InputAt(1)->IsFPRegister()) { 1459 __ movmskps(i.TempRegister(0), i.InputDoubleRegister(1)); 1460 } else { 1461 __ movss(kScratchDoubleReg, i.InputOperand(1)); 1462 __ movmskps(i.TempRegister(0), kScratchDoubleReg); 1463 } 1464 __ test(i.TempRegister(0), Immediate(1)); 1465 __ j(zero, &done_compare, Label::kNear); 1466 __ bind(&compare_swap); 1467 if (instr->InputAt(1)->IsFPRegister()) { 1468 __ movss(i.InputDoubleRegister(0), i.InputDoubleRegister(1)); 1469 } else { 1470 __ movss(i.InputDoubleRegister(0), i.InputOperand(1)); 1471 } 1472 __ bind(&done_compare); 1473 __ bind(ool->exit()); 1474 break; 1475 } 1476 case kSSEFloat64Min: { 1477 Label compare_swap, done_compare; 1478 if (instr->InputAt(1)->IsFPRegister()) { 1479 __ ucomisd(i.InputDoubleRegister(0), i.InputDoubleRegister(1)); 1480 } else { 1481 __ ucomisd(i.InputDoubleRegister(0), i.InputOperand(1)); 1482 } 1483 auto ool = 1484 new (zone()) OutOfLineLoadFloat64NaN(this, i.OutputDoubleRegister()); 1485 __ j(parity_even, ool->entry()); 1486 __ j(below, &done_compare, Label::kNear); 1487 __ j(above, &compare_swap, Label::kNear); 1488 if (instr->InputAt(1)->IsFPRegister()) { 1489 __ movmskpd(i.TempRegister(0), i.InputDoubleRegister(1)); 1490 } else { 1491 __ movsd(kScratchDoubleReg, i.InputOperand(1)); 1492 __ movmskpd(i.TempRegister(0), kScratchDoubleReg); 1493 } 1494 __ test(i.TempRegister(0), Immediate(1)); 1495 __ j(zero, &done_compare, Label::kNear); 1496 __ bind(&compare_swap); 1497 if (instr->InputAt(1)->IsFPRegister()) { 1498 __ movsd(i.InputDoubleRegister(0), i.InputDoubleRegister(1)); 1499 } else { 1500 __ movsd(i.InputDoubleRegister(0), i.InputOperand(1)); 1501 } 1502 __ bind(&done_compare); 1503 __ bind(ool->exit()); 1504 break; 1505 } 1506 case kSSEFloat64Mod: { 1507 // TODO(dcarney): alignment is wrong. 1508 __ sub(esp, Immediate(kDoubleSize)); 1509 // Move values to st(0) and st(1). 1510 __ movsd(Operand(esp, 0), i.InputDoubleRegister(1)); 1511 __ fld_d(Operand(esp, 0)); 1512 __ movsd(Operand(esp, 0), i.InputDoubleRegister(0)); 1513 __ fld_d(Operand(esp, 0)); 1514 // Loop while fprem isn't done. 1515 Label mod_loop; 1516 __ bind(&mod_loop); 1517 // This instructions traps on all kinds inputs, but we are assuming the 1518 // floating point control word is set to ignore them all. 1519 __ fprem(); 1520 // The following 2 instruction implicitly use eax. 1521 __ fnstsw_ax(); 1522 __ sahf(); 1523 __ j(parity_even, &mod_loop); 1524 // Move output to stack and clean up. 1525 __ fstp(1); 1526 __ fstp_d(Operand(esp, 0)); 1527 __ movsd(i.OutputDoubleRegister(), Operand(esp, 0)); 1528 __ add(esp, Immediate(kDoubleSize)); 1529 break; 1530 } 1531 case kSSEFloat64Abs: { 1532 // TODO(bmeurer): Use 128-bit constants. 1533 __ pcmpeqd(kScratchDoubleReg, kScratchDoubleReg); 1534 __ psrlq(kScratchDoubleReg, 1); 1535 __ andpd(i.OutputDoubleRegister(), kScratchDoubleReg); 1536 break; 1537 } 1538 case kSSEFloat64Neg: { 1539 // TODO(bmeurer): Use 128-bit constants. 1540 __ pcmpeqd(kScratchDoubleReg, kScratchDoubleReg); 1541 __ psllq(kScratchDoubleReg, 63); 1542 __ xorpd(i.OutputDoubleRegister(), kScratchDoubleReg); 1543 break; 1544 } 1545 case kSSEFloat64Sqrt: 1546 __ sqrtsd(i.OutputDoubleRegister(), i.InputOperand(0)); 1547 break; 1548 case kSSEFloat64Round: { 1549 CpuFeatureScope sse_scope(masm(), SSE4_1); 1550 RoundingMode const mode = 1551 static_cast<RoundingMode>(MiscField::decode(instr->opcode())); 1552 __ roundsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0), mode); 1553 break; 1554 } 1555 case kSSEFloat32ToFloat64: 1556 __ cvtss2sd(i.OutputDoubleRegister(), i.InputOperand(0)); 1557 break; 1558 case kSSEFloat64ToFloat32: 1559 __ cvtsd2ss(i.OutputDoubleRegister(), i.InputOperand(0)); 1560 break; 1561 case kSSEFloat32ToInt32: 1562 __ cvttss2si(i.OutputRegister(), i.InputOperand(0)); 1563 break; 1564 case kSSEFloat32ToUint32: { 1565 Label success; 1566 __ cvttss2si(i.OutputRegister(), i.InputOperand(0)); 1567 __ test(i.OutputRegister(), i.OutputRegister()); 1568 __ j(positive, &success); 1569 __ Move(kScratchDoubleReg, static_cast<float>(INT32_MIN)); 1570 __ addss(kScratchDoubleReg, i.InputOperand(0)); 1571 __ cvttss2si(i.OutputRegister(), kScratchDoubleReg); 1572 __ or_(i.OutputRegister(), Immediate(0x80000000)); 1573 __ bind(&success); 1574 break; 1575 } 1576 case kSSEFloat64ToInt32: 1577 __ cvttsd2si(i.OutputRegister(), i.InputOperand(0)); 1578 break; 1579 case kSSEFloat64ToUint32: { 1580 __ Move(kScratchDoubleReg, -2147483648.0); 1581 __ addsd(kScratchDoubleReg, i.InputOperand(0)); 1582 __ cvttsd2si(i.OutputRegister(), kScratchDoubleReg); 1583 __ add(i.OutputRegister(), Immediate(0x80000000)); 1584 break; 1585 } 1586 case kSSEInt32ToFloat32: 1587 __ cvtsi2ss(i.OutputDoubleRegister(), i.InputOperand(0)); 1588 break; 1589 case kSSEUint32ToFloat32: { 1590 Register scratch0 = i.TempRegister(0); 1591 Register scratch1 = i.TempRegister(1); 1592 __ mov(scratch0, i.InputOperand(0)); 1593 __ Cvtui2ss(i.OutputDoubleRegister(), scratch0, scratch1); 1594 break; 1595 } 1596 case kSSEInt32ToFloat64: 1597 __ cvtsi2sd(i.OutputDoubleRegister(), i.InputOperand(0)); 1598 break; 1599 case kSSEUint32ToFloat64: 1600 __ LoadUint32(i.OutputDoubleRegister(), i.InputOperand(0)); 1601 break; 1602 case kSSEFloat64ExtractLowWord32: 1603 if (instr->InputAt(0)->IsFPStackSlot()) { 1604 __ mov(i.OutputRegister(), i.InputOperand(0)); 1605 } else { 1606 __ movd(i.OutputRegister(), i.InputDoubleRegister(0)); 1607 } 1608 break; 1609 case kSSEFloat64ExtractHighWord32: 1610 if (instr->InputAt(0)->IsFPStackSlot()) { 1611 __ mov(i.OutputRegister(), i.InputOperand(0, kDoubleSize / 2)); 1612 } else { 1613 __ Pextrd(i.OutputRegister(), i.InputDoubleRegister(0), 1); 1614 } 1615 break; 1616 case kSSEFloat64InsertLowWord32: 1617 __ Pinsrd(i.OutputDoubleRegister(), i.InputOperand(1), 0); 1618 break; 1619 case kSSEFloat64InsertHighWord32: 1620 __ Pinsrd(i.OutputDoubleRegister(), i.InputOperand(1), 1); 1621 break; 1622 case kSSEFloat64LoadLowWord32: 1623 __ movd(i.OutputDoubleRegister(), i.InputOperand(0)); 1624 break; 1625 case kAVXFloat32Add: { 1626 CpuFeatureScope avx_scope(masm(), AVX); 1627 __ vaddss(i.OutputDoubleRegister(), i.InputDoubleRegister(0), 1628 i.InputOperand(1)); 1629 break; 1630 } 1631 case kAVXFloat32Sub: { 1632 CpuFeatureScope avx_scope(masm(), AVX); 1633 __ vsubss(i.OutputDoubleRegister(), i.InputDoubleRegister(0), 1634 i.InputOperand(1)); 1635 break; 1636 } 1637 case kAVXFloat32Mul: { 1638 CpuFeatureScope avx_scope(masm(), AVX); 1639 __ vmulss(i.OutputDoubleRegister(), i.InputDoubleRegister(0), 1640 i.InputOperand(1)); 1641 break; 1642 } 1643 case kAVXFloat32Div: { 1644 CpuFeatureScope avx_scope(masm(), AVX); 1645 __ vdivss(i.OutputDoubleRegister(), i.InputDoubleRegister(0), 1646 i.InputOperand(1)); 1647 // Don't delete this mov. It may improve performance on some CPUs, 1648 // when there is a (v)mulss depending on the result. 1649 __ movaps(i.OutputDoubleRegister(), i.OutputDoubleRegister()); 1650 break; 1651 } 1652 case kAVXFloat64Add: { 1653 CpuFeatureScope avx_scope(masm(), AVX); 1654 __ vaddsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0), 1655 i.InputOperand(1)); 1656 break; 1657 } 1658 case kAVXFloat64Sub: { 1659 CpuFeatureScope avx_scope(masm(), AVX); 1660 __ vsubsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0), 1661 i.InputOperand(1)); 1662 break; 1663 } 1664 case kAVXFloat64Mul: { 1665 CpuFeatureScope avx_scope(masm(), AVX); 1666 __ vmulsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0), 1667 i.InputOperand(1)); 1668 break; 1669 } 1670 case kAVXFloat64Div: { 1671 CpuFeatureScope avx_scope(masm(), AVX); 1672 __ vdivsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0), 1673 i.InputOperand(1)); 1674 // Don't delete this mov. It may improve performance on some CPUs, 1675 // when there is a (v)mulsd depending on the result. 1676 __ movaps(i.OutputDoubleRegister(), i.OutputDoubleRegister()); 1677 break; 1678 } 1679 case kAVXFloat32Abs: { 1680 // TODO(bmeurer): Use RIP relative 128-bit constants. 1681 __ pcmpeqd(kScratchDoubleReg, kScratchDoubleReg); 1682 __ psrlq(kScratchDoubleReg, 33); 1683 CpuFeatureScope avx_scope(masm(), AVX); 1684 __ vandps(i.OutputDoubleRegister(), kScratchDoubleReg, i.InputOperand(0)); 1685 break; 1686 } 1687 case kAVXFloat32Neg: { 1688 // TODO(bmeurer): Use RIP relative 128-bit constants. 1689 __ pcmpeqd(kScratchDoubleReg, kScratchDoubleReg); 1690 __ psllq(kScratchDoubleReg, 31); 1691 CpuFeatureScope avx_scope(masm(), AVX); 1692 __ vxorps(i.OutputDoubleRegister(), kScratchDoubleReg, i.InputOperand(0)); 1693 break; 1694 } 1695 case kAVXFloat64Abs: { 1696 // TODO(bmeurer): Use RIP relative 128-bit constants. 1697 __ pcmpeqd(kScratchDoubleReg, kScratchDoubleReg); 1698 __ psrlq(kScratchDoubleReg, 1); 1699 CpuFeatureScope avx_scope(masm(), AVX); 1700 __ vandpd(i.OutputDoubleRegister(), kScratchDoubleReg, i.InputOperand(0)); 1701 break; 1702 } 1703 case kAVXFloat64Neg: { 1704 // TODO(bmeurer): Use RIP relative 128-bit constants. 1705 __ pcmpeqd(kScratchDoubleReg, kScratchDoubleReg); 1706 __ psllq(kScratchDoubleReg, 63); 1707 CpuFeatureScope avx_scope(masm(), AVX); 1708 __ vxorpd(i.OutputDoubleRegister(), kScratchDoubleReg, i.InputOperand(0)); 1709 break; 1710 } 1711 case kSSEFloat64SilenceNaN: 1712 __ xorpd(kScratchDoubleReg, kScratchDoubleReg); 1713 __ subsd(i.InputDoubleRegister(0), kScratchDoubleReg); 1714 break; 1715 case kIA32Movsxbl: 1716 __ movsx_b(i.OutputRegister(), i.MemoryOperand()); 1717 break; 1718 case kIA32Movzxbl: 1719 __ movzx_b(i.OutputRegister(), i.MemoryOperand()); 1720 break; 1721 case kIA32Movb: { 1722 size_t index = 0; 1723 Operand operand = i.MemoryOperand(&index); 1724 if (HasImmediateInput(instr, index)) { 1725 __ mov_b(operand, i.InputInt8(index)); 1726 } else { 1727 __ mov_b(operand, i.InputRegister(index)); 1728 } 1729 break; 1730 } 1731 case kIA32Movsxwl: 1732 __ movsx_w(i.OutputRegister(), i.MemoryOperand()); 1733 break; 1734 case kIA32Movzxwl: 1735 __ movzx_w(i.OutputRegister(), i.MemoryOperand()); 1736 break; 1737 case kIA32Movw: { 1738 size_t index = 0; 1739 Operand operand = i.MemoryOperand(&index); 1740 if (HasImmediateInput(instr, index)) { 1741 __ mov_w(operand, i.InputInt16(index)); 1742 } else { 1743 __ mov_w(operand, i.InputRegister(index)); 1744 } 1745 break; 1746 } 1747 case kIA32Movl: 1748 if (instr->HasOutput()) { 1749 __ mov(i.OutputRegister(), i.MemoryOperand()); 1750 } else { 1751 size_t index = 0; 1752 Operand operand = i.MemoryOperand(&index); 1753 if (HasImmediateInput(instr, index)) { 1754 __ mov(operand, i.InputImmediate(index)); 1755 } else { 1756 __ mov(operand, i.InputRegister(index)); 1757 } 1758 } 1759 break; 1760 case kIA32Movsd: 1761 if (instr->HasOutput()) { 1762 __ movsd(i.OutputDoubleRegister(), i.MemoryOperand()); 1763 } else { 1764 size_t index = 0; 1765 Operand operand = i.MemoryOperand(&index); 1766 __ movsd(operand, i.InputDoubleRegister(index)); 1767 } 1768 break; 1769 case kIA32Movss: 1770 if (instr->HasOutput()) { 1771 __ movss(i.OutputDoubleRegister(), i.MemoryOperand()); 1772 } else { 1773 size_t index = 0; 1774 Operand operand = i.MemoryOperand(&index); 1775 __ movss(operand, i.InputDoubleRegister(index)); 1776 } 1777 break; 1778 case kIA32BitcastFI: 1779 if (instr->InputAt(0)->IsFPStackSlot()) { 1780 __ mov(i.OutputRegister(), i.InputOperand(0)); 1781 } else { 1782 __ movd(i.OutputRegister(), i.InputDoubleRegister(0)); 1783 } 1784 break; 1785 case kIA32BitcastIF: 1786 if (instr->InputAt(0)->IsRegister()) { 1787 __ movd(i.OutputDoubleRegister(), i.InputRegister(0)); 1788 } else { 1789 __ movss(i.OutputDoubleRegister(), i.InputOperand(0)); 1790 } 1791 break; 1792 case kIA32Lea: { 1793 AddressingMode mode = AddressingModeField::decode(instr->opcode()); 1794 // Shorten "leal" to "addl", "subl" or "shll" if the register allocation 1795 // and addressing mode just happens to work out. The "addl"/"subl" forms 1796 // in these cases are faster based on measurements. 1797 if (mode == kMode_MI) { 1798 __ Move(i.OutputRegister(), Immediate(i.InputInt32(0))); 1799 } else if (i.InputRegister(0).is(i.OutputRegister())) { 1800 if (mode == kMode_MRI) { 1801 int32_t constant_summand = i.InputInt32(1); 1802 if (constant_summand > 0) { 1803 __ add(i.OutputRegister(), Immediate(constant_summand)); 1804 } else if (constant_summand < 0) { 1805 __ sub(i.OutputRegister(), Immediate(-constant_summand)); 1806 } 1807 } else if (mode == kMode_MR1) { 1808 if (i.InputRegister(1).is(i.OutputRegister())) { 1809 __ shl(i.OutputRegister(), 1); 1810 } else { 1811 __ add(i.OutputRegister(), i.InputRegister(1)); 1812 } 1813 } else if (mode == kMode_M2) { 1814 __ shl(i.OutputRegister(), 1); 1815 } else if (mode == kMode_M4) { 1816 __ shl(i.OutputRegister(), 2); 1817 } else if (mode == kMode_M8) { 1818 __ shl(i.OutputRegister(), 3); 1819 } else { 1820 __ lea(i.OutputRegister(), i.MemoryOperand()); 1821 } 1822 } else if (mode == kMode_MR1 && 1823 i.InputRegister(1).is(i.OutputRegister())) { 1824 __ add(i.OutputRegister(), i.InputRegister(0)); 1825 } else { 1826 __ lea(i.OutputRegister(), i.MemoryOperand()); 1827 } 1828 break; 1829 } 1830 case kIA32PushFloat32: 1831 if (instr->InputAt(0)->IsFPRegister()) { 1832 __ sub(esp, Immediate(kFloatSize)); 1833 __ movss(Operand(esp, 0), i.InputDoubleRegister(0)); 1834 frame_access_state()->IncreaseSPDelta(kFloatSize / kPointerSize); 1835 } else if (HasImmediateInput(instr, 0)) { 1836 __ Move(kScratchDoubleReg, i.InputDouble(0)); 1837 __ sub(esp, Immediate(kDoubleSize)); 1838 __ movss(Operand(esp, 0), kScratchDoubleReg); 1839 frame_access_state()->IncreaseSPDelta(kDoubleSize / kPointerSize); 1840 } else { 1841 __ movsd(kScratchDoubleReg, i.InputOperand(0)); 1842 __ sub(esp, Immediate(kDoubleSize)); 1843 __ movss(Operand(esp, 0), kScratchDoubleReg); 1844 frame_access_state()->IncreaseSPDelta(kDoubleSize / kPointerSize); 1845 } 1846 break; 1847 case kIA32PushFloat64: 1848 if (instr->InputAt(0)->IsFPRegister()) { 1849 __ sub(esp, Immediate(kDoubleSize)); 1850 __ movsd(Operand(esp, 0), i.InputDoubleRegister(0)); 1851 frame_access_state()->IncreaseSPDelta(kDoubleSize / kPointerSize); 1852 } else if (HasImmediateInput(instr, 0)) { 1853 __ Move(kScratchDoubleReg, i.InputDouble(0)); 1854 __ sub(esp, Immediate(kDoubleSize)); 1855 __ movsd(Operand(esp, 0), kScratchDoubleReg); 1856 frame_access_state()->IncreaseSPDelta(kDoubleSize / kPointerSize); 1857 } else { 1858 __ movsd(kScratchDoubleReg, i.InputOperand(0)); 1859 __ sub(esp, Immediate(kDoubleSize)); 1860 __ movsd(Operand(esp, 0), kScratchDoubleReg); 1861 frame_access_state()->IncreaseSPDelta(kDoubleSize / kPointerSize); 1862 } 1863 break; 1864 case kIA32Push: 1865 if (AddressingModeField::decode(instr->opcode()) != kMode_None) { 1866 size_t index = 0; 1867 Operand operand = i.MemoryOperand(&index); 1868 __ push(operand); 1869 frame_access_state()->IncreaseSPDelta(kFloatSize / kPointerSize); 1870 } else if (instr->InputAt(0)->IsFPRegister()) { 1871 __ sub(esp, Immediate(kFloatSize)); 1872 __ movsd(Operand(esp, 0), i.InputDoubleRegister(0)); 1873 frame_access_state()->IncreaseSPDelta(kFloatSize / kPointerSize); 1874 } else if (HasImmediateInput(instr, 0)) { 1875 __ push(i.InputImmediate(0)); 1876 frame_access_state()->IncreaseSPDelta(1); 1877 } else { 1878 __ push(i.InputOperand(0)); 1879 frame_access_state()->IncreaseSPDelta(1); 1880 } 1881 break; 1882 case kIA32Poke: { 1883 int const slot = MiscField::decode(instr->opcode()); 1884 if (HasImmediateInput(instr, 0)) { 1885 __ mov(Operand(esp, slot * kPointerSize), i.InputImmediate(0)); 1886 } else { 1887 __ mov(Operand(esp, slot * kPointerSize), i.InputRegister(0)); 1888 } 1889 break; 1890 } 1891 case kIA32Xchgb: { 1892 size_t index = 0; 1893 Operand operand = i.MemoryOperand(&index); 1894 __ xchg_b(i.InputRegister(index), operand); 1895 break; 1896 } 1897 case kIA32Xchgw: { 1898 size_t index = 0; 1899 Operand operand = i.MemoryOperand(&index); 1900 __ xchg_w(i.InputRegister(index), operand); 1901 break; 1902 } 1903 case kIA32Xchgl: { 1904 size_t index = 0; 1905 Operand operand = i.MemoryOperand(&index); 1906 __ xchg(i.InputRegister(index), operand); 1907 break; 1908 } 1909 case kCheckedLoadInt8: 1910 ASSEMBLE_CHECKED_LOAD_INTEGER(movsx_b); 1911 break; 1912 case kCheckedLoadUint8: 1913 ASSEMBLE_CHECKED_LOAD_INTEGER(movzx_b); 1914 break; 1915 case kCheckedLoadInt16: 1916 ASSEMBLE_CHECKED_LOAD_INTEGER(movsx_w); 1917 break; 1918 case kCheckedLoadUint16: 1919 ASSEMBLE_CHECKED_LOAD_INTEGER(movzx_w); 1920 break; 1921 case kCheckedLoadWord32: 1922 ASSEMBLE_CHECKED_LOAD_INTEGER(mov); 1923 break; 1924 case kCheckedLoadFloat32: 1925 ASSEMBLE_CHECKED_LOAD_FLOAT(movss, OutOfLineLoadFloat32NaN, s); 1926 break; 1927 case kCheckedLoadFloat64: 1928 ASSEMBLE_CHECKED_LOAD_FLOAT(movsd, OutOfLineLoadFloat64NaN, d); 1929 break; 1930 case kCheckedStoreWord8: 1931 ASSEMBLE_CHECKED_STORE_INTEGER(mov_b); 1932 break; 1933 case kCheckedStoreWord16: 1934 ASSEMBLE_CHECKED_STORE_INTEGER(mov_w); 1935 break; 1936 case kCheckedStoreWord32: 1937 ASSEMBLE_CHECKED_STORE_INTEGER(mov); 1938 break; 1939 case kCheckedStoreFloat32: 1940 ASSEMBLE_CHECKED_STORE_FLOAT(movss); 1941 break; 1942 case kCheckedStoreFloat64: 1943 ASSEMBLE_CHECKED_STORE_FLOAT(movsd); 1944 break; 1945 case kIA32StackCheck: { 1946 ExternalReference const stack_limit = 1947 ExternalReference::address_of_stack_limit(isolate()); 1948 __ cmp(esp, Operand::StaticVariable(stack_limit)); 1949 break; 1950 } 1951 case kCheckedLoadWord64: 1952 case kCheckedStoreWord64: 1953 UNREACHABLE(); // currently unsupported checked int64 load/store. 1954 break; 1955 case kAtomicLoadInt8: 1956 case kAtomicLoadUint8: 1957 case kAtomicLoadInt16: 1958 case kAtomicLoadUint16: 1959 case kAtomicLoadWord32: 1960 case kAtomicStoreWord8: 1961 case kAtomicStoreWord16: 1962 case kAtomicStoreWord32: 1963 UNREACHABLE(); // Won't be generated by instruction selector. 1964 break; 1965 } 1966 return kSuccess; 1967} // NOLINT(readability/fn_size) 1968 1969static Condition FlagsConditionToCondition(FlagsCondition condition) { 1970 switch (condition) { 1971 case kUnorderedEqual: 1972 case kEqual: 1973 return equal; 1974 break; 1975 case kUnorderedNotEqual: 1976 case kNotEqual: 1977 return not_equal; 1978 break; 1979 case kSignedLessThan: 1980 return less; 1981 break; 1982 case kSignedGreaterThanOrEqual: 1983 return greater_equal; 1984 break; 1985 case kSignedLessThanOrEqual: 1986 return less_equal; 1987 break; 1988 case kSignedGreaterThan: 1989 return greater; 1990 break; 1991 case kUnsignedLessThan: 1992 return below; 1993 break; 1994 case kUnsignedGreaterThanOrEqual: 1995 return above_equal; 1996 break; 1997 case kUnsignedLessThanOrEqual: 1998 return below_equal; 1999 break; 2000 case kUnsignedGreaterThan: 2001 return above; 2002 break; 2003 case kOverflow: 2004 return overflow; 2005 break; 2006 case kNotOverflow: 2007 return no_overflow; 2008 break; 2009 default: 2010 UNREACHABLE(); 2011 return no_condition; 2012 break; 2013 } 2014} 2015 2016// Assembles a branch after an instruction. 2017void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) { 2018 Label::Distance flabel_distance = 2019 branch->fallthru ? Label::kNear : Label::kFar; 2020 Label* tlabel = branch->true_label; 2021 Label* flabel = branch->false_label; 2022 if (branch->condition == kUnorderedEqual) { 2023 __ j(parity_even, flabel, flabel_distance); 2024 } else if (branch->condition == kUnorderedNotEqual) { 2025 __ j(parity_even, tlabel); 2026 } 2027 __ j(FlagsConditionToCondition(branch->condition), tlabel); 2028 2029 // Add a jump if not falling through to the next block. 2030 if (!branch->fallthru) __ jmp(flabel); 2031} 2032 2033 2034void CodeGenerator::AssembleArchJump(RpoNumber target) { 2035 if (!IsNextInAssemblyOrder(target)) __ jmp(GetLabel(target)); 2036} 2037 2038void CodeGenerator::AssembleArchTrap(Instruction* instr, 2039 FlagsCondition condition) { 2040 class OutOfLineTrap final : public OutOfLineCode { 2041 public: 2042 OutOfLineTrap(CodeGenerator* gen, bool frame_elided, Instruction* instr) 2043 : OutOfLineCode(gen), 2044 frame_elided_(frame_elided), 2045 instr_(instr), 2046 gen_(gen) {} 2047 2048 void Generate() final { 2049 IA32OperandConverter i(gen_, instr_); 2050 2051 Builtins::Name trap_id = 2052 static_cast<Builtins::Name>(i.InputInt32(instr_->InputCount() - 1)); 2053 bool old_has_frame = __ has_frame(); 2054 if (frame_elided_) { 2055 __ set_has_frame(true); 2056 __ EnterFrame(StackFrame::WASM_COMPILED); 2057 } 2058 GenerateCallToTrap(trap_id); 2059 if (frame_elided_) { 2060 __ set_has_frame(old_has_frame); 2061 } 2062 } 2063 2064 private: 2065 void GenerateCallToTrap(Builtins::Name trap_id) { 2066 if (trap_id == Builtins::builtin_count) { 2067 // We cannot test calls to the runtime in cctest/test-run-wasm. 2068 // Therefore we emit a call to C here instead of a call to the runtime. 2069 __ PrepareCallCFunction(0, esi); 2070 __ CallCFunction( 2071 ExternalReference::wasm_call_trap_callback_for_testing(isolate()), 2072 0); 2073 __ LeaveFrame(StackFrame::WASM_COMPILED); 2074 __ Ret(); 2075 } else { 2076 gen_->AssembleSourcePosition(instr_); 2077 __ Call(handle(isolate()->builtins()->builtin(trap_id), isolate()), 2078 RelocInfo::CODE_TARGET); 2079 ReferenceMap* reference_map = 2080 new (gen_->zone()) ReferenceMap(gen_->zone()); 2081 gen_->RecordSafepoint(reference_map, Safepoint::kSimple, 0, 2082 Safepoint::kNoLazyDeopt); 2083 if (FLAG_debug_code) { 2084 __ ud2(); 2085 } 2086 } 2087 } 2088 2089 bool frame_elided_; 2090 Instruction* instr_; 2091 CodeGenerator* gen_; 2092 }; 2093 bool frame_elided = !frame_access_state()->has_frame(); 2094 auto ool = new (zone()) OutOfLineTrap(this, frame_elided, instr); 2095 Label* tlabel = ool->entry(); 2096 Label end; 2097 if (condition == kUnorderedEqual) { 2098 __ j(parity_even, &end); 2099 } else if (condition == kUnorderedNotEqual) { 2100 __ j(parity_even, tlabel); 2101 } 2102 __ j(FlagsConditionToCondition(condition), tlabel); 2103 __ bind(&end); 2104} 2105 2106// Assembles boolean materializations after an instruction. 2107void CodeGenerator::AssembleArchBoolean(Instruction* instr, 2108 FlagsCondition condition) { 2109 IA32OperandConverter i(this, instr); 2110 Label done; 2111 2112 // Materialize a full 32-bit 1 or 0 value. The result register is always the 2113 // last output of the instruction. 2114 Label check; 2115 DCHECK_NE(0u, instr->OutputCount()); 2116 Register reg = i.OutputRegister(instr->OutputCount() - 1); 2117 if (condition == kUnorderedEqual) { 2118 __ j(parity_odd, &check, Label::kNear); 2119 __ Move(reg, Immediate(0)); 2120 __ jmp(&done, Label::kNear); 2121 } else if (condition == kUnorderedNotEqual) { 2122 __ j(parity_odd, &check, Label::kNear); 2123 __ mov(reg, Immediate(1)); 2124 __ jmp(&done, Label::kNear); 2125 } 2126 Condition cc = FlagsConditionToCondition(condition); 2127 2128 __ bind(&check); 2129 if (reg.is_byte_register()) { 2130 // setcc for byte registers (al, bl, cl, dl). 2131 __ setcc(cc, reg); 2132 __ movzx_b(reg, reg); 2133 } else { 2134 // Emit a branch to set a register to either 1 or 0. 2135 Label set; 2136 __ j(cc, &set, Label::kNear); 2137 __ Move(reg, Immediate(0)); 2138 __ jmp(&done, Label::kNear); 2139 __ bind(&set); 2140 __ mov(reg, Immediate(1)); 2141 } 2142 __ bind(&done); 2143} 2144 2145 2146void CodeGenerator::AssembleArchLookupSwitch(Instruction* instr) { 2147 IA32OperandConverter i(this, instr); 2148 Register input = i.InputRegister(0); 2149 for (size_t index = 2; index < instr->InputCount(); index += 2) { 2150 __ cmp(input, Immediate(i.InputInt32(index + 0))); 2151 __ j(equal, GetLabel(i.InputRpo(index + 1))); 2152 } 2153 AssembleArchJump(i.InputRpo(1)); 2154} 2155 2156 2157void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) { 2158 IA32OperandConverter i(this, instr); 2159 Register input = i.InputRegister(0); 2160 size_t const case_count = instr->InputCount() - 2; 2161 Label** cases = zone()->NewArray<Label*>(case_count); 2162 for (size_t index = 0; index < case_count; ++index) { 2163 cases[index] = GetLabel(i.InputRpo(index + 2)); 2164 } 2165 Label* const table = AddJumpTable(cases, case_count); 2166 __ cmp(input, Immediate(case_count)); 2167 __ j(above_equal, GetLabel(i.InputRpo(1))); 2168 __ jmp(Operand::JumpTable(input, times_4, table)); 2169} 2170 2171CodeGenerator::CodeGenResult CodeGenerator::AssembleDeoptimizerCall( 2172 int deoptimization_id, SourcePosition pos) { 2173 DeoptimizeKind deoptimization_kind = GetDeoptimizationKind(deoptimization_id); 2174 DeoptimizeReason deoptimization_reason = 2175 GetDeoptimizationReason(deoptimization_id); 2176 Deoptimizer::BailoutType bailout_type = 2177 deoptimization_kind == DeoptimizeKind::kSoft ? Deoptimizer::SOFT 2178 : Deoptimizer::EAGER; 2179 Address deopt_entry = Deoptimizer::GetDeoptimizationEntry( 2180 isolate(), deoptimization_id, bailout_type); 2181 if (deopt_entry == nullptr) return kTooManyDeoptimizationBailouts; 2182 __ RecordDeoptReason(deoptimization_reason, pos, deoptimization_id); 2183 __ call(deopt_entry, RelocInfo::RUNTIME_ENTRY); 2184 return kSuccess; 2185} 2186 2187 2188// The calling convention for JSFunctions on IA32 passes arguments on the 2189// stack and the JSFunction and context in EDI and ESI, respectively, thus 2190// the steps of the call look as follows: 2191 2192// --{ before the call instruction }-------------------------------------------- 2193// | caller frame | 2194// ^ esp ^ ebp 2195 2196// --{ push arguments and setup ESI, EDI }-------------------------------------- 2197// | args + receiver | caller frame | 2198// ^ esp ^ ebp 2199// [edi = JSFunction, esi = context] 2200 2201// --{ call [edi + kCodeEntryOffset] }------------------------------------------ 2202// | RET | args + receiver | caller frame | 2203// ^ esp ^ ebp 2204 2205// =={ prologue of called function }============================================ 2206// --{ push ebp }--------------------------------------------------------------- 2207// | FP | RET | args + receiver | caller frame | 2208// ^ esp ^ ebp 2209 2210// --{ mov ebp, esp }----------------------------------------------------------- 2211// | FP | RET | args + receiver | caller frame | 2212// ^ ebp,esp 2213 2214// --{ push esi }--------------------------------------------------------------- 2215// | CTX | FP | RET | args + receiver | caller frame | 2216// ^esp ^ ebp 2217 2218// --{ push edi }--------------------------------------------------------------- 2219// | FNC | CTX | FP | RET | args + receiver | caller frame | 2220// ^esp ^ ebp 2221 2222// --{ subi esp, #N }----------------------------------------------------------- 2223// | callee frame | FNC | CTX | FP | RET | args + receiver | caller frame | 2224// ^esp ^ ebp 2225 2226// =={ body of called function }================================================ 2227 2228// =={ epilogue of called function }============================================ 2229// --{ mov esp, ebp }----------------------------------------------------------- 2230// | FP | RET | args + receiver | caller frame | 2231// ^ esp,ebp 2232 2233// --{ pop ebp }----------------------------------------------------------- 2234// | | RET | args + receiver | caller frame | 2235// ^ esp ^ ebp 2236 2237// --{ ret #A+1 }----------------------------------------------------------- 2238// | | caller frame | 2239// ^ esp ^ ebp 2240 2241 2242// Runtime function calls are accomplished by doing a stub call to the 2243// CEntryStub (a real code object). On IA32 passes arguments on the 2244// stack, the number of arguments in EAX, the address of the runtime function 2245// in EBX, and the context in ESI. 2246 2247// --{ before the call instruction }-------------------------------------------- 2248// | caller frame | 2249// ^ esp ^ ebp 2250 2251// --{ push arguments and setup EAX, EBX, and ESI }----------------------------- 2252// | args + receiver | caller frame | 2253// ^ esp ^ ebp 2254// [eax = #args, ebx = runtime function, esi = context] 2255 2256// --{ call #CEntryStub }------------------------------------------------------- 2257// | RET | args + receiver | caller frame | 2258// ^ esp ^ ebp 2259 2260// =={ body of runtime function }=============================================== 2261 2262// --{ runtime returns }-------------------------------------------------------- 2263// | caller frame | 2264// ^ esp ^ ebp 2265 2266// Other custom linkages (e.g. for calling directly into and out of C++) may 2267// need to save callee-saved registers on the stack, which is done in the 2268// function prologue of generated code. 2269 2270// --{ before the call instruction }-------------------------------------------- 2271// | caller frame | 2272// ^ esp ^ ebp 2273 2274// --{ set up arguments in registers on stack }--------------------------------- 2275// | args | caller frame | 2276// ^ esp ^ ebp 2277// [r0 = arg0, r1 = arg1, ...] 2278 2279// --{ call code }-------------------------------------------------------------- 2280// | RET | args | caller frame | 2281// ^ esp ^ ebp 2282 2283// =={ prologue of called function }============================================ 2284// --{ push ebp }--------------------------------------------------------------- 2285// | FP | RET | args | caller frame | 2286// ^ esp ^ ebp 2287 2288// --{ mov ebp, esp }----------------------------------------------------------- 2289// | FP | RET | args | caller frame | 2290// ^ ebp,esp 2291 2292// --{ save registers }--------------------------------------------------------- 2293// | regs | FP | RET | args | caller frame | 2294// ^ esp ^ ebp 2295 2296// --{ subi esp, #N }----------------------------------------------------------- 2297// | callee frame | regs | FP | RET | args | caller frame | 2298// ^esp ^ ebp 2299 2300// =={ body of called function }================================================ 2301 2302// =={ epilogue of called function }============================================ 2303// --{ restore registers }------------------------------------------------------ 2304// | regs | FP | RET | args | caller frame | 2305// ^ esp ^ ebp 2306 2307// --{ mov esp, ebp }----------------------------------------------------------- 2308// | FP | RET | args | caller frame | 2309// ^ esp,ebp 2310 2311// --{ pop ebp }---------------------------------------------------------------- 2312// | RET | args | caller frame | 2313// ^ esp ^ ebp 2314 2315void CodeGenerator::FinishFrame(Frame* frame) { 2316 CallDescriptor* descriptor = linkage()->GetIncomingDescriptor(); 2317 const RegList saves = descriptor->CalleeSavedRegisters(); 2318 if (saves != 0) { // Save callee-saved registers. 2319 DCHECK(!info()->is_osr()); 2320 int pushed = 0; 2321 for (int i = Register::kNumRegisters - 1; i >= 0; i--) { 2322 if (!((1 << i) & saves)) continue; 2323 ++pushed; 2324 } 2325 frame->AllocateSavedCalleeRegisterSlots(pushed); 2326 } 2327} 2328 2329void CodeGenerator::AssembleConstructFrame() { 2330 CallDescriptor* descriptor = linkage()->GetIncomingDescriptor(); 2331 if (frame_access_state()->has_frame()) { 2332 if (descriptor->IsCFunctionCall()) { 2333 __ push(ebp); 2334 __ mov(ebp, esp); 2335 } else if (descriptor->IsJSFunctionCall()) { 2336 __ Prologue(this->info()->GeneratePreagedPrologue()); 2337 if (descriptor->PushArgumentCount()) { 2338 __ push(kJavaScriptCallArgCountRegister); 2339 } 2340 } else { 2341 __ StubPrologue(info()->GetOutputStackFrameType()); 2342 } 2343 } 2344 2345 int shrink_slots = 2346 frame()->GetTotalFrameSlotCount() - descriptor->CalculateFixedFrameSize(); 2347 2348 if (info()->is_osr()) { 2349 // TurboFan OSR-compiled functions cannot be entered directly. 2350 __ Abort(kShouldNotDirectlyEnterOsrFunction); 2351 2352 // Unoptimized code jumps directly to this entrypoint while the unoptimized 2353 // frame is still on the stack. Optimized code uses OSR values directly from 2354 // the unoptimized frame. Thus, all that needs to be done is to allocate the 2355 // remaining stack slots. 2356 if (FLAG_code_comments) __ RecordComment("-- OSR entrypoint --"); 2357 osr_pc_offset_ = __ pc_offset(); 2358 shrink_slots -= OsrHelper(info()).UnoptimizedFrameSlots(); 2359 } 2360 2361 const RegList saves = descriptor->CalleeSavedRegisters(); 2362 if (shrink_slots > 0) { 2363 __ sub(esp, Immediate(shrink_slots * kPointerSize)); 2364 } 2365 2366 if (saves != 0) { // Save callee-saved registers. 2367 DCHECK(!info()->is_osr()); 2368 int pushed = 0; 2369 for (int i = Register::kNumRegisters - 1; i >= 0; i--) { 2370 if (!((1 << i) & saves)) continue; 2371 __ push(Register::from_code(i)); 2372 ++pushed; 2373 } 2374 } 2375} 2376 2377void CodeGenerator::AssembleReturn(InstructionOperand* pop) { 2378 CallDescriptor* descriptor = linkage()->GetIncomingDescriptor(); 2379 2380 const RegList saves = descriptor->CalleeSavedRegisters(); 2381 // Restore registers. 2382 if (saves != 0) { 2383 for (int i = 0; i < Register::kNumRegisters; i++) { 2384 if (!((1 << i) & saves)) continue; 2385 __ pop(Register::from_code(i)); 2386 } 2387 } 2388 2389 // Might need ecx for scratch if pop_size is too big or if there is a variable 2390 // pop count. 2391 DCHECK_EQ(0u, descriptor->CalleeSavedRegisters() & ecx.bit()); 2392 size_t pop_size = descriptor->StackParameterCount() * kPointerSize; 2393 IA32OperandConverter g(this, nullptr); 2394 if (descriptor->IsCFunctionCall()) { 2395 AssembleDeconstructFrame(); 2396 } else if (frame_access_state()->has_frame()) { 2397 // Canonicalize JSFunction return sites for now if they always have the same 2398 // number of return args. 2399 if (pop->IsImmediate() && g.ToConstant(pop).ToInt32() == 0) { 2400 if (return_label_.is_bound()) { 2401 __ jmp(&return_label_); 2402 return; 2403 } else { 2404 __ bind(&return_label_); 2405 AssembleDeconstructFrame(); 2406 } 2407 } else { 2408 AssembleDeconstructFrame(); 2409 } 2410 } 2411 DCHECK_EQ(0u, descriptor->CalleeSavedRegisters() & edx.bit()); 2412 DCHECK_EQ(0u, descriptor->CalleeSavedRegisters() & ecx.bit()); 2413 if (pop->IsImmediate()) { 2414 DCHECK_EQ(Constant::kInt32, g.ToConstant(pop).type()); 2415 pop_size += g.ToConstant(pop).ToInt32() * kPointerSize; 2416 __ Ret(static_cast<int>(pop_size), ecx); 2417 } else { 2418 Register pop_reg = g.ToRegister(pop); 2419 Register scratch_reg = pop_reg.is(ecx) ? edx : ecx; 2420 __ pop(scratch_reg); 2421 __ lea(esp, Operand(esp, pop_reg, times_4, static_cast<int>(pop_size))); 2422 __ jmp(scratch_reg); 2423 } 2424} 2425 2426 2427void CodeGenerator::AssembleMove(InstructionOperand* source, 2428 InstructionOperand* destination) { 2429 IA32OperandConverter g(this, nullptr); 2430 // Dispatch on the source and destination operand kinds. Not all 2431 // combinations are possible. 2432 if (source->IsRegister()) { 2433 DCHECK(destination->IsRegister() || destination->IsStackSlot()); 2434 Register src = g.ToRegister(source); 2435 Operand dst = g.ToOperand(destination); 2436 __ mov(dst, src); 2437 } else if (source->IsStackSlot()) { 2438 DCHECK(destination->IsRegister() || destination->IsStackSlot()); 2439 Operand src = g.ToOperand(source); 2440 if (destination->IsRegister()) { 2441 Register dst = g.ToRegister(destination); 2442 __ mov(dst, src); 2443 } else { 2444 Operand dst = g.ToOperand(destination); 2445 __ push(src); 2446 __ pop(dst); 2447 } 2448 } else if (source->IsConstant()) { 2449 Constant src_constant = g.ToConstant(source); 2450 if (src_constant.type() == Constant::kHeapObject) { 2451 Handle<HeapObject> src = src_constant.ToHeapObject(); 2452 if (destination->IsRegister()) { 2453 Register dst = g.ToRegister(destination); 2454 __ LoadHeapObject(dst, src); 2455 } else { 2456 DCHECK(destination->IsStackSlot()); 2457 Operand dst = g.ToOperand(destination); 2458 AllowDeferredHandleDereference embedding_raw_address; 2459 if (isolate()->heap()->InNewSpace(*src)) { 2460 __ PushHeapObject(src); 2461 __ pop(dst); 2462 } else { 2463 __ mov(dst, src); 2464 } 2465 } 2466 } else if (destination->IsRegister()) { 2467 Register dst = g.ToRegister(destination); 2468 __ Move(dst, g.ToImmediate(source)); 2469 } else if (destination->IsStackSlot()) { 2470 Operand dst = g.ToOperand(destination); 2471 __ Move(dst, g.ToImmediate(source)); 2472 } else if (src_constant.type() == Constant::kFloat32) { 2473 // TODO(turbofan): Can we do better here? 2474 uint32_t src = src_constant.ToFloat32AsInt(); 2475 if (destination->IsFPRegister()) { 2476 XMMRegister dst = g.ToDoubleRegister(destination); 2477 __ Move(dst, src); 2478 } else { 2479 DCHECK(destination->IsFPStackSlot()); 2480 Operand dst = g.ToOperand(destination); 2481 __ Move(dst, Immediate(src)); 2482 } 2483 } else { 2484 DCHECK_EQ(Constant::kFloat64, src_constant.type()); 2485 uint64_t src = src_constant.ToFloat64AsInt(); 2486 uint32_t lower = static_cast<uint32_t>(src); 2487 uint32_t upper = static_cast<uint32_t>(src >> 32); 2488 if (destination->IsFPRegister()) { 2489 XMMRegister dst = g.ToDoubleRegister(destination); 2490 __ Move(dst, src); 2491 } else { 2492 DCHECK(destination->IsFPStackSlot()); 2493 Operand dst0 = g.ToOperand(destination); 2494 Operand dst1 = g.HighOperand(destination); 2495 __ Move(dst0, Immediate(lower)); 2496 __ Move(dst1, Immediate(upper)); 2497 } 2498 } 2499 } else if (source->IsFPRegister()) { 2500 XMMRegister src = g.ToDoubleRegister(source); 2501 if (destination->IsFPRegister()) { 2502 XMMRegister dst = g.ToDoubleRegister(destination); 2503 __ movaps(dst, src); 2504 } else { 2505 DCHECK(destination->IsFPStackSlot()); 2506 Operand dst = g.ToOperand(destination); 2507 MachineRepresentation rep = 2508 LocationOperand::cast(source)->representation(); 2509 if (rep == MachineRepresentation::kFloat64) { 2510 __ movsd(dst, src); 2511 } else if (rep == MachineRepresentation::kFloat32) { 2512 __ movss(dst, src); 2513 } else { 2514 DCHECK_EQ(MachineRepresentation::kSimd128, rep); 2515 __ movups(dst, src); 2516 } 2517 } 2518 } else if (source->IsFPStackSlot()) { 2519 DCHECK(destination->IsFPRegister() || destination->IsFPStackSlot()); 2520 Operand src = g.ToOperand(source); 2521 MachineRepresentation rep = LocationOperand::cast(source)->representation(); 2522 if (destination->IsFPRegister()) { 2523 XMMRegister dst = g.ToDoubleRegister(destination); 2524 if (rep == MachineRepresentation::kFloat64) { 2525 __ movsd(dst, src); 2526 } else if (rep == MachineRepresentation::kFloat32) { 2527 __ movss(dst, src); 2528 } else { 2529 DCHECK_EQ(MachineRepresentation::kSimd128, rep); 2530 __ movups(dst, src); 2531 } 2532 } else { 2533 Operand dst = g.ToOperand(destination); 2534 if (rep == MachineRepresentation::kFloat64) { 2535 __ movsd(kScratchDoubleReg, src); 2536 __ movsd(dst, kScratchDoubleReg); 2537 } else if (rep == MachineRepresentation::kFloat32) { 2538 __ movss(kScratchDoubleReg, src); 2539 __ movss(dst, kScratchDoubleReg); 2540 } else { 2541 DCHECK_EQ(MachineRepresentation::kSimd128, rep); 2542 __ movups(kScratchDoubleReg, src); 2543 __ movups(dst, kScratchDoubleReg); 2544 } 2545 } 2546 } else { 2547 UNREACHABLE(); 2548 } 2549} 2550 2551 2552void CodeGenerator::AssembleSwap(InstructionOperand* source, 2553 InstructionOperand* destination) { 2554 IA32OperandConverter g(this, nullptr); 2555 // Dispatch on the source and destination operand kinds. Not all 2556 // combinations are possible. 2557 if (source->IsRegister() && destination->IsRegister()) { 2558 // Register-register. 2559 Register src = g.ToRegister(source); 2560 Register dst = g.ToRegister(destination); 2561 __ push(src); 2562 __ mov(src, dst); 2563 __ pop(dst); 2564 } else if (source->IsRegister() && destination->IsStackSlot()) { 2565 // Register-memory. 2566 Register src = g.ToRegister(source); 2567 __ push(src); 2568 frame_access_state()->IncreaseSPDelta(1); 2569 Operand dst = g.ToOperand(destination); 2570 __ mov(src, dst); 2571 frame_access_state()->IncreaseSPDelta(-1); 2572 dst = g.ToOperand(destination); 2573 __ pop(dst); 2574 } else if (source->IsStackSlot() && destination->IsStackSlot()) { 2575 // Memory-memory. 2576 Operand dst1 = g.ToOperand(destination); 2577 __ push(dst1); 2578 frame_access_state()->IncreaseSPDelta(1); 2579 Operand src1 = g.ToOperand(source); 2580 __ push(src1); 2581 Operand dst2 = g.ToOperand(destination); 2582 __ pop(dst2); 2583 frame_access_state()->IncreaseSPDelta(-1); 2584 Operand src2 = g.ToOperand(source); 2585 __ pop(src2); 2586 } else if (source->IsFPRegister() && destination->IsFPRegister()) { 2587 // XMM register-register swap. 2588 XMMRegister src = g.ToDoubleRegister(source); 2589 XMMRegister dst = g.ToDoubleRegister(destination); 2590 __ movaps(kScratchDoubleReg, src); 2591 __ movaps(src, dst); 2592 __ movaps(dst, kScratchDoubleReg); 2593 } else if (source->IsFPRegister() && destination->IsFPStackSlot()) { 2594 // XMM register-memory swap. 2595 XMMRegister reg = g.ToDoubleRegister(source); 2596 Operand other = g.ToOperand(destination); 2597 MachineRepresentation rep = LocationOperand::cast(source)->representation(); 2598 if (rep == MachineRepresentation::kFloat64) { 2599 __ movsd(kScratchDoubleReg, other); 2600 __ movsd(other, reg); 2601 __ movaps(reg, kScratchDoubleReg); 2602 } else if (rep == MachineRepresentation::kFloat32) { 2603 __ movss(kScratchDoubleReg, other); 2604 __ movss(other, reg); 2605 __ movaps(reg, kScratchDoubleReg); 2606 } else { 2607 DCHECK_EQ(MachineRepresentation::kSimd128, rep); 2608 __ movups(kScratchDoubleReg, other); 2609 __ movups(other, reg); 2610 __ movups(reg, kScratchDoubleReg); 2611 } 2612 } else if (source->IsFPStackSlot() && destination->IsFPStackSlot()) { 2613 // Double-width memory-to-memory. 2614 Operand src0 = g.ToOperand(source); 2615 Operand dst0 = g.ToOperand(destination); 2616 MachineRepresentation rep = LocationOperand::cast(source)->representation(); 2617 if (rep == MachineRepresentation::kFloat64) { 2618 Operand src1 = g.HighOperand(source); 2619 Operand dst1 = g.HighOperand(destination); 2620 __ movsd(kScratchDoubleReg, dst0); // Save dst in scratch register. 2621 __ push(src0); // Then use stack to copy src to destination. 2622 __ pop(dst0); 2623 __ push(src1); 2624 __ pop(dst1); 2625 __ movsd(src0, kScratchDoubleReg); 2626 } else if (rep == MachineRepresentation::kFloat32) { 2627 __ movss(kScratchDoubleReg, dst0); // Save dst in scratch register. 2628 __ push(src0); // Then use stack to copy src to destination. 2629 __ pop(dst0); 2630 __ movss(src0, kScratchDoubleReg); 2631 } else { 2632 DCHECK_EQ(MachineRepresentation::kSimd128, rep); 2633 // Use the XOR trick to swap without a temporary. 2634 __ movups(kScratchDoubleReg, src0); 2635 __ xorps(kScratchDoubleReg, dst0); // scratch contains src ^ dst. 2636 __ movups(src0, kScratchDoubleReg); 2637 __ xorps(kScratchDoubleReg, dst0); // scratch contains src. 2638 __ movups(dst0, kScratchDoubleReg); 2639 __ xorps(kScratchDoubleReg, src0); // scratch contains dst. 2640 __ movups(src0, kScratchDoubleReg); 2641 } 2642 } else { 2643 // No other combinations are possible. 2644 UNREACHABLE(); 2645 } 2646} 2647 2648 2649void CodeGenerator::AssembleJumpTable(Label** targets, size_t target_count) { 2650 for (size_t index = 0; index < target_count; ++index) { 2651 __ dd(targets[index]); 2652 } 2653} 2654 2655 2656void CodeGenerator::EnsureSpaceForLazyDeopt() { 2657 if (!info()->ShouldEnsureSpaceForLazyDeopt()) { 2658 return; 2659 } 2660 2661 int space_needed = Deoptimizer::patch_size(); 2662 // Ensure that we have enough space after the previous lazy-bailout 2663 // instruction for patching the code here. 2664 int current_pc = masm()->pc_offset(); 2665 if (current_pc < last_lazy_deopt_pc_ + space_needed) { 2666 int padding_size = last_lazy_deopt_pc_ + space_needed - current_pc; 2667 __ Nop(padding_size); 2668 } 2669} 2670 2671#undef __ 2672 2673} // namespace compiler 2674} // namespace internal 2675} // namespace v8 2676