| // 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 <ostream> |
| |
| #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" |
| |
| #if V8_OS_STARBOARD |
| #include "src/poems.h" |
| #endif |
| |
| #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<Smi>::value; |
| constexpr MachineType MachineTypeOf<Object>::value; |
| |
| namespace compiler { |
| |
| static_assert(std::is_convertible<TNode<Number>, TNode<Object>>::value, |
| "test subtyping"); |
| static_assert(std::is_convertible<TNode<UnionT<Smi, HeapNumber>>, |
| TNode<UnionT<Smi, HeapObject>>>::value, |
| "test subtyping"); |
| static_assert( |
| !std::is_convertible<TNode<UnionT<Smi, HeapObject>>, TNode<Number>>::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) |
| : 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<int>(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<NodeId>(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<Code> 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> 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<Int32T> CodeAssembler::Int32Constant(int32_t value) { |
| return UncheckedCast<Int32T>(raw_assembler()->Int32Constant(value)); |
| } |
| |
| TNode<Int64T> CodeAssembler::Int64Constant(int64_t value) { |
| return UncheckedCast<Int64T>(raw_assembler()->Int64Constant(value)); |
| } |
| |
| TNode<IntPtrT> CodeAssembler::IntPtrConstant(intptr_t value) { |
| return UncheckedCast<IntPtrT>(raw_assembler()->IntPtrConstant(value)); |
| } |
| |
| TNode<Number> CodeAssembler::NumberConstant(double value) { |
| int smi_value; |
| if (DoubleToSmiInteger(value, &smi_value)) { |
| return UncheckedCast<Number>(SmiConstant(smi_value)); |
| } else { |
| return UncheckedCast<Number>(raw_assembler()->NumberConstant(value)); |
| } |
| } |
| |
| TNode<Smi> CodeAssembler::SmiConstant(Smi* value) { |
| return UncheckedCast<Smi>( |
| BitcastWordToTaggedSigned(IntPtrConstant(bit_cast<intptr_t>(value)))); |
| } |
| |
| TNode<Smi> CodeAssembler::SmiConstant(int value) { |
| return SmiConstant(Smi::FromInt(value)); |
| } |
| |
| TNode<HeapObject> CodeAssembler::UntypedHeapConstant( |
| Handle<HeapObject> object) { |
| return UncheckedCast<HeapObject>(raw_assembler()->HeapConstant(object)); |
| } |
| |
| TNode<String> CodeAssembler::StringConstant(const char* str) { |
| return UncheckedCast<String>( |
| HeapConstant(factory()->NewStringFromAsciiChecked(str, TENURED))); |
| } |
| |
| TNode<Oddball> CodeAssembler::BooleanConstant(bool value) { |
| return UncheckedCast<Oddball>(raw_assembler()->BooleanConstant(value)); |
| } |
| |
| TNode<ExternalReference> CodeAssembler::ExternalConstant( |
| ExternalReference address) { |
| return UncheckedCast<ExternalReference>( |
| raw_assembler()->ExternalConstant(address)); |
| } |
| |
| TNode<Float64T> CodeAssembler::Float64Constant(double value) { |
| return UncheckedCast<Float64T>(raw_assembler()->Float64Constant(value)); |
| } |
| |
| TNode<HeapNumber> CodeAssembler::NaNConstant() { |
| return UncheckedCast<HeapNumber>(LoadRoot(Heap::kNanValueRootIndex)); |
| } |
| |
| bool CodeAssembler::ToInt32Constant(Node* node, int32_t& out_value) { |
| Int64Matcher m(node); |
| if (m.HasValue() && |
| m.IsInRange(std::numeric_limits<int32_t>::min(), |
| std::numeric_limits<int32_t>::max())) { |
| out_value = static_cast<int32_t>(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<intptr_t>(1) << kSmiShiftSize) - 1)); |
| out_value = Smi::cast(bit_cast<Object*>(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<Context> CodeAssembler::GetJSContextParameter() { |
| CallDescriptor* desc = raw_assembler()->call_descriptor(); |
| DCHECK(desc->IsJSFunctionCall()); |
| return CAST(Parameter(Linkage::GetJSCallContextParamIndex( |
| static_cast<int>(desc->JSParameterCount())))); |
| } |
| |
| void CodeAssembler::Return(SloppyTNode<Object> value) { |
| return raw_assembler()->Return(value); |
| } |
| |
| void CodeAssembler::Return(SloppyTNode<Object> value1, |
| SloppyTNode<Object> value2) { |
| return raw_assembler()->Return(value1, value2); |
| } |
| |
| void CodeAssembler::Return(SloppyTNode<Object> value1, |
| SloppyTNode<Object> value2, |
| SloppyTNode<Object> 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<char*>(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(); |
| } |
| |
| #define DEFINE_CODE_ASSEMBLER_BINARY_OP(name, ResType, Arg1Type, Arg2Type) \ |
| TNode<ResType> CodeAssembler::name(SloppyTNode<Arg1Type> a, \ |
| SloppyTNode<Arg2Type> b) { \ |
| return UncheckedCast<ResType>(raw_assembler()->name(a, b)); \ |
| } |
| CODE_ASSEMBLER_BINARY_OP_LIST(DEFINE_CODE_ASSEMBLER_BINARY_OP) |
| #undef DEFINE_CODE_ASSEMBLER_BINARY_OP |
| |
| TNode<WordT> CodeAssembler::IntPtrAdd(SloppyTNode<WordT> left, |
| SloppyTNode<WordT> 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<WordT>(raw_assembler()->IntPtrAdd(left, right)); |
| } |
| |
| TNode<WordT> CodeAssembler::IntPtrSub(SloppyTNode<WordT> left, |
| SloppyTNode<WordT> 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<IntPtrT>(raw_assembler()->IntPtrSub(left, right)); |
| } |
| |
| TNode<WordT> CodeAssembler::IntPtrMul(SloppyTNode<WordT> left, |
| SloppyTNode<WordT> 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<IntPtrT>(raw_assembler()->IntPtrMul(left, right)); |
| } |
| |
| TNode<WordT> CodeAssembler::WordShl(SloppyTNode<WordT> value, int shift) { |
| return (shift != 0) ? WordShl(value, IntPtrConstant(shift)) : value; |
| } |
| |
| TNode<WordT> CodeAssembler::WordShr(SloppyTNode<WordT> value, int shift) { |
| return (shift != 0) ? WordShr(value, IntPtrConstant(shift)) : value; |
| } |
| |
| TNode<Word32T> CodeAssembler::Word32Shr(SloppyTNode<Word32T> value, int shift) { |
| return (shift != 0) ? Word32Shr(value, Int32Constant(shift)) : value; |
| } |
| |
| TNode<WordT> CodeAssembler::WordOr(SloppyTNode<WordT> left, |
| SloppyTNode<WordT> 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<WordT>(raw_assembler()->WordOr(left, right)); |
| } |
| |
| TNode<WordT> CodeAssembler::WordAnd(SloppyTNode<WordT> left, |
| SloppyTNode<WordT> 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<WordT>(raw_assembler()->WordAnd(left, right)); |
| } |
| |
| TNode<WordT> CodeAssembler::WordXor(SloppyTNode<WordT> left, |
| SloppyTNode<WordT> 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<WordT>(raw_assembler()->WordXor(left, right)); |
| } |
| |
| TNode<WordT> CodeAssembler::WordShl(SloppyTNode<WordT> left, |
| SloppyTNode<IntegralT> 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<WordT>(raw_assembler()->WordShl(left, right)); |
| } |
| |
| TNode<WordT> CodeAssembler::WordShr(SloppyTNode<WordT> left, |
| SloppyTNode<IntegralT> 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<uintptr_t>(left_constant) >> |
| right_constant); |
| } |
| } else if (is_right_constant) { |
| if (right_constant == 0) { |
| return left; |
| } |
| } |
| return UncheckedCast<WordT>(raw_assembler()->WordShr(left, right)); |
| } |
| |
| TNode<WordT> CodeAssembler::WordSar(SloppyTNode<WordT> left, |
| SloppyTNode<IntegralT> 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<WordT>(raw_assembler()->WordSar(left, right)); |
| } |
| |
| TNode<Word32T> CodeAssembler::Word32Or(SloppyTNode<Word32T> left, |
| SloppyTNode<Word32T> 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<Word32T>(raw_assembler()->Word32Or(left, right)); |
| } |
| |
| TNode<Word32T> CodeAssembler::Word32And(SloppyTNode<Word32T> left, |
| SloppyTNode<Word32T> 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<Word32T>(raw_assembler()->Word32And(left, right)); |
| } |
| |
| TNode<Word32T> CodeAssembler::Word32Xor(SloppyTNode<Word32T> left, |
| SloppyTNode<Word32T> 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<Word32T>(raw_assembler()->Word32Xor(left, right)); |
| } |
| |
| TNode<Word32T> CodeAssembler::Word32Shl(SloppyTNode<Word32T> left, |
| SloppyTNode<Word32T> 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<Word32T>(raw_assembler()->Word32Shl(left, right)); |
| } |
| |
| TNode<Word32T> CodeAssembler::Word32Shr(SloppyTNode<Word32T> left, |
| SloppyTNode<Word32T> 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<uint32_t>(left_constant) >> |
| right_constant); |
| } |
| } else if (is_right_constant) { |
| if (right_constant == 0) { |
| return left; |
| } |
| } |
| return UncheckedCast<Word32T>(raw_assembler()->Word32Shr(left, right)); |
| } |
| |
| TNode<Word32T> CodeAssembler::Word32Sar(SloppyTNode<Word32T> left, |
| SloppyTNode<Word32T> 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<Word32T>(raw_assembler()->Word32Sar(left, right)); |
| } |
| |
| TNode<Word64T> CodeAssembler::Word64Or(SloppyTNode<Word64T> left, |
| SloppyTNode<Word64T> 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<Word64T>(raw_assembler()->Word64Or(left, right)); |
| } |
| |
| TNode<Word64T> CodeAssembler::Word64And(SloppyTNode<Word64T> left, |
| SloppyTNode<Word64T> 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<Word64T>(raw_assembler()->Word64And(left, right)); |
| } |
| |
| TNode<Word64T> CodeAssembler::Word64Xor(SloppyTNode<Word64T> left, |
| SloppyTNode<Word64T> 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<Word64T>(raw_assembler()->Word64Xor(left, right)); |
| } |
| |
| TNode<Word64T> CodeAssembler::Word64Shl(SloppyTNode<Word64T> left, |
| SloppyTNode<Word64T> 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<Word64T>(raw_assembler()->Word64Shl(left, right)); |
| } |
| |
| TNode<Word64T> CodeAssembler::Word64Shr(SloppyTNode<Word64T> left, |
| SloppyTNode<Word64T> 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<uint64_t>(left_constant) >> |
| right_constant); |
| } |
| } else if (is_right_constant) { |
| if (right_constant == 0) { |
| return left; |
| } |
| } |
| return UncheckedCast<Word64T>(raw_assembler()->Word64Shr(left, right)); |
| } |
| |
| TNode<Word64T> CodeAssembler::Word64Sar(SloppyTNode<Word64T> left, |
| SloppyTNode<Word64T> 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<Word64T>(raw_assembler()->Word64Sar(left, right)); |
| } |
| |
| TNode<UintPtrT> CodeAssembler::ChangeUint32ToWord(SloppyTNode<Word32T> value) { |
| if (raw_assembler()->machine()->Is64()) { |
| return UncheckedCast<UintPtrT>( |
| raw_assembler()->ChangeUint32ToUint64(value)); |
| } |
| return ReinterpretCast<UintPtrT>(value); |
| } |
| |
| TNode<IntPtrT> CodeAssembler::ChangeInt32ToIntPtr(SloppyTNode<Word32T> value) { |
| if (raw_assembler()->machine()->Is64()) { |
| return ReinterpretCast<IntPtrT>(raw_assembler()->ChangeInt32ToInt64(value)); |
| } |
| return ReinterpretCast<IntPtrT>(value); |
| } |
| |
| TNode<UintPtrT> CodeAssembler::ChangeFloat64ToUintPtr( |
| SloppyTNode<Float64T> value) { |
| if (raw_assembler()->machine()->Is64()) { |
| return ReinterpretCast<UintPtrT>( |
| raw_assembler()->ChangeFloat64ToUint64(value)); |
| } |
| return ReinterpretCast<UintPtrT>( |
| 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<ResType> CodeAssembler::name(SloppyTNode<ArgType> a) { \ |
| return UncheckedCast<ResType>(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<Object> CodeAssembler::LoadRoot(Heap::RootListIndex root_index) { |
| if (isolate()->heap()->RootCanBeTreatedAsConstant(root_index)) { |
| Handle<Object> root = isolate()->heap()->root_handle(root_index); |
| if (root->IsSmi()) { |
| return SmiConstant(Smi::cast(*root)); |
| } else { |
| return HeapConstant(Handle<HeapObject>::cast(root)); |
| } |
| } |
| |
| Node* roots_array_start = |
| ExternalConstant(ExternalReference::roots_array_start(isolate())); |
| return UncheckedCast<Object>(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 <class... TArgs> |
| TNode<Object> CodeAssembler::CallRuntimeImpl(Runtime::FunctionId function, |
| SloppyTNode<Object> context, |
| TArgs... args) { |
| int argc = static_cast<int>(sizeof...(args)); |
| CallDescriptor* desc = Linkage::GetRuntimeCallDescriptor( |
| zone(), function, argc, Operator::kNoProperties, |
| CallDescriptor::kNoFlags); |
| int return_count = static_cast<int>(desc->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(desc, arraysize(nodes), nodes); |
| CallEpilogue(); |
| return UncheckedCast<Object>(return_value); |
| } |
| |
| // Instantiate CallRuntime() for argument counts used by CSA-generated code |
| #define INSTANTIATE(...) \ |
| template V8_EXPORT_PRIVATE TNode<Object> CodeAssembler::CallRuntimeImpl( \ |
| Runtime::FunctionId, __VA_ARGS__); |
| REPEAT_1_TO_7(INSTANTIATE, SloppyTNode<Object>) |
| #undef INSTANTIATE |
| |
| template <class... TArgs> |
| TNode<Object> CodeAssembler::TailCallRuntimeImpl(Runtime::FunctionId function, |
| SloppyTNode<Object> context, |
| TArgs... args) { |
| int argc = static_cast<int>(sizeof...(args)); |
| CallDescriptor* desc = Linkage::GetRuntimeCallDescriptor( |
| zone(), function, argc, Operator::kNoProperties, |
| CallDescriptor::kNoFlags); |
| int return_count = static_cast<int>(desc->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<Object>( |
| raw_assembler()->TailCallN(desc, arraysize(nodes), nodes)); |
| } |
| |
| // Instantiate TailCallRuntime() for argument counts used by CSA-generated code |
| #define INSTANTIATE(...) \ |
| template V8_EXPORT_PRIVATE TNode<Object> CodeAssembler::TailCallRuntimeImpl( \ |
| Runtime::FunctionId, __VA_ARGS__); |
| REPEAT_1_TO_7(INSTANTIATE, SloppyTNode<Object>) |
| #undef INSTANTIATE |
| |
| template <class... TArgs> |
| 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); |
| CallDescriptor* desc = 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(desc, input_count, inputs); |
| CallEpilogue(); |
| return return_value; |
| } |
| |
| template <class... TArgs> |
| Node* CodeAssembler::TailCallStubImpl(const CallInterfaceDescriptor& descriptor, |
| Node* target, Node* context, |
| TArgs... args) { |
| DCHECK_EQ(descriptor.GetParameterCount(), sizeof...(args)); |
| size_t result_size = 1; |
| CallDescriptor* desc = 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(desc, 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 <class... TArgs> |
| 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); |
| CallDescriptor* desc = Linkage::GetStubCallDescriptor( |
| isolate(), zone(), descriptor, stack_parameter_count, |
| CallDescriptor::kNoFlags, Operator::kNoProperties, |
| MachineType::AnyTagged(), 0); |
| |
| Node* nodes[] = {target, args..., context}; |
| return raw_assembler()->TailCallN(desc, 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 <class... TArgs> |
| Node* CodeAssembler::TailCallBytecodeDispatch( |
| const CallInterfaceDescriptor& descriptor, Node* target, TArgs... args) { |
| DCHECK_EQ(descriptor.GetParameterCount(), sizeof...(args)); |
| CallDescriptor* desc = Linkage::GetBytecodeDispatchCallDescriptor( |
| isolate(), zone(), descriptor, descriptor.GetStackParameterCount()); |
| |
| Node* nodes[] = {target, args...}; |
| CHECK_EQ(descriptor.GetParameterCount() + 1, arraysize(nodes)); |
| return raw_assembler()->TailCallN(desc, 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<MachineType>* signature, |
| int input_count, Node* const* inputs) { |
| CallDescriptor* desc = Linkage::GetSimplifiedCDescriptor(zone(), signature); |
| return raw_assembler()->CallN(desc, 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<IntegralT> condition, |
| Label* true_label) { |
| Label false_label(this); |
| Branch(condition, true_label, &false_label); |
| Bind(&false_label); |
| } |
| |
| void CodeAssembler::GotoIfNot(SloppyTNode<IntegralT> condition, |
| Label* false_label) { |
| Label true_label(this); |
| Branch(condition, &true_label, false_label); |
| Bind(&true_label); |
| } |
| |
| void CodeAssembler::Branch(SloppyTNode<IntegralT> 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<Node*>(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<int>(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<ObjectType>(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 |