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