1 // Copyright 2015 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-assembler.h" 6 7 #include <ostream> 8 9 #include "src/code-factory.h" 10 #include "src/compiler/graph.h" 11 #include "src/compiler/instruction-selector.h" 12 #include "src/compiler/linkage.h" 13 #include "src/compiler/node-matchers.h" 14 #include "src/compiler/pipeline.h" 15 #include "src/compiler/raw-machine-assembler.h" 16 #include "src/compiler/schedule.h" 17 #include "src/frames.h" 18 #include "src/interface-descriptors.h" 19 #include "src/interpreter/bytecodes.h" 20 #include "src/lsan.h" 21 #include "src/machine-type.h" 22 #include "src/macro-assembler.h" 23 #include "src/objects-inl.h" 24 #include "src/utils.h" 25 #include "src/zone/zone.h" 26 27 namespace v8 { 28 namespace internal { 29 30 constexpr MachineType MachineTypeOf<Smi>::value; 31 constexpr MachineType MachineTypeOf<Object>::value; 32 33 namespace compiler { 34 35 static_assert(std::is_convertible<TNode<Number>, TNode<Object>>::value, 36 "test subtyping"); 37 static_assert(std::is_convertible<TNode<UnionT<Smi, HeapNumber>>, 38 TNode<UnionT<Smi, HeapObject>>>::value, 39 "test subtyping"); 40 static_assert( 41 !std::is_convertible<TNode<UnionT<Smi, HeapObject>>, TNode<Number>>::value, 42 "test subtyping"); 43 44 CodeAssemblerState::CodeAssemblerState( 45 Isolate* isolate, Zone* zone, const CallInterfaceDescriptor& descriptor, 46 Code::Kind kind, const char* name, PoisoningMitigationLevel poisoning_level, 47 uint32_t stub_key, int32_t builtin_index) 48 // TODO(rmcilroy): Should we use Linkage::GetBytecodeDispatchDescriptor for 49 // bytecode handlers? 50 : CodeAssemblerState( 51 isolate, zone, 52 Linkage::GetStubCallDescriptor( 53 zone, descriptor, descriptor.GetStackParameterCount(), 54 CallDescriptor::kNoFlags, Operator::kNoProperties), 55 kind, name, poisoning_level, stub_key, builtin_index) {} 56 57 CodeAssemblerState::CodeAssemblerState(Isolate* isolate, Zone* zone, 58 int parameter_count, Code::Kind kind, 59 const char* name, 60 PoisoningMitigationLevel poisoning_level, 61 int32_t builtin_index) 62 : CodeAssemblerState( 63 isolate, zone, 64 Linkage::GetJSCallDescriptor(zone, false, parameter_count, 65 kind == Code::BUILTIN 66 ? CallDescriptor::kPushArgumentCount 67 : CallDescriptor::kNoFlags), 68 kind, name, poisoning_level, 0, builtin_index) {} 69 70 CodeAssemblerState::CodeAssemblerState(Isolate* isolate, Zone* zone, 71 CallDescriptor* call_descriptor, 72 Code::Kind kind, const char* name, 73 PoisoningMitigationLevel poisoning_level, 74 uint32_t stub_key, int32_t builtin_index) 75 : raw_assembler_(new RawMachineAssembler( 76 isolate, new (zone) Graph(zone), call_descriptor, 77 MachineType::PointerRepresentation(), 78 InstructionSelector::SupportedMachineOperatorFlags(), 79 InstructionSelector::AlignmentRequirements(), poisoning_level)), 80 kind_(kind), 81 name_(name), 82 stub_key_(stub_key), 83 builtin_index_(builtin_index), 84 code_generated_(false), 85 variables_(zone) {} 86 87 CodeAssemblerState::~CodeAssemblerState() {} 88 89 int CodeAssemblerState::parameter_count() const { 90 return static_cast<int>(raw_assembler_->call_descriptor()->ParameterCount()); 91 } 92 93 CodeAssembler::~CodeAssembler() {} 94 95 #if DEBUG 96 void CodeAssemblerState::PrintCurrentBlock(std::ostream& os) { 97 raw_assembler_->PrintCurrentBlock(os); 98 } 99 100 bool CodeAssemblerState::InsideBlock() { return raw_assembler_->InsideBlock(); } 101 #endif 102 103 void CodeAssemblerState::SetInitialDebugInformation(const char* msg, 104 const char* file, 105 int line) { 106 #if DEBUG 107 AssemblerDebugInfo debug_info = {msg, file, line}; 108 raw_assembler_->SetInitialDebugInformation(debug_info); 109 #endif // DEBUG 110 } 111 112 class BreakOnNodeDecorator final : public GraphDecorator { 113 public: 114 explicit BreakOnNodeDecorator(NodeId node_id) : node_id_(node_id) {} 115 116 void Decorate(Node* node) final { 117 if (node->id() == node_id_) { 118 base::OS::DebugBreak(); 119 } 120 } 121 122 private: 123 NodeId node_id_; 124 }; 125 126 void CodeAssembler::BreakOnNode(int node_id) { 127 Graph* graph = raw_assembler()->graph(); 128 Zone* zone = graph->zone(); 129 GraphDecorator* decorator = 130 new (zone) BreakOnNodeDecorator(static_cast<NodeId>(node_id)); 131 graph->AddDecorator(decorator); 132 } 133 134 void CodeAssembler::RegisterCallGenerationCallbacks( 135 const CodeAssemblerCallback& call_prologue, 136 const CodeAssemblerCallback& call_epilogue) { 137 // The callback can be registered only once. 138 DCHECK(!state_->call_prologue_); 139 DCHECK(!state_->call_epilogue_); 140 state_->call_prologue_ = call_prologue; 141 state_->call_epilogue_ = call_epilogue; 142 } 143 144 void CodeAssembler::UnregisterCallGenerationCallbacks() { 145 state_->call_prologue_ = nullptr; 146 state_->call_epilogue_ = nullptr; 147 } 148 149 void CodeAssembler::CallPrologue() { 150 if (state_->call_prologue_) { 151 state_->call_prologue_(); 152 } 153 } 154 155 void CodeAssembler::CallEpilogue() { 156 if (state_->call_epilogue_) { 157 state_->call_epilogue_(); 158 } 159 } 160 161 bool CodeAssembler::Word32ShiftIsSafe() const { 162 return raw_assembler()->machine()->Word32ShiftIsSafe(); 163 } 164 165 PoisoningMitigationLevel CodeAssembler::poisoning_level() const { 166 return raw_assembler()->poisoning_level(); 167 } 168 169 // static 170 Handle<Code> CodeAssembler::GenerateCode(CodeAssemblerState* state, 171 const AssemblerOptions& options) { 172 DCHECK(!state->code_generated_); 173 174 RawMachineAssembler* rasm = state->raw_assembler_.get(); 175 Schedule* schedule = rasm->Export(); 176 177 JumpOptimizationInfo jump_opt; 178 bool should_optimize_jumps = 179 rasm->isolate()->serializer_enabled() && FLAG_turbo_rewrite_far_jumps; 180 181 Handle<Code> code = 182 Pipeline::GenerateCodeForCodeStub( 183 rasm->isolate(), rasm->call_descriptor(), rasm->graph(), schedule, 184 state->kind_, state->name_, state->stub_key_, state->builtin_index_, 185 should_optimize_jumps ? &jump_opt : nullptr, rasm->poisoning_level(), 186 options) 187 .ToHandleChecked(); 188 189 if (jump_opt.is_optimizable()) { 190 jump_opt.set_optimizing(); 191 192 // Regenerate machine code 193 code = 194 Pipeline::GenerateCodeForCodeStub( 195 rasm->isolate(), rasm->call_descriptor(), rasm->graph(), schedule, 196 state->kind_, state->name_, state->stub_key_, state->builtin_index_, 197 &jump_opt, rasm->poisoning_level(), options) 198 .ToHandleChecked(); 199 } 200 201 state->code_generated_ = true; 202 return code; 203 } 204 205 bool CodeAssembler::Is64() const { return raw_assembler()->machine()->Is64(); } 206 207 bool CodeAssembler::IsFloat64RoundUpSupported() const { 208 return raw_assembler()->machine()->Float64RoundUp().IsSupported(); 209 } 210 211 bool CodeAssembler::IsFloat64RoundDownSupported() const { 212 return raw_assembler()->machine()->Float64RoundDown().IsSupported(); 213 } 214 215 bool CodeAssembler::IsFloat64RoundTiesEvenSupported() const { 216 return raw_assembler()->machine()->Float64RoundTiesEven().IsSupported(); 217 } 218 219 bool CodeAssembler::IsFloat64RoundTruncateSupported() const { 220 return raw_assembler()->machine()->Float64RoundTruncate().IsSupported(); 221 } 222 223 bool CodeAssembler::IsInt32AbsWithOverflowSupported() const { 224 return raw_assembler()->machine()->Int32AbsWithOverflow().IsSupported(); 225 } 226 227 bool CodeAssembler::IsInt64AbsWithOverflowSupported() const { 228 return raw_assembler()->machine()->Int64AbsWithOverflow().IsSupported(); 229 } 230 231 bool CodeAssembler::IsIntPtrAbsWithOverflowSupported() const { 232 return Is64() ? IsInt64AbsWithOverflowSupported() 233 : IsInt32AbsWithOverflowSupported(); 234 } 235 236 #ifdef DEBUG 237 void CodeAssembler::GenerateCheckMaybeObjectIsObject(Node* node, 238 const char* location) { 239 Label ok(this); 240 GotoIf(WordNotEqual(WordAnd(BitcastMaybeObjectToWord(node), 241 IntPtrConstant(kHeapObjectTagMask)), 242 IntPtrConstant(kWeakHeapObjectTag)), 243 &ok); 244 Node* message_node = StringConstant(location); 245 DebugAbort(message_node); 246 Unreachable(); 247 Bind(&ok); 248 } 249 #endif 250 251 TNode<Int32T> CodeAssembler::Int32Constant(int32_t value) { 252 return UncheckedCast<Int32T>(raw_assembler()->Int32Constant(value)); 253 } 254 255 TNode<Int64T> CodeAssembler::Int64Constant(int64_t value) { 256 return UncheckedCast<Int64T>(raw_assembler()->Int64Constant(value)); 257 } 258 259 TNode<IntPtrT> CodeAssembler::IntPtrConstant(intptr_t value) { 260 return UncheckedCast<IntPtrT>(raw_assembler()->IntPtrConstant(value)); 261 } 262 263 TNode<Number> CodeAssembler::NumberConstant(double value) { 264 int smi_value; 265 if (DoubleToSmiInteger(value, &smi_value)) { 266 return UncheckedCast<Number>(SmiConstant(smi_value)); 267 } else { 268 // We allocate the heap number constant eagerly at this point instead of 269 // deferring allocation to code generation 270 // (see AllocateAndInstallRequestedHeapObjects) since that makes it easier 271 // to generate constant lookups for embedded builtins. 272 return UncheckedCast<Number>( 273 HeapConstant(isolate()->factory()->NewHeapNumber(value, TENURED))); 274 } 275 } 276 277 TNode<Smi> CodeAssembler::SmiConstant(Smi* value) { 278 return UncheckedCast<Smi>( 279 BitcastWordToTaggedSigned(IntPtrConstant(bit_cast<intptr_t>(value)))); 280 } 281 282 TNode<Smi> CodeAssembler::SmiConstant(int value) { 283 return SmiConstant(Smi::FromInt(value)); 284 } 285 286 TNode<HeapObject> CodeAssembler::UntypedHeapConstant( 287 Handle<HeapObject> object) { 288 return UncheckedCast<HeapObject>(raw_assembler()->HeapConstant(object)); 289 } 290 291 TNode<String> CodeAssembler::StringConstant(const char* str) { 292 Handle<String> internalized_string = 293 factory()->InternalizeOneByteString(OneByteVector(str)); 294 return UncheckedCast<String>(HeapConstant(internalized_string)); 295 } 296 297 TNode<Oddball> CodeAssembler::BooleanConstant(bool value) { 298 return UncheckedCast<Oddball>(raw_assembler()->BooleanConstant(value)); 299 } 300 301 TNode<ExternalReference> CodeAssembler::ExternalConstant( 302 ExternalReference address) { 303 return UncheckedCast<ExternalReference>( 304 raw_assembler()->ExternalConstant(address)); 305 } 306 307 TNode<Float64T> CodeAssembler::Float64Constant(double value) { 308 return UncheckedCast<Float64T>(raw_assembler()->Float64Constant(value)); 309 } 310 311 TNode<HeapNumber> CodeAssembler::NaNConstant() { 312 return UncheckedCast<HeapNumber>(LoadRoot(Heap::kNanValueRootIndex)); 313 } 314 315 bool CodeAssembler::ToInt32Constant(Node* node, int32_t& out_value) { 316 { 317 Int64Matcher m(node); 318 if (m.HasValue() && m.IsInRange(std::numeric_limits<int32_t>::min(), 319 std::numeric_limits<int32_t>::max())) { 320 out_value = static_cast<int32_t>(m.Value()); 321 return true; 322 } 323 } 324 325 { 326 Int32Matcher m(node); 327 if (m.HasValue()) { 328 out_value = m.Value(); 329 return true; 330 } 331 } 332 333 return false; 334 } 335 336 bool CodeAssembler::ToInt64Constant(Node* node, int64_t& out_value) { 337 Int64Matcher m(node); 338 if (m.HasValue()) out_value = m.Value(); 339 return m.HasValue(); 340 } 341 342 bool CodeAssembler::ToSmiConstant(Node* node, Smi*& out_value) { 343 if (node->opcode() == IrOpcode::kBitcastWordToTaggedSigned) { 344 node = node->InputAt(0); 345 } 346 IntPtrMatcher m(node); 347 if (m.HasValue()) { 348 intptr_t value = m.Value(); 349 // Make sure that the value is actually a smi 350 CHECK_EQ(0, value & ((static_cast<intptr_t>(1) << kSmiShiftSize) - 1)); 351 out_value = Smi::cast(bit_cast<Object*>(value)); 352 return true; 353 } 354 return false; 355 } 356 357 bool CodeAssembler::ToIntPtrConstant(Node* node, intptr_t& out_value) { 358 if (node->opcode() == IrOpcode::kBitcastWordToTaggedSigned || 359 node->opcode() == IrOpcode::kBitcastWordToTagged) { 360 node = node->InputAt(0); 361 } 362 IntPtrMatcher m(node); 363 if (m.HasValue()) out_value = m.Value(); 364 return m.HasValue(); 365 } 366 367 bool CodeAssembler::IsUndefinedConstant(TNode<Object> node) { 368 compiler::HeapObjectMatcher m(node); 369 return m.Is(isolate()->factory()->undefined_value()); 370 } 371 372 bool CodeAssembler::IsNullConstant(TNode<Object> node) { 373 compiler::HeapObjectMatcher m(node); 374 return m.Is(isolate()->factory()->null_value()); 375 } 376 377 Node* CodeAssembler::Parameter(int index) { 378 if (index == kTargetParameterIndex) return raw_assembler()->TargetParameter(); 379 return raw_assembler()->Parameter(index); 380 } 381 382 bool CodeAssembler::IsJSFunctionCall() const { 383 auto call_descriptor = raw_assembler()->call_descriptor(); 384 return call_descriptor->IsJSFunctionCall(); 385 } 386 387 TNode<Context> CodeAssembler::GetJSContextParameter() { 388 auto call_descriptor = raw_assembler()->call_descriptor(); 389 DCHECK(call_descriptor->IsJSFunctionCall()); 390 return CAST(Parameter(Linkage::GetJSCallContextParamIndex( 391 static_cast<int>(call_descriptor->JSParameterCount())))); 392 } 393 394 void CodeAssembler::Return(SloppyTNode<Object> value) { 395 return raw_assembler()->Return(value); 396 } 397 398 void CodeAssembler::Return(SloppyTNode<Object> value1, 399 SloppyTNode<Object> value2) { 400 return raw_assembler()->Return(value1, value2); 401 } 402 403 void CodeAssembler::Return(SloppyTNode<Object> value1, 404 SloppyTNode<Object> value2, 405 SloppyTNode<Object> value3) { 406 return raw_assembler()->Return(value1, value2, value3); 407 } 408 409 void CodeAssembler::PopAndReturn(Node* pop, Node* value) { 410 return raw_assembler()->PopAndReturn(pop, value); 411 } 412 413 void CodeAssembler::ReturnIf(Node* condition, Node* value) { 414 Label if_return(this), if_continue(this); 415 Branch(condition, &if_return, &if_continue); 416 Bind(&if_return); 417 Return(value); 418 Bind(&if_continue); 419 } 420 421 void CodeAssembler::ReturnRaw(Node* value) { 422 return raw_assembler()->Return(value); 423 } 424 425 void CodeAssembler::DebugAbort(Node* message) { 426 raw_assembler()->DebugAbort(message); 427 } 428 429 void CodeAssembler::DebugBreak() { raw_assembler()->DebugBreak(); } 430 431 void CodeAssembler::Unreachable() { 432 DebugBreak(); 433 raw_assembler()->Unreachable(); 434 } 435 436 void CodeAssembler::Comment(const char* format, ...) { 437 if (!FLAG_code_comments) return; 438 char buffer[4 * KB]; 439 StringBuilder builder(buffer, arraysize(buffer)); 440 va_list arguments; 441 va_start(arguments, format); 442 builder.AddFormattedList(format, arguments); 443 va_end(arguments); 444 445 // Copy the string before recording it in the assembler to avoid 446 // issues when the stack allocated buffer goes out of scope. 447 const int prefix_len = 2; 448 int length = builder.position() + 1; 449 char* copy = reinterpret_cast<char*>(malloc(length + prefix_len)); 450 LSAN_IGNORE_OBJECT(copy); 451 MemCopy(copy + prefix_len, builder.Finalize(), length); 452 copy[0] = ';'; 453 copy[1] = ' '; 454 raw_assembler()->Comment(copy); 455 } 456 457 void CodeAssembler::Bind(Label* label) { return label->Bind(); } 458 459 #if DEBUG 460 void CodeAssembler::Bind(Label* label, AssemblerDebugInfo debug_info) { 461 return label->Bind(debug_info); 462 } 463 #endif // DEBUG 464 465 Node* CodeAssembler::LoadFramePointer() { 466 return raw_assembler()->LoadFramePointer(); 467 } 468 469 Node* CodeAssembler::LoadParentFramePointer() { 470 return raw_assembler()->LoadParentFramePointer(); 471 } 472 473 Node* CodeAssembler::LoadStackPointer() { 474 return raw_assembler()->LoadStackPointer(); 475 } 476 477 TNode<Object> CodeAssembler::TaggedPoisonOnSpeculation( 478 SloppyTNode<Object> value) { 479 return UncheckedCast<Object>( 480 raw_assembler()->TaggedPoisonOnSpeculation(value)); 481 } 482 483 TNode<WordT> CodeAssembler::WordPoisonOnSpeculation(SloppyTNode<WordT> value) { 484 return UncheckedCast<WordT>(raw_assembler()->WordPoisonOnSpeculation(value)); 485 } 486 487 #define DEFINE_CODE_ASSEMBLER_BINARY_OP(name, ResType, Arg1Type, Arg2Type) \ 488 TNode<ResType> CodeAssembler::name(SloppyTNode<Arg1Type> a, \ 489 SloppyTNode<Arg2Type> b) { \ 490 return UncheckedCast<ResType>(raw_assembler()->name(a, b)); \ 491 } 492 CODE_ASSEMBLER_BINARY_OP_LIST(DEFINE_CODE_ASSEMBLER_BINARY_OP) 493 #undef DEFINE_CODE_ASSEMBLER_BINARY_OP 494 495 TNode<WordT> CodeAssembler::IntPtrAdd(SloppyTNode<WordT> left, 496 SloppyTNode<WordT> right) { 497 intptr_t left_constant; 498 bool is_left_constant = ToIntPtrConstant(left, left_constant); 499 intptr_t right_constant; 500 bool is_right_constant = ToIntPtrConstant(right, right_constant); 501 if (is_left_constant) { 502 if (is_right_constant) { 503 return IntPtrConstant(left_constant + right_constant); 504 } 505 if (left_constant == 0) { 506 return right; 507 } 508 } else if (is_right_constant) { 509 if (right_constant == 0) { 510 return left; 511 } 512 } 513 return UncheckedCast<WordT>(raw_assembler()->IntPtrAdd(left, right)); 514 } 515 516 TNode<WordT> CodeAssembler::IntPtrSub(SloppyTNode<WordT> left, 517 SloppyTNode<WordT> right) { 518 intptr_t left_constant; 519 bool is_left_constant = ToIntPtrConstant(left, left_constant); 520 intptr_t right_constant; 521 bool is_right_constant = ToIntPtrConstant(right, right_constant); 522 if (is_left_constant) { 523 if (is_right_constant) { 524 return IntPtrConstant(left_constant - right_constant); 525 } 526 } else if (is_right_constant) { 527 if (right_constant == 0) { 528 return left; 529 } 530 } 531 return UncheckedCast<IntPtrT>(raw_assembler()->IntPtrSub(left, right)); 532 } 533 534 TNode<WordT> CodeAssembler::IntPtrMul(SloppyTNode<WordT> left, 535 SloppyTNode<WordT> right) { 536 intptr_t left_constant; 537 bool is_left_constant = ToIntPtrConstant(left, left_constant); 538 intptr_t right_constant; 539 bool is_right_constant = ToIntPtrConstant(right, right_constant); 540 if (is_left_constant) { 541 if (is_right_constant) { 542 return IntPtrConstant(left_constant * right_constant); 543 } 544 if (base::bits::IsPowerOfTwo(left_constant)) { 545 return WordShl(right, WhichPowerOf2(left_constant)); 546 } 547 } else if (is_right_constant) { 548 if (base::bits::IsPowerOfTwo(right_constant)) { 549 return WordShl(left, WhichPowerOf2(right_constant)); 550 } 551 } 552 return UncheckedCast<IntPtrT>(raw_assembler()->IntPtrMul(left, right)); 553 } 554 555 TNode<WordT> CodeAssembler::WordShl(SloppyTNode<WordT> value, int shift) { 556 return (shift != 0) ? WordShl(value, IntPtrConstant(shift)) : value; 557 } 558 559 TNode<WordT> CodeAssembler::WordShr(SloppyTNode<WordT> value, int shift) { 560 return (shift != 0) ? WordShr(value, IntPtrConstant(shift)) : value; 561 } 562 563 TNode<WordT> CodeAssembler::WordSar(SloppyTNode<WordT> value, int shift) { 564 return (shift != 0) ? WordSar(value, IntPtrConstant(shift)) : value; 565 } 566 567 TNode<Word32T> CodeAssembler::Word32Shr(SloppyTNode<Word32T> value, int shift) { 568 return (shift != 0) ? Word32Shr(value, Int32Constant(shift)) : value; 569 } 570 571 TNode<WordT> CodeAssembler::WordOr(SloppyTNode<WordT> left, 572 SloppyTNode<WordT> right) { 573 intptr_t left_constant; 574 bool is_left_constant = ToIntPtrConstant(left, left_constant); 575 intptr_t right_constant; 576 bool is_right_constant = ToIntPtrConstant(right, right_constant); 577 if (is_left_constant) { 578 if (is_right_constant) { 579 return IntPtrConstant(left_constant | right_constant); 580 } 581 if (left_constant == 0) { 582 return right; 583 } 584 } else if (is_right_constant) { 585 if (right_constant == 0) { 586 return left; 587 } 588 } 589 return UncheckedCast<WordT>(raw_assembler()->WordOr(left, right)); 590 } 591 592 TNode<WordT> CodeAssembler::WordAnd(SloppyTNode<WordT> left, 593 SloppyTNode<WordT> right) { 594 intptr_t left_constant; 595 bool is_left_constant = ToIntPtrConstant(left, left_constant); 596 intptr_t right_constant; 597 bool is_right_constant = ToIntPtrConstant(right, right_constant); 598 if (is_left_constant) { 599 if (is_right_constant) { 600 return IntPtrConstant(left_constant & right_constant); 601 } 602 } 603 return UncheckedCast<WordT>(raw_assembler()->WordAnd(left, right)); 604 } 605 606 TNode<WordT> CodeAssembler::WordXor(SloppyTNode<WordT> left, 607 SloppyTNode<WordT> right) { 608 intptr_t left_constant; 609 bool is_left_constant = ToIntPtrConstant(left, left_constant); 610 intptr_t right_constant; 611 bool is_right_constant = ToIntPtrConstant(right, right_constant); 612 if (is_left_constant) { 613 if (is_right_constant) { 614 return IntPtrConstant(left_constant ^ right_constant); 615 } 616 } 617 return UncheckedCast<WordT>(raw_assembler()->WordXor(left, right)); 618 } 619 620 TNode<WordT> CodeAssembler::WordShl(SloppyTNode<WordT> left, 621 SloppyTNode<IntegralT> right) { 622 intptr_t left_constant; 623 bool is_left_constant = ToIntPtrConstant(left, left_constant); 624 intptr_t right_constant; 625 bool is_right_constant = ToIntPtrConstant(right, right_constant); 626 if (is_left_constant) { 627 if (is_right_constant) { 628 return IntPtrConstant(left_constant << right_constant); 629 } 630 } else if (is_right_constant) { 631 if (right_constant == 0) { 632 return left; 633 } 634 } 635 return UncheckedCast<WordT>(raw_assembler()->WordShl(left, right)); 636 } 637 638 TNode<WordT> CodeAssembler::WordShr(SloppyTNode<WordT> left, 639 SloppyTNode<IntegralT> right) { 640 intptr_t left_constant; 641 bool is_left_constant = ToIntPtrConstant(left, left_constant); 642 intptr_t right_constant; 643 bool is_right_constant = ToIntPtrConstant(right, right_constant); 644 if (is_left_constant) { 645 if (is_right_constant) { 646 return IntPtrConstant(static_cast<uintptr_t>(left_constant) >> 647 right_constant); 648 } 649 } else if (is_right_constant) { 650 if (right_constant == 0) { 651 return left; 652 } 653 } 654 return UncheckedCast<WordT>(raw_assembler()->WordShr(left, right)); 655 } 656 657 TNode<WordT> CodeAssembler::WordSar(SloppyTNode<WordT> left, 658 SloppyTNode<IntegralT> right) { 659 intptr_t left_constant; 660 bool is_left_constant = ToIntPtrConstant(left, left_constant); 661 intptr_t right_constant; 662 bool is_right_constant = ToIntPtrConstant(right, right_constant); 663 if (is_left_constant) { 664 if (is_right_constant) { 665 return IntPtrConstant(left_constant >> right_constant); 666 } 667 } else if (is_right_constant) { 668 if (right_constant == 0) { 669 return left; 670 } 671 } 672 return UncheckedCast<WordT>(raw_assembler()->WordSar(left, right)); 673 } 674 675 TNode<Word32T> CodeAssembler::Word32Or(SloppyTNode<Word32T> left, 676 SloppyTNode<Word32T> right) { 677 int32_t left_constant; 678 bool is_left_constant = ToInt32Constant(left, left_constant); 679 int32_t right_constant; 680 bool is_right_constant = ToInt32Constant(right, right_constant); 681 if (is_left_constant) { 682 if (is_right_constant) { 683 return Int32Constant(left_constant | right_constant); 684 } 685 if (left_constant == 0) { 686 return right; 687 } 688 } else if (is_right_constant) { 689 if (right_constant == 0) { 690 return left; 691 } 692 } 693 return UncheckedCast<Word32T>(raw_assembler()->Word32Or(left, right)); 694 } 695 696 TNode<Word32T> CodeAssembler::Word32And(SloppyTNode<Word32T> left, 697 SloppyTNode<Word32T> right) { 698 int32_t left_constant; 699 bool is_left_constant = ToInt32Constant(left, left_constant); 700 int32_t right_constant; 701 bool is_right_constant = ToInt32Constant(right, right_constant); 702 if (is_left_constant) { 703 if (is_right_constant) { 704 return Int32Constant(left_constant & right_constant); 705 } 706 } 707 return UncheckedCast<Word32T>(raw_assembler()->Word32And(left, right)); 708 } 709 710 TNode<Word32T> CodeAssembler::Word32Xor(SloppyTNode<Word32T> left, 711 SloppyTNode<Word32T> right) { 712 int32_t left_constant; 713 bool is_left_constant = ToInt32Constant(left, left_constant); 714 int32_t right_constant; 715 bool is_right_constant = ToInt32Constant(right, right_constant); 716 if (is_left_constant) { 717 if (is_right_constant) { 718 return Int32Constant(left_constant ^ right_constant); 719 } 720 } 721 return UncheckedCast<Word32T>(raw_assembler()->Word32Xor(left, right)); 722 } 723 724 TNode<Word32T> CodeAssembler::Word32Shl(SloppyTNode<Word32T> left, 725 SloppyTNode<Word32T> right) { 726 int32_t left_constant; 727 bool is_left_constant = ToInt32Constant(left, left_constant); 728 int32_t right_constant; 729 bool is_right_constant = ToInt32Constant(right, right_constant); 730 if (is_left_constant) { 731 if (is_right_constant) { 732 return Int32Constant(left_constant << right_constant); 733 } 734 } else if (is_right_constant) { 735 if (right_constant == 0) { 736 return left; 737 } 738 } 739 return UncheckedCast<Word32T>(raw_assembler()->Word32Shl(left, right)); 740 } 741 742 TNode<Word32T> CodeAssembler::Word32Shr(SloppyTNode<Word32T> left, 743 SloppyTNode<Word32T> right) { 744 int32_t left_constant; 745 bool is_left_constant = ToInt32Constant(left, left_constant); 746 int32_t right_constant; 747 bool is_right_constant = ToInt32Constant(right, right_constant); 748 if (is_left_constant) { 749 if (is_right_constant) { 750 return Int32Constant(static_cast<uint32_t>(left_constant) >> 751 right_constant); 752 } 753 } else if (is_right_constant) { 754 if (right_constant == 0) { 755 return left; 756 } 757 } 758 return UncheckedCast<Word32T>(raw_assembler()->Word32Shr(left, right)); 759 } 760 761 TNode<Word32T> CodeAssembler::Word32Sar(SloppyTNode<Word32T> left, 762 SloppyTNode<Word32T> right) { 763 int32_t left_constant; 764 bool is_left_constant = ToInt32Constant(left, left_constant); 765 int32_t right_constant; 766 bool is_right_constant = ToInt32Constant(right, right_constant); 767 if (is_left_constant) { 768 if (is_right_constant) { 769 return Int32Constant(left_constant >> right_constant); 770 } 771 } else if (is_right_constant) { 772 if (right_constant == 0) { 773 return left; 774 } 775 } 776 return UncheckedCast<Word32T>(raw_assembler()->Word32Sar(left, right)); 777 } 778 779 TNode<Word64T> CodeAssembler::Word64Or(SloppyTNode<Word64T> left, 780 SloppyTNode<Word64T> right) { 781 int64_t left_constant; 782 bool is_left_constant = ToInt64Constant(left, left_constant); 783 int64_t right_constant; 784 bool is_right_constant = ToInt64Constant(right, right_constant); 785 if (is_left_constant) { 786 if (is_right_constant) { 787 return Int64Constant(left_constant | right_constant); 788 } 789 if (left_constant == 0) { 790 return right; 791 } 792 } else if (is_right_constant) { 793 if (right_constant == 0) { 794 return left; 795 } 796 } 797 return UncheckedCast<Word64T>(raw_assembler()->Word64Or(left, right)); 798 } 799 800 TNode<Word64T> CodeAssembler::Word64And(SloppyTNode<Word64T> left, 801 SloppyTNode<Word64T> right) { 802 int64_t left_constant; 803 bool is_left_constant = ToInt64Constant(left, left_constant); 804 int64_t right_constant; 805 bool is_right_constant = ToInt64Constant(right, right_constant); 806 if (is_left_constant) { 807 if (is_right_constant) { 808 return Int64Constant(left_constant & right_constant); 809 } 810 } 811 return UncheckedCast<Word64T>(raw_assembler()->Word64And(left, right)); 812 } 813 814 TNode<Word64T> CodeAssembler::Word64Xor(SloppyTNode<Word64T> left, 815 SloppyTNode<Word64T> right) { 816 int64_t left_constant; 817 bool is_left_constant = ToInt64Constant(left, left_constant); 818 int64_t right_constant; 819 bool is_right_constant = ToInt64Constant(right, right_constant); 820 if (is_left_constant) { 821 if (is_right_constant) { 822 return Int64Constant(left_constant ^ right_constant); 823 } 824 } 825 return UncheckedCast<Word64T>(raw_assembler()->Word64Xor(left, right)); 826 } 827 828 TNode<Word64T> CodeAssembler::Word64Shl(SloppyTNode<Word64T> left, 829 SloppyTNode<Word64T> right) { 830 int64_t left_constant; 831 bool is_left_constant = ToInt64Constant(left, left_constant); 832 int64_t right_constant; 833 bool is_right_constant = ToInt64Constant(right, right_constant); 834 if (is_left_constant) { 835 if (is_right_constant) { 836 return Int64Constant(left_constant << right_constant); 837 } 838 } else if (is_right_constant) { 839 if (right_constant == 0) { 840 return left; 841 } 842 } 843 return UncheckedCast<Word64T>(raw_assembler()->Word64Shl(left, right)); 844 } 845 846 TNode<Word64T> CodeAssembler::Word64Shr(SloppyTNode<Word64T> left, 847 SloppyTNode<Word64T> right) { 848 int64_t left_constant; 849 bool is_left_constant = ToInt64Constant(left, left_constant); 850 int64_t right_constant; 851 bool is_right_constant = ToInt64Constant(right, right_constant); 852 if (is_left_constant) { 853 if (is_right_constant) { 854 return Int64Constant(static_cast<uint64_t>(left_constant) >> 855 right_constant); 856 } 857 } else if (is_right_constant) { 858 if (right_constant == 0) { 859 return left; 860 } 861 } 862 return UncheckedCast<Word64T>(raw_assembler()->Word64Shr(left, right)); 863 } 864 865 TNode<Word64T> CodeAssembler::Word64Sar(SloppyTNode<Word64T> left, 866 SloppyTNode<Word64T> right) { 867 int64_t left_constant; 868 bool is_left_constant = ToInt64Constant(left, left_constant); 869 int64_t right_constant; 870 bool is_right_constant = ToInt64Constant(right, right_constant); 871 if (is_left_constant) { 872 if (is_right_constant) { 873 return Int64Constant(left_constant >> right_constant); 874 } 875 } else if (is_right_constant) { 876 if (right_constant == 0) { 877 return left; 878 } 879 } 880 return UncheckedCast<Word64T>(raw_assembler()->Word64Sar(left, right)); 881 } 882 883 #define CODE_ASSEMBLER_COMPARE(Name, ArgT, VarT, ToConstant, op) \ 884 TNode<BoolT> CodeAssembler::Name(SloppyTNode<ArgT> left, \ 885 SloppyTNode<ArgT> right) { \ 886 VarT lhs, rhs; \ 887 if (ToConstant(left, lhs) && ToConstant(right, rhs)) { \ 888 return BoolConstant(lhs op rhs); \ 889 } \ 890 return UncheckedCast<BoolT>(raw_assembler()->Name(left, right)); \ 891 } 892 893 CODE_ASSEMBLER_COMPARE(IntPtrEqual, WordT, intptr_t, ToIntPtrConstant, ==) 894 CODE_ASSEMBLER_COMPARE(WordEqual, WordT, intptr_t, ToIntPtrConstant, ==) 895 CODE_ASSEMBLER_COMPARE(WordNotEqual, WordT, intptr_t, ToIntPtrConstant, !=) 896 CODE_ASSEMBLER_COMPARE(Word32Equal, Word32T, int32_t, ToInt32Constant, ==) 897 CODE_ASSEMBLER_COMPARE(Word32NotEqual, Word32T, int32_t, ToInt32Constant, !=) 898 CODE_ASSEMBLER_COMPARE(Word64Equal, Word64T, int64_t, ToInt64Constant, ==) 899 CODE_ASSEMBLER_COMPARE(Word64NotEqual, Word64T, int64_t, ToInt64Constant, !=) 900 #undef CODE_ASSEMBLER_COMPARE 901 902 TNode<UintPtrT> CodeAssembler::ChangeUint32ToWord(SloppyTNode<Word32T> value) { 903 if (raw_assembler()->machine()->Is64()) { 904 return UncheckedCast<UintPtrT>( 905 raw_assembler()->ChangeUint32ToUint64(value)); 906 } 907 return ReinterpretCast<UintPtrT>(value); 908 } 909 910 TNode<IntPtrT> CodeAssembler::ChangeInt32ToIntPtr(SloppyTNode<Word32T> value) { 911 if (raw_assembler()->machine()->Is64()) { 912 return ReinterpretCast<IntPtrT>(raw_assembler()->ChangeInt32ToInt64(value)); 913 } 914 return ReinterpretCast<IntPtrT>(value); 915 } 916 917 TNode<UintPtrT> CodeAssembler::ChangeFloat64ToUintPtr( 918 SloppyTNode<Float64T> value) { 919 if (raw_assembler()->machine()->Is64()) { 920 return ReinterpretCast<UintPtrT>( 921 raw_assembler()->ChangeFloat64ToUint64(value)); 922 } 923 return ReinterpretCast<UintPtrT>( 924 raw_assembler()->ChangeFloat64ToUint32(value)); 925 } 926 927 Node* CodeAssembler::RoundIntPtrToFloat64(Node* value) { 928 if (raw_assembler()->machine()->Is64()) { 929 return raw_assembler()->RoundInt64ToFloat64(value); 930 } 931 return raw_assembler()->ChangeInt32ToFloat64(value); 932 } 933 934 #define DEFINE_CODE_ASSEMBLER_UNARY_OP(name, ResType, ArgType) \ 935 TNode<ResType> CodeAssembler::name(SloppyTNode<ArgType> a) { \ 936 return UncheckedCast<ResType>(raw_assembler()->name(a)); \ 937 } 938 CODE_ASSEMBLER_UNARY_OP_LIST(DEFINE_CODE_ASSEMBLER_UNARY_OP) 939 #undef DEFINE_CODE_ASSEMBLER_UNARY_OP 940 941 Node* CodeAssembler::Load(MachineType rep, Node* base, 942 LoadSensitivity needs_poisoning) { 943 return raw_assembler()->Load(rep, base, needs_poisoning); 944 } 945 946 Node* CodeAssembler::Load(MachineType rep, Node* base, Node* offset, 947 LoadSensitivity needs_poisoning) { 948 return raw_assembler()->Load(rep, base, offset, needs_poisoning); 949 } 950 951 Node* CodeAssembler::AtomicLoad(MachineType rep, Node* base, Node* offset) { 952 return raw_assembler()->AtomicLoad(rep, base, offset); 953 } 954 955 TNode<Object> CodeAssembler::LoadRoot(Heap::RootListIndex root_index) { 956 if (isolate()->heap()->RootCanBeTreatedAsConstant(root_index)) { 957 Handle<Object> root = isolate()->heap()->root_handle(root_index); 958 if (root->IsSmi()) { 959 return SmiConstant(Smi::cast(*root)); 960 } else { 961 return HeapConstant(Handle<HeapObject>::cast(root)); 962 } 963 } 964 965 // TODO(jgruber): In theory we could generate better code for this by 966 // letting the macro assembler decide how to load from the roots list. In most 967 // cases, it would boil down to loading from a fixed kRootRegister offset. 968 Node* roots_array_start = 969 ExternalConstant(ExternalReference::roots_array_start(isolate())); 970 return UncheckedCast<Object>(Load(MachineType::AnyTagged(), roots_array_start, 971 IntPtrConstant(root_index * kPointerSize))); 972 } 973 974 Node* CodeAssembler::Store(Node* base, Node* value) { 975 return raw_assembler()->Store(MachineRepresentation::kTagged, base, value, 976 kFullWriteBarrier); 977 } 978 979 Node* CodeAssembler::Store(Node* base, Node* offset, Node* value) { 980 return raw_assembler()->Store(MachineRepresentation::kTagged, base, offset, 981 value, kFullWriteBarrier); 982 } 983 984 Node* CodeAssembler::StoreWithMapWriteBarrier(Node* base, Node* offset, 985 Node* value) { 986 return raw_assembler()->Store(MachineRepresentation::kTagged, base, offset, 987 value, kMapWriteBarrier); 988 } 989 990 Node* CodeAssembler::StoreNoWriteBarrier(MachineRepresentation rep, Node* base, 991 Node* value) { 992 return raw_assembler()->Store(rep, base, value, kNoWriteBarrier); 993 } 994 995 Node* CodeAssembler::StoreNoWriteBarrier(MachineRepresentation rep, Node* base, 996 Node* offset, Node* value) { 997 return raw_assembler()->Store(rep, base, offset, value, kNoWriteBarrier); 998 } 999 1000 Node* CodeAssembler::AtomicStore(MachineRepresentation rep, Node* base, 1001 Node* offset, Node* value) { 1002 return raw_assembler()->AtomicStore(rep, base, offset, value); 1003 } 1004 1005 #define ATOMIC_FUNCTION(name) \ 1006 Node* CodeAssembler::Atomic##name(MachineType type, Node* base, \ 1007 Node* offset, Node* value) { \ 1008 return raw_assembler()->Atomic##name(type, base, offset, value); \ 1009 } 1010 ATOMIC_FUNCTION(Exchange); 1011 ATOMIC_FUNCTION(Add); 1012 ATOMIC_FUNCTION(Sub); 1013 ATOMIC_FUNCTION(And); 1014 ATOMIC_FUNCTION(Or); 1015 ATOMIC_FUNCTION(Xor); 1016 #undef ATOMIC_FUNCTION 1017 1018 Node* CodeAssembler::AtomicCompareExchange(MachineType type, Node* base, 1019 Node* offset, Node* old_value, 1020 Node* new_value) { 1021 return raw_assembler()->AtomicCompareExchange(type, base, offset, old_value, 1022 new_value); 1023 } 1024 1025 Node* CodeAssembler::StoreRoot(Heap::RootListIndex root_index, Node* value) { 1026 DCHECK(Heap::RootCanBeWrittenAfterInitialization(root_index)); 1027 Node* roots_array_start = 1028 ExternalConstant(ExternalReference::roots_array_start(isolate())); 1029 return StoreNoWriteBarrier(MachineRepresentation::kTagged, roots_array_start, 1030 IntPtrConstant(root_index * kPointerSize), value); 1031 } 1032 1033 Node* CodeAssembler::Retain(Node* value) { 1034 return raw_assembler()->Retain(value); 1035 } 1036 1037 Node* CodeAssembler::Projection(int index, Node* value) { 1038 return raw_assembler()->Projection(index, value); 1039 } 1040 1041 void CodeAssembler::GotoIfException(Node* node, Label* if_exception, 1042 Variable* exception_var) { 1043 if (if_exception == nullptr) { 1044 // If no handler is supplied, don't add continuations 1045 return; 1046 } 1047 1048 DCHECK(!node->op()->HasProperty(Operator::kNoThrow)); 1049 1050 Label success(this), exception(this, Label::kDeferred); 1051 success.MergeVariables(); 1052 exception.MergeVariables(); 1053 1054 raw_assembler()->Continuations(node, success.label_, exception.label_); 1055 1056 Bind(&exception); 1057 const Operator* op = raw_assembler()->common()->IfException(); 1058 Node* exception_value = raw_assembler()->AddNode(op, node, node); 1059 if (exception_var != nullptr) { 1060 exception_var->Bind(exception_value); 1061 } 1062 Goto(if_exception); 1063 1064 Bind(&success); 1065 } 1066 1067 namespace { 1068 template <size_t kMaxSize> 1069 class NodeArray { 1070 public: 1071 void Add(Node* node) { 1072 DCHECK_GT(kMaxSize, size()); 1073 *ptr_++ = node; 1074 } 1075 1076 Node* const* data() const { return arr_; } 1077 int size() const { return static_cast<int>(ptr_ - arr_); } 1078 1079 private: 1080 Node* arr_[kMaxSize]; 1081 Node** ptr_ = arr_; 1082 }; 1083 } // namespace 1084 1085 TNode<Object> CodeAssembler::CallRuntimeImpl( 1086 Runtime::FunctionId function, TNode<Object> context, 1087 std::initializer_list<TNode<Object>> args) { 1088 int result_size = Runtime::FunctionForId(function)->result_size; 1089 TNode<Code> centry = 1090 HeapConstant(CodeFactory::RuntimeCEntry(isolate(), result_size)); 1091 return CallRuntimeWithCEntryImpl(function, centry, context, args); 1092 } 1093 1094 TNode<Object> CodeAssembler::CallRuntimeWithCEntryImpl( 1095 Runtime::FunctionId function, TNode<Code> centry, TNode<Object> context, 1096 std::initializer_list<TNode<Object>> args) { 1097 constexpr size_t kMaxNumArgs = 6; 1098 DCHECK_GE(kMaxNumArgs, args.size()); 1099 int argc = static_cast<int>(args.size()); 1100 auto call_descriptor = Linkage::GetRuntimeCallDescriptor( 1101 zone(), function, argc, Operator::kNoProperties, 1102 CallDescriptor::kNoFlags); 1103 1104 Node* ref = ExternalConstant(ExternalReference::Create(function)); 1105 Node* arity = Int32Constant(argc); 1106 1107 NodeArray<kMaxNumArgs + 4> inputs; 1108 inputs.Add(centry); 1109 for (auto arg : args) inputs.Add(arg); 1110 inputs.Add(ref); 1111 inputs.Add(arity); 1112 inputs.Add(context); 1113 1114 CallPrologue(); 1115 Node* return_value = 1116 raw_assembler()->CallN(call_descriptor, inputs.size(), inputs.data()); 1117 CallEpilogue(); 1118 return UncheckedCast<Object>(return_value); 1119 } 1120 1121 void CodeAssembler::TailCallRuntimeImpl( 1122 Runtime::FunctionId function, TNode<Int32T> arity, TNode<Object> context, 1123 std::initializer_list<TNode<Object>> args) { 1124 int result_size = Runtime::FunctionForId(function)->result_size; 1125 TNode<Code> centry = 1126 HeapConstant(CodeFactory::RuntimeCEntry(isolate(), result_size)); 1127 return TailCallRuntimeWithCEntryImpl(function, arity, centry, context, args); 1128 } 1129 1130 void CodeAssembler::TailCallRuntimeWithCEntryImpl( 1131 Runtime::FunctionId function, TNode<Int32T> arity, TNode<Code> centry, 1132 TNode<Object> context, std::initializer_list<TNode<Object>> args) { 1133 constexpr size_t kMaxNumArgs = 6; 1134 DCHECK_GE(kMaxNumArgs, args.size()); 1135 int argc = static_cast<int>(args.size()); 1136 auto call_descriptor = Linkage::GetRuntimeCallDescriptor( 1137 zone(), function, argc, Operator::kNoProperties, 1138 CallDescriptor::kNoFlags); 1139 1140 Node* ref = ExternalConstant(ExternalReference::Create(function)); 1141 1142 NodeArray<kMaxNumArgs + 4> inputs; 1143 inputs.Add(centry); 1144 for (auto arg : args) inputs.Add(arg); 1145 inputs.Add(ref); 1146 inputs.Add(arity); 1147 inputs.Add(context); 1148 1149 raw_assembler()->TailCallN(call_descriptor, inputs.size(), inputs.data()); 1150 } 1151 1152 Node* CodeAssembler::CallStubN(const CallInterfaceDescriptor& descriptor, 1153 size_t result_size, int input_count, 1154 Node* const* inputs) { 1155 // implicit nodes are target and optionally context. 1156 int implicit_nodes = descriptor.HasContextParameter() ? 2 : 1; 1157 DCHECK_LE(implicit_nodes, input_count); 1158 int argc = input_count - implicit_nodes; 1159 DCHECK_LE(descriptor.GetParameterCount(), argc); 1160 // Extra arguments not mentioned in the descriptor are passed on the stack. 1161 int stack_parameter_count = argc - descriptor.GetRegisterParameterCount(); 1162 DCHECK_LE(descriptor.GetStackParameterCount(), stack_parameter_count); 1163 DCHECK_EQ(result_size, descriptor.GetReturnCount()); 1164 1165 auto call_descriptor = Linkage::GetStubCallDescriptor( 1166 zone(), descriptor, stack_parameter_count, CallDescriptor::kNoFlags, 1167 Operator::kNoProperties); 1168 1169 CallPrologue(); 1170 Node* return_value = 1171 raw_assembler()->CallN(call_descriptor, input_count, inputs); 1172 CallEpilogue(); 1173 return return_value; 1174 } 1175 1176 void CodeAssembler::TailCallStubImpl(const CallInterfaceDescriptor& descriptor, 1177 TNode<Code> target, TNode<Object> context, 1178 std::initializer_list<Node*> args) { 1179 constexpr size_t kMaxNumArgs = 11; 1180 DCHECK_GE(kMaxNumArgs, args.size()); 1181 DCHECK_EQ(descriptor.GetParameterCount(), args.size()); 1182 auto call_descriptor = Linkage::GetStubCallDescriptor( 1183 zone(), descriptor, descriptor.GetStackParameterCount(), 1184 CallDescriptor::kNoFlags, Operator::kNoProperties); 1185 1186 NodeArray<kMaxNumArgs + 2> inputs; 1187 inputs.Add(target); 1188 for (auto arg : args) inputs.Add(arg); 1189 if (descriptor.HasContextParameter()) { 1190 inputs.Add(context); 1191 } 1192 1193 raw_assembler()->TailCallN(call_descriptor, inputs.size(), inputs.data()); 1194 } 1195 1196 Node* CodeAssembler::CallStubRImpl(const CallInterfaceDescriptor& descriptor, 1197 size_t result_size, SloppyTNode<Code> target, 1198 SloppyTNode<Object> context, 1199 std::initializer_list<Node*> args) { 1200 constexpr size_t kMaxNumArgs = 10; 1201 DCHECK_GE(kMaxNumArgs, args.size()); 1202 1203 NodeArray<kMaxNumArgs + 2> inputs; 1204 inputs.Add(target); 1205 for (auto arg : args) inputs.Add(arg); 1206 if (descriptor.HasContextParameter()) { 1207 inputs.Add(context); 1208 } 1209 1210 return CallStubN(descriptor, result_size, inputs.size(), inputs.data()); 1211 } 1212 1213 Node* CodeAssembler::TailCallStubThenBytecodeDispatchImpl( 1214 const CallInterfaceDescriptor& descriptor, Node* target, Node* context, 1215 std::initializer_list<Node*> args) { 1216 constexpr size_t kMaxNumArgs = 6; 1217 DCHECK_GE(kMaxNumArgs, args.size()); 1218 1219 DCHECK_LE(descriptor.GetParameterCount(), args.size()); 1220 int argc = static_cast<int>(args.size()); 1221 // Extra arguments not mentioned in the descriptor are passed on the stack. 1222 int stack_parameter_count = argc - descriptor.GetRegisterParameterCount(); 1223 DCHECK_LE(descriptor.GetStackParameterCount(), stack_parameter_count); 1224 auto call_descriptor = Linkage::GetStubCallDescriptor( 1225 zone(), descriptor, stack_parameter_count, CallDescriptor::kNoFlags, 1226 Operator::kNoProperties); 1227 1228 NodeArray<kMaxNumArgs + 2> inputs; 1229 inputs.Add(target); 1230 for (auto arg : args) inputs.Add(arg); 1231 inputs.Add(context); 1232 1233 return raw_assembler()->TailCallN(call_descriptor, inputs.size(), 1234 inputs.data()); 1235 } 1236 1237 template <class... TArgs> 1238 Node* CodeAssembler::TailCallBytecodeDispatch( 1239 const CallInterfaceDescriptor& descriptor, Node* target, TArgs... args) { 1240 DCHECK_EQ(descriptor.GetParameterCount(), sizeof...(args)); 1241 auto call_descriptor = Linkage::GetBytecodeDispatchCallDescriptor( 1242 zone(), descriptor, descriptor.GetStackParameterCount()); 1243 1244 Node* nodes[] = {target, args...}; 1245 CHECK_EQ(descriptor.GetParameterCount() + 1, arraysize(nodes)); 1246 return raw_assembler()->TailCallN(call_descriptor, arraysize(nodes), nodes); 1247 } 1248 1249 // Instantiate TailCallBytecodeDispatch() for argument counts used by 1250 // CSA-generated code 1251 template V8_EXPORT_PRIVATE Node* CodeAssembler::TailCallBytecodeDispatch( 1252 const CallInterfaceDescriptor& descriptor, Node* target, Node*, Node*, 1253 Node*, Node*); 1254 1255 TNode<Object> CodeAssembler::TailCallJSCode(TNode<Code> code, 1256 TNode<Context> context, 1257 TNode<JSFunction> function, 1258 TNode<Object> new_target, 1259 TNode<Int32T> arg_count) { 1260 JSTrampolineDescriptor descriptor; 1261 auto call_descriptor = Linkage::GetStubCallDescriptor( 1262 zone(), descriptor, descriptor.GetStackParameterCount(), 1263 CallDescriptor::kFixedTargetRegister, Operator::kNoProperties); 1264 1265 Node* nodes[] = {code, function, new_target, arg_count, context}; 1266 CHECK_EQ(descriptor.GetParameterCount() + 2, arraysize(nodes)); 1267 return UncheckedCast<Object>( 1268 raw_assembler()->TailCallN(call_descriptor, arraysize(nodes), nodes)); 1269 } 1270 1271 Node* CodeAssembler::CallCFunctionN(Signature<MachineType>* signature, 1272 int input_count, Node* const* inputs) { 1273 auto call_descriptor = Linkage::GetSimplifiedCDescriptor(zone(), signature); 1274 return raw_assembler()->CallN(call_descriptor, input_count, inputs); 1275 } 1276 1277 Node* CodeAssembler::CallCFunction1(MachineType return_type, 1278 MachineType arg0_type, Node* function, 1279 Node* arg0) { 1280 return raw_assembler()->CallCFunction1(return_type, arg0_type, function, 1281 arg0); 1282 } 1283 1284 Node* CodeAssembler::CallCFunction1WithCallerSavedRegisters( 1285 MachineType return_type, MachineType arg0_type, Node* function, Node* arg0, 1286 SaveFPRegsMode mode) { 1287 DCHECK(return_type.LessThanOrEqualPointerSize()); 1288 return raw_assembler()->CallCFunction1WithCallerSavedRegisters( 1289 return_type, arg0_type, function, arg0, mode); 1290 } 1291 1292 Node* CodeAssembler::CallCFunction2(MachineType return_type, 1293 MachineType arg0_type, 1294 MachineType arg1_type, Node* function, 1295 Node* arg0, Node* arg1) { 1296 return raw_assembler()->CallCFunction2(return_type, arg0_type, arg1_type, 1297 function, arg0, arg1); 1298 } 1299 1300 Node* CodeAssembler::CallCFunction3(MachineType return_type, 1301 MachineType arg0_type, 1302 MachineType arg1_type, 1303 MachineType arg2_type, Node* function, 1304 Node* arg0, Node* arg1, Node* arg2) { 1305 return raw_assembler()->CallCFunction3(return_type, arg0_type, arg1_type, 1306 arg2_type, function, arg0, arg1, arg2); 1307 } 1308 1309 Node* CodeAssembler::CallCFunction3WithCallerSavedRegisters( 1310 MachineType return_type, MachineType arg0_type, MachineType arg1_type, 1311 MachineType arg2_type, Node* function, Node* arg0, Node* arg1, Node* arg2, 1312 SaveFPRegsMode mode) { 1313 DCHECK(return_type.LessThanOrEqualPointerSize()); 1314 return raw_assembler()->CallCFunction3WithCallerSavedRegisters( 1315 return_type, arg0_type, arg1_type, arg2_type, function, arg0, arg1, arg2, 1316 mode); 1317 } 1318 1319 Node* CodeAssembler::CallCFunction4( 1320 MachineType return_type, MachineType arg0_type, MachineType arg1_type, 1321 MachineType arg2_type, MachineType arg3_type, Node* function, Node* arg0, 1322 Node* arg1, Node* arg2, Node* arg3) { 1323 return raw_assembler()->CallCFunction4(return_type, arg0_type, arg1_type, 1324 arg2_type, arg3_type, function, arg0, 1325 arg1, arg2, arg3); 1326 } 1327 1328 Node* CodeAssembler::CallCFunction5( 1329 MachineType return_type, MachineType arg0_type, MachineType arg1_type, 1330 MachineType arg2_type, MachineType arg3_type, MachineType arg4_type, 1331 Node* function, Node* arg0, Node* arg1, Node* arg2, Node* arg3, 1332 Node* arg4) { 1333 return raw_assembler()->CallCFunction5( 1334 return_type, arg0_type, arg1_type, arg2_type, arg3_type, arg4_type, 1335 function, arg0, arg1, arg2, arg3, arg4); 1336 } 1337 1338 Node* CodeAssembler::CallCFunction6( 1339 MachineType return_type, MachineType arg0_type, MachineType arg1_type, 1340 MachineType arg2_type, MachineType arg3_type, MachineType arg4_type, 1341 MachineType arg5_type, Node* function, Node* arg0, Node* arg1, Node* arg2, 1342 Node* arg3, Node* arg4, Node* arg5) { 1343 return raw_assembler()->CallCFunction6( 1344 return_type, arg0_type, arg1_type, arg2_type, arg3_type, arg4_type, 1345 arg5_type, function, arg0, arg1, arg2, arg3, arg4, arg5); 1346 } 1347 1348 Node* CodeAssembler::CallCFunction9( 1349 MachineType return_type, MachineType arg0_type, MachineType arg1_type, 1350 MachineType arg2_type, MachineType arg3_type, MachineType arg4_type, 1351 MachineType arg5_type, MachineType arg6_type, MachineType arg7_type, 1352 MachineType arg8_type, Node* function, Node* arg0, Node* arg1, Node* arg2, 1353 Node* arg3, Node* arg4, Node* arg5, Node* arg6, Node* arg7, Node* arg8) { 1354 return raw_assembler()->CallCFunction9( 1355 return_type, arg0_type, arg1_type, arg2_type, arg3_type, arg4_type, 1356 arg5_type, arg6_type, arg7_type, arg8_type, function, arg0, arg1, arg2, 1357 arg3, arg4, arg5, arg6, arg7, arg8); 1358 } 1359 1360 void CodeAssembler::Goto(Label* label) { 1361 label->MergeVariables(); 1362 raw_assembler()->Goto(label->label_); 1363 } 1364 1365 void CodeAssembler::GotoIf(SloppyTNode<IntegralT> condition, 1366 Label* true_label) { 1367 Label false_label(this); 1368 Branch(condition, true_label, &false_label); 1369 Bind(&false_label); 1370 } 1371 1372 void CodeAssembler::GotoIfNot(SloppyTNode<IntegralT> condition, 1373 Label* false_label) { 1374 Label true_label(this); 1375 Branch(condition, &true_label, false_label); 1376 Bind(&true_label); 1377 } 1378 1379 void CodeAssembler::Branch(SloppyTNode<IntegralT> condition, Label* true_label, 1380 Label* false_label) { 1381 int32_t constant; 1382 if (ToInt32Constant(condition, constant)) { 1383 if ((true_label->is_used() || true_label->is_bound()) && 1384 (false_label->is_used() || false_label->is_bound())) { 1385 return Goto(constant ? true_label : false_label); 1386 } 1387 } 1388 true_label->MergeVariables(); 1389 false_label->MergeVariables(); 1390 return raw_assembler()->Branch(condition, true_label->label_, 1391 false_label->label_); 1392 } 1393 1394 void CodeAssembler::Branch(TNode<BoolT> condition, 1395 std::function<void()> true_body, 1396 std::function<void()> false_body) { 1397 int32_t constant; 1398 if (ToInt32Constant(condition, constant)) { 1399 return constant ? true_body() : false_body(); 1400 } 1401 1402 Label vtrue(this), vfalse(this); 1403 Branch(condition, &vtrue, &vfalse); 1404 1405 Bind(&vtrue); 1406 true_body(); 1407 1408 Bind(&vfalse); 1409 false_body(); 1410 } 1411 1412 void CodeAssembler::Branch(TNode<BoolT> condition, Label* true_label, 1413 std::function<void()> false_body) { 1414 int32_t constant; 1415 if (ToInt32Constant(condition, constant)) { 1416 return constant ? Goto(true_label) : false_body(); 1417 } 1418 1419 Label vfalse(this); 1420 Branch(condition, true_label, &vfalse); 1421 Bind(&vfalse); 1422 false_body(); 1423 } 1424 1425 void CodeAssembler::Branch(TNode<BoolT> condition, 1426 std::function<void()> true_body, 1427 Label* false_label) { 1428 int32_t constant; 1429 if (ToInt32Constant(condition, constant)) { 1430 return constant ? true_body() : Goto(false_label); 1431 } 1432 1433 Label vtrue(this); 1434 Branch(condition, &vtrue, false_label); 1435 Bind(&vtrue); 1436 true_body(); 1437 } 1438 1439 void CodeAssembler::Switch(Node* index, Label* default_label, 1440 const int32_t* case_values, Label** case_labels, 1441 size_t case_count) { 1442 RawMachineLabel** labels = 1443 new (zone()->New(sizeof(RawMachineLabel*) * case_count)) 1444 RawMachineLabel*[case_count]; 1445 for (size_t i = 0; i < case_count; ++i) { 1446 labels[i] = case_labels[i]->label_; 1447 case_labels[i]->MergeVariables(); 1448 } 1449 default_label->MergeVariables(); 1450 return raw_assembler()->Switch(index, default_label->label_, case_values, 1451 labels, case_count); 1452 } 1453 1454 bool CodeAssembler::UnalignedLoadSupported(MachineRepresentation rep) const { 1455 return raw_assembler()->machine()->UnalignedLoadSupported(rep); 1456 } 1457 bool CodeAssembler::UnalignedStoreSupported(MachineRepresentation rep) const { 1458 return raw_assembler()->machine()->UnalignedStoreSupported(rep); 1459 } 1460 1461 // RawMachineAssembler delegate helpers: 1462 Isolate* CodeAssembler::isolate() const { return raw_assembler()->isolate(); } 1463 1464 Factory* CodeAssembler::factory() const { return isolate()->factory(); } 1465 1466 Zone* CodeAssembler::zone() const { return raw_assembler()->zone(); } 1467 1468 RawMachineAssembler* CodeAssembler::raw_assembler() const { 1469 return state_->raw_assembler_.get(); 1470 } 1471 1472 // The core implementation of Variable is stored through an indirection so 1473 // that it can outlive the often block-scoped Variable declarations. This is 1474 // needed to ensure that variable binding and merging through phis can 1475 // properly be verified. 1476 class CodeAssemblerVariable::Impl : public ZoneObject { 1477 public: 1478 explicit Impl(MachineRepresentation rep) 1479 : 1480 #if DEBUG 1481 debug_info_(AssemblerDebugInfo(nullptr, nullptr, -1)), 1482 #endif 1483 value_(nullptr), 1484 rep_(rep) { 1485 } 1486 1487 #if DEBUG 1488 AssemblerDebugInfo debug_info() const { return debug_info_; } 1489 void set_debug_info(AssemblerDebugInfo debug_info) { 1490 debug_info_ = debug_info; 1491 } 1492 1493 AssemblerDebugInfo debug_info_; 1494 #endif // DEBUG 1495 Node* value_; 1496 MachineRepresentation rep_; 1497 }; 1498 1499 CodeAssemblerVariable::CodeAssemblerVariable(CodeAssembler* assembler, 1500 MachineRepresentation rep) 1501 : impl_(new (assembler->zone()) Impl(rep)), state_(assembler->state()) { 1502 state_->variables_.insert(impl_); 1503 } 1504 1505 CodeAssemblerVariable::CodeAssemblerVariable(CodeAssembler* assembler, 1506 MachineRepresentation rep, 1507 Node* initial_value) 1508 : CodeAssemblerVariable(assembler, rep) { 1509 Bind(initial_value); 1510 } 1511 1512 #if DEBUG 1513 CodeAssemblerVariable::CodeAssemblerVariable(CodeAssembler* assembler, 1514 AssemblerDebugInfo debug_info, 1515 MachineRepresentation rep) 1516 : impl_(new (assembler->zone()) Impl(rep)), state_(assembler->state()) { 1517 impl_->set_debug_info(debug_info); 1518 state_->variables_.insert(impl_); 1519 } 1520 1521 CodeAssemblerVariable::CodeAssemblerVariable(CodeAssembler* assembler, 1522 AssemblerDebugInfo debug_info, 1523 MachineRepresentation rep, 1524 Node* initial_value) 1525 : CodeAssemblerVariable(assembler, debug_info, rep) { 1526 impl_->set_debug_info(debug_info); 1527 Bind(initial_value); 1528 } 1529 #endif // DEBUG 1530 1531 CodeAssemblerVariable::~CodeAssemblerVariable() { 1532 state_->variables_.erase(impl_); 1533 } 1534 1535 void CodeAssemblerVariable::Bind(Node* value) { impl_->value_ = value; } 1536 1537 Node* CodeAssemblerVariable::value() const { 1538 #if DEBUG 1539 if (!IsBound()) { 1540 std::stringstream str; 1541 str << "#Use of unbound variable:" 1542 << "#\n Variable: " << *this << "#\n Current Block: "; 1543 state_->PrintCurrentBlock(str); 1544 FATAL("%s", str.str().c_str()); 1545 } 1546 if (!state_->InsideBlock()) { 1547 std::stringstream str; 1548 str << "#Accessing variable value outside a block:" 1549 << "#\n Variable: " << *this; 1550 FATAL("%s", str.str().c_str()); 1551 } 1552 #endif // DEBUG 1553 return impl_->value_; 1554 } 1555 1556 MachineRepresentation CodeAssemblerVariable::rep() const { return impl_->rep_; } 1557 1558 bool CodeAssemblerVariable::IsBound() const { return impl_->value_ != nullptr; } 1559 1560 std::ostream& operator<<(std::ostream& os, 1561 const CodeAssemblerVariable::Impl& impl) { 1562 #if DEBUG 1563 AssemblerDebugInfo info = impl.debug_info(); 1564 if (info.name) os << "V" << info; 1565 #endif // DEBUG 1566 return os; 1567 } 1568 1569 std::ostream& operator<<(std::ostream& os, 1570 const CodeAssemblerVariable& variable) { 1571 os << *variable.impl_; 1572 return os; 1573 } 1574 1575 CodeAssemblerLabel::CodeAssemblerLabel(CodeAssembler* assembler, 1576 size_t vars_count, 1577 CodeAssemblerVariable* const* vars, 1578 CodeAssemblerLabel::Type type) 1579 : bound_(false), 1580 merge_count_(0), 1581 state_(assembler->state()), 1582 label_(nullptr) { 1583 void* buffer = assembler->zone()->New(sizeof(RawMachineLabel)); 1584 label_ = new (buffer) 1585 RawMachineLabel(type == kDeferred ? RawMachineLabel::kDeferred 1586 : RawMachineLabel::kNonDeferred); 1587 for (size_t i = 0; i < vars_count; ++i) { 1588 variable_phis_[vars[i]->impl_] = nullptr; 1589 } 1590 } 1591 1592 CodeAssemblerLabel::~CodeAssemblerLabel() { label_->~RawMachineLabel(); } 1593 1594 void CodeAssemblerLabel::MergeVariables() { 1595 ++merge_count_; 1596 for (CodeAssemblerVariable::Impl* var : state_->variables_) { 1597 size_t count = 0; 1598 Node* node = var->value_; 1599 if (node != nullptr) { 1600 auto i = variable_merges_.find(var); 1601 if (i != variable_merges_.end()) { 1602 i->second.push_back(node); 1603 count = i->second.size(); 1604 } else { 1605 count = 1; 1606 variable_merges_[var] = std::vector<Node*>(1, node); 1607 } 1608 } 1609 // If the following asserts, then you've jumped to a label without a bound 1610 // variable along that path that expects to merge its value into a phi. 1611 DCHECK(variable_phis_.find(var) == variable_phis_.end() || 1612 count == merge_count_); 1613 USE(count); 1614 1615 // If the label is already bound, we already know the set of variables to 1616 // merge and phi nodes have already been created. 1617 if (bound_) { 1618 auto phi = variable_phis_.find(var); 1619 if (phi != variable_phis_.end()) { 1620 DCHECK_NOT_NULL(phi->second); 1621 state_->raw_assembler_->AppendPhiInput(phi->second, node); 1622 } else { 1623 auto i = variable_merges_.find(var); 1624 if (i != variable_merges_.end()) { 1625 // If the following assert fires, then you've declared a variable that 1626 // has the same bound value along all paths up until the point you 1627 // bound this label, but then later merged a path with a new value for 1628 // the variable after the label bind (it's not possible to add phis to 1629 // the bound label after the fact, just make sure to list the variable 1630 // in the label's constructor's list of merged variables). 1631 #if DEBUG 1632 if (find_if(i->second.begin(), i->second.end(), 1633 [node](Node* e) -> bool { return node != e; }) != 1634 i->second.end()) { 1635 std::stringstream str; 1636 str << "Unmerged variable found when jumping to block. \n" 1637 << "# Variable: " << *var; 1638 if (bound_) { 1639 str << "\n# Target block: " << *label_->block(); 1640 } 1641 str << "\n# Current Block: "; 1642 state_->PrintCurrentBlock(str); 1643 FATAL("%s", str.str().c_str()); 1644 } 1645 #endif // DEBUG 1646 } 1647 } 1648 } 1649 } 1650 } 1651 1652 #if DEBUG 1653 void CodeAssemblerLabel::Bind(AssemblerDebugInfo debug_info) { 1654 if (bound_) { 1655 std::stringstream str; 1656 str << "Cannot bind the same label twice:" 1657 << "\n# current: " << debug_info 1658 << "\n# previous: " << *label_->block(); 1659 FATAL("%s", str.str().c_str()); 1660 } 1661 state_->raw_assembler_->Bind(label_, debug_info); 1662 UpdateVariablesAfterBind(); 1663 } 1664 #endif // DEBUG 1665 1666 void CodeAssemblerLabel::Bind() { 1667 DCHECK(!bound_); 1668 state_->raw_assembler_->Bind(label_); 1669 UpdateVariablesAfterBind(); 1670 } 1671 1672 void CodeAssemblerLabel::UpdateVariablesAfterBind() { 1673 // Make sure that all variables that have changed along any path up to this 1674 // point are marked as merge variables. 1675 for (auto var : state_->variables_) { 1676 Node* shared_value = nullptr; 1677 auto i = variable_merges_.find(var); 1678 if (i != variable_merges_.end()) { 1679 for (auto value : i->second) { 1680 DCHECK_NOT_NULL(value); 1681 if (value != shared_value) { 1682 if (shared_value == nullptr) { 1683 shared_value = value; 1684 } else { 1685 variable_phis_[var] = nullptr; 1686 } 1687 } 1688 } 1689 } 1690 } 1691 1692 for (auto var : variable_phis_) { 1693 CodeAssemblerVariable::Impl* var_impl = var.first; 1694 auto i = variable_merges_.find(var_impl); 1695 #if DEBUG 1696 bool not_found = i == variable_merges_.end(); 1697 if (not_found || i->second.size() != merge_count_) { 1698 std::stringstream str; 1699 str << "A variable that has been marked as beeing merged at the label" 1700 << "\n# doesn't have a bound value along all of the paths that " 1701 << "\n# have been merged into the label up to this point." 1702 << "\n#" 1703 << "\n# This can happen in the following cases:" 1704 << "\n# - By explicitly marking it so in the label constructor" 1705 << "\n# - By having seen different bound values at branches" 1706 << "\n#" 1707 << "\n# Merge count: expected=" << merge_count_ 1708 << " vs. found=" << (not_found ? 0 : i->second.size()) 1709 << "\n# Variable: " << *var_impl 1710 << "\n# Current Block: " << *label_->block(); 1711 FATAL("%s", str.str().c_str()); 1712 } 1713 #endif // DEBUG 1714 Node* phi = state_->raw_assembler_->Phi( 1715 var.first->rep_, static_cast<int>(merge_count_), &(i->second[0])); 1716 variable_phis_[var_impl] = phi; 1717 } 1718 1719 // Bind all variables to a merge phi, the common value along all paths or 1720 // null. 1721 for (auto var : state_->variables_) { 1722 auto i = variable_phis_.find(var); 1723 if (i != variable_phis_.end()) { 1724 var->value_ = i->second; 1725 } else { 1726 auto j = variable_merges_.find(var); 1727 if (j != variable_merges_.end() && j->second.size() == merge_count_) { 1728 var->value_ = j->second.back(); 1729 } else { 1730 var->value_ = nullptr; 1731 } 1732 } 1733 } 1734 1735 bound_ = true; 1736 } 1737 1738 } // namespace compiler 1739 1740 Smi* CheckObjectType(Object* value, Smi* type, String* location) { 1741 #ifdef DEBUG 1742 const char* expected; 1743 switch (static_cast<ObjectType>(type->value())) { 1744 #define TYPE_CASE(Name) \ 1745 case ObjectType::k##Name: \ 1746 if (value->Is##Name()) return Smi::FromInt(0); \ 1747 expected = #Name; \ 1748 break; 1749 #define TYPE_STRUCT_CASE(NAME, Name, name) \ 1750 case ObjectType::k##Name: \ 1751 if (value->Is##Name()) return Smi::FromInt(0); \ 1752 expected = #Name; \ 1753 break; 1754 1755 TYPE_CASE(Object) 1756 OBJECT_TYPE_LIST(TYPE_CASE) 1757 HEAP_OBJECT_TYPE_LIST(TYPE_CASE) 1758 STRUCT_LIST(TYPE_STRUCT_CASE) 1759 #undef TYPE_CASE 1760 #undef TYPE_STRUCT_CASE 1761 } 1762 std::stringstream value_description; 1763 value->Print(value_description); 1764 V8_Fatal(__FILE__, __LINE__, 1765 "Type cast failed in %s\n" 1766 " Expected %s but found %s", 1767 location->ToAsciiArray(), expected, value_description.str().c_str()); 1768 #else 1769 UNREACHABLE(); 1770 #endif 1771 } 1772 1773 } // namespace internal 1774 } // namespace v8 1775