Commit 33c08596 authored by ahaas's avatar ahaas Committed by Commit bot

[wasm] Int64Lowering of Int64Sub on ia32 and arm.

Int64Sub is lowered to a new turbofan operator, Int32SubPair. The new
operator takes 4 inputs an generates 2 outputs. The inputs are the low
word of the left input, high word of the left input, the low word of the
right input, and high word of the right input. The ouputs are the low
and high word of the result of the subtraction.

The implementation is very similar to the implementation of Int64Add.

@v8-arm-ports: please take a careful look at the implementation of sbc
in the simulator.

R=titzer@chromium.org, v8-arm-ports@googlegroups.com

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

Cr-Commit-Position: refs/heads/master@{#34808}
parent 84dd29bb
......@@ -1333,11 +1333,12 @@ bool Simulator::CarryFrom(int32_t left, int32_t right, int32_t carry) {
// Calculate C flag value for subtractions.
bool Simulator::BorrowFrom(int32_t left, int32_t right) {
bool Simulator::BorrowFrom(int32_t left, int32_t right, int32_t carry) {
uint32_t uleft = static_cast<uint32_t>(left);
uint32_t uright = static_cast<uint32_t>(right);
return (uright > uleft);
return (uright > uleft) ||
(!carry && (((uright + 1) > uleft) || (uright > (uleft - 1))));
}
......@@ -2493,8 +2494,15 @@ void Simulator::DecodeType01(Instruction* instr) {
}
case SBC: {
Format(instr, "sbc'cond's 'rd, 'rn, 'shift_rm");
Format(instr, "sbc'cond's 'rd, 'rn, 'imm");
// Format(instr, "sbc'cond's 'rd, 'rn, 'shift_rm");
// Format(instr, "sbc'cond's 'rd, 'rn, 'imm");
alu_out = (rn_val - shifter_operand) - (GetCarry() ? 0 : 1);
set_register(rd, alu_out);
if (instr->HasS()) {
SetNZFlags(alu_out);
SetCFlag(!BorrowFrom(rn_val, shifter_operand, GetCarry()));
SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, false));
}
break;
}
......
......@@ -262,7 +262,7 @@ class Simulator {
void SetCFlag(bool val);
void SetVFlag(bool val);
bool CarryFrom(int32_t left, int32_t right, int32_t carry = 0);
bool BorrowFrom(int32_t left, int32_t right);
bool BorrowFrom(int32_t left, int32_t right, int32_t carry = 1);
bool OverflowFrom(int32_t alu_out,
int32_t left,
int32_t right,
......
......@@ -798,6 +798,17 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
Operand(i.InputRegister(3)));
DCHECK_EQ(LeaveCC, i.OutputSBit());
break;
case kArmSubPair:
// i.InputRegister(0) ... left low word.
// i.InputRegister(1) ... left high word.
// i.InputRegister(2) ... right low word.
// i.InputRegister(3) ... right high word.
__ sub(i.OutputRegister(0), i.InputRegister(0), i.InputRegister(2),
SBit::SetCC);
__ sbc(i.OutputRegister(1), i.InputRegister(1),
Operand(i.InputRegister(3)));
DCHECK_EQ(LeaveCC, i.OutputSBit());
break;
case kArmLslPair:
if (instr->InputAt(2)->IsImmediate()) {
__ LslPair(i.OutputRegister(0), i.OutputRegister(1), i.InputRegister(0),
......
......@@ -47,6 +47,7 @@ namespace compiler {
V(ArmRbit) \
V(ArmUxtah) \
V(ArmAddPair) \
V(ArmSubPair) \
V(ArmLslPair) \
V(ArmLsrPair) \
V(ArmAsrPair) \
......
......@@ -49,6 +49,7 @@ int InstructionScheduler::GetTargetInstructionFlags(
case kArmUxtah:
case kArmRbit:
case kArmAddPair:
case kArmSubPair:
case kArmLslPair:
case kArmLsrPair:
case kArmAsrPair:
......
......@@ -783,6 +783,22 @@ void InstructionSelector::VisitInt32PairAdd(Node* node) {
Emit(kArmAddPair, 2, outputs, 4, inputs);
}
void InstructionSelector::VisitInt32PairSub(Node* node) {
ArmOperandGenerator g(this);
// We use UseUniqueRegister here to avoid register sharing with the output
// register.
InstructionOperand inputs[] = {
g.UseRegister(node->InputAt(0)), g.UseUniqueRegister(node->InputAt(1)),
g.UseRegister(node->InputAt(2)), g.UseUniqueRegister(node->InputAt(3))};
InstructionOperand outputs[] = {
g.DefineAsRegister(node),
g.DefineAsRegister(NodeProperties::FindProjection(node, 1))};
Emit(kArmSubPair, 2, outputs, 4, inputs);
}
void InstructionSelector::VisitWord32PairShl(Node* node) {
ArmOperandGenerator g(this);
// We use g.UseUniqueRegister here for InputAt(0) to guarantee that there is
......
......@@ -702,6 +702,31 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
}
break;
}
case kIA32SubPair: {
// i.OutputRegister(0) == i.InputRegister(0) ... left low word.
// i.InputRegister(1) ... left high word.
// i.InputRegister(2) ... right low word.
// i.InputRegister(3) ... right high word.
bool use_temp = false;
if (i.OutputRegister(0).code() == i.InputRegister(1).code() ||
i.OutputRegister(0).code() == i.InputRegister(3).code()) {
// We cannot write to the output register directly, because it would
// overwrite an input for adc. We have to use the temp register.
use_temp = true;
__ Move(i.TempRegister(0), i.InputRegister(0));
__ sub(i.TempRegister(0), i.InputRegister(2));
} else {
__ sub(i.OutputRegister(0), i.InputRegister(2));
}
__ sbb(i.InputRegister(1), Operand(i.InputRegister(3)));
if (i.OutputRegister(1).code() != i.InputRegister(1).code()) {
__ Move(i.OutputRegister(1), i.InputRegister(1));
}
if (use_temp) {
__ Move(i.OutputRegister(0), i.TempRegister(0));
}
break;
}
case kIA32ShlPair:
if (HasImmediateInput(instr, 2)) {
__ ShlPair(i.InputRegister(1), i.InputRegister(0), i.InputInt6(2));
......
......@@ -30,6 +30,7 @@ namespace compiler {
V(IA32Shr) \
V(IA32Sar) \
V(IA32AddPair) \
V(IA32SubPair) \
V(IA32ShlPair) \
V(IA32ShrPair) \
V(IA32SarPair) \
......
......@@ -32,6 +32,7 @@ int InstructionScheduler::GetTargetInstructionFlags(
case kIA32Shr:
case kIA32Sar:
case kIA32AddPair:
case kIA32SubPair:
case kIA32ShlPair:
case kIA32ShrPair:
case kIA32SarPair:
......
......@@ -601,6 +601,24 @@ void InstructionSelector::VisitInt32PairAdd(Node* node) {
Emit(kIA32AddPair, 2, outputs, 4, inputs, 1, temps);
}
void InstructionSelector::VisitInt32PairSub(Node* node) {
IA32OperandGenerator g(this);
// We use UseUniqueRegister here to avoid register sharing with the temp
// register.
InstructionOperand inputs[] = {
g.UseRegister(node->InputAt(0)), g.UseUniqueRegister(node->InputAt(1)),
g.UseRegister(node->InputAt(2)), g.UseUniqueRegister(node->InputAt(3))};
InstructionOperand outputs[] = {
g.DefineSameAsFirst(node),
g.DefineAsRegister(NodeProperties::FindProjection(node, 1))};
InstructionOperand temps[] = {g.TempRegister()};
Emit(kIA32SubPair, 2, outputs, 4, inputs, 1, temps);
}
void InstructionSelector::VisitWord32PairShl(Node* node) {
IA32OperandGenerator g(this);
......
......@@ -1154,6 +1154,10 @@ void InstructionSelector::VisitNode(Node* node) {
MarkAsWord32(NodeProperties::FindProjection(node, 0));
MarkAsWord32(NodeProperties::FindProjection(node, 1));
return VisitInt32PairAdd(node);
case IrOpcode::kInt32PairSub:
MarkAsWord32(NodeProperties::FindProjection(node, 0));
MarkAsWord32(NodeProperties::FindProjection(node, 1));
return VisitInt32PairSub(node);
case IrOpcode::kWord32PairShl:
MarkAsWord32(NodeProperties::FindProjection(node, 0));
MarkAsWord32(NodeProperties::FindProjection(node, 1));
......@@ -1389,10 +1393,12 @@ void InstructionSelector::VisitBitcastInt64ToFloat64(Node* node) {
#endif // V8_TARGET_ARCH_32_BIT
// 32 bit targets do not implement the following instructions.
// 64 bit targets do not implement the following instructions.
#if V8_TARGET_ARCH_64_BIT
void InstructionSelector::VisitInt32PairAdd(Node* node) { UNIMPLEMENTED(); }
void InstructionSelector::VisitInt32PairSub(Node* node) { UNIMPLEMENTED(); }
void InstructionSelector::VisitWord32PairShl(Node* node) { UNIMPLEMENTED(); }
void InstructionSelector::VisitWord32PairShr(Node* node) { UNIMPLEMENTED(); }
......@@ -1478,6 +1484,7 @@ void InstructionSelector::VisitProjection(Node* node) {
case IrOpcode::kTryTruncateFloat32ToUint64:
case IrOpcode::kTryTruncateFloat64ToUint64:
case IrOpcode::kInt32PairAdd:
case IrOpcode::kInt32PairSub:
case IrOpcode::kWord32PairShl:
case IrOpcode::kWord32PairShr:
case IrOpcode::kWord32PairSar:
......
......@@ -273,6 +273,24 @@ void Int64Lowering::LowerNode(Node* node) {
}
// kExprI64Sub:
case IrOpcode::kInt64Sub: {
DCHECK(node->InputCount() == 2);
Node* right = node->InputAt(1);
node->ReplaceInput(1, GetReplacementLow(right));
node->AppendInput(zone(), GetReplacementHigh(right));
Node* left = node->InputAt(0);
node->ReplaceInput(0, GetReplacementLow(left));
node->InsertInput(zone(), 1, GetReplacementHigh(left));
NodeProperties::ChangeOp(node, machine()->Int32PairSub());
// We access the additional return values through projections.
Node* low_node = graph()->NewNode(common()->Projection(0), node);
Node* high_node = graph()->NewNode(common()->Projection(1), node);
ReplaceNode(node, low_node, high_node);
break;
}
// kExprI64Mul:
// kExprI64DivS:
// kExprI64DivU:
......
......@@ -197,6 +197,7 @@ MachineRepresentation StackSlotRepresentationOf(Operator const* op) {
V(LoadFramePointer, Operator::kNoProperties, 0, 0, 1) \
V(LoadParentFramePointer, Operator::kNoProperties, 0, 0, 1) \
V(Int32PairAdd, Operator::kNoProperties, 4, 0, 2) \
V(Int32PairSub, Operator::kNoProperties, 4, 0, 2) \
V(Word32PairShl, Operator::kNoProperties, 3, 0, 2) \
V(Word32PairShr, Operator::kNoProperties, 3, 0, 2) \
V(Word32PairSar, Operator::kNoProperties, 3, 0, 2)
......
......@@ -181,6 +181,7 @@ class MachineOperatorBuilder final : public ZoneObject {
const Operator* Word64Equal();
const Operator* Int32PairAdd();
const Operator* Int32PairSub();
const Operator* Word32PairShl();
const Operator* Word32PairShr();
const Operator* Word32PairSar();
......
......@@ -397,6 +397,8 @@ void InstructionSelector::VisitWord32Sar(Node* node) {
void InstructionSelector::VisitInt32PairAdd(Node* node) { UNIMPLEMENTED(); }
void InstructionSelector::VisitInt32PairSub(Node* node) { UNIMPLEMENTED(); }
void InstructionSelector::VisitWord32PairShl(Node* node) { UNIMPLEMENTED(); }
void InstructionSelector::VisitWord32PairShr(Node* node) { UNIMPLEMENTED(); }
......
......@@ -335,6 +335,7 @@
V(CheckedLoad) \
V(CheckedStore) \
V(Int32PairAdd) \
V(Int32PairSub) \
V(Word32PairShl) \
V(Word32PairShr) \
V(Word32PairSar)
......
......@@ -801,6 +801,8 @@ void InstructionSelector::VisitInt32PairAdd(Node* node) {
Emit(kPPC_AddPair, 2, outputs, 4, inputs);
}
void InstructionSelector::VisitInt32PairSub(Node* node) { UNIMPLEMENTED(); }
void VisitPairShift(InstructionSelector* selector, ArchOpcode opcode,
Node* node) {
PPCOperandGenerator g(selector);
......
......@@ -327,6 +327,9 @@ class RawMachineAssembler {
Node* Int32PairAdd(Node* a_low, Node* a_high, Node* b_low, Node* b_high) {
return AddNode(machine()->Int32PairAdd(), a_low, a_high, b_low, b_high);
}
Node* Int32PairSub(Node* a_low, Node* a_high, Node* b_low, Node* b_high) {
return AddNode(machine()->Int32PairSub(), a_low, a_high, b_low, b_high);
}
Node* Word32PairShl(Node* low_word, Node* high_word, Node* shift) {
return AddNode(machine()->Word32PairShl(), low_word, high_word, shift);
}
......
......@@ -2443,6 +2443,8 @@ Type* Typer::Visitor::TypeCheckedStore(Node* node) {
Type* Typer::Visitor::TypeInt32PairAdd(Node* node) { return Type::Internal(); }
Type* Typer::Visitor::TypeInt32PairSub(Node* node) { return Type::Internal(); }
Type* Typer::Visitor::TypeWord32PairShl(Node* node) { return Type::Internal(); }
Type* Typer::Visitor::TypeWord32PairShr(Node* node) { return Type::Internal(); }
......
......@@ -947,6 +947,7 @@ void Verifier::Visitor::Check(Node* node) {
case IrOpcode::kFloat64InsertLowWord32:
case IrOpcode::kFloat64InsertHighWord32:
case IrOpcode::kInt32PairAdd:
case IrOpcode::kInt32PairSub:
case IrOpcode::kWord32PairShl:
case IrOpcode::kWord32PairShr:
case IrOpcode::kWord32PairSar:
......
......@@ -498,6 +498,9 @@ Node* WasmGraphBuilder::Binop(wasm::WasmOpcode opcode, Node* left,
op = m->Int64Add();
break;
// kExprI64Sub:
case wasm::kExprI64Sub:
op = m->Int64Sub();
break;
// kExprI64Mul:
// kExprI64DivS:
case wasm::kExprI64DivS:
......@@ -570,9 +573,6 @@ Node* WasmGraphBuilder::Binop(wasm::WasmOpcode opcode, Node* left,
#if WASM_64
// Opcodes only supported on 64-bit platforms.
// TODO(titzer): query the machine operator builder here instead of #ifdef.
case wasm::kExprI64Sub:
op = m->Int64Sub();
break;
case wasm::kExprI64Mul:
op = m->Int64Mul();
break;
......
......@@ -562,6 +562,8 @@ void InstructionSelector::VisitInt32PairAdd(Node* node) {
Emit(kX87AddPair, 2, outputs, 4, inputs, 1, temps);
}
void InstructionSelector::VisitInt32PairSub(Node* node) { UNIMPLEMENTED(); }
void InstructionSelector::VisitWord32PairShl(Node* node) {
X87OperandGenerator g(this);
......
......@@ -4197,16 +4197,16 @@ TEST(RunTruncateFloat64ToFloat32) {
FOR_FLOAT64_INPUTS(i) { CHECK_FLOAT_EQ(DoubleToFloat32(*i), m.Call(*i)); }
}
int64_t ToInt64(uint32_t low, uint32_t high) {
return (static_cast<int64_t>(high) << 32) | static_cast<int64_t>(low);
uint64_t ToInt64(uint32_t low, uint32_t high) {
return (static_cast<uint64_t>(high) << 32) | static_cast<uint64_t>(low);
}
#if V8_TARGET_ARCH_32_BIT && !V8_TARGET_ARCH_MIPS && !V8_TARGET_ARCH_PPC && \
!V8_TARGET_ARCH_X87
TEST(RunInt32PairAdd) {
BufferedRawMachineAssemblerTester<int32_t> m(
MachineType::Int32(), MachineType::Int32(), MachineType::Int32(),
MachineType::Int32());
MachineType::Uint32(), MachineType::Uint32(), MachineType::Uint32(),
MachineType::Uint32());
uint32_t high;
uint32_t low;
......@@ -4220,12 +4220,12 @@ TEST(RunInt32PairAdd) {
m.Projection(1, PairAdd));
m.Return(m.Int32Constant(74));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) {
m.Call(static_cast<int32_t>(*i & 0xffffffff),
static_cast<int32_t>(*i >> 32),
static_cast<int32_t>(*j & 0xffffffff),
static_cast<int32_t>(*j >> 32));
FOR_UINT64_INPUTS(i) {
FOR_UINT64_INPUTS(j) {
m.Call(static_cast<uint32_t>(*i & 0xffffffff),
static_cast<uint32_t>(*i >> 32),
static_cast<uint32_t>(*j & 0xffffffff),
static_cast<uint32_t>(*j >> 32));
CHECK_EQ(*i + *j, ToInt64(low, high));
}
}
......@@ -4266,6 +4266,69 @@ TEST(RunInt32PairAddWithSharedInput) {
TestInt32PairAddWithSharedInput(1, 1, 0, 0);
}
TEST(RunInt32PairSub) {
BufferedRawMachineAssemblerTester<int32_t> m(
MachineType::Uint32(), MachineType::Uint32(), MachineType::Uint32(),
MachineType::Uint32());
uint32_t high;
uint32_t low;
Node* PairSub = m.Int32PairSub(m.Parameter(0), m.Parameter(1), m.Parameter(2),
m.Parameter(3));
m.StoreToPointer(&low, MachineRepresentation::kWord32,
m.Projection(0, PairSub));
m.StoreToPointer(&high, MachineRepresentation::kWord32,
m.Projection(1, PairSub));
m.Return(m.Int32Constant(74));
FOR_UINT64_INPUTS(i) {
FOR_UINT64_INPUTS(j) {
m.Call(static_cast<uint32_t>(*i & 0xffffffff),
static_cast<uint32_t>(*i >> 32),
static_cast<uint32_t>(*j & 0xffffffff),
static_cast<uint32_t>(*j >> 32));
CHECK_EQ(*i - *j, ToInt64(low, high));
}
}
}
void TestInt32PairSubWithSharedInput(int a, int b, int c, int d) {
BufferedRawMachineAssemblerTester<int32_t> m(MachineType::Uint32(),
MachineType::Uint32());
uint32_t high;
uint32_t low;
Node* PairSub = m.Int32PairSub(m.Parameter(a), m.Parameter(b), m.Parameter(c),
m.Parameter(d));
m.StoreToPointer(&low, MachineRepresentation::kWord32,
m.Projection(0, PairSub));
m.StoreToPointer(&high, MachineRepresentation::kWord32,
m.Projection(1, PairSub));
m.Return(m.Int32Constant(74));
FOR_UINT32_INPUTS(i) {
FOR_UINT32_INPUTS(j) {
m.Call(*i, *j);
uint32_t inputs[] = {*i, *j};
CHECK_EQ(ToInt64(inputs[a], inputs[b]) - ToInt64(inputs[c], inputs[d]),
ToInt64(low, high));
}
}
}
TEST(RunInt32PairSubWithSharedInput) {
TestInt32PairSubWithSharedInput(0, 0, 0, 0);
TestInt32PairSubWithSharedInput(1, 0, 0, 0);
TestInt32PairSubWithSharedInput(0, 1, 0, 0);
TestInt32PairSubWithSharedInput(0, 0, 1, 0);
TestInt32PairSubWithSharedInput(0, 0, 0, 1);
TestInt32PairSubWithSharedInput(1, 1, 0, 0);
}
TEST(RunWord32PairShl) {
BufferedRawMachineAssemblerTester<int32_t> m(
MachineType::Uint32(), MachineType::Uint32(), MachineType::Uint32());
......@@ -4282,7 +4345,7 @@ TEST(RunWord32PairShl) {
m.Projection(1, PairAdd));
m.Return(m.Int32Constant(74));
FOR_INT64_INPUTS(i) {
FOR_UINT64_INPUTS(i) {
for (uint32_t j = 0; j < 64; j++) {
m.Call(static_cast<uint32_t>(*i & 0xffffffff),
static_cast<uint32_t>(*i >> 32), j);
......
......@@ -41,12 +41,13 @@
#define FOREACH_I64_OPERATOR(V) \
V(DepthFirst, true) \
V(I64Phi, false) \
V(I64Const, true) \
V(I64Return, true) \
V(I64Param, true) \
V(I64LoadStore, true) \
V(I64Add, !MIPS_OR_X87) \
V(I64Sub, false) \
V(I64Sub, !MIPS_OR_X87) \
V(I64Mul, false) \
V(I64DivS, true) \
V(I64DivU, true) \
......@@ -137,6 +138,14 @@ TEST(Run_WasmI64Add) {
}
}
// kExprI64Sub:
TEST(Run_Wasm_I64Sub) {
REQUIRE(I64Sub);
WasmRunner<int64_t> r(MachineType::Int64(), MachineType::Int64());
BUILD(r, WASM_I64_SUB(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) { CHECK_EQ(*i - *j, r.Call(*i, *j)); }
}
}
// kExprI64Mul:
// kExprI64DivS:
......@@ -1241,6 +1250,7 @@ TEST(Run_Wasm_MemI64_Sum) {
REQUIRE(I64LoadStore);
REQUIRE(I64Add);
REQUIRE(I64Sub);
REQUIRE(I64Phi);
const int kNumElems = 20;
TestingModule module;
uint64_t* memory = module.AddMemoryElems<uint64_t>(kNumElems);
......
......@@ -320,6 +320,22 @@ TEST_F(Int64LoweringTest, Int64Add) {
start(), start()));
}
// kExprI64Sub:
TEST_F(Int64LoweringTest, Int64Sub) {
LowerGraph(graph()->NewNode(machine()->Int64Sub(), Int64Constant(value(0)),
Int64Constant(value(1))),
MachineRepresentation::kWord64);
Capture<Node*> sub;
Matcher<Node*> sub_matcher = IsInt32PairSub(
IsInt32Constant(low_word_value(0)), IsInt32Constant(high_word_value(0)),
IsInt32Constant(low_word_value(1)), IsInt32Constant(high_word_value(1)));
EXPECT_THAT(graph()->end()->InputAt(1),
IsReturn2(IsProjection(0, AllOf(CaptureEq(&sub), sub_matcher)),
IsProjection(1, AllOf(CaptureEq(&sub), sub_matcher)),
start(), start()));
}
// kExprI64Mul:
// kExprI64DivS:
// kExprI64DivU:
......
......@@ -2181,6 +2181,7 @@ Matcher<Node*> IsLoadFramePointer() {
}
IS_QUADOP_MATCHER(Int32PairAdd)
IS_QUADOP_MATCHER(Int32PairSub)
#define IS_TERNOP_MATCHER(Name) \
Matcher<Node*> Is##Name(const Matcher<Node*>& lhs_matcher, \
......
......@@ -364,6 +364,11 @@ Matcher<Node*> IsInt32PairAdd(const Matcher<Node*>& a_matcher,
const Matcher<Node*>& b_matcher,
const Matcher<Node*>& c_matcher,
const Matcher<Node*>& d_matcher);
Matcher<Node*> IsInt32PairSub(const Matcher<Node*>& a_matcher,
const Matcher<Node*>& b_matcher,
const Matcher<Node*>& c_matcher,
const Matcher<Node*>& d_matcher);
Matcher<Node*> IsWord32PairShl(const Matcher<Node*>& lhs_matcher,
const Matcher<Node*>& mid_matcher,
const Matcher<Node*>& rhs_matcher);
......
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