// Copyright 2015 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/compiler/code-assembler.h" #include #include "src/code-factory.h" #include "src/compiler/graph.h" #include "src/compiler/instruction-selector.h" #include "src/compiler/linkage.h" #include "src/compiler/node-matchers.h" #include "src/compiler/pipeline.h" #include "src/compiler/raw-machine-assembler.h" #include "src/compiler/schedule.h" #include "src/frames.h" #include "src/interface-descriptors.h" #include "src/interpreter/bytecodes.h" #include "src/machine-type.h" #include "src/macro-assembler.h" #include "src/objects-inl.h" #include "src/utils.h" #include "src/zone/zone.h" #define REPEAT_1_TO_2(V, T) V(T) V(T, T) #define REPEAT_1_TO_3(V, T) REPEAT_1_TO_2(V, T) V(T, T, T) #define REPEAT_1_TO_4(V, T) REPEAT_1_TO_3(V, T) V(T, T, T, T) #define REPEAT_1_TO_5(V, T) REPEAT_1_TO_4(V, T) V(T, T, T, T, T) #define REPEAT_1_TO_6(V, T) REPEAT_1_TO_5(V, T) V(T, T, T, T, T, T) #define REPEAT_1_TO_7(V, T) REPEAT_1_TO_6(V, T) V(T, T, T, T, T, T, T) #define REPEAT_1_TO_8(V, T) REPEAT_1_TO_7(V, T) V(T, T, T, T, T, T, T, T) #define REPEAT_1_TO_9(V, T) REPEAT_1_TO_8(V, T) V(T, T, T, T, T, T, T, T, T) #define REPEAT_1_TO_10(V, T) REPEAT_1_TO_9(V, T) V(T, T, T, T, T, T, T, T, T, T) #define REPEAT_1_TO_11(V, T) \ REPEAT_1_TO_10(V, T) V(T, T, T, T, T, T, T, T, T, T, T) #define REPEAT_1_TO_12(V, T) \ REPEAT_1_TO_11(V, T) V(T, T, T, T, T, T, T, T, T, T, T, T) namespace v8 { namespace internal { constexpr MachineType MachineTypeOf::value; constexpr MachineType MachineTypeOf::value; namespace compiler { static_assert(std::is_convertible, TNode>::value, "test subtyping"); static_assert(std::is_convertible>, TNode>>::value, "test subtyping"); static_assert( !std::is_convertible>, TNode>::value, "test subtyping"); CodeAssemblerState::CodeAssemblerState( Isolate* isolate, Zone* zone, const CallInterfaceDescriptor& descriptor, Code::Kind kind, const char* name, size_t result_size, uint32_t stub_key, int32_t builtin_index) // TODO(rmcilroy): Should we use Linkage::GetBytecodeDispatchDescriptor for // bytecode handlers? : CodeAssemblerState( isolate, zone, Linkage::GetStubCallDescriptor( isolate, zone, descriptor, descriptor.GetStackParameterCount(), CallDescriptor::kNoFlags, Operator::kNoProperties, MachineType::AnyTagged(), result_size), kind, name, stub_key, builtin_index) {} CodeAssemblerState::CodeAssemblerState(Isolate* isolate, Zone* zone, int parameter_count, Code::Kind kind, const char* name, int32_t builtin_index) : CodeAssemblerState( isolate, zone, Linkage::GetJSCallDescriptor(zone, false, parameter_count, kind == Code::BUILTIN ? CallDescriptor::kPushArgumentCount : CallDescriptor::kNoFlags), kind, name, 0, builtin_index) {} CodeAssemblerState::CodeAssemblerState(Isolate* isolate, Zone* zone, CallDescriptor* call_descriptor, Code::Kind kind, const char* name, uint32_t stub_key, int32_t builtin_index) : raw_assembler_(new RawMachineAssembler( isolate, new (zone) Graph(zone), call_descriptor, MachineType::PointerRepresentation(), InstructionSelector::SupportedMachineOperatorFlags(), InstructionSelector::AlignmentRequirements())), kind_(kind), name_(name), stub_key_(stub_key), builtin_index_(builtin_index), code_generated_(false), variables_(zone) {} CodeAssemblerState::~CodeAssemblerState() {} int CodeAssemblerState::parameter_count() const { return static_cast(raw_assembler_->call_descriptor()->ParameterCount()); } CodeAssembler::~CodeAssembler() {} #if DEBUG void CodeAssemblerState::PrintCurrentBlock(std::ostream& os) { raw_assembler_->PrintCurrentBlock(os); } bool CodeAssemblerState::InsideBlock() { return raw_assembler_->InsideBlock(); } #endif void CodeAssemblerState::SetInitialDebugInformation(const char* msg, const char* file, int line) { #if DEBUG AssemblerDebugInfo debug_info = {msg, file, line}; raw_assembler_->SetInitialDebugInformation(debug_info); #endif // DEBUG } class BreakOnNodeDecorator final : public GraphDecorator { public: explicit BreakOnNodeDecorator(NodeId node_id) : node_id_(node_id) {} void Decorate(Node* node) final { if (node->id() == node_id_) { base::OS::DebugBreak(); } } private: NodeId node_id_; }; void CodeAssembler::BreakOnNode(int node_id) { Graph* graph = raw_assembler()->graph(); Zone* zone = graph->zone(); GraphDecorator* decorator = new (zone) BreakOnNodeDecorator(static_cast(node_id)); graph->AddDecorator(decorator); } void CodeAssembler::RegisterCallGenerationCallbacks( const CodeAssemblerCallback& call_prologue, const CodeAssemblerCallback& call_epilogue) { // The callback can be registered only once. DCHECK(!state_->call_prologue_); DCHECK(!state_->call_epilogue_); state_->call_prologue_ = call_prologue; state_->call_epilogue_ = call_epilogue; } void CodeAssembler::UnregisterCallGenerationCallbacks() { state_->call_prologue_ = nullptr; state_->call_epilogue_ = nullptr; } void CodeAssembler::CallPrologue() { if (state_->call_prologue_) { state_->call_prologue_(); } } void CodeAssembler::CallEpilogue() { if (state_->call_epilogue_) { state_->call_epilogue_(); } } bool CodeAssembler::Word32ShiftIsSafe() const { return raw_assembler()->machine()->Word32ShiftIsSafe(); } // static Handle CodeAssembler::GenerateCode(CodeAssemblerState* state) { DCHECK(!state->code_generated_); RawMachineAssembler* rasm = state->raw_assembler_.get(); Schedule* schedule = rasm->Export(); JumpOptimizationInfo jump_opt; bool should_optimize_jumps = rasm->isolate()->serializer_enabled() && FLAG_turbo_rewrite_far_jumps; Handle code = Pipeline::GenerateCodeForCodeStub( rasm->isolate(), rasm->call_descriptor(), rasm->graph(), schedule, state->kind_, state->name_, state->stub_key_, state->builtin_index_, should_optimize_jumps ? &jump_opt : nullptr); if (jump_opt.is_optimizable()) { jump_opt.set_optimizing(); // Regenerate machine code code = Pipeline::GenerateCodeForCodeStub( rasm->isolate(), rasm->call_descriptor(), rasm->graph(), schedule, state->kind_, state->name_, state->stub_key_, state->builtin_index_, &jump_opt); } state->code_generated_ = true; return code; } bool CodeAssembler::Is64() const { return raw_assembler()->machine()->Is64(); } bool CodeAssembler::IsFloat64RoundUpSupported() const { return raw_assembler()->machine()->Float64RoundUp().IsSupported(); } bool CodeAssembler::IsFloat64RoundDownSupported() const { return raw_assembler()->machine()->Float64RoundDown().IsSupported(); } bool CodeAssembler::IsFloat64RoundTiesEvenSupported() const { return raw_assembler()->machine()->Float64RoundTiesEven().IsSupported(); } bool CodeAssembler::IsFloat64RoundTruncateSupported() const { return raw_assembler()->machine()->Float64RoundTruncate().IsSupported(); } bool CodeAssembler::IsInt32AbsWithOverflowSupported() const { return raw_assembler()->machine()->Int32AbsWithOverflow().IsSupported(); } bool CodeAssembler::IsInt64AbsWithOverflowSupported() const { return raw_assembler()->machine()->Int64AbsWithOverflow().IsSupported(); } bool CodeAssembler::IsIntPtrAbsWithOverflowSupported() const { return Is64() ? IsInt64AbsWithOverflowSupported() : IsInt32AbsWithOverflowSupported(); } TNode CodeAssembler::Int32Constant(int32_t value) { return UncheckedCast(raw_assembler()->Int32Constant(value)); } TNode CodeAssembler::Int64Constant(int64_t value) { return UncheckedCast(raw_assembler()->Int64Constant(value)); } TNode CodeAssembler::IntPtrConstant(intptr_t value) { return UncheckedCast(raw_assembler()->IntPtrConstant(value)); } TNode CodeAssembler::NumberConstant(double value) { int smi_value; if (DoubleToSmiInteger(value, &smi_value)) { return UncheckedCast(SmiConstant(smi_value)); } else { return UncheckedCast(raw_assembler()->NumberConstant(value)); } } TNode CodeAssembler::SmiConstant(Smi* value) { return UncheckedCast( BitcastWordToTaggedSigned(IntPtrConstant(bit_cast(value)))); } TNode CodeAssembler::SmiConstant(int value) { return SmiConstant(Smi::FromInt(value)); } TNode CodeAssembler::UntypedHeapConstant( Handle object) { return UncheckedCast(raw_assembler()->HeapConstant(object)); } TNode CodeAssembler::StringConstant(const char* str) { return UncheckedCast( HeapConstant(factory()->NewStringFromAsciiChecked(str, TENURED))); } TNode CodeAssembler::BooleanConstant(bool value) { return UncheckedCast(raw_assembler()->BooleanConstant(value)); } TNode CodeAssembler::ExternalConstant( ExternalReference address) { return UncheckedCast( raw_assembler()->ExternalConstant(address)); } TNode CodeAssembler::Float64Constant(double value) { return UncheckedCast(raw_assembler()->Float64Constant(value)); } TNode CodeAssembler::NaNConstant() { return UncheckedCast(LoadRoot(Heap::kNanValueRootIndex)); } bool CodeAssembler::ToInt32Constant(Node* node, int32_t& out_value) { Int64Matcher m(node); if (m.HasValue() && m.IsInRange(std::numeric_limits::min(), std::numeric_limits::max())) { out_value = static_cast(m.Value()); return true; } return false; } bool CodeAssembler::ToInt64Constant(Node* node, int64_t& out_value) { Int64Matcher m(node); if (m.HasValue()) out_value = m.Value(); return m.HasValue(); } bool CodeAssembler::ToSmiConstant(Node* node, Smi*& out_value) { if (node->opcode() == IrOpcode::kBitcastWordToTaggedSigned) { node = node->InputAt(0); } IntPtrMatcher m(node); if (m.HasValue()) { intptr_t value = m.Value(); // Make sure that the value is actually a smi CHECK_EQ(0, value & ((static_cast(1) << kSmiShiftSize) - 1)); out_value = Smi::cast(bit_cast(value)); return true; } return false; } bool CodeAssembler::ToIntPtrConstant(Node* node, intptr_t& out_value) { if (node->opcode() == IrOpcode::kBitcastWordToTaggedSigned || node->opcode() == IrOpcode::kBitcastWordToTagged) { node = node->InputAt(0); } IntPtrMatcher m(node); if (m.HasValue()) out_value = m.Value(); return m.HasValue(); } Node* CodeAssembler::Parameter(int value) { return raw_assembler()->Parameter(value); } TNode CodeAssembler::GetJSContextParameter() { auto call_descriptor = raw_assembler()->call_descriptor(); DCHECK(call_descriptor->IsJSFunctionCall()); return CAST(Parameter(Linkage::GetJSCallContextParamIndex( static_cast(call_descriptor->JSParameterCount())))); } void CodeAssembler::Return(SloppyTNode value) { return raw_assembler()->Return(value); } void CodeAssembler::Return(SloppyTNode value1, SloppyTNode value2) { return raw_assembler()->Return(value1, value2); } void CodeAssembler::Return(SloppyTNode value1, SloppyTNode value2, SloppyTNode value3) { return raw_assembler()->Return(value1, value2, value3); } void CodeAssembler::PopAndReturn(Node* pop, Node* value) { return raw_assembler()->PopAndReturn(pop, value); } void CodeAssembler::ReturnIf(Node* condition, Node* value) { Label if_return(this), if_continue(this); Branch(condition, &if_return, &if_continue); Bind(&if_return); Return(value); Bind(&if_continue); } void CodeAssembler::DebugAbort(Node* message) { raw_assembler()->DebugAbort(message); } void CodeAssembler::DebugBreak() { raw_assembler()->DebugBreak(); } void CodeAssembler::Unreachable() { DebugBreak(); raw_assembler()->Unreachable(); } void CodeAssembler::Comment(const char* format, ...) { if (!FLAG_code_comments) return; char buffer[4 * KB]; StringBuilder builder(buffer, arraysize(buffer)); va_list arguments; va_start(arguments, format); builder.AddFormattedList(format, arguments); va_end(arguments); // Copy the string before recording it in the assembler to avoid // issues when the stack allocated buffer goes out of scope. const int prefix_len = 2; int length = builder.position() + 1; char* copy = reinterpret_cast(malloc(length + prefix_len)); MemCopy(copy + prefix_len, builder.Finalize(), length); copy[0] = ';'; copy[1] = ' '; raw_assembler()->Comment(copy); } void CodeAssembler::Bind(Label* label) { return label->Bind(); } #if DEBUG void CodeAssembler::Bind(Label* label, AssemblerDebugInfo debug_info) { return label->Bind(debug_info); } #endif // DEBUG Node* CodeAssembler::LoadFramePointer() { return raw_assembler()->LoadFramePointer(); } Node* CodeAssembler::LoadParentFramePointer() { return raw_assembler()->LoadParentFramePointer(); } Node* CodeAssembler::LoadStackPointer() { return raw_assembler()->LoadStackPointer(); } Node* CodeAssembler::SpeculationPoison() { return raw_assembler()->SpeculationPoison(); } #define DEFINE_CODE_ASSEMBLER_BINARY_OP(name, ResType, Arg1Type, Arg2Type) \ TNode CodeAssembler::name(SloppyTNode a, \ SloppyTNode b) { \ return UncheckedCast(raw_assembler()->name(a, b)); \ } CODE_ASSEMBLER_BINARY_OP_LIST(DEFINE_CODE_ASSEMBLER_BINARY_OP) #undef DEFINE_CODE_ASSEMBLER_BINARY_OP TNode CodeAssembler::IntPtrAdd(SloppyTNode left, SloppyTNode right) { intptr_t left_constant; bool is_left_constant = ToIntPtrConstant(left, left_constant); intptr_t right_constant; bool is_right_constant = ToIntPtrConstant(right, right_constant); if (is_left_constant) { if (is_right_constant) { return IntPtrConstant(left_constant + right_constant); } if (left_constant == 0) { return right; } } else if (is_right_constant) { if (right_constant == 0) { return left; } } return UncheckedCast(raw_assembler()->IntPtrAdd(left, right)); } TNode CodeAssembler::IntPtrSub(SloppyTNode left, SloppyTNode right) { intptr_t left_constant; bool is_left_constant = ToIntPtrConstant(left, left_constant); intptr_t right_constant; bool is_right_constant = ToIntPtrConstant(right, right_constant); if (is_left_constant) { if (is_right_constant) { return IntPtrConstant(left_constant - right_constant); } } else if (is_right_constant) { if (right_constant == 0) { return left; } } return UncheckedCast(raw_assembler()->IntPtrSub(left, right)); } TNode CodeAssembler::IntPtrMul(SloppyTNode left, SloppyTNode right) { intptr_t left_constant; bool is_left_constant = ToIntPtrConstant(left, left_constant); intptr_t right_constant; bool is_right_constant = ToIntPtrConstant(right, right_constant); if (is_left_constant) { if (is_right_constant) { return IntPtrConstant(left_constant * right_constant); } if (base::bits::IsPowerOfTwo(left_constant)) { return WordShl(right, WhichPowerOf2(left_constant)); } } else if (is_right_constant) { if (base::bits::IsPowerOfTwo(right_constant)) { return WordShl(left, WhichPowerOf2(right_constant)); } } return UncheckedCast(raw_assembler()->IntPtrMul(left, right)); } TNode CodeAssembler::WordShl(SloppyTNode value, int shift) { return (shift != 0) ? WordShl(value, IntPtrConstant(shift)) : value; } TNode CodeAssembler::WordShr(SloppyTNode value, int shift) { return (shift != 0) ? WordShr(value, IntPtrConstant(shift)) : value; } TNode CodeAssembler::Word32Shr(SloppyTNode value, int shift) { return (shift != 0) ? Word32Shr(value, Int32Constant(shift)) : value; } TNode CodeAssembler::WordOr(SloppyTNode left, SloppyTNode right) { intptr_t left_constant; bool is_left_constant = ToIntPtrConstant(left, left_constant); intptr_t right_constant; bool is_right_constant = ToIntPtrConstant(right, right_constant); if (is_left_constant) { if (is_right_constant) { return IntPtrConstant(left_constant | right_constant); } if (left_constant == 0) { return right; } } else if (is_right_constant) { if (right_constant == 0) { return left; } } return UncheckedCast(raw_assembler()->WordOr(left, right)); } TNode CodeAssembler::WordAnd(SloppyTNode left, SloppyTNode right) { intptr_t left_constant; bool is_left_constant = ToIntPtrConstant(left, left_constant); intptr_t right_constant; bool is_right_constant = ToIntPtrConstant(right, right_constant); if (is_left_constant) { if (is_right_constant) { return IntPtrConstant(left_constant & right_constant); } } return UncheckedCast(raw_assembler()->WordAnd(left, right)); } TNode CodeAssembler::WordXor(SloppyTNode left, SloppyTNode right) { intptr_t left_constant; bool is_left_constant = ToIntPtrConstant(left, left_constant); intptr_t right_constant; bool is_right_constant = ToIntPtrConstant(right, right_constant); if (is_left_constant) { if (is_right_constant) { return IntPtrConstant(left_constant ^ right_constant); } } return UncheckedCast(raw_assembler()->WordXor(left, right)); } TNode CodeAssembler::WordShl(SloppyTNode left, SloppyTNode right) { intptr_t left_constant; bool is_left_constant = ToIntPtrConstant(left, left_constant); intptr_t right_constant; bool is_right_constant = ToIntPtrConstant(right, right_constant); if (is_left_constant) { if (is_right_constant) { return IntPtrConstant(left_constant << right_constant); } } else if (is_right_constant) { if (right_constant == 0) { return left; } } return UncheckedCast(raw_assembler()->WordShl(left, right)); } TNode CodeAssembler::WordShr(SloppyTNode left, SloppyTNode right) { intptr_t left_constant; bool is_left_constant = ToIntPtrConstant(left, left_constant); intptr_t right_constant; bool is_right_constant = ToIntPtrConstant(right, right_constant); if (is_left_constant) { if (is_right_constant) { return IntPtrConstant(static_cast(left_constant) >> right_constant); } } else if (is_right_constant) { if (right_constant == 0) { return left; } } return UncheckedCast(raw_assembler()->WordShr(left, right)); } TNode CodeAssembler::WordSar(SloppyTNode left, SloppyTNode right) { intptr_t left_constant; bool is_left_constant = ToIntPtrConstant(left, left_constant); intptr_t right_constant; bool is_right_constant = ToIntPtrConstant(right, right_constant); if (is_left_constant) { if (is_right_constant) { return IntPtrConstant(left_constant >> right_constant); } } else if (is_right_constant) { if (right_constant == 0) { return left; } } return UncheckedCast(raw_assembler()->WordSar(left, right)); } TNode CodeAssembler::Word32Or(SloppyTNode left, SloppyTNode right) { int32_t left_constant; bool is_left_constant = ToInt32Constant(left, left_constant); int32_t right_constant; bool is_right_constant = ToInt32Constant(right, right_constant); if (is_left_constant) { if (is_right_constant) { return Int32Constant(left_constant | right_constant); } if (left_constant == 0) { return right; } } else if (is_right_constant) { if (right_constant == 0) { return left; } } return UncheckedCast(raw_assembler()->Word32Or(left, right)); } TNode CodeAssembler::Word32And(SloppyTNode left, SloppyTNode right) { int32_t left_constant; bool is_left_constant = ToInt32Constant(left, left_constant); int32_t right_constant; bool is_right_constant = ToInt32Constant(right, right_constant); if (is_left_constant) { if (is_right_constant) { return Int32Constant(left_constant & right_constant); } } return UncheckedCast(raw_assembler()->Word32And(left, right)); } TNode CodeAssembler::Word32Xor(SloppyTNode left, SloppyTNode right) { int32_t left_constant; bool is_left_constant = ToInt32Constant(left, left_constant); int32_t right_constant; bool is_right_constant = ToInt32Constant(right, right_constant); if (is_left_constant) { if (is_right_constant) { return Int32Constant(left_constant ^ right_constant); } } return UncheckedCast(raw_assembler()->Word32Xor(left, right)); } TNode CodeAssembler::Word32Shl(SloppyTNode left, SloppyTNode right) { int32_t left_constant; bool is_left_constant = ToInt32Constant(left, left_constant); int32_t right_constant; bool is_right_constant = ToInt32Constant(right, right_constant); if (is_left_constant) { if (is_right_constant) { return Int32Constant(left_constant << right_constant); } } else if (is_right_constant) { if (right_constant == 0) { return left; } } return UncheckedCast(raw_assembler()->Word32Shl(left, right)); } TNode CodeAssembler::Word32Shr(SloppyTNode left, SloppyTNode right) { int32_t left_constant; bool is_left_constant = ToInt32Constant(left, left_constant); int32_t right_constant; bool is_right_constant = ToInt32Constant(right, right_constant); if (is_left_constant) { if (is_right_constant) { return Int32Constant(static_cast(left_constant) >> right_constant); } } else if (is_right_constant) { if (right_constant == 0) { return left; } } return UncheckedCast(raw_assembler()->Word32Shr(left, right)); } TNode CodeAssembler::Word32Sar(SloppyTNode left, SloppyTNode right) { int32_t left_constant; bool is_left_constant = ToInt32Constant(left, left_constant); int32_t right_constant; bool is_right_constant = ToInt32Constant(right, right_constant); if (is_left_constant) { if (is_right_constant) { return Int32Constant(left_constant >> right_constant); } } else if (is_right_constant) { if (right_constant == 0) { return left; } } return UncheckedCast(raw_assembler()->Word32Sar(left, right)); } TNode CodeAssembler::Word64Or(SloppyTNode left, SloppyTNode right) { int64_t left_constant; bool is_left_constant = ToInt64Constant(left, left_constant); int64_t right_constant; bool is_right_constant = ToInt64Constant(right, right_constant); if (is_left_constant) { if (is_right_constant) { return Int64Constant(left_constant | right_constant); } if (left_constant == 0) { return right; } } else if (is_right_constant) { if (right_constant == 0) { return left; } } return UncheckedCast(raw_assembler()->Word64Or(left, right)); } TNode CodeAssembler::Word64And(SloppyTNode left, SloppyTNode right) { int64_t left_constant; bool is_left_constant = ToInt64Constant(left, left_constant); int64_t right_constant; bool is_right_constant = ToInt64Constant(right, right_constant); if (is_left_constant) { if (is_right_constant) { return Int64Constant(left_constant & right_constant); } } return UncheckedCast(raw_assembler()->Word64And(left, right)); } TNode CodeAssembler::Word64Xor(SloppyTNode left, SloppyTNode right) { int64_t left_constant; bool is_left_constant = ToInt64Constant(left, left_constant); int64_t right_constant; bool is_right_constant = ToInt64Constant(right, right_constant); if (is_left_constant) { if (is_right_constant) { return Int64Constant(left_constant ^ right_constant); } } return UncheckedCast(raw_assembler()->Word64Xor(left, right)); } TNode CodeAssembler::Word64Shl(SloppyTNode left, SloppyTNode right) { int64_t left_constant; bool is_left_constant = ToInt64Constant(left, left_constant); int64_t right_constant; bool is_right_constant = ToInt64Constant(right, right_constant); if (is_left_constant) { if (is_right_constant) { return Int64Constant(left_constant << right_constant); } } else if (is_right_constant) { if (right_constant == 0) { return left; } } return UncheckedCast(raw_assembler()->Word64Shl(left, right)); } TNode CodeAssembler::Word64Shr(SloppyTNode left, SloppyTNode right) { int64_t left_constant; bool is_left_constant = ToInt64Constant(left, left_constant); int64_t right_constant; bool is_right_constant = ToInt64Constant(right, right_constant); if (is_left_constant) { if (is_right_constant) { return Int64Constant(static_cast(left_constant) >> right_constant); } } else if (is_right_constant) { if (right_constant == 0) { return left; } } return UncheckedCast(raw_assembler()->Word64Shr(left, right)); } TNode CodeAssembler::Word64Sar(SloppyTNode left, SloppyTNode right) { int64_t left_constant; bool is_left_constant = ToInt64Constant(left, left_constant); int64_t right_constant; bool is_right_constant = ToInt64Constant(right, right_constant); if (is_left_constant) { if (is_right_constant) { return Int64Constant(left_constant >> right_constant); } } else if (is_right_constant) { if (right_constant == 0) { return left; } } return UncheckedCast(raw_assembler()->Word64Sar(left, right)); } TNode CodeAssembler::ChangeUint32ToWord(SloppyTNode value) { if (raw_assembler()->machine()->Is64()) { return UncheckedCast( raw_assembler()->ChangeUint32ToUint64(value)); } return ReinterpretCast(value); } TNode CodeAssembler::ChangeInt32ToIntPtr(SloppyTNode value) { if (raw_assembler()->machine()->Is64()) { return ReinterpretCast(raw_assembler()->ChangeInt32ToInt64(value)); } return ReinterpretCast(value); } TNode CodeAssembler::ChangeFloat64ToUintPtr( SloppyTNode value) { if (raw_assembler()->machine()->Is64()) { return ReinterpretCast( raw_assembler()->ChangeFloat64ToUint64(value)); } return ReinterpretCast( raw_assembler()->ChangeFloat64ToUint32(value)); } Node* CodeAssembler::RoundIntPtrToFloat64(Node* value) { if (raw_assembler()->machine()->Is64()) { return raw_assembler()->RoundInt64ToFloat64(value); } return raw_assembler()->ChangeInt32ToFloat64(value); } #define DEFINE_CODE_ASSEMBLER_UNARY_OP(name, ResType, ArgType) \ TNode CodeAssembler::name(SloppyTNode a) { \ return UncheckedCast(raw_assembler()->name(a)); \ } CODE_ASSEMBLER_UNARY_OP_LIST(DEFINE_CODE_ASSEMBLER_UNARY_OP) #undef DEFINE_CODE_ASSEMBLER_UNARY_OP Node* CodeAssembler::Load(MachineType rep, Node* base) { return raw_assembler()->Load(rep, base); } Node* CodeAssembler::Load(MachineType rep, Node* base, Node* offset) { return raw_assembler()->Load(rep, base, offset); } Node* CodeAssembler::AtomicLoad(MachineType rep, Node* base, Node* offset) { return raw_assembler()->AtomicLoad(rep, base, offset); } TNode CodeAssembler::LoadRoot(Heap::RootListIndex root_index) { if (isolate()->heap()->RootCanBeTreatedAsConstant(root_index)) { Handle root = isolate()->heap()->root_handle(root_index); if (root->IsSmi()) { return SmiConstant(Smi::cast(*root)); } else { return HeapConstant(Handle::cast(root)); } } Node* roots_array_start = ExternalConstant(ExternalReference::roots_array_start(isolate())); return UncheckedCast(Load(MachineType::AnyTagged(), roots_array_start, IntPtrConstant(root_index * kPointerSize))); } Node* CodeAssembler::Store(Node* base, Node* value) { return raw_assembler()->Store(MachineRepresentation::kTagged, base, value, kFullWriteBarrier); } Node* CodeAssembler::Store(Node* base, Node* offset, Node* value) { return raw_assembler()->Store(MachineRepresentation::kTagged, base, offset, value, kFullWriteBarrier); } Node* CodeAssembler::StoreWithMapWriteBarrier(Node* base, Node* offset, Node* value) { return raw_assembler()->Store(MachineRepresentation::kTagged, base, offset, value, kMapWriteBarrier); } Node* CodeAssembler::StoreNoWriteBarrier(MachineRepresentation rep, Node* base, Node* value) { return raw_assembler()->Store(rep, base, value, kNoWriteBarrier); } Node* CodeAssembler::StoreNoWriteBarrier(MachineRepresentation rep, Node* base, Node* offset, Node* value) { return raw_assembler()->Store(rep, base, offset, value, kNoWriteBarrier); } Node* CodeAssembler::AtomicStore(MachineRepresentation rep, Node* base, Node* offset, Node* value) { return raw_assembler()->AtomicStore(rep, base, offset, value); } #define ATOMIC_FUNCTION(name) \ Node* CodeAssembler::Atomic##name(MachineType type, Node* base, \ Node* offset, Node* value) { \ return raw_assembler()->Atomic##name(type, base, offset, value); \ } ATOMIC_FUNCTION(Exchange); ATOMIC_FUNCTION(Add); ATOMIC_FUNCTION(Sub); ATOMIC_FUNCTION(And); ATOMIC_FUNCTION(Or); ATOMIC_FUNCTION(Xor); #undef ATOMIC_FUNCTION Node* CodeAssembler::AtomicCompareExchange(MachineType type, Node* base, Node* offset, Node* old_value, Node* new_value) { return raw_assembler()->AtomicCompareExchange(type, base, offset, old_value, new_value); } Node* CodeAssembler::StoreRoot(Heap::RootListIndex root_index, Node* value) { DCHECK(Heap::RootCanBeWrittenAfterInitialization(root_index)); Node* roots_array_start = ExternalConstant(ExternalReference::roots_array_start(isolate())); return StoreNoWriteBarrier(MachineRepresentation::kTagged, roots_array_start, IntPtrConstant(root_index * kPointerSize), value); } Node* CodeAssembler::Retain(Node* value) { return raw_assembler()->Retain(value); } Node* CodeAssembler::Projection(int index, Node* value) { return raw_assembler()->Projection(index, value); } void CodeAssembler::GotoIfException(Node* node, Label* if_exception, Variable* exception_var) { DCHECK(!node->op()->HasProperty(Operator::kNoThrow)); if (if_exception == nullptr) { // If no handler is supplied, don't add continuations return; } Label success(this), exception(this, Label::kDeferred); success.MergeVariables(); exception.MergeVariables(); raw_assembler()->Continuations(node, success.label_, exception.label_); Bind(&exception); const Operator* op = raw_assembler()->common()->IfException(); Node* exception_value = raw_assembler()->AddNode(op, node, node); if (exception_var != nullptr) { exception_var->Bind(exception_value); } Goto(if_exception); Bind(&success); } template TNode CodeAssembler::CallRuntimeImpl(Runtime::FunctionId function, SloppyTNode context, TArgs... args) { int argc = static_cast(sizeof...(args)); auto call_descriptor = Linkage::GetRuntimeCallDescriptor( zone(), function, argc, Operator::kNoProperties, CallDescriptor::kNoFlags); int return_count = static_cast(call_descriptor->ReturnCount()); Node* centry = HeapConstant(CodeFactory::RuntimeCEntry(isolate(), return_count)); Node* ref = ExternalConstant(ExternalReference(function, isolate())); Node* arity = Int32Constant(argc); Node* nodes[] = {centry, args..., ref, arity, context}; CallPrologue(); Node* return_value = raw_assembler()->CallN(call_descriptor, arraysize(nodes), nodes); CallEpilogue(); return UncheckedCast(return_value); } // Instantiate CallRuntime() for argument counts used by CSA-generated code #define INSTANTIATE(...) \ template V8_EXPORT_PRIVATE TNode CodeAssembler::CallRuntimeImpl( \ Runtime::FunctionId, __VA_ARGS__); REPEAT_1_TO_7(INSTANTIATE, SloppyTNode) #undef INSTANTIATE template TNode CodeAssembler::TailCallRuntimeImpl(Runtime::FunctionId function, SloppyTNode context, TArgs... args) { int argc = static_cast(sizeof...(args)); auto call_descriptor = Linkage::GetRuntimeCallDescriptor( zone(), function, argc, Operator::kNoProperties, CallDescriptor::kNoFlags); int return_count = static_cast(call_descriptor->ReturnCount()); Node* centry = HeapConstant(CodeFactory::RuntimeCEntry(isolate(), return_count)); Node* ref = ExternalConstant(ExternalReference(function, isolate())); Node* arity = Int32Constant(argc); Node* nodes[] = {centry, args..., ref, arity, context}; return UncheckedCast( raw_assembler()->TailCallN(call_descriptor, arraysize(nodes), nodes)); } // Instantiate TailCallRuntime() for argument counts used by CSA-generated code #define INSTANTIATE(...) \ template V8_EXPORT_PRIVATE TNode CodeAssembler::TailCallRuntimeImpl( \ Runtime::FunctionId, __VA_ARGS__); REPEAT_1_TO_7(INSTANTIATE, SloppyTNode) #undef INSTANTIATE template Node* CodeAssembler::CallStubR(const CallInterfaceDescriptor& descriptor, size_t result_size, Node* target, Node* context, TArgs... args) { Node* nodes[] = {target, args..., context}; int input_count = arraysize(nodes); if (context == nullptr) --input_count; return CallStubN(descriptor, result_size, input_count, nodes, context != nullptr); } // Instantiate CallStubR() for argument counts used by CSA-generated code. #define INSTANTIATE(...) \ template V8_EXPORT_PRIVATE Node* CodeAssembler::CallStubR( \ const CallInterfaceDescriptor& descriptor, size_t, Node*, __VA_ARGS__); REPEAT_1_TO_11(INSTANTIATE, Node*) #undef INSTANTIATE Node* CodeAssembler::CallStubN(const CallInterfaceDescriptor& descriptor, size_t result_size, int input_count, Node* const* inputs, bool pass_context) { // implicit nodes are target and optionally context. int implicit_nodes = pass_context ? 2 : 1; DCHECK_LE(implicit_nodes, input_count); int argc = input_count - implicit_nodes; DCHECK_LE(descriptor.GetParameterCount(), argc); // Extra arguments not mentioned in the descriptor are passed on the stack. int stack_parameter_count = argc - descriptor.GetRegisterParameterCount(); DCHECK_LE(descriptor.GetStackParameterCount(), stack_parameter_count); auto call_descriptor = Linkage::GetStubCallDescriptor( isolate(), zone(), descriptor, stack_parameter_count, CallDescriptor::kNoFlags, Operator::kNoProperties, MachineType::AnyTagged(), result_size, pass_context ? Linkage::kPassContext : Linkage::kNoContext); CallPrologue(); Node* return_value = raw_assembler()->CallN(call_descriptor, input_count, inputs); CallEpilogue(); return return_value; } template Node* CodeAssembler::TailCallStubImpl(const CallInterfaceDescriptor& descriptor, Node* target, Node* context, TArgs... args) { DCHECK_EQ(descriptor.GetParameterCount(), sizeof...(args)); size_t result_size = 1; auto call_descriptor = Linkage::GetStubCallDescriptor( isolate(), zone(), descriptor, descriptor.GetStackParameterCount(), CallDescriptor::kNoFlags, Operator::kNoProperties, MachineType::AnyTagged(), result_size); Node* nodes[] = {target, args..., context}; CHECK_EQ(descriptor.GetParameterCount() + 2, arraysize(nodes)); return raw_assembler()->TailCallN(call_descriptor, arraysize(nodes), nodes); } // Instantiate TailCallStub() for argument counts used by CSA-generated code #define INSTANTIATE(...) \ template V8_EXPORT_PRIVATE Node* CodeAssembler::TailCallStubImpl( \ const CallInterfaceDescriptor& descriptor, Node*, __VA_ARGS__); REPEAT_1_TO_12(INSTANTIATE, Node*) #undef INSTANTIATE template Node* CodeAssembler::TailCallStubThenBytecodeDispatch( const CallInterfaceDescriptor& descriptor, Node* target, Node* context, TArgs... args) { DCHECK_LE(descriptor.GetParameterCount(), sizeof...(args)); // Extra arguments not mentioned in the descriptor are passed on the stack. int stack_parameter_count = sizeof...(args) - descriptor.GetRegisterParameterCount(); DCHECK_LE(descriptor.GetStackParameterCount(), stack_parameter_count); auto call_descriptor = Linkage::GetStubCallDescriptor( isolate(), zone(), descriptor, stack_parameter_count, CallDescriptor::kNoFlags, Operator::kNoProperties, MachineType::AnyTagged(), 0); Node* nodes[] = {target, args..., context}; return raw_assembler()->TailCallN(call_descriptor, arraysize(nodes), nodes); } // Instantiate TailCallJSAndBytecodeDispatch() for argument counts used by // CSA-generated code #define INSTANTIATE(...) \ template V8_EXPORT_PRIVATE Node* \ CodeAssembler::TailCallStubThenBytecodeDispatch( \ const CallInterfaceDescriptor&, Node*, Node*, Node*, __VA_ARGS__); REPEAT_1_TO_7(INSTANTIATE, Node*) #undef INSTANTIATE template Node* CodeAssembler::TailCallBytecodeDispatch( const CallInterfaceDescriptor& descriptor, Node* target, TArgs... args) { DCHECK_EQ(descriptor.GetParameterCount(), sizeof...(args)); auto call_descriptor = Linkage::GetBytecodeDispatchCallDescriptor( isolate(), zone(), descriptor, descriptor.GetStackParameterCount()); Node* nodes[] = {target, args...}; CHECK_EQ(descriptor.GetParameterCount() + 1, arraysize(nodes)); return raw_assembler()->TailCallN(call_descriptor, arraysize(nodes), nodes); } // Instantiate TailCallBytecodeDispatch() for argument counts used by // CSA-generated code template V8_EXPORT_PRIVATE Node* CodeAssembler::TailCallBytecodeDispatch( const CallInterfaceDescriptor& descriptor, Node* target, Node*, Node*, Node*, Node*); Node* CodeAssembler::CallCFunctionN(Signature* signature, int input_count, Node* const* inputs) { auto call_descriptor = Linkage::GetSimplifiedCDescriptor(zone(), signature); return raw_assembler()->CallN(call_descriptor, input_count, inputs); } Node* CodeAssembler::CallCFunction1(MachineType return_type, MachineType arg0_type, Node* function, Node* arg0) { return raw_assembler()->CallCFunction1(return_type, arg0_type, function, arg0); } Node* CodeAssembler::CallCFunction1WithCallerSavedRegisters( MachineType return_type, MachineType arg0_type, Node* function, Node* arg0, SaveFPRegsMode mode) { DCHECK(return_type.LessThanOrEqualPointerSize()); return raw_assembler()->CallCFunction1WithCallerSavedRegisters( return_type, arg0_type, function, arg0, mode); } Node* CodeAssembler::CallCFunction2(MachineType return_type, MachineType arg0_type, MachineType arg1_type, Node* function, Node* arg0, Node* arg1) { return raw_assembler()->CallCFunction2(return_type, arg0_type, arg1_type, function, arg0, arg1); } Node* CodeAssembler::CallCFunction3(MachineType return_type, MachineType arg0_type, MachineType arg1_type, MachineType arg2_type, Node* function, Node* arg0, Node* arg1, Node* arg2) { return raw_assembler()->CallCFunction3(return_type, arg0_type, arg1_type, arg2_type, function, arg0, arg1, arg2); } Node* CodeAssembler::CallCFunction3WithCallerSavedRegisters( MachineType return_type, MachineType arg0_type, MachineType arg1_type, MachineType arg2_type, Node* function, Node* arg0, Node* arg1, Node* arg2, SaveFPRegsMode mode) { DCHECK(return_type.LessThanOrEqualPointerSize()); return raw_assembler()->CallCFunction3WithCallerSavedRegisters( return_type, arg0_type, arg1_type, arg2_type, function, arg0, arg1, arg2, mode); } Node* CodeAssembler::CallCFunction4( MachineType return_type, MachineType arg0_type, MachineType arg1_type, MachineType arg2_type, MachineType arg3_type, Node* function, Node* arg0, Node* arg1, Node* arg2, Node* arg3) { return raw_assembler()->CallCFunction4(return_type, arg0_type, arg1_type, arg2_type, arg3_type, function, arg0, arg1, arg2, arg3); } Node* CodeAssembler::CallCFunction5( MachineType return_type, MachineType arg0_type, MachineType arg1_type, MachineType arg2_type, MachineType arg3_type, MachineType arg4_type, Node* function, Node* arg0, Node* arg1, Node* arg2, Node* arg3, Node* arg4) { return raw_assembler()->CallCFunction5( return_type, arg0_type, arg1_type, arg2_type, arg3_type, arg4_type, function, arg0, arg1, arg2, arg3, arg4); } Node* CodeAssembler::CallCFunction6( MachineType return_type, MachineType arg0_type, MachineType arg1_type, MachineType arg2_type, MachineType arg3_type, MachineType arg4_type, MachineType arg5_type, Node* function, Node* arg0, Node* arg1, Node* arg2, Node* arg3, Node* arg4, Node* arg5) { return raw_assembler()->CallCFunction6( return_type, arg0_type, arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, function, arg0, arg1, arg2, arg3, arg4, arg5); } Node* CodeAssembler::CallCFunction9( MachineType return_type, MachineType arg0_type, MachineType arg1_type, MachineType arg2_type, MachineType arg3_type, MachineType arg4_type, MachineType arg5_type, MachineType arg6_type, MachineType arg7_type, MachineType arg8_type, Node* function, Node* arg0, Node* arg1, Node* arg2, Node* arg3, Node* arg4, Node* arg5, Node* arg6, Node* arg7, Node* arg8) { return raw_assembler()->CallCFunction9( return_type, arg0_type, arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, arg7_type, arg8_type, function, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); } void CodeAssembler::Goto(Label* label) { label->MergeVariables(); raw_assembler()->Goto(label->label_); } void CodeAssembler::GotoIf(SloppyTNode condition, Label* true_label) { Label false_label(this); Branch(condition, true_label, &false_label); Bind(&false_label); } void CodeAssembler::GotoIfNot(SloppyTNode condition, Label* false_label) { Label true_label(this); Branch(condition, &true_label, false_label); Bind(&true_label); } void CodeAssembler::Branch(SloppyTNode condition, Label* true_label, Label* false_label) { true_label->MergeVariables(); false_label->MergeVariables(); return raw_assembler()->Branch(condition, true_label->label_, false_label->label_); } void CodeAssembler::Switch(Node* index, Label* default_label, const int32_t* case_values, Label** case_labels, size_t case_count) { RawMachineLabel** labels = new (zone()->New(sizeof(RawMachineLabel*) * case_count)) RawMachineLabel*[case_count]; for (size_t i = 0; i < case_count; ++i) { labels[i] = case_labels[i]->label_; case_labels[i]->MergeVariables(); } default_label->MergeVariables(); return raw_assembler()->Switch(index, default_label->label_, case_values, labels, case_count); } bool CodeAssembler::UnalignedLoadSupported(MachineRepresentation rep) const { return raw_assembler()->machine()->UnalignedLoadSupported(rep); } bool CodeAssembler::UnalignedStoreSupported(MachineRepresentation rep) const { return raw_assembler()->machine()->UnalignedStoreSupported(rep); } // RawMachineAssembler delegate helpers: Isolate* CodeAssembler::isolate() const { return raw_assembler()->isolate(); } Factory* CodeAssembler::factory() const { return isolate()->factory(); } Zone* CodeAssembler::zone() const { return raw_assembler()->zone(); } RawMachineAssembler* CodeAssembler::raw_assembler() const { return state_->raw_assembler_.get(); } // The core implementation of Variable is stored through an indirection so // that it can outlive the often block-scoped Variable declarations. This is // needed to ensure that variable binding and merging through phis can // properly be verified. class CodeAssemblerVariable::Impl : public ZoneObject { public: explicit Impl(MachineRepresentation rep) : #if DEBUG debug_info_(AssemblerDebugInfo(nullptr, nullptr, -1)), #endif value_(nullptr), rep_(rep) { } #if DEBUG AssemblerDebugInfo debug_info() const { return debug_info_; } void set_debug_info(AssemblerDebugInfo debug_info) { debug_info_ = debug_info; } AssemblerDebugInfo debug_info_; #endif // DEBUG Node* value_; MachineRepresentation rep_; }; CodeAssemblerVariable::CodeAssemblerVariable(CodeAssembler* assembler, MachineRepresentation rep) : impl_(new (assembler->zone()) Impl(rep)), state_(assembler->state()) { state_->variables_.insert(impl_); } CodeAssemblerVariable::CodeAssemblerVariable(CodeAssembler* assembler, MachineRepresentation rep, Node* initial_value) : CodeAssemblerVariable(assembler, rep) { Bind(initial_value); } #if DEBUG CodeAssemblerVariable::CodeAssemblerVariable(CodeAssembler* assembler, AssemblerDebugInfo debug_info, MachineRepresentation rep) : impl_(new (assembler->zone()) Impl(rep)), state_(assembler->state()) { impl_->set_debug_info(debug_info); state_->variables_.insert(impl_); } CodeAssemblerVariable::CodeAssemblerVariable(CodeAssembler* assembler, AssemblerDebugInfo debug_info, MachineRepresentation rep, Node* initial_value) : CodeAssemblerVariable(assembler, debug_info, rep) { impl_->set_debug_info(debug_info); Bind(initial_value); } #endif // DEBUG CodeAssemblerVariable::~CodeAssemblerVariable() { state_->variables_.erase(impl_); } void CodeAssemblerVariable::Bind(Node* value) { impl_->value_ = value; } Node* CodeAssemblerVariable::value() const { #if DEBUG if (!IsBound()) { std::stringstream str; str << "#Use of unbound variable:" << "#\n Variable: " << *this << "#\n Current Block: "; state_->PrintCurrentBlock(str); FATAL("%s", str.str().c_str()); } if (!state_->InsideBlock()) { std::stringstream str; str << "#Accessing variable value outside a block:" << "#\n Variable: " << *this; FATAL("%s", str.str().c_str()); } #endif // DEBUG return impl_->value_; } MachineRepresentation CodeAssemblerVariable::rep() const { return impl_->rep_; } bool CodeAssemblerVariable::IsBound() const { return impl_->value_ != nullptr; } std::ostream& operator<<(std::ostream& os, const CodeAssemblerVariable::Impl& impl) { #if DEBUG AssemblerDebugInfo info = impl.debug_info(); if (info.name) os << "V" << info; #endif // DEBUG return os; } std::ostream& operator<<(std::ostream& os, const CodeAssemblerVariable& variable) { os << *variable.impl_; return os; } CodeAssemblerLabel::CodeAssemblerLabel(CodeAssembler* assembler, size_t vars_count, CodeAssemblerVariable* const* vars, CodeAssemblerLabel::Type type) : bound_(false), merge_count_(0), state_(assembler->state()), label_(nullptr) { void* buffer = assembler->zone()->New(sizeof(RawMachineLabel)); label_ = new (buffer) RawMachineLabel(type == kDeferred ? RawMachineLabel::kDeferred : RawMachineLabel::kNonDeferred); for (size_t i = 0; i < vars_count; ++i) { variable_phis_[vars[i]->impl_] = nullptr; } } CodeAssemblerLabel::~CodeAssemblerLabel() { label_->~RawMachineLabel(); } void CodeAssemblerLabel::MergeVariables() { ++merge_count_; for (CodeAssemblerVariable::Impl* var : state_->variables_) { size_t count = 0; Node* node = var->value_; if (node != nullptr) { auto i = variable_merges_.find(var); if (i != variable_merges_.end()) { i->second.push_back(node); count = i->second.size(); } else { count = 1; variable_merges_[var] = std::vector(1, node); } } // If the following asserts, then you've jumped to a label without a bound // variable along that path that expects to merge its value into a phi. DCHECK(variable_phis_.find(var) == variable_phis_.end() || count == merge_count_); USE(count); // If the label is already bound, we already know the set of variables to // merge and phi nodes have already been created. if (bound_) { auto phi = variable_phis_.find(var); if (phi != variable_phis_.end()) { DCHECK_NOT_NULL(phi->second); state_->raw_assembler_->AppendPhiInput(phi->second, node); } else { auto i = variable_merges_.find(var); if (i != variable_merges_.end()) { // If the following assert fires, then you've declared a variable that // has the same bound value along all paths up until the point you // bound this label, but then later merged a path with a new value for // the variable after the label bind (it's not possible to add phis to // the bound label after the fact, just make sure to list the variable // in the label's constructor's list of merged variables). #if DEBUG if (find_if(i->second.begin(), i->second.end(), [node](Node* e) -> bool { return node != e; }) != i->second.end()) { std::stringstream str; str << "Unmerged variable found when jumping to block. \n" << "# Variable: " << *var; if (bound_) { str << "\n# Target block: " << *label_->block(); } str << "\n# Current Block: "; state_->PrintCurrentBlock(str); FATAL("%s", str.str().c_str()); } #endif // DEBUG } } } } } #if DEBUG void CodeAssemblerLabel::Bind(AssemblerDebugInfo debug_info) { if (bound_) { std::stringstream str; str << "Cannot bind the same label twice:" << "\n# current: " << debug_info << "\n# previous: " << *label_->block(); FATAL("%s", str.str().c_str()); } state_->raw_assembler_->Bind(label_, debug_info); UpdateVariablesAfterBind(); } #endif // DEBUG void CodeAssemblerLabel::Bind() { DCHECK(!bound_); state_->raw_assembler_->Bind(label_); UpdateVariablesAfterBind(); } void CodeAssemblerLabel::UpdateVariablesAfterBind() { // Make sure that all variables that have changed along any path up to this // point are marked as merge variables. for (auto var : state_->variables_) { Node* shared_value = nullptr; auto i = variable_merges_.find(var); if (i != variable_merges_.end()) { for (auto value : i->second) { DCHECK_NOT_NULL(value); if (value != shared_value) { if (shared_value == nullptr) { shared_value = value; } else { variable_phis_[var] = nullptr; } } } } } for (auto var : variable_phis_) { CodeAssemblerVariable::Impl* var_impl = var.first; auto i = variable_merges_.find(var_impl); #if DEBUG bool not_found = i == variable_merges_.end(); if (not_found || i->second.size() != merge_count_) { std::stringstream str; str << "A variable that has been marked as beeing merged at the label" << "\n# doesn't have a bound value along all of the paths that " << "\n# have been merged into the label up to this point." << "\n#" << "\n# This can happen in the following cases:" << "\n# - By explicitly marking it so in the label constructor" << "\n# - By having seen different bound values at branches" << "\n#" << "\n# Merge count: expected=" << merge_count_ << " vs. found=" << (not_found ? 0 : i->second.size()) << "\n# Variable: " << *var_impl << "\n# Current Block: " << *label_->block(); FATAL("%s", str.str().c_str()); } #endif // DEBUG Node* phi = state_->raw_assembler_->Phi( var.first->rep_, static_cast(merge_count_), &(i->second[0])); variable_phis_[var_impl] = phi; } // Bind all variables to a merge phi, the common value along all paths or // null. for (auto var : state_->variables_) { auto i = variable_phis_.find(var); if (i != variable_phis_.end()) { var->value_ = i->second; } else { auto j = variable_merges_.find(var); if (j != variable_merges_.end() && j->second.size() == merge_count_) { var->value_ = j->second.back(); } else { var->value_ = nullptr; } } } bound_ = true; } } // namespace compiler Smi* CheckObjectType(Object* value, Smi* type, String* location) { #ifdef DEBUG const char* expected; switch (static_cast(type->value())) { #define TYPE_CASE(Name) \ case ObjectType::k##Name: \ if (value->Is##Name()) return Smi::FromInt(0); \ expected = #Name; \ break; #define TYPE_STRUCT_CASE(NAME, Name, name) \ case ObjectType::k##Name: \ if (value->Is##Name()) return Smi::FromInt(0); \ expected = #Name; \ break; TYPE_CASE(Object) OBJECT_TYPE_LIST(TYPE_CASE) HEAP_OBJECT_TYPE_LIST(TYPE_CASE) STRUCT_LIST(TYPE_STRUCT_CASE) #undef TYPE_CASE #undef TYPE_STRUCT_CASE } std::stringstream value_description; value->Print(value_description); V8_Fatal(__FILE__, __LINE__, "Type cast failed in %s\n" " Expected %s but found %s", location->ToAsciiArray(), expected, value_description.str().c_str()); #else UNREACHABLE(); #endif } } // namespace internal } // namespace v8