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 163868 : explicit RawMachineAssemblerTester(ParamMachTypes... p)
27 : : HandleAndZoneScope(),
28 : CallHelper<ReturnType>(
29 : main_isolate(),
30 163868 : 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 163868 : p...),
37 : true),
38 : MachineType::PointerRepresentation(),
39 : InstructionSelector::SupportedMachineOperatorFlags(),
40 819340 : InstructionSelector::AlignmentRequirements()) {}
41 :
42 : template <typename... ParamMachTypes>
43 640 : RawMachineAssemblerTester(Code::Kind kind, ParamMachTypes... p)
44 : : HandleAndZoneScope(),
45 : CallHelper<ReturnType>(
46 : main_isolate(),
47 640 : 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 640 : p...),
54 : true),
55 : MachineType::PointerRepresentation(),
56 : InstructionSelector::SupportedMachineOperatorFlags(),
57 : InstructionSelector::AlignmentRequirements()),
58 3200 : kind_(kind) {}
59 :
60 328188 : ~RawMachineAssemblerTester() override = default;
61 :
62 168 : void CheckNumber(double expected, Object number) {
63 336 : CHECK(this->isolate()->factory()->NewNumber(expected)->SameValue(number));
64 168 : }
65 :
66 16 : void CheckString(const char* expected, Object string) {
67 32 : CHECK(
68 : this->isolate()->factory()->InternalizeUtf8String(expected)->SameValue(
69 : string));
70 16 : }
71 :
72 9740 : void GenerateCode() { Generate(); }
73 :
74 : Handle<Code> GetCode() {
75 : Generate();
76 : return code_.ToHandleChecked();
77 : }
78 :
79 : protected:
80 35282852 : Address Generate() override {
81 35282852 : if (code_.is_null()) {
82 163612 : Schedule* schedule = this->Export();
83 : auto call_descriptor = this->call_descriptor();
84 : Graph* graph = this->graph();
85 327224 : OptimizedCompilationInfo info(ArrayVector("testing"), main_zone(), kind_);
86 163612 : code_ = Pipeline::GenerateCodeForTesting(
87 : &info, main_isolate(), call_descriptor, graph,
88 327224 : AssemblerOptions::Default(main_isolate()), schedule);
89 : }
90 35282852 : 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 16512 : class BufferedRawMachineAssemblerTester
100 : : public RawMachineAssemblerTester<int32_t> {
101 : public:
102 : template <typename... ParamMachTypes>
103 16512 : explicit BufferedRawMachineAssemblerTester(ParamMachTypes... p)
104 : : RawMachineAssemblerTester<int32_t>(
105 : MachineType::Pointer(), ((void)p, MachineType::Pointer())...),
106 : test_graph_signature_(
107 16512 : CSignature::New(this->main_zone(), MachineType::Int32(), p...)),
108 33024 : return_parameter_index_(sizeof...(p)) {
109 : static_assert(sizeof...(p) <= arraysize(parameter_nodes_),
110 : "increase parameter_nodes_ array");
111 2088 : std::array<MachineType, sizeof...(p)> p_arr{{p...}};
112 4280 : for (size_t i = 0; i < p_arr.size(); ++i) {
113 2192 : parameter_nodes_[i] = Load(p_arr[i], RawMachineAssembler::Parameter(i));
114 : }
115 16512 : }
116 :
117 2707520 : 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 2096 : 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 16504 : void Return(Node* input) {
134 16504 : Store(MachineTypeForC<ReturnType>().representation(),
135 : RawMachineAssembler::Parameter(return_parameter_index_), input,
136 : kNoWriteBarrier);
137 16504 : RawMachineAssembler::Return(Int32Constant(1234));
138 16504 : }
139 :
140 : template <typename... Params>
141 2572272 : ReturnType Call(Params... p) {
142 : uintptr_t zap_data[] = {kZapValue, kZapValue};
143 3456 : ReturnType return_value;
144 : STATIC_ASSERT(sizeof(return_value) <= sizeof(zap_data));
145 : MemCopy(&return_value, &zap_data, sizeof(return_value));
146 2572272 : CSignature::VerifyParams<Params...>(test_graph_signature_);
147 2707520 : CallHelper<int32_t>::Call(reinterpret_cast<void*>(&p)...,
148 2693196 : reinterpret_cast<void*>(&return_value));
149 2707520 : return return_value;
150 : }
151 :
152 : private:
153 : CSignature* test_graph_signature_;
154 : Node* parameter_nodes_[4];
155 : uint32_t return_parameter_index_;
156 : };
157 :
158 : template <>
159 20 : class BufferedRawMachineAssemblerTester<void>
160 : : public RawMachineAssemblerTester<void> {
161 : public:
162 : template <typename... ParamMachTypes>
163 20 : explicit BufferedRawMachineAssemblerTester(ParamMachTypes... p)
164 : : RawMachineAssemblerTester<void>(((void)p, MachineType::Pointer())...),
165 : test_graph_signature_(
166 : CSignature::New(RawMachineAssemblerTester<void>::main_zone(),
167 40 : MachineType::None(), p...)) {
168 : static_assert(sizeof...(p) <= arraysize(parameter_nodes_),
169 : "increase parameter_nodes_ array");
170 16 : std::array<MachineType, sizeof...(p)> p_arr{{p...}};
171 56 : for (size_t i = 0; i < p_arr.size(); ++i) {
172 40 : parameter_nodes_[i] = Load(p_arr[i], RawMachineAssembler::Parameter(i));
173 : }
174 20 : }
175 :
176 236396 : Address Generate() override { return RawMachineAssemblerTester::Generate(); }
177 :
178 : // The BufferedRawMachineAssemblerTester does not pass parameters directly
179 : // to the constructed IR graph. Instead it passes a pointer to the parameter
180 : // to the IR graph, and adds Load nodes to the IR graph to load the
181 : // parameters from memory. Thereby it is possible to pass 64 bit parameters
182 : // to the IR graph.
183 : Node* Parameter(size_t index) {
184 : CHECK_GT(arraysize(parameter_nodes_), index);
185 16 : return parameter_nodes_[index];
186 : }
187 :
188 : template <typename... Params>
189 : void Call(Params... p) {
190 236392 : CSignature::VerifyParams<Params...>(test_graph_signature_);
191 236392 : CallHelper<void>::Call(reinterpret_cast<void*>(&p)...);
192 : }
193 :
194 : private:
195 : CSignature* test_graph_signature_;
196 : Node* parameter_nodes_[4];
197 : };
198 :
199 : static const bool USE_RESULT_BUFFER = true;
200 : static const bool USE_RETURN_REGISTER = false;
201 : static const int32_t CHECK_VALUE = 0x99BEEDCE;
202 :
203 :
204 : // TODO(titzer): use the C-style calling convention, or any register-based
205 : // calling convention for binop tests.
206 : template <typename CType, bool use_result_buffer>
207 : class BinopTester {
208 : public:
209 832 : explicit BinopTester(RawMachineAssemblerTester<int32_t>* tester,
210 : MachineType rep)
211 : : T(tester),
212 : param0(T->LoadFromPointer(&p0, rep)),
213 : param1(T->LoadFromPointer(&p1, rep)),
214 : rep(rep),
215 : p0(static_cast<CType>(0)),
216 : p1(static_cast<CType>(0)),
217 832 : result(static_cast<CType>(0)) {}
218 :
219 : RawMachineAssemblerTester<int32_t>* T;
220 : Node* param0;
221 : Node* param1;
222 :
223 404228 : CType call(CType a0, CType a1) {
224 2535536 : p0 = a0;
225 2535536 : p1 = a1;
226 : if (use_result_buffer) {
227 808456 : CHECK_EQ(CHECK_VALUE, T->Call());
228 404228 : return result;
229 : } else {
230 2210040 : return static_cast<CType>(T->Call());
231 : }
232 : }
233 :
234 68 : void AddReturn(Node* val) {
235 : if (use_result_buffer) {
236 136 : T->Store(rep.representation(), T->PointerConstant(&result),
237 : T->Int32Constant(0), val, kNoWriteBarrier);
238 68 : T->Return(T->Int32Constant(CHECK_VALUE));
239 : } else {
240 820 : T->Return(val);
241 : }
242 68 : }
243 :
244 : template <typename Ci, typename Cj, typename Fn>
245 : void Run(const Ci& ci, const Cj& cj, const Fn& fn) {
246 : typename Ci::const_iterator i;
247 : typename Cj::const_iterator j;
248 : for (i = ci.begin(); i != ci.end(); ++i) {
249 : for (j = cj.begin(); j != cj.end(); ++j) {
250 : CHECK_EQ(fn(*i, *j), this->call(*i, *j));
251 : }
252 : }
253 : }
254 :
255 : protected:
256 : MachineType rep;
257 : CType p0;
258 : CType p1;
259 : CType result;
260 : };
261 :
262 :
263 : // A helper class for testing code sequences that take two int parameters and
264 : // return an int value.
265 : class Int32BinopTester : public BinopTester<int32_t, USE_RETURN_REGISTER> {
266 : public:
267 : explicit Int32BinopTester(RawMachineAssemblerTester<int32_t>* tester)
268 : : BinopTester<int32_t, USE_RETURN_REGISTER>(tester,
269 632 : MachineType::Int32()) {}
270 : };
271 :
272 :
273 : // A helper class for testing code sequences that take two int parameters and
274 : // return an int value.
275 : class Int64BinopTester : public BinopTester<int64_t, USE_RETURN_REGISTER> {
276 : public:
277 : explicit Int64BinopTester(RawMachineAssemblerTester<int32_t>* tester)
278 : : BinopTester<int64_t, USE_RETURN_REGISTER>(tester,
279 16 : MachineType::Int64()) {}
280 : };
281 :
282 :
283 : // A helper class for testing code sequences that take two uint parameters and
284 : // return an uint value.
285 : class Uint32BinopTester : public BinopTester<uint32_t, USE_RETURN_REGISTER> {
286 : public:
287 : explicit Uint32BinopTester(RawMachineAssemblerTester<int32_t>* tester)
288 : : BinopTester<uint32_t, USE_RETURN_REGISTER>(tester,
289 116 : MachineType::Uint32()) {}
290 :
291 : uint32_t call(uint32_t a0, uint32_t a1) {
292 335476 : p0 = a0;
293 335476 : p1 = a1;
294 670948 : return static_cast<uint32_t>(T->Call());
295 : }
296 : };
297 :
298 :
299 : // A helper class for testing code sequences that take two float parameters and
300 : // return a float value.
301 : class Float32BinopTester : public BinopTester<float, USE_RESULT_BUFFER> {
302 : public:
303 : explicit Float32BinopTester(RawMachineAssemblerTester<int32_t>* tester)
304 24 : : BinopTester<float, USE_RESULT_BUFFER>(tester, MachineType::Float32()) {}
305 : };
306 :
307 :
308 : // A helper class for testing code sequences that take two double parameters and
309 : // return a double value.
310 : class Float64BinopTester : public BinopTester<double, USE_RESULT_BUFFER> {
311 : public:
312 : explicit Float64BinopTester(RawMachineAssemblerTester<int32_t>* tester)
313 44 : : BinopTester<double, USE_RESULT_BUFFER>(tester, MachineType::Float64()) {
314 : }
315 : };
316 :
317 :
318 : // A helper class for testing code sequences that take two pointer parameters
319 : // and return a pointer value.
320 : // TODO(titzer): pick word size of pointers based on V8_TARGET.
321 : template <typename Type>
322 : class PointerBinopTester : public BinopTester<Type, USE_RETURN_REGISTER> {
323 : public:
324 : explicit PointerBinopTester(RawMachineAssemblerTester<int32_t>* tester)
325 : : BinopTester<Type, USE_RETURN_REGISTER>(tester, MachineType::Pointer()) {
326 : }
327 : };
328 :
329 :
330 : // A helper class for testing code sequences that take two tagged parameters and
331 : // return a tagged value.
332 : template <typename Type>
333 : class TaggedBinopTester : public BinopTester<Type, USE_RETURN_REGISTER> {
334 : public:
335 : explicit TaggedBinopTester(RawMachineAssemblerTester<int32_t>* tester)
336 : : BinopTester<Type, USE_RETURN_REGISTER>(tester,
337 : MachineType::AnyTagged()) {}
338 : };
339 :
340 : // A helper class for testing compares. Wraps a machine opcode and provides
341 : // evaluation routines and the operators.
342 : class CompareWrapper {
343 : public:
344 140 : explicit CompareWrapper(IrOpcode::Value op) : opcode(op) {}
345 :
346 8184 : Node* MakeNode(RawMachineAssemblerTester<int32_t>* m, Node* a, Node* b) {
347 16368 : return m->AddNode(op(m->machine()), a, b);
348 : }
349 :
350 8184 : const Operator* op(MachineOperatorBuilder* machine) {
351 8184 : switch (opcode) {
352 : case IrOpcode::kWord32Equal:
353 1632 : return machine->Word32Equal();
354 : case IrOpcode::kInt32LessThan:
355 1632 : return machine->Int32LessThan();
356 : case IrOpcode::kInt32LessThanOrEqual:
357 1632 : return machine->Int32LessThanOrEqual();
358 : case IrOpcode::kUint32LessThan:
359 1632 : return machine->Uint32LessThan();
360 : case IrOpcode::kUint32LessThanOrEqual:
361 1632 : return machine->Uint32LessThanOrEqual();
362 : case IrOpcode::kFloat64Equal:
363 8 : return machine->Float64Equal();
364 : case IrOpcode::kFloat64LessThan:
365 8 : return machine->Float64LessThan();
366 : case IrOpcode::kFloat64LessThanOrEqual:
367 8 : return machine->Float64LessThanOrEqual();
368 : default:
369 0 : UNREACHABLE();
370 : }
371 : return nullptr;
372 : }
373 :
374 2141164 : bool Int32Compare(int32_t a, int32_t b) {
375 2141164 : switch (opcode) {
376 : case IrOpcode::kWord32Equal:
377 438976 : return a == b;
378 : case IrOpcode::kInt32LessThan:
379 425536 : return a < b;
380 : case IrOpcode::kInt32LessThanOrEqual:
381 425536 : return a <= b;
382 : case IrOpcode::kUint32LessThan:
383 425560 : return static_cast<uint32_t>(a) < static_cast<uint32_t>(b);
384 : case IrOpcode::kUint32LessThanOrEqual:
385 425556 : return static_cast<uint32_t>(a) <= static_cast<uint32_t>(b);
386 : default:
387 0 : UNREACHABLE();
388 : }
389 : return false;
390 : }
391 :
392 20452 : bool Float64Compare(double a, double b) {
393 20452 : switch (opcode) {
394 : case IrOpcode::kFloat64Equal:
395 10008 : return a == b;
396 : case IrOpcode::kFloat64LessThan:
397 10024 : return a < b;
398 : case IrOpcode::kFloat64LessThanOrEqual:
399 420 : return a <= b;
400 : default:
401 0 : UNREACHABLE();
402 : }
403 : return false;
404 : }
405 :
406 : IrOpcode::Value opcode;
407 : };
408 :
409 :
410 : // A small closure class to generate code for a function of two inputs that
411 : // produces a single output so that it can be used in many different contexts.
412 : // The {expected()} method should compute the expected output for a given
413 : // pair of inputs.
414 : template <typename T>
415 120 : class BinopGen {
416 : public:
417 : virtual void gen(RawMachineAssemblerTester<int32_t>* m, Node* a, Node* b) = 0;
418 : virtual T expected(T a, T b) = 0;
419 120 : virtual ~BinopGen() = default;
420 : };
421 :
422 : // A helper class to generate various combination of input shape combinations
423 : // and run the generated code to ensure it produces the correct results.
424 : class Int32BinopInputShapeTester {
425 : public:
426 : explicit Int32BinopInputShapeTester(BinopGen<int32_t>* g)
427 120 : : gen(g), input_a(0), input_b(0) {}
428 :
429 : void TestAllInputShapes();
430 :
431 : private:
432 : BinopGen<int32_t>* gen;
433 : int32_t input_a;
434 : int32_t input_b;
435 :
436 : void Run(RawMachineAssemblerTester<int32_t>* m);
437 : void RunLeft(RawMachineAssemblerTester<int32_t>* m);
438 : void RunRight(RawMachineAssemblerTester<int32_t>* m);
439 : };
440 : } // namespace compiler
441 : } // namespace internal
442 : } // namespace v8
443 :
444 : #endif // V8_CCTEST_COMPILER_CODEGEN_TESTER_H_
|