Commit 2e3c36fb authored by bradnelson's avatar bradnelson Committed by Commit bot

Adding support for multiple returns in compiled functions.

This will allow exploration of possibilities like passing around buffer base and length.

BUG=None
TEST=test-multiple-return
LOG=N
R=mtrofin@chromium.org,titzer@chromium.org

Review URL: https://codereview.chromium.org/1391333003

Cr-Commit-Position: refs/heads/master@{#31184}
parent deccbde1
......@@ -123,12 +123,17 @@ std::ostream& operator<<(std::ostream& os, ParameterInfo const& i) {
V(IfDefault, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
V(Throw, Operator::kKontrol, 1, 1, 1, 0, 0, 1) \
V(Deoptimize, Operator::kNoThrow, 1, 1, 1, 0, 0, 1) \
V(Return, Operator::kNoThrow, 1, 1, 1, 0, 0, 1) \
V(Terminate, Operator::kKontrol, 0, 1, 1, 0, 0, 1) \
V(OsrNormalEntry, Operator::kFoldable, 0, 1, 1, 0, 1, 1) \
V(OsrLoopEntry, Operator::kFoldable, 0, 1, 1, 0, 1, 1)
#define CACHED_RETURN_LIST(V) \
V(1) \
V(2) \
V(3)
#define CACHED_END_LIST(V) \
V(1) \
V(2) \
......@@ -249,6 +254,19 @@ struct CommonOperatorGlobalCache final {
CACHED_END_LIST(CACHED_END)
#undef CACHED_END
template <size_t kInputCount>
struct ReturnOperator final : public Operator {
ReturnOperator()
: Operator( // --
IrOpcode::kReturn, Operator::kNoThrow, // opcode
"Return", // name
kInputCount, 1, 1, 0, 0, 1) {} // counts
};
#define CACHED_RETURN(input_count) \
ReturnOperator<input_count> kReturn##input_count##Operator;
CACHED_RETURN_LIST(CACHED_RETURN)
#undef CACHED_RETURN
template <BranchHint kBranchHint>
struct BranchOperator final : public Operator1<BranchHint> {
BranchOperator()
......@@ -397,6 +415,24 @@ const Operator* CommonOperatorBuilder::End(size_t control_input_count) {
}
const Operator* CommonOperatorBuilder::Return(int value_input_count) {
switch (value_input_count) {
#define CACHED_RETURN(input_count) \
case input_count: \
return &cache_.kReturn##input_count##Operator;
CACHED_RETURN_LIST(CACHED_RETURN)
#undef CACHED_RETURN
default:
break;
}
// Uncached.
return new (zone()) Operator( //--
IrOpcode::kReturn, Operator::kNoThrow, // opcode
"Return", // name
value_input_count, 1, 1, 0, 0, 1); // counts
}
const Operator* CommonOperatorBuilder::Branch(BranchHint hint) {
switch (hint) {
case BranchHint::kNone:
......
......@@ -121,7 +121,7 @@ class CommonOperatorBuilder final : public ZoneObject {
const Operator* IfDefault();
const Operator* Throw();
const Operator* Deoptimize();
const Operator* Return();
const Operator* Return(int value_input_count = 1);
const Operator* Terminate();
const Operator* Start(int value_output_count);
......
......@@ -504,7 +504,7 @@ void InstructionSelector::VisitControl(BasicBlock* block) {
}
case BasicBlock::kReturn: {
DCHECK_EQ(IrOpcode::kReturn, input->opcode());
return VisitReturn(input->InputAt(0));
return VisitReturn(input);
}
case BasicBlock::kDeoptimize: {
// If the result itself is a return, return its input.
......@@ -1009,15 +1009,19 @@ void InstructionSelector::VisitGoto(BasicBlock* target) {
}
void InstructionSelector::VisitReturn(Node* value) {
DCHECK_NOT_NULL(value);
void InstructionSelector::VisitReturn(Node* ret) {
OperandGenerator g(this);
if (linkage()->GetIncomingDescriptor()->ReturnCount() == 0) {
Emit(kArchRet, g.NoOutput());
} else {
Emit(kArchRet, g.NoOutput(),
g.UseLocation(value, linkage()->GetReturnLocation(),
linkage()->GetReturnType()));
const int ret_count = ret->op()->ValueInputCount();
auto value_locations = zone()->NewArray<InstructionOperand>(ret_count);
for (int i = 0; i < ret_count; ++i) {
value_locations[i] =
g.UseLocation(ret->InputAt(i), linkage()->GetReturnLocation(i),
linkage()->GetReturnType(i));
}
Emit(kArchRet, 0, nullptr, ret_count, value_locations);
}
}
......
......@@ -213,7 +213,7 @@ class InstructionSelector final {
void VisitBranch(Node* input, BasicBlock* tbranch, BasicBlock* fbranch);
void VisitSwitch(Node* node, const SwitchInfo& sw);
void VisitDeoptimize(Node* value);
void VisitReturn(Node* value);
void VisitReturn(Node* ret);
void VisitThrow(Node* value);
// ===========================================================================
......
......@@ -304,12 +304,14 @@ class Linkage : public ZoneObject {
}
// Get the location where this function should place its return value.
LinkageLocation GetReturnLocation() const {
return incoming_->GetReturnLocation(0);
LinkageLocation GetReturnLocation(size_t index = 0) const {
return incoming_->GetReturnLocation(index);
}
// Get the machine type of this function's return value.
MachineType GetReturnType() const { return incoming_->GetReturnType(0); }
MachineType GetReturnType(size_t index = 0) const {
return incoming_->GetReturnType(index);
}
// Get the frame offset for a given spill slot. The location depends on the
// calling convention and the specific frame layout, and may thus be
......
......@@ -101,6 +101,22 @@ void RawMachineAssembler::Return(Node* value) {
}
void RawMachineAssembler::Return(Node* v1, Node* v2) {
Node* values[] = {v1, v2};
Node* ret = MakeNode(common()->Return(2), 2, values);
schedule()->AddReturn(CurrentBlock(), ret);
current_block_ = nullptr;
}
void RawMachineAssembler::Return(Node* v1, Node* v2, Node* v3) {
Node* values[] = {v1, v2, v3};
Node* ret = MakeNode(common()->Return(3), 3, values);
schedule()->AddReturn(CurrentBlock(), ret);
current_block_ = nullptr;
}
Node* RawMachineAssembler::CallN(CallDescriptor* desc, Node* function,
Node** args) {
int param_count =
......
......@@ -536,6 +536,8 @@ class RawMachineAssembler {
void Switch(Node* index, Label* default_label, int32_t* case_values,
Label** case_labels, size_t case_count);
void Return(Node* value);
void Return(Node* v1, Node* v2);
void Return(Node* v1, Node* v2, Node* v3);
void Bind(Label* label);
void Deoptimize(Node* state);
......
......@@ -63,6 +63,7 @@
'compiler/test-loop-assignment-analysis.cc',
'compiler/test-loop-analysis.cc',
'compiler/test-machine-operator-reducer.cc',
'compiler/test-multiple-return.cc',
'compiler/test-node.cc',
'compiler/test-operator.cc',
'compiler/test-osr.cc',
......
// Copyright 2014 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 <cmath>
#include <functional>
#include <limits>
#include "src/assembler.h"
#include "src/base/bits.h"
#include "src/base/utils/random-number-generator.h"
#include "src/codegen.h"
#include "src/compiler.h"
#include "src/compiler/linkage.h"
#include "src/macro-assembler.h"
#include "test/cctest/cctest.h"
#include "test/cctest/compiler/codegen-tester.h"
#include "test/cctest/compiler/value-helper.h"
using namespace v8::base;
using namespace v8::internal;
using namespace v8::internal::compiler;
namespace {
CallDescriptor* GetCallDescriptor(Zone* zone, int return_count,
int param_count) {
MachineSignature::Builder msig(zone, return_count, param_count);
LocationSignature::Builder locations(zone, return_count, param_count);
// Add return location(s).
for (int i = 0; i < return_count; i++) {
msig.AddReturn(compiler::kMachInt32);
locations.AddReturn(LinkageLocation::ForRegister(i));
}
// Add register and/or stack parameter(s).
for (int i = 0; i < param_count; i++) {
msig.AddParam(compiler::kMachInt32);
locations.AddParam(LinkageLocation::ForRegister(i));
}
const RegList kCalleeSaveRegisters = 0;
const RegList kCalleeSaveFPRegisters = 0;
// The target for WASM calls is always a code object.
MachineType target_type = compiler::kMachAnyTagged;
LinkageLocation target_loc = LinkageLocation::ForAnyRegister();
return new (zone) CallDescriptor( // --
CallDescriptor::kCallCodeObject, // kind
target_type, // target MachineType
target_loc, // target location
msig.Build(), // machine_sig
locations.Build(), // location_sig
0, // js_parameter_count
compiler::Operator::kNoProperties, // properties
kCalleeSaveRegisters, // callee-saved registers
kCalleeSaveFPRegisters, // callee-saved fp regs
CallDescriptor::kNoFlags, // flags
"c-call");
}
} // namespace
TEST(ReturnThreeValues) {
Zone zone;
CallDescriptor* desc = GetCallDescriptor(&zone, 3, 2);
HandleAndZoneScope handles;
RawMachineAssembler m(handles.main_isolate(),
new (handles.main_zone()) Graph(handles.main_zone()),
desc, kMachPtr,
InstructionSelector::SupportedMachineOperatorFlags());
Node* p0 = m.Parameter(0);
Node* p1 = m.Parameter(1);
Node* add = m.Int32Add(p0, p1);
Node* sub = m.Int32Sub(p0, p1);
Node* mul = m.Int32Mul(p0, p1);
m.Return(add, sub, mul);
CompilationInfo info("testing", handles.main_isolate(), handles.main_zone());
Handle<Code> code =
Pipeline::GenerateCodeForTesting(&info, desc, m.graph(), m.Export());
#ifdef ENABLE_DISASSEMBLER
if (FLAG_print_code) {
OFStream os(stdout);
code->Disassemble("three_value", os);
}
#endif
RawMachineAssemblerTester<int32_t> mt;
Node* a = mt.Int32Constant(123);
Node* b = mt.Int32Constant(456);
Node* ret3 = mt.AddNode(mt.common()->Call(desc), mt.HeapConstant(code), a, b);
Node* x = mt.AddNode(mt.common()->Projection(0), ret3);
Node* y = mt.AddNode(mt.common()->Projection(1), ret3);
Node* z = mt.AddNode(mt.common()->Projection(2), ret3);
Node* ret = mt.Int32Add(mt.Int32Add(x, y), z);
mt.Return(ret);
#ifdef ENABLE_DISASSEMBLER
Handle<Code> code2 = mt.GetCode();
if (FLAG_print_code) {
OFStream os(stdout);
code2->Disassemble("three_value_call", os);
}
#endif
CHECK_EQ((123 + 456) + (123 - 456) + (123 * 456), mt.Call());
}
......@@ -53,7 +53,6 @@ const SharedOperator kSharedOperators[] = {
SHARED(IfFalse, Operator::kKontrol, 0, 0, 1, 0, 0, 1),
SHARED(IfSuccess, Operator::kKontrol, 0, 0, 1, 0, 0, 1),
SHARED(Throw, Operator::kKontrol, 1, 1, 1, 0, 0, 1),
SHARED(Return, Operator::kNoThrow, 1, 1, 1, 0, 0, 1),
SHARED(Terminate, Operator::kKontrol, 0, 1, 1, 0, 0, 1)
#undef SHARED
};
......@@ -188,6 +187,23 @@ TEST_F(CommonOperatorTest, End) {
}
TEST_F(CommonOperatorTest, Return) {
TRACED_FOREACH(int, input_count, kArguments) {
const Operator* const op = common()->Return(input_count);
EXPECT_EQ(IrOpcode::kReturn, op->opcode());
EXPECT_EQ(Operator::kNoThrow, op->properties());
EXPECT_EQ(input_count, op->ValueInputCount());
EXPECT_EQ(1, op->EffectInputCount());
EXPECT_EQ(1, static_cast<uint32_t>(op->ControlInputCount()));
EXPECT_EQ(2 + input_count, static_cast<uint32_t>(
OperatorProperties::GetTotalInputCount(op)));
EXPECT_EQ(0, op->ValueOutputCount());
EXPECT_EQ(0, op->EffectOutputCount());
EXPECT_EQ(1, op->ControlOutputCount());
}
}
TEST_F(CommonOperatorTest, Branch) {
TRACED_FOREACH(BranchHint, hint, kBranchHints) {
const Operator* const op = common()->Branch(hint);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment