Line data Source code
1 : // Copyright 2014 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #ifndef V8_CCTEST_COMPILER_CODEGEN_TESTER_H_
6 : #define V8_CCTEST_COMPILER_CODEGEN_TESTER_H_
7 :
8 : #include "src/compiler/backend/instruction-selector.h"
9 : #include "src/compiler/pipeline.h"
10 : #include "src/compiler/raw-machine-assembler.h"
11 : #include "src/optimized-compilation-info.h"
12 : #include "src/simulator.h"
13 : #include "test/cctest/cctest.h"
14 : #include "test/cctest/compiler/call-tester.h"
15 :
16 : namespace v8 {
17 : namespace internal {
18 : namespace compiler {
19 :
20 : template <typename ReturnType>
21 : class RawMachineAssemblerTester : public HandleAndZoneScope,
22 : public CallHelper<ReturnType>,
23 : public RawMachineAssembler {
24 : public:
25 : template <typename... ParamMachTypes>
26 204835 : explicit RawMachineAssemblerTester(ParamMachTypes... p)
27 : : HandleAndZoneScope(),
28 : CallHelper<ReturnType>(
29 : main_isolate(),
30 204835 : CSignature::New(main_zone(), MachineTypeForC<ReturnType>(), p...)),
31 : RawMachineAssembler(
32 : main_isolate(), new (main_zone()) Graph(main_zone()),
33 : Linkage::GetSimplifiedCDescriptor(
34 : main_zone(),
35 : CSignature::New(main_zone(), MachineTypeForC<ReturnType>(),
36 204835 : p...),
37 : true),
38 : MachineType::PointerRepresentation(),
39 : InstructionSelector::SupportedMachineOperatorFlags(),
40 1024175 : InstructionSelector::AlignmentRequirements()) {}
41 :
42 : template <typename... ParamMachTypes>
43 800 : RawMachineAssemblerTester(Code::Kind kind, ParamMachTypes... p)
44 : : HandleAndZoneScope(),
45 : CallHelper<ReturnType>(
46 : main_isolate(),
47 800 : CSignature::New(main_zone(), MachineTypeForC<ReturnType>(), p...)),
48 : RawMachineAssembler(
49 : main_isolate(), new (main_zone()) Graph(main_zone()),
50 : Linkage::GetSimplifiedCDescriptor(
51 : main_zone(),
52 : CSignature::New(main_zone(), MachineTypeForC<ReturnType>(),
53 800 : p...),
54 : true),
55 : MachineType::PointerRepresentation(),
56 : InstructionSelector::SupportedMachineOperatorFlags(),
57 : InstructionSelector::AlignmentRequirements()),
58 4000 : kind_(kind) {}
59 :
60 410235 : ~RawMachineAssemblerTester() override = default;
61 :
62 210 : void CheckNumber(double expected, Object number) {
63 420 : CHECK(this->isolate()->factory()->NewNumber(expected)->SameValue(number));
64 210 : }
65 :
66 20 : void CheckString(const char* expected, Object string) {
67 40 : CHECK(
68 : this->isolate()->factory()->InternalizeUtf8String(expected)->SameValue(
69 : string));
70 20 : }
71 :
72 12175 : void GenerateCode() { Generate(); }
73 :
74 : Handle<Code> GetCode() {
75 : Generate();
76 : return code_.ToHandleChecked();
77 : }
78 :
79 : protected:
80 44103565 : Address Generate() override {
81 44103565 : if (code_.is_null()) {
82 204515 : Schedule* schedule = this->Export();
83 : auto call_descriptor = this->call_descriptor();
84 : Graph* graph = this->graph();
85 409030 : OptimizedCompilationInfo info(ArrayVector("testing"), main_zone(), kind_);
86 204515 : code_ = Pipeline::GenerateCodeForTesting(
87 : &info, main_isolate(), call_descriptor, graph,
88 409030 : AssemblerOptions::Default(main_isolate()), schedule);
89 : }
90 44103565 : return this->code_.ToHandleChecked()->entry();
91 : }
92 :
93 : private:
94 : Code::Kind kind_ = Code::Kind::STUB;
95 : MaybeHandle<Code> code_;
96 : };
97 :
98 : template <typename ReturnType>
99 20640 : class BufferedRawMachineAssemblerTester
100 : : public RawMachineAssemblerTester<int32_t> {
101 : public:
102 : template <typename... ParamMachTypes>
103 20640 : explicit BufferedRawMachineAssemblerTester(ParamMachTypes... p)
104 : : RawMachineAssemblerTester<int32_t>(
105 : MachineType::Pointer(), ((void)p, MachineType::Pointer())...),
106 : test_graph_signature_(
107 20640 : CSignature::New(this->main_zone(), MachineType::Int32(), p...)),
108 41280 : return_parameter_index_(sizeof...(p)) {
109 : static_assert(sizeof...(p) <= arraysize(parameter_nodes_),
110 : "increase parameter_nodes_ array");
111 2610 : std::array<MachineType, sizeof...(p)> p_arr{{p...}};
112 5350 : for (size_t i = 0; i < p_arr.size(); ++i) {
113 2740 : parameter_nodes_[i] = Load(p_arr[i], RawMachineAssembler::Parameter(i));
114 : }
115 20640 : }
116 :
117 3384400 : Address Generate() override { return RawMachineAssemblerTester::Generate(); }
118 :
119 : // The BufferedRawMachineAssemblerTester does not pass parameters directly
120 : // to the constructed IR graph. Instead it passes a pointer to the parameter
121 : // to the IR graph, and adds Load nodes to the IR graph to load the
122 : // parameters from memory. Thereby it is possible to pass 64 bit parameters
123 : // to the IR graph.
124 : Node* Parameter(size_t index) {
125 : CHECK_GT(arraysize(parameter_nodes_), index);
126 2620 : return parameter_nodes_[index];
127 : }
128 :
129 : // The BufferedRawMachineAssemblerTester adds a Store node to the IR graph
130 : // to store the graph's return value in memory. The memory address for the
131 : // Store node is provided as a parameter. By storing the return value in
132 : // memory it is possible to return 64 bit values.
133 20630 : void Return(Node* input) {
134 20630 : Store(MachineTypeForC<ReturnType>().representation(),
135 : RawMachineAssembler::Parameter(return_parameter_index_), input,
136 : kNoWriteBarrier);
137 20630 : RawMachineAssembler::Return(Int32Constant(1234));
138 20630 : }
139 :
140 : template <typename... Params>
141 3215340 : ReturnType Call(Params... p) {
142 4320 : ReturnType return_value;
143 3215340 : CSignature::VerifyParams<Params...>(test_graph_signature_);
144 3384400 : CallHelper<int32_t>::Call(reinterpret_cast<void*>(&p)...,
145 3366495 : reinterpret_cast<void*>(&return_value));
146 3384400 : return return_value;
147 : }
148 :
149 : private:
150 : CSignature* test_graph_signature_;
151 : Node* parameter_nodes_[4];
152 : uint32_t return_parameter_index_;
153 : };
154 :
155 : template <>
156 25 : class BufferedRawMachineAssemblerTester<void>
157 : : public RawMachineAssemblerTester<void> {
158 : public:
159 : template <typename... ParamMachTypes>
160 25 : explicit BufferedRawMachineAssemblerTester(ParamMachTypes... p)
161 : : RawMachineAssemblerTester<void>(((void)p, MachineType::Pointer())...),
162 : test_graph_signature_(
163 : CSignature::New(RawMachineAssemblerTester<void>::main_zone(),
164 50 : MachineType::None(), p...)) {
165 : static_assert(sizeof...(p) <= arraysize(parameter_nodes_),
166 : "increase parameter_nodes_ array");
167 20 : std::array<MachineType, sizeof...(p)> p_arr{{p...}};
168 70 : for (size_t i = 0; i < p_arr.size(); ++i) {
169 50 : parameter_nodes_[i] = Load(p_arr[i], RawMachineAssembler::Parameter(i));
170 : }
171 25 : }
172 :
173 295495 : Address Generate() override { return RawMachineAssemblerTester::Generate(); }
174 :
175 : // The BufferedRawMachineAssemblerTester does not pass parameters directly
176 : // to the constructed IR graph. Instead it passes a pointer to the parameter
177 : // to the IR graph, and adds Load nodes to the IR graph to load the
178 : // parameters from memory. Thereby it is possible to pass 64 bit parameters
179 : // to the IR graph.
180 : Node* Parameter(size_t index) {
181 : CHECK_GT(arraysize(parameter_nodes_), index);
182 20 : return parameter_nodes_[index];
183 : }
184 :
185 : template <typename... Params>
186 : void Call(Params... p) {
187 295490 : CSignature::VerifyParams<Params...>(test_graph_signature_);
188 295490 : CallHelper<void>::Call(reinterpret_cast<void*>(&p)...);
189 : }
190 :
191 : private:
192 : CSignature* test_graph_signature_;
193 : Node* parameter_nodes_[4];
194 : };
195 :
196 : static const bool USE_RESULT_BUFFER = true;
197 : static const bool USE_RETURN_REGISTER = false;
198 : static const int32_t CHECK_VALUE = 0x99BEEDCE;
199 :
200 :
201 : // TODO(titzer): use the C-style calling convention, or any register-based
202 : // calling convention for binop tests.
203 : template <typename CType, bool use_result_buffer>
204 : class BinopTester {
205 : public:
206 1040 : explicit BinopTester(RawMachineAssemblerTester<int32_t>* tester,
207 : MachineType rep)
208 : : T(tester),
209 : param0(T->LoadFromPointer(&p0, rep)),
210 : param1(T->LoadFromPointer(&p1, rep)),
211 : rep(rep),
212 : p0(static_cast<CType>(0)),
213 : p1(static_cast<CType>(0)),
214 1040 : result(static_cast<CType>(0)) {}
215 :
216 : RawMachineAssemblerTester<int32_t>* T;
217 : Node* param0;
218 : Node* param1;
219 :
220 505285 : CType call(CType a0, CType a1) {
221 3169420 : p0 = a0;
222 3169420 : p1 = a1;
223 : if (use_result_buffer) {
224 1010570 : CHECK_EQ(CHECK_VALUE, T->Call());
225 505285 : return result;
226 : } else {
227 2762550 : return static_cast<CType>(T->Call());
228 : }
229 : }
230 :
231 85 : void AddReturn(Node* val) {
232 : if (use_result_buffer) {
233 170 : T->Store(rep.representation(), T->PointerConstant(&result),
234 : T->Int32Constant(0), val, kNoWriteBarrier);
235 85 : T->Return(T->Int32Constant(CHECK_VALUE));
236 : } else {
237 1025 : T->Return(val);
238 : }
239 85 : }
240 :
241 : template <typename Ci, typename Cj, typename Fn>
242 : void Run(const Ci& ci, const Cj& cj, const Fn& fn) {
243 : typename Ci::const_iterator i;
244 : typename Cj::const_iterator j;
245 : for (i = ci.begin(); i != ci.end(); ++i) {
246 : for (j = cj.begin(); j != cj.end(); ++j) {
247 : CHECK_EQ(fn(*i, *j), this->call(*i, *j));
248 : }
249 : }
250 : }
251 :
252 : protected:
253 : MachineType rep;
254 : CType p0;
255 : CType p1;
256 : CType result;
257 : };
258 :
259 :
260 : // A helper class for testing code sequences that take two int parameters and
261 : // return an int value.
262 : class Int32BinopTester : public BinopTester<int32_t, USE_RETURN_REGISTER> {
263 : public:
264 : explicit Int32BinopTester(RawMachineAssemblerTester<int32_t>* tester)
265 : : BinopTester<int32_t, USE_RETURN_REGISTER>(tester,
266 790 : MachineType::Int32()) {}
267 : };
268 :
269 :
270 : // A helper class for testing code sequences that take two int parameters and
271 : // return an int value.
272 : class Int64BinopTester : public BinopTester<int64_t, USE_RETURN_REGISTER> {
273 : public:
274 : explicit Int64BinopTester(RawMachineAssemblerTester<int32_t>* tester)
275 : : BinopTester<int64_t, USE_RETURN_REGISTER>(tester,
276 20 : MachineType::Int64()) {}
277 : };
278 :
279 :
280 : // A helper class for testing code sequences that take two uint parameters and
281 : // return an uint value.
282 : class Uint32BinopTester : public BinopTester<uint32_t, USE_RETURN_REGISTER> {
283 : public:
284 : explicit Uint32BinopTester(RawMachineAssemblerTester<int32_t>* tester)
285 : : BinopTester<uint32_t, USE_RETURN_REGISTER>(tester,
286 145 : MachineType::Uint32()) {}
287 :
288 : uint32_t call(uint32_t a0, uint32_t a1) {
289 419345 : p0 = a0;
290 419345 : p1 = a1;
291 838685 : return static_cast<uint32_t>(T->Call());
292 : }
293 : };
294 :
295 :
296 : // A helper class for testing code sequences that take two float parameters and
297 : // return a float value.
298 : class Float32BinopTester : public BinopTester<float, USE_RESULT_BUFFER> {
299 : public:
300 : explicit Float32BinopTester(RawMachineAssemblerTester<int32_t>* tester)
301 30 : : BinopTester<float, USE_RESULT_BUFFER>(tester, MachineType::Float32()) {}
302 : };
303 :
304 :
305 : // A helper class for testing code sequences that take two double parameters and
306 : // return a double value.
307 : class Float64BinopTester : public BinopTester<double, USE_RESULT_BUFFER> {
308 : public:
309 : explicit Float64BinopTester(RawMachineAssemblerTester<int32_t>* tester)
310 55 : : BinopTester<double, USE_RESULT_BUFFER>(tester, MachineType::Float64()) {
311 : }
312 : };
313 :
314 :
315 : // A helper class for testing code sequences that take two pointer parameters
316 : // and return a pointer value.
317 : // TODO(titzer): pick word size of pointers based on V8_TARGET.
318 : template <typename Type>
319 : class PointerBinopTester : public BinopTester<Type, USE_RETURN_REGISTER> {
320 : public:
321 : explicit PointerBinopTester(RawMachineAssemblerTester<int32_t>* tester)
322 : : BinopTester<Type, USE_RETURN_REGISTER>(tester, MachineType::Pointer()) {
323 : }
324 : };
325 :
326 :
327 : // A helper class for testing code sequences that take two tagged parameters and
328 : // return a tagged value.
329 : template <typename Type>
330 : class TaggedBinopTester : public BinopTester<Type, USE_RETURN_REGISTER> {
331 : public:
332 : explicit TaggedBinopTester(RawMachineAssemblerTester<int32_t>* tester)
333 : : BinopTester<Type, USE_RETURN_REGISTER>(tester,
334 : MachineType::AnyTagged()) {}
335 : };
336 :
337 : // A helper class for testing compares. Wraps a machine opcode and provides
338 : // evaluation routines and the operators.
339 : class CompareWrapper {
340 : public:
341 175 : explicit CompareWrapper(IrOpcode::Value op) : opcode(op) {}
342 :
343 10230 : Node* MakeNode(RawMachineAssemblerTester<int32_t>* m, Node* a, Node* b) {
344 20460 : return m->AddNode(op(m->machine()), a, b);
345 : }
346 :
347 10230 : const Operator* op(MachineOperatorBuilder* machine) {
348 10230 : switch (opcode) {
349 : case IrOpcode::kWord32Equal:
350 2040 : return machine->Word32Equal();
351 : case IrOpcode::kInt32LessThan:
352 2040 : return machine->Int32LessThan();
353 : case IrOpcode::kInt32LessThanOrEqual:
354 2040 : return machine->Int32LessThanOrEqual();
355 : case IrOpcode::kUint32LessThan:
356 2040 : return machine->Uint32LessThan();
357 : case IrOpcode::kUint32LessThanOrEqual:
358 2040 : return machine->Uint32LessThanOrEqual();
359 : case IrOpcode::kFloat64Equal:
360 10 : return machine->Float64Equal();
361 : case IrOpcode::kFloat64LessThan:
362 10 : return machine->Float64LessThan();
363 : case IrOpcode::kFloat64LessThanOrEqual:
364 10 : return machine->Float64LessThanOrEqual();
365 : default:
366 0 : UNREACHABLE();
367 : }
368 : return nullptr;
369 : }
370 :
371 2676455 : bool Int32Compare(int32_t a, int32_t b) {
372 2676455 : switch (opcode) {
373 : case IrOpcode::kWord32Equal:
374 548720 : return a == b;
375 : case IrOpcode::kInt32LessThan:
376 531920 : return a < b;
377 : case IrOpcode::kInt32LessThanOrEqual:
378 531920 : return a <= b;
379 : case IrOpcode::kUint32LessThan:
380 531950 : return static_cast<uint32_t>(a) < static_cast<uint32_t>(b);
381 : case IrOpcode::kUint32LessThanOrEqual:
382 531945 : return static_cast<uint32_t>(a) <= static_cast<uint32_t>(b);
383 : default:
384 0 : UNREACHABLE();
385 : }
386 : return false;
387 : }
388 :
389 25565 : bool Float64Compare(double a, double b) {
390 25565 : switch (opcode) {
391 : case IrOpcode::kFloat64Equal:
392 12510 : return a == b;
393 : case IrOpcode::kFloat64LessThan:
394 12530 : return a < b;
395 : case IrOpcode::kFloat64LessThanOrEqual:
396 525 : return a <= b;
397 : default:
398 0 : UNREACHABLE();
399 : }
400 : return false;
401 : }
402 :
403 : IrOpcode::Value opcode;
404 : };
405 :
406 :
407 : // A small closure class to generate code for a function of two inputs that
408 : // produces a single output so that it can be used in many different contexts.
409 : // The {expected()} method should compute the expected output for a given
410 : // pair of inputs.
411 : template <typename T>
412 150 : class BinopGen {
413 : public:
414 : virtual void gen(RawMachineAssemblerTester<int32_t>* m, Node* a, Node* b) = 0;
415 : virtual T expected(T a, T b) = 0;
416 150 : virtual ~BinopGen() = default;
417 : };
418 :
419 : // A helper class to generate various combination of input shape combinations
420 : // and run the generated code to ensure it produces the correct results.
421 : class Int32BinopInputShapeTester {
422 : public:
423 : explicit Int32BinopInputShapeTester(BinopGen<int32_t>* g)
424 150 : : gen(g), input_a(0), input_b(0) {}
425 :
426 : void TestAllInputShapes();
427 :
428 : private:
429 : BinopGen<int32_t>* gen;
430 : int32_t input_a;
431 : int32_t input_b;
432 :
433 : void Run(RawMachineAssemblerTester<int32_t>* m);
434 : void RunLeft(RawMachineAssemblerTester<int32_t>* m);
435 : void RunRight(RawMachineAssemblerTester<int32_t>* m);
436 : };
437 : } // namespace compiler
438 : } // namespace internal
439 : } // namespace v8
440 :
441 : #endif // V8_CCTEST_COMPILER_CODEGEN_TESTER_H_
|