| // Copyright 2018 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. |
| |
| #include "src/codegen/assembler-inl.h" |
| #include "src/codegen/code-stub-assembler.h" |
| #include "src/codegen/macro-assembler.h" |
| |
| #include "test/cctest/cctest.h" |
| #include "test/cctest/compiler/code-assembler-tester.h" |
| #include "test/cctest/compiler/function-tester.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace compiler { |
| namespace test_run_retpoline { |
| |
| #define __ assembler. |
| |
| namespace { |
| |
| // Function that takes a number of pointer-sized integer arguments, calculates a |
| // weighted sum of them and returns it. |
| Handle<Code> BuildCallee(Isolate* isolate, CallDescriptor* call_descriptor) { |
| CodeAssemblerTester tester(isolate, call_descriptor, "callee"); |
| CodeStubAssembler assembler(tester.state()); |
| int param_count = static_cast<int>(call_descriptor->StackParameterCount()); |
| TNode<IntPtrT> sum = __ IntPtrConstant(0); |
| for (int i = 0; i < param_count; ++i) { |
| TNode<IntPtrT> product = __ Signed(__ IntPtrMul( |
| __ UncheckedParameter<IntPtrT>(i), __ IntPtrConstant(i + 1))); |
| sum = __ IntPtrAdd(sum, product); |
| } |
| __ Return(sum); |
| return tester.GenerateCodeCloseAndEscape(); |
| } |
| |
| // Function that tail-calls another function with a number of pointer-sized |
| // integer arguments. |
| Handle<Code> BuildCaller(Isolate* isolate, CallDescriptor* call_descriptor, |
| CallDescriptor* callee_descriptor, bool tail) { |
| CodeAssemblerTester tester(isolate, call_descriptor, "caller"); |
| CodeStubAssembler assembler(tester.state()); |
| std::vector<Node*> params; |
| // The first parameter is always the callee. |
| Handle<Code> callee = BuildCallee(isolate, callee_descriptor); |
| // defeat the instruction selector. |
| CodeStubAssembler::TVariable<Code> target_var(&assembler); |
| CodeStubAssembler::Label t(&assembler), f(&assembler), |
| end(&assembler, &target_var); |
| __ Branch(__ Int32Constant(0), &t, &f); |
| __ BIND(&t); |
| target_var = __ HeapConstant(callee); |
| __ Goto(&end); |
| __ BIND(&f); |
| target_var = __ HeapConstant(callee); |
| __ Goto(&end); |
| __ BIND(&end); |
| params.push_back(target_var.value()); |
| |
| int param_count = static_cast<int>(callee_descriptor->StackParameterCount()); |
| for (int i = 0; i < param_count; ++i) { |
| params.push_back(__ IntPtrConstant(i)); |
| } |
| DCHECK_EQ(param_count + 1, params.size()); |
| if (tail) { |
| tester.raw_assembler_for_testing()->TailCallN( |
| callee_descriptor, param_count + 1, params.data()); |
| } else { |
| Node* result = tester.raw_assembler_for_testing()->CallN( |
| callee_descriptor, param_count + 1, params.data()); |
| __ Return(__ UncheckedCast<IntPtrT>(result)); |
| } |
| return tester.GenerateCodeCloseAndEscape(); |
| } |
| |
| // Setup function, which calls "caller". |
| Handle<Code> BuildSetupFunction(Isolate* isolate, |
| CallDescriptor* caller_descriptor, |
| CallDescriptor* callee_descriptor, bool tail) { |
| CodeAssemblerTester tester(isolate, 0); |
| CodeStubAssembler assembler(tester.state()); |
| std::vector<Node*> params; |
| // The first parameter is always the callee. |
| params.push_back(__ HeapConstant( |
| BuildCaller(isolate, caller_descriptor, callee_descriptor, tail))); |
| // Set up arguments for "Caller". |
| int param_count = static_cast<int>(caller_descriptor->StackParameterCount()); |
| for (int i = 0; i < param_count; ++i) { |
| // Use values that are different from the ones we will pass to this |
| // function's callee later. |
| params.push_back(__ IntPtrConstant(i + 42)); |
| } |
| DCHECK_EQ(param_count + 1, params.size()); |
| Node* raw_result = tester.raw_assembler_for_testing()->CallN( |
| caller_descriptor, param_count + 1, params.data()); |
| __ Return(__ SmiTag(raw_result)); |
| return tester.GenerateCodeCloseAndEscape(); |
| } |
| |
| CallDescriptor* CreateDescriptorForStackArguments(Zone* zone, |
| int stack_param_count) { |
| LocationSignature::Builder locations(zone, 1, |
| static_cast<size_t>(stack_param_count)); |
| |
| locations.AddReturn(LinkageLocation::ForRegister(kReturnRegister0.code(), |
| MachineType::IntPtr())); |
| |
| for (int i = 0; i < stack_param_count; ++i) { |
| locations.AddParam(LinkageLocation::ForCallerFrameSlot( |
| i - stack_param_count, MachineType::IntPtr())); |
| } |
| |
| return zone->New<CallDescriptor>( |
| CallDescriptor::kCallCodeObject, // kind |
| MachineType::AnyTagged(), // target MachineType |
| LinkageLocation::ForAnyRegister( |
| MachineType::AnyTagged()), // target location |
| locations.Build(), // location_sig |
| stack_param_count, // stack_parameter_count |
| Operator::kNoProperties, // properties |
| kNoCalleeSaved, // callee-saved registers |
| kNoCalleeSaved, // callee-saved fp |
| CallDescriptor::kRetpoline); // flags |
| } |
| |
| // Test a tail call from a caller with n parameters to a callee with m |
| // parameters. All parameters are pointer-sized. |
| void TestHelper(int n, int m, bool tail) { |
| HandleAndZoneScope scope; |
| Isolate* isolate = scope.main_isolate(); |
| CanonicalHandleScope canonical(isolate); |
| Zone* zone = scope.main_zone(); |
| CallDescriptor* caller_descriptor = |
| CreateDescriptorForStackArguments(zone, n); |
| CallDescriptor* callee_descriptor = |
| CreateDescriptorForStackArguments(zone, m); |
| Handle<Code> setup = |
| BuildSetupFunction(isolate, caller_descriptor, callee_descriptor, tail); |
| FunctionTester ft(setup, 0); |
| Handle<Object> result = ft.Call().ToHandleChecked(); |
| int expected = 0; |
| for (int i = 0; i < m; ++i) expected += (i + 1) * i; |
| CHECK_EQ(expected, Handle<Smi>::cast(result)->value()); |
| } |
| |
| } // namespace |
| |
| #undef __ |
| |
| TEST(RetpolineOddEven) { |
| TestHelper(1, 0, false); |
| TestHelper(1, 2, false); |
| TestHelper(3, 2, false); |
| TestHelper(3, 4, false); |
| } |
| |
| TEST(RetpolineOddEvenTail) { |
| TestHelper(1, 0, true); |
| TestHelper(1, 2, true); |
| TestHelper(3, 2, true); |
| TestHelper(3, 4, true); |
| } |
| |
| TEST(RetpolineOddOdd) { |
| TestHelper(1, 1, false); |
| TestHelper(1, 3, false); |
| TestHelper(3, 1, false); |
| TestHelper(3, 3, false); |
| } |
| |
| TEST(RetpolineOddOddTail) { |
| TestHelper(1, 1, true); |
| TestHelper(1, 3, true); |
| TestHelper(3, 1, true); |
| TestHelper(3, 3, true); |
| } |
| |
| TEST(RetpolineEvenEven) { |
| TestHelper(0, 0, false); |
| TestHelper(0, 2, false); |
| TestHelper(2, 0, false); |
| TestHelper(2, 2, false); |
| } |
| |
| TEST(RetpolineEvenEvenTail) { |
| TestHelper(0, 0, true); |
| TestHelper(0, 2, true); |
| TestHelper(2, 0, true); |
| TestHelper(2, 2, true); |
| } |
| |
| TEST(RetpolineEvenOdd) { |
| TestHelper(0, 1, false); |
| TestHelper(0, 3, false); |
| TestHelper(2, 1, false); |
| TestHelper(2, 3, false); |
| } |
| |
| TEST(RetpolineEvenOddTail) { |
| TestHelper(0, 1, true); |
| TestHelper(0, 3, true); |
| TestHelper(2, 1, true); |
| TestHelper(2, 3, true); |
| } |
| |
| } // namespace test_run_retpoline |
| } // namespace compiler |
| } // namespace internal |
| } // namespace v8 |