2018-01-16 09:06:23 +00:00
|
|
|
// 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.
|
|
|
|
|
2019-05-21 09:30:15 +00:00
|
|
|
#include "src/codegen/assembler-inl.h"
|
|
|
|
#include "src/codegen/code-stub-assembler.h"
|
|
|
|
#include "src/codegen/macro-assembler.h"
|
2018-01-16 09:06:23 +00:00
|
|
|
|
|
|
|
#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 {
|
2018-01-16 20:22:11 +00:00
|
|
|
namespace test_run_retpoline {
|
2018-01-16 09:06:23 +00:00
|
|
|
|
|
|
|
#define __ assembler.
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
// Function that takes a number of pointer-sized integer arguments, calculates a
|
|
|
|
// weighted sum of them and returns it.
|
2018-02-09 19:19:25 +00:00
|
|
|
Handle<Code> BuildCallee(Isolate* isolate, CallDescriptor* call_descriptor) {
|
|
|
|
CodeAssemblerTester tester(isolate, call_descriptor, "callee");
|
2018-01-16 09:06:23 +00:00
|
|
|
CodeStubAssembler assembler(tester.state());
|
2021-03-05 23:43:45 +00:00
|
|
|
int param_slots = static_cast<int>(call_descriptor->ParameterSlotCount());
|
2019-11-27 16:02:28 +00:00
|
|
|
TNode<IntPtrT> sum = __ IntPtrConstant(0);
|
2021-03-05 23:43:45 +00:00
|
|
|
for (int i = 0; i < param_slots; ++i) {
|
2020-09-30 17:40:39 +00:00
|
|
|
TNode<IntPtrT> product = __ Signed(__ IntPtrMul(
|
|
|
|
__ UncheckedParameter<IntPtrT>(i), __ IntPtrConstant(i + 1)));
|
2018-01-16 09:06:23 +00:00
|
|
|
sum = __ IntPtrAdd(sum, product);
|
|
|
|
}
|
|
|
|
__ Return(sum);
|
|
|
|
return tester.GenerateCodeCloseAndEscape();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function that tail-calls another function with a number of pointer-sized
|
|
|
|
// integer arguments.
|
2018-02-09 19:19:25 +00:00
|
|
|
Handle<Code> BuildCaller(Isolate* isolate, CallDescriptor* call_descriptor,
|
2018-01-16 09:06:23 +00:00
|
|
|
CallDescriptor* callee_descriptor, bool tail) {
|
2018-02-09 19:19:25 +00:00
|
|
|
CodeAssemblerTester tester(isolate, call_descriptor, "caller");
|
2018-01-16 09:06:23 +00:00
|
|
|
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.
|
2020-07-10 13:52:47 +00:00
|
|
|
CodeStubAssembler::TVariable<Code> target_var(&assembler);
|
2018-01-16 09:06:23 +00:00
|
|
|
CodeStubAssembler::Label t(&assembler), f(&assembler),
|
|
|
|
end(&assembler, &target_var);
|
|
|
|
__ Branch(__ Int32Constant(0), &t, &f);
|
|
|
|
__ BIND(&t);
|
2020-07-10 13:52:47 +00:00
|
|
|
target_var = __ HeapConstant(callee);
|
2018-01-16 09:06:23 +00:00
|
|
|
__ Goto(&end);
|
|
|
|
__ BIND(&f);
|
2020-07-10 13:52:47 +00:00
|
|
|
target_var = __ HeapConstant(callee);
|
2018-01-16 09:06:23 +00:00
|
|
|
__ Goto(&end);
|
|
|
|
__ BIND(&end);
|
|
|
|
params.push_back(target_var.value());
|
|
|
|
|
2021-03-05 23:43:45 +00:00
|
|
|
int param_slots = static_cast<int>(callee_descriptor->ParameterSlotCount());
|
|
|
|
for (int i = 0; i < param_slots; ++i) {
|
2018-01-16 09:06:23 +00:00
|
|
|
params.push_back(__ IntPtrConstant(i));
|
|
|
|
}
|
2021-03-05 23:43:45 +00:00
|
|
|
DCHECK_EQ(param_slots + 1, params.size());
|
2018-01-16 09:06:23 +00:00
|
|
|
if (tail) {
|
|
|
|
tester.raw_assembler_for_testing()->TailCallN(
|
2021-03-05 23:43:45 +00:00
|
|
|
callee_descriptor, param_slots + 1, params.data());
|
2018-01-16 09:06:23 +00:00
|
|
|
} else {
|
|
|
|
Node* result = tester.raw_assembler_for_testing()->CallN(
|
2021-03-05 23:43:45 +00:00
|
|
|
callee_descriptor, param_slots + 1, params.data());
|
2019-11-27 16:02:28 +00:00
|
|
|
__ Return(__ UncheckedCast<IntPtrT>(result));
|
2018-01-16 09:06:23 +00:00
|
|
|
}
|
|
|
|
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".
|
2021-03-05 23:43:45 +00:00
|
|
|
int param_slots = static_cast<int>(caller_descriptor->ParameterSlotCount());
|
|
|
|
for (int i = 0; i < param_slots; ++i) {
|
2018-01-16 09:06:23 +00:00
|
|
|
// Use values that are different from the ones we will pass to this
|
|
|
|
// function's callee later.
|
|
|
|
params.push_back(__ IntPtrConstant(i + 42));
|
|
|
|
}
|
2021-03-05 23:43:45 +00:00
|
|
|
DCHECK_EQ(param_slots + 1, params.size());
|
2021-02-22 16:56:41 +00:00
|
|
|
TNode<IntPtrT> intptr_result =
|
|
|
|
__ UncheckedCast<IntPtrT>(tester.raw_assembler_for_testing()->CallN(
|
2021-03-05 23:43:45 +00:00
|
|
|
caller_descriptor, param_slots + 1, params.data()));
|
2021-02-22 16:56:41 +00:00
|
|
|
__ Return(__ SmiTag(intptr_result));
|
2018-01-16 09:06:23 +00:00
|
|
|
return tester.GenerateCodeCloseAndEscape();
|
|
|
|
}
|
|
|
|
|
2021-03-05 23:43:45 +00:00
|
|
|
CallDescriptor* CreateDescriptorForStackArguments(Zone* zone, int param_slots) {
|
2018-01-16 09:06:23 +00:00
|
|
|
LocationSignature::Builder locations(zone, 1,
|
2021-03-05 23:43:45 +00:00
|
|
|
static_cast<size_t>(param_slots));
|
2018-01-16 09:06:23 +00:00
|
|
|
|
|
|
|
locations.AddReturn(LinkageLocation::ForRegister(kReturnRegister0.code(),
|
|
|
|
MachineType::IntPtr()));
|
|
|
|
|
2021-03-05 23:43:45 +00:00
|
|
|
for (int i = 0; i < param_slots; ++i) {
|
2018-01-16 09:06:23 +00:00
|
|
|
locations.AddParam(LinkageLocation::ForCallerFrameSlot(
|
2021-03-05 23:43:45 +00:00
|
|
|
i - param_slots, MachineType::IntPtr()));
|
2018-01-16 09:06:23 +00:00
|
|
|
}
|
|
|
|
|
2020-07-09 10:43:44 +00:00
|
|
|
return zone->New<CallDescriptor>(
|
|
|
|
CallDescriptor::kCallCodeObject, // kind
|
|
|
|
MachineType::AnyTagged(), // target MachineType
|
|
|
|
LinkageLocation::ForAnyRegister(
|
|
|
|
MachineType::AnyTagged()), // target location
|
|
|
|
locations.Build(), // location_sig
|
2021-03-05 23:43:45 +00:00
|
|
|
param_slots, // stack parameter slots
|
2020-07-09 10:43:44 +00:00
|
|
|
Operator::kNoProperties, // properties
|
|
|
|
kNoCalleeSaved, // callee-saved registers
|
|
|
|
kNoCalleeSaved, // callee-saved fp
|
|
|
|
CallDescriptor::kRetpoline); // flags
|
2018-01-16 09:06:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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();
|
2018-07-31 11:12:53 +00:00
|
|
|
CanonicalHandleScope canonical(isolate);
|
2018-01-16 09:06:23 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-01-16 20:22:11 +00:00
|
|
|
} // namespace test_run_retpoline
|
2018-01-16 09:06:23 +00:00
|
|
|
} // namespace compiler
|
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|