| // Copyright 2014 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. |
| |
| #ifndef V8_CCTEST_COMPILER_GRAPH_BUILDER_TESTER_H_ |
| #define V8_CCTEST_COMPILER_GRAPH_BUILDER_TESTER_H_ |
| |
| #include "src/compilation-info.h" |
| #include "src/compiler/common-operator.h" |
| #include "src/compiler/instruction-selector.h" |
| #include "src/compiler/linkage.h" |
| #include "src/compiler/machine-operator.h" |
| #include "src/compiler/operator-properties.h" |
| #include "src/compiler/pipeline.h" |
| #include "src/compiler/simplified-operator.h" |
| #include "test/cctest/cctest.h" |
| #include "test/cctest/compiler/call-tester.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace compiler { |
| |
| class GraphAndBuilders { |
| public: |
| explicit GraphAndBuilders(Zone* zone) |
| : main_graph_(new (zone) Graph(zone)), |
| main_common_(zone), |
| main_machine_(zone, MachineType::PointerRepresentation(), |
| InstructionSelector::SupportedMachineOperatorFlags(), |
| InstructionSelector::AlignmentRequirements()), |
| main_simplified_(zone) {} |
| |
| Graph* graph() const { return main_graph_; } |
| Zone* zone() const { return graph()->zone(); } |
| CommonOperatorBuilder* common() { return &main_common_; } |
| MachineOperatorBuilder* machine() { return &main_machine_; } |
| SimplifiedOperatorBuilder* simplified() { return &main_simplified_; } |
| |
| protected: |
| // Prefixed with main_ to avoid naming conflicts. |
| Graph* main_graph_; |
| CommonOperatorBuilder main_common_; |
| MachineOperatorBuilder main_machine_; |
| SimplifiedOperatorBuilder main_simplified_; |
| }; |
| |
| |
| template <typename ReturnType> |
| class GraphBuilderTester : public HandleAndZoneScope, |
| public GraphAndBuilders, |
| public CallHelper<ReturnType> { |
| public: |
| template <typename... ParamMachTypes> |
| explicit GraphBuilderTester(ParamMachTypes... p) |
| : GraphAndBuilders(main_zone()), |
| CallHelper<ReturnType>( |
| main_isolate(), |
| CSignature::New(main_zone(), MachineTypeForC<ReturnType>(), p...)), |
| effect_(nullptr), |
| return_(nullptr), |
| parameters_(main_zone()->template NewArray<Node*>(parameter_count())) { |
| Begin(static_cast<int>(parameter_count())); |
| InitParameters(); |
| } |
| virtual ~GraphBuilderTester() {} |
| |
| void GenerateCode() { Generate(); } |
| Node* Parameter(size_t index) { |
| CHECK_LT(index, parameter_count()); |
| return parameters_[index]; |
| } |
| |
| Isolate* isolate() { return main_isolate(); } |
| Factory* factory() { return isolate()->factory(); } |
| |
| // Initialize graph and builder. |
| void Begin(int num_parameters) { |
| CHECK_NULL(graph()->start()); |
| Node* start = graph()->NewNode(common()->Start(num_parameters + 3)); |
| graph()->SetStart(start); |
| effect_ = start; |
| } |
| |
| void Return(Node* value) { |
| Node* zero = graph()->NewNode(common()->Int32Constant(0)); |
| return_ = graph()->NewNode(common()->Return(), zero, value, effect_, |
| graph()->start()); |
| effect_ = nullptr; |
| } |
| |
| // Close the graph. |
| void End() { |
| Node* end = graph()->NewNode(common()->End(1), return_); |
| graph()->SetEnd(end); |
| } |
| |
| Node* PointerConstant(void* value) { |
| intptr_t intptr_value = reinterpret_cast<intptr_t>(value); |
| return kPointerSize == 8 ? NewNode(common()->Int64Constant(intptr_value)) |
| : Int32Constant(static_cast<int>(intptr_value)); |
| } |
| Node* Int32Constant(int32_t value) { |
| return NewNode(common()->Int32Constant(value)); |
| } |
| Node* HeapConstant(Handle<HeapObject> object) { |
| return NewNode(common()->HeapConstant(object)); |
| } |
| |
| Node* BooleanNot(Node* a) { return NewNode(simplified()->BooleanNot(), a); } |
| |
| Node* NumberEqual(Node* a, Node* b) { |
| return NewNode(simplified()->NumberEqual(), a, b); |
| } |
| Node* NumberLessThan(Node* a, Node* b) { |
| return NewNode(simplified()->NumberLessThan(), a, b); |
| } |
| Node* NumberLessThanOrEqual(Node* a, Node* b) { |
| return NewNode(simplified()->NumberLessThanOrEqual(), a, b); |
| } |
| Node* NumberAdd(Node* a, Node* b) { |
| return NewNode(simplified()->NumberAdd(), a, b); |
| } |
| Node* NumberSubtract(Node* a, Node* b) { |
| return NewNode(simplified()->NumberSubtract(), a, b); |
| } |
| Node* NumberMultiply(Node* a, Node* b) { |
| return NewNode(simplified()->NumberMultiply(), a, b); |
| } |
| Node* NumberDivide(Node* a, Node* b) { |
| return NewNode(simplified()->NumberDivide(), a, b); |
| } |
| Node* NumberModulus(Node* a, Node* b) { |
| return NewNode(simplified()->NumberModulus(), a, b); |
| } |
| Node* NumberToInt32(Node* a) { |
| return NewNode(simplified()->NumberToInt32(), a); |
| } |
| Node* NumberToUint32(Node* a) { |
| return NewNode(simplified()->NumberToUint32(), a); |
| } |
| |
| Node* StringEqual(Node* a, Node* b) { |
| return NewNode(simplified()->StringEqual(), a, b); |
| } |
| Node* StringLessThan(Node* a, Node* b) { |
| return NewNode(simplified()->StringLessThan(), a, b); |
| } |
| Node* StringLessThanOrEqual(Node* a, Node* b) { |
| return NewNode(simplified()->StringLessThanOrEqual(), a, b); |
| } |
| |
| Node* ChangeTaggedToInt32(Node* a) { |
| return NewNode(simplified()->ChangeTaggedToInt32(), a); |
| } |
| Node* ChangeTaggedToUint32(Node* a) { |
| return NewNode(simplified()->ChangeTaggedToUint32(), a); |
| } |
| Node* ChangeTaggedToFloat64(Node* a) { |
| return NewNode(simplified()->ChangeTaggedToFloat64(), a); |
| } |
| Node* ChangeInt32ToTagged(Node* a) { |
| return NewNode(simplified()->ChangeInt32ToTagged(), a); |
| } |
| Node* ChangeUint32ToTagged(Node* a) { |
| return NewNode(simplified()->ChangeUint32ToTagged(), a); |
| } |
| Node* ChangeFloat64ToTagged(Node* a) { |
| return NewNode(simplified()->ChangeFloat64ToTagged(), a); |
| } |
| Node* ChangeTaggedToBit(Node* a) { |
| return NewNode(simplified()->ChangeTaggedToBit(), a); |
| } |
| Node* ChangeBitToTagged(Node* a) { |
| return NewNode(simplified()->ChangeBitToTagged(), a); |
| } |
| |
| Node* LoadField(const FieldAccess& access, Node* object) { |
| return NewNode(simplified()->LoadField(access), object); |
| } |
| Node* StoreField(const FieldAccess& access, Node* object, Node* value) { |
| return NewNode(simplified()->StoreField(access), object, value); |
| } |
| Node* LoadElement(const ElementAccess& access, Node* object, Node* index) { |
| return NewNode(simplified()->LoadElement(access), object, index); |
| } |
| Node* StoreElement(const ElementAccess& access, Node* object, Node* index, |
| Node* value) { |
| return NewNode(simplified()->StoreElement(access), object, index, value); |
| } |
| |
| template <typename... NodePtrs> |
| Node* NewNode(const Operator* op, NodePtrs... n) { |
| std::array<Node*, sizeof...(n)> inputs{{n...}}; |
| return MakeNode(op, inputs.size(), inputs.data()); |
| } |
| |
| Node* NewNode(const Operator* op, int value_input_count, |
| Node** value_inputs) { |
| return MakeNode(op, value_input_count, value_inputs); |
| } |
| |
| Handle<Code> GetCode() { |
| Generate(); |
| return code_.ToHandleChecked(); |
| } |
| |
| protected: |
| Node* MakeNode(const Operator* op, int value_input_count, |
| Node** value_inputs) { |
| CHECK_EQ(op->ValueInputCount(), value_input_count); |
| |
| CHECK(!OperatorProperties::HasContextInput(op)); |
| CHECK(!OperatorProperties::HasFrameStateInput(op)); |
| bool has_control = op->ControlInputCount() == 1; |
| bool has_effect = op->EffectInputCount() == 1; |
| |
| CHECK_LT(op->ControlInputCount(), 2); |
| CHECK_LT(op->EffectInputCount(), 2); |
| |
| Node* result = nullptr; |
| if (!has_control && !has_effect) { |
| result = graph()->NewNode(op, value_input_count, value_inputs); |
| } else { |
| int input_count_with_deps = value_input_count; |
| if (has_control) ++input_count_with_deps; |
| if (has_effect) ++input_count_with_deps; |
| Node** buffer = zone()->template NewArray<Node*>(input_count_with_deps); |
| memcpy(buffer, value_inputs, kPointerSize * value_input_count); |
| Node** current_input = buffer + value_input_count; |
| if (has_effect) { |
| *current_input++ = effect_; |
| } |
| if (has_control) { |
| *current_input++ = graph()->start(); |
| } |
| result = graph()->NewNode(op, input_count_with_deps, buffer); |
| if (has_effect) { |
| effect_ = result; |
| } |
| // This graph builder does not support control flow. |
| CHECK_EQ(0, op->ControlOutputCount()); |
| } |
| |
| return result; |
| } |
| |
| virtual byte* Generate() { |
| if (code_.is_null()) { |
| Zone* zone = graph()->zone(); |
| CallDescriptor* desc = |
| Linkage::GetSimplifiedCDescriptor(zone, this->csig_); |
| CompilationInfo info(ArrayVector("testing"), main_zone(), Code::STUB); |
| code_ = Pipeline::GenerateCodeForTesting(&info, main_isolate(), desc, |
| graph()); |
| #ifdef ENABLE_DISASSEMBLER |
| if (!code_.is_null() && FLAG_print_opt_code) { |
| OFStream os(stdout); |
| code_.ToHandleChecked()->Disassemble("test code", os); |
| } |
| #endif |
| } |
| return code_.ToHandleChecked()->entry(); |
| } |
| |
| void InitParameters() { |
| int param_count = static_cast<int>(parameter_count()); |
| for (int i = 0; i < param_count; ++i) { |
| parameters_[i] = this->NewNode(common()->Parameter(i), graph()->start()); |
| } |
| } |
| |
| size_t parameter_count() const { return this->csig_->parameter_count(); } |
| |
| private: |
| Node* effect_; |
| Node* return_; |
| Node** parameters_; |
| MaybeHandle<Code> code_; |
| }; |
| |
| } // namespace compiler |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_CCTEST_COMPILER_GRAPH_BUILDER_TESTER_H_ |