Line data Source code
1 : // Copyright 2017 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 : #include "src/assembler-inl.h"
6 : #include "src/base/utils/random-number-generator.h"
7 : #include "src/code-stub-assembler.h"
8 : #include "src/macro-assembler.h"
9 :
10 : #include "test/cctest/cctest.h"
11 : #include "test/cctest/compiler/code-assembler-tester.h"
12 : #include "test/cctest/compiler/function-tester.h"
13 :
14 : namespace v8 {
15 : namespace internal {
16 : namespace compiler {
17 : namespace test_run_tail_calls {
18 :
19 : #define __ assembler.
20 :
21 : namespace {
22 :
23 : // Function that takes a number of pointer-sized integer arguments, calculates a
24 : // weighted sum of them and returns it.
25 144 : Handle<Code> BuildCallee(Isolate* isolate, CallDescriptor* call_descriptor) {
26 288 : CodeAssemblerTester tester(isolate, call_descriptor, "callee");
27 144 : CodeStubAssembler assembler(tester.state());
28 144 : int param_count = static_cast<int>(call_descriptor->StackParameterCount());
29 288 : Node* sum = __ IntPtrConstant(0);
30 2176 : for (int i = 0; i < param_count; ++i) {
31 4064 : Node* product = __ IntPtrMul(__ Parameter(i), __ IntPtrConstant(i + 1));
32 2032 : sum = __ IntPtrAdd(sum, product);
33 : }
34 144 : __ Return(sum);
35 144 : return tester.GenerateCodeCloseAndEscape();
36 : }
37 :
38 : // Function that tail-calls another function with a number of pointer-sized
39 : // integer arguments.
40 144 : Handle<Code> BuildCaller(Isolate* isolate, CallDescriptor* call_descriptor,
41 : CallDescriptor* callee_descriptor) {
42 288 : CodeAssemblerTester tester(isolate, call_descriptor, "caller");
43 144 : CodeStubAssembler assembler(tester.state());
44 : std::vector<Node*> params;
45 : // The first parameter is always the callee.
46 432 : params.push_back(__ HeapConstant(BuildCallee(isolate, callee_descriptor)));
47 144 : int param_count = static_cast<int>(callee_descriptor->StackParameterCount());
48 2176 : for (int i = 0; i < param_count; ++i) {
49 3048 : params.push_back(__ IntPtrConstant(i));
50 : }
51 : DCHECK_EQ(param_count + 1, params.size());
52 144 : tester.raw_assembler_for_testing()->TailCallN(callee_descriptor,
53 144 : param_count + 1, params.data());
54 144 : return tester.GenerateCodeCloseAndEscape();
55 : }
56 :
57 : // Setup function, which calls "caller".
58 144 : Handle<Code> BuildSetupFunction(Isolate* isolate,
59 : CallDescriptor* caller_descriptor,
60 : CallDescriptor* callee_descriptor) {
61 288 : CodeAssemblerTester tester(isolate, 0);
62 144 : CodeStubAssembler assembler(tester.state());
63 : std::vector<Node*> params;
64 : // The first parameter is always the callee.
65 288 : params.push_back(__ HeapConstant(
66 144 : BuildCaller(isolate, caller_descriptor, callee_descriptor)));
67 : // Set up arguments for "Caller".
68 144 : int param_count = static_cast<int>(caller_descriptor->StackParameterCount());
69 2600 : for (int i = 0; i < param_count; ++i) {
70 : // Use values that are different from the ones we will pass to this
71 : // function's callee later.
72 3684 : params.push_back(__ IntPtrConstant(i + 42));
73 : }
74 : DCHECK_EQ(param_count + 1, params.size());
75 144 : Node* raw_result = tester.raw_assembler_for_testing()->CallN(
76 144 : caller_descriptor, param_count + 1, params.data());
77 288 : __ Return(__ SmiTag(raw_result));
78 144 : return tester.GenerateCodeCloseAndEscape();
79 : }
80 :
81 288 : CallDescriptor* CreateDescriptorForStackArguments(Zone* zone,
82 : int stack_param_count) {
83 : LocationSignature::Builder locations(zone, 1,
84 288 : static_cast<size_t>(stack_param_count));
85 :
86 : locations.AddReturn(LinkageLocation::ForRegister(kReturnRegister0.code(),
87 : MachineType::IntPtr()));
88 :
89 4776 : for (int i = 0; i < stack_param_count; ++i) {
90 2244 : locations.AddParam(LinkageLocation::ForCallerFrameSlot(
91 : i - stack_param_count, MachineType::IntPtr()));
92 : }
93 :
94 : return new (zone)
95 : CallDescriptor(CallDescriptor::kCallCodeObject, // kind
96 : MachineType::AnyTagged(), // target MachineType
97 : LinkageLocation::ForAnyRegister(
98 : MachineType::AnyTagged()), // target location
99 : locations.Build(), // location_sig
100 : stack_param_count, // stack_parameter_count
101 : Operator::kNoProperties, // properties
102 : kNoCalleeSaved, // callee-saved registers
103 : kNoCalleeSaved, // callee-saved fp
104 288 : CallDescriptor::kNoFlags); // flags
105 : }
106 :
107 : // Test a tail call from a caller with n parameters to a callee with m
108 : // parameters. All parameters are pointer-sized.
109 144 : void TestHelper(int n, int m) {
110 288 : HandleAndZoneScope scope;
111 : Isolate* isolate = scope.main_isolate();
112 288 : CanonicalHandleScope canonical(isolate);
113 : Zone* zone = scope.main_zone();
114 : CallDescriptor* caller_descriptor =
115 144 : CreateDescriptorForStackArguments(zone, n);
116 : CallDescriptor* callee_descriptor =
117 144 : CreateDescriptorForStackArguments(zone, m);
118 : Handle<Code> setup =
119 144 : BuildSetupFunction(isolate, caller_descriptor, callee_descriptor);
120 144 : FunctionTester ft(setup, 0);
121 288 : Handle<Object> result = ft.Call().ToHandleChecked();
122 : int expected = 0;
123 1160 : for (int i = 0; i < m; ++i) expected += (i + 1) * i;
124 144 : CHECK_EQ(expected, Handle<Smi>::cast(result)->value());
125 144 : }
126 :
127 : } // namespace
128 :
129 : #undef __
130 :
131 26067 : TEST(CallerOddCalleeEven) {
132 4 : TestHelper(1, 0);
133 4 : TestHelper(1, 2);
134 4 : TestHelper(3, 2);
135 4 : TestHelper(3, 4);
136 4 : }
137 :
138 26067 : TEST(CallerOddCalleeOdd) {
139 4 : TestHelper(1, 1);
140 4 : TestHelper(1, 3);
141 4 : TestHelper(3, 1);
142 4 : TestHelper(3, 3);
143 4 : }
144 :
145 26067 : TEST(CallerEvenCalleeEven) {
146 4 : TestHelper(0, 0);
147 4 : TestHelper(0, 2);
148 4 : TestHelper(2, 0);
149 4 : TestHelper(2, 2);
150 4 : }
151 :
152 26067 : TEST(CallerEvenCalleeOdd) {
153 4 : TestHelper(0, 1);
154 4 : TestHelper(0, 3);
155 4 : TestHelper(2, 1);
156 4 : TestHelper(2, 3);
157 4 : }
158 :
159 26067 : TEST(FuzzStackParamCount) {
160 : const int kNumTests = 20;
161 : const int kMaxSlots = 30;
162 4 : base::RandomNumberGenerator* const rng = CcTest::random_number_generator();
163 164 : for (int i = 0; i < kNumTests; ++i) {
164 80 : int n = rng->NextInt(kMaxSlots);
165 80 : int m = rng->NextInt(kMaxSlots);
166 80 : TestHelper(n, m);
167 : }
168 4 : }
169 :
170 : } // namespace test_run_tail_calls
171 : } // namespace compiler
172 : } // namespace internal
173 78189 : } // namespace v8
|