From: bmeurer@chromium.org Date: Mon, 3 Nov 2014 10:04:37 +0000 (+0000) Subject: [turbofan] Also optimize unsigned division by constant. X-Git-Tag: upstream/4.7.83~5953 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=498920f91cde6f958fe6caab67bb04d776e78460;p=platform%2Fupstream%2Fv8.git [turbofan] Also optimize unsigned division by constant. TEST=cctest,mjsunit,unittests R=jarin@chromium.org Review URL: https://codereview.chromium.org/697663003 Cr-Commit-Position: refs/heads/master@{#25061} git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@25061 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/arm64/macro-assembler-arm64-inl.h b/src/arm64/macro-assembler-arm64-inl.h index 22c49efbc..4a4d644da 100644 --- a/src/arm64/macro-assembler-arm64-inl.h +++ b/src/arm64/macro-assembler-arm64-inl.h @@ -1126,6 +1126,14 @@ void MacroAssembler::Smulh(const Register& rd, } +void MacroAssembler::Umull(const Register& rd, const Register& rn, + const Register& rm) { + DCHECK(allow_macro_instructions_); + DCHECK(!rd.IsZero()); + umaddl(rd, rn, rm, xzr); +} + + void MacroAssembler::Stnp(const CPURegister& rt, const CPURegister& rt2, const MemOperand& dst) { diff --git a/src/arm64/macro-assembler-arm64.h b/src/arm64/macro-assembler-arm64.h index 99e6d37d8..cff42d7df 100644 --- a/src/arm64/macro-assembler-arm64.h +++ b/src/arm64/macro-assembler-arm64.h @@ -490,6 +490,7 @@ class MacroAssembler : public Assembler { inline void Smulh(const Register& rd, const Register& rn, const Register& rm); + inline void Umull(const Register& rd, const Register& rn, const Register& rm); inline void Stnp(const CPURegister& rt, const CPURegister& rt2, const MemOperand& dst); diff --git a/src/compiler/arm/code-generator-arm.cc b/src/compiler/arm/code-generator-arm.cc index 814f5e7da..91bb66045 100644 --- a/src/compiler/arm/code-generator-arm.cc +++ b/src/compiler/arm/code-generator-arm.cc @@ -248,6 +248,10 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { i.InputRegister(2)); DCHECK_EQ(LeaveCC, i.OutputSBit()); break; + case kArmUmull: + __ umull(i.OutputRegister(0), i.OutputRegister(1), i.InputRegister(0), + i.InputRegister(1), i.OutputSBit()); + break; case kArmSdiv: { CpuFeatureScope scope(masm(), SUDIV); __ sdiv(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1)); diff --git a/src/compiler/arm/instruction-codes-arm.h b/src/compiler/arm/instruction-codes-arm.h index 04559a39c..c48369e7d 100644 --- a/src/compiler/arm/instruction-codes-arm.h +++ b/src/compiler/arm/instruction-codes-arm.h @@ -28,6 +28,7 @@ namespace compiler { V(ArmMls) \ V(ArmSmmul) \ V(ArmSmmla) \ + V(ArmUmull) \ V(ArmSdiv) \ V(ArmUdiv) \ V(ArmMov) \ diff --git a/src/compiler/arm/instruction-selector-arm.cc b/src/compiler/arm/instruction-selector-arm.cc index 3f3aff454..a071bbccf 100644 --- a/src/compiler/arm/instruction-selector-arm.cc +++ b/src/compiler/arm/instruction-selector-arm.cc @@ -86,6 +86,7 @@ class ArmOperandGenerator : public OperandGenerator { case kArmMls: case kArmSmmul: case kArmSmmla: + case kArmUmull: case kArmSdiv: case kArmUdiv: case kArmBfc: @@ -658,6 +659,15 @@ void InstructionSelector::VisitInt32MulHigh(Node* node) { } +void InstructionSelector::VisitUint32MulHigh(Node* node) { + ArmOperandGenerator g(this); + InstructionOperand* outputs[] = {g.TempRegister(), g.DefineAsRegister(node)}; + InstructionOperand* inputs[] = {g.UseRegister(node->InputAt(0)), + g.UseRegister(node->InputAt(1))}; + Emit(kArmUmull, arraysize(outputs), outputs, arraysize(inputs), inputs); +} + + static void EmitDiv(InstructionSelector* selector, ArchOpcode div_opcode, ArchOpcode f64i32_opcode, ArchOpcode i32f64_opcode, InstructionOperand* result_operand, diff --git a/src/compiler/arm64/code-generator-arm64.cc b/src/compiler/arm64/code-generator-arm64.cc index 38c6531c5..35f4d3961 100644 --- a/src/compiler/arm64/code-generator-arm64.cc +++ b/src/compiler/arm64/code-generator-arm64.cc @@ -267,6 +267,9 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { case kArm64Smull: __ Smull(i.OutputRegister(), i.InputRegister32(0), i.InputRegister32(1)); break; + case kArm64Umull: + __ Umull(i.OutputRegister(), i.InputRegister32(0), i.InputRegister32(1)); + break; case kArm64Madd: __ Madd(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1), i.InputRegister(2)); diff --git a/src/compiler/arm64/instruction-codes-arm64.h b/src/compiler/arm64/instruction-codes-arm64.h index ab14a7cc6..c798a0d91 100644 --- a/src/compiler/arm64/instruction-codes-arm64.h +++ b/src/compiler/arm64/instruction-codes-arm64.h @@ -37,6 +37,7 @@ namespace compiler { V(Arm64Mul) \ V(Arm64Mul32) \ V(Arm64Smull) \ + V(Arm64Umull) \ V(Arm64Madd) \ V(Arm64Madd32) \ V(Arm64Msub) \ diff --git a/src/compiler/arm64/instruction-selector-arm64.cc b/src/compiler/arm64/instruction-selector-arm64.cc index 39cd6ca77..bf768c91b 100644 --- a/src/compiler/arm64/instruction-selector-arm64.cc +++ b/src/compiler/arm64/instruction-selector-arm64.cc @@ -778,6 +778,16 @@ void InstructionSelector::VisitInt32MulHigh(Node* node) { } +void InstructionSelector::VisitUint32MulHigh(Node* node) { + // TODO(arm64): Can we do better here? + Arm64OperandGenerator g(this); + InstructionOperand* const smull_operand = g.TempRegister(); + Emit(kArm64Umull, smull_operand, g.UseRegister(node->InputAt(0)), + g.UseRegister(node->InputAt(1))); + Emit(kArm64Lsr, g.DefineAsRegister(node), smull_operand, g.TempImmediate(32)); +} + + void InstructionSelector::VisitInt32Div(Node* node) { VisitRRR(this, kArm64Idiv32, node); } diff --git a/src/compiler/change-lowering.cc b/src/compiler/change-lowering.cc index 3b3998086..0b4a39952 100644 --- a/src/compiler/change-lowering.cc +++ b/src/compiler/change-lowering.cc @@ -53,7 +53,7 @@ Node* ChangeLowering::HeapNumberValueIndexConstant() { Node* ChangeLowering::SmiMaxValueConstant() { const int smi_value_size = machine()->Is32() ? SmiTagging<4>::SmiValueSize() : SmiTagging<8>::SmiValueSize(); - return jsgraph()->IntPtrConstant( + return jsgraph()->Int32Constant( -(static_cast(0xffffffffu << (smi_value_size - 1)) + 1)); } diff --git a/src/compiler/ia32/code-generator-ia32.cc b/src/compiler/ia32/code-generator-ia32.cc index fd4663653..8711242a7 100644 --- a/src/compiler/ia32/code-generator-ia32.cc +++ b/src/compiler/ia32/code-generator-ia32.cc @@ -247,6 +247,9 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { case kIA32ImulHigh: __ imul(i.InputRegister(1)); break; + case kIA32UmulHigh: + __ mul(i.InputRegister(1)); + break; case kIA32Idiv: __ cdq(); __ idiv(i.InputOperand(1)); diff --git a/src/compiler/ia32/instruction-codes-ia32.h b/src/compiler/ia32/instruction-codes-ia32.h index c922a3d9a..f72a1cad9 100644 --- a/src/compiler/ia32/instruction-codes-ia32.h +++ b/src/compiler/ia32/instruction-codes-ia32.h @@ -21,6 +21,7 @@ namespace compiler { V(IA32Sub) \ V(IA32Imul) \ V(IA32ImulHigh) \ + V(IA32UmulHigh) \ V(IA32Idiv) \ V(IA32Udiv) \ V(IA32Not) \ diff --git a/src/compiler/ia32/instruction-selector-ia32.cc b/src/compiler/ia32/instruction-selector-ia32.cc index 1dd9c165b..c242fb431 100644 --- a/src/compiler/ia32/instruction-selector-ia32.cc +++ b/src/compiler/ia32/instruction-selector-ia32.cc @@ -646,22 +646,43 @@ void InstructionSelector::VisitInt32Mul(Node* node) { } -void InstructionSelector::VisitInt32MulHigh(Node* node) { - IA32OperandGenerator g(this); - Emit(kIA32ImulHigh, g.DefineAsFixed(node, edx), - g.UseFixed(node->InputAt(0), eax), - g.UseUniqueRegister(node->InputAt(1))); +namespace { + +void VisitMulHigh(InstructionSelector* selector, Node* node, + ArchOpcode opcode) { + IA32OperandGenerator g(selector); + selector->Emit(opcode, g.DefineAsFixed(node, edx), + g.UseFixed(node->InputAt(0), eax), + g.UseUniqueRegister(node->InputAt(1))); } -static inline void VisitDiv(InstructionSelector* selector, Node* node, - ArchOpcode opcode) { +void VisitDiv(InstructionSelector* selector, Node* node, ArchOpcode opcode) { IA32OperandGenerator g(selector); InstructionOperand* temps[] = {g.TempRegister(edx)}; - size_t temp_count = arraysize(temps); selector->Emit(opcode, g.DefineAsFixed(node, eax), g.UseFixed(node->InputAt(0), eax), - g.UseUnique(node->InputAt(1)), temp_count, temps); + g.UseUnique(node->InputAt(1)), arraysize(temps), temps); +} + + +void VisitMod(InstructionSelector* selector, Node* node, ArchOpcode opcode) { + IA32OperandGenerator g(selector); + selector->Emit(opcode, g.DefineAsFixed(node, edx), + g.UseFixed(node->InputAt(0), eax), + g.UseUnique(node->InputAt(1))); +} + +} // namespace + + +void InstructionSelector::VisitInt32MulHigh(Node* node) { + VisitMulHigh(this, node, kIA32ImulHigh); +} + + +void InstructionSelector::VisitUint32MulHigh(Node* node) { + VisitMulHigh(this, node, kIA32UmulHigh); } @@ -675,15 +696,6 @@ void InstructionSelector::VisitUint32Div(Node* node) { } -static inline void VisitMod(InstructionSelector* selector, Node* node, - ArchOpcode opcode) { - IA32OperandGenerator g(selector); - selector->Emit(opcode, g.DefineAsFixed(node, edx), - g.UseFixed(node->InputAt(0), eax), - g.UseUnique(node->InputAt(1))); -} - - void InstructionSelector::VisitInt32Mod(Node* node) { VisitMod(this, node, kIA32Idiv); } diff --git a/src/compiler/instruction-selector.cc b/src/compiler/instruction-selector.cc index 122bb5ff2..877230ca2 100644 --- a/src/compiler/instruction-selector.cc +++ b/src/compiler/instruction-selector.cc @@ -738,6 +738,8 @@ void InstructionSelector::VisitNode(Node* node) { return VisitUint32LessThanOrEqual(node); case IrOpcode::kUint32Mod: return VisitUint32Mod(node); + case IrOpcode::kUint32MulHigh: + return VisitUint32MulHigh(node); case IrOpcode::kInt64Add: return VisitInt64Add(node); case IrOpcode::kInt64Sub: diff --git a/src/compiler/machine-operator-reducer.cc b/src/compiler/machine-operator-reducer.cc index 3803af5e3..1f73262f9 100644 --- a/src/compiler/machine-operator-reducer.cc +++ b/src/compiler/machine-operator-reducer.cc @@ -49,11 +49,13 @@ Node* MachineOperatorReducer::Word32And(Node* lhs, uint32_t rhs) { Node* MachineOperatorReducer::Word32Sar(Node* lhs, uint32_t rhs) { + if (rhs == 0) return lhs; return graph()->NewNode(machine()->Word32Sar(), lhs, Uint32Constant(rhs)); } Node* MachineOperatorReducer::Word32Shr(Node* lhs, uint32_t rhs) { + if (rhs == 0) return lhs; return graph()->NewNode(machine()->Word32Shr(), lhs, Uint32Constant(rhs)); } @@ -78,7 +80,8 @@ Node* MachineOperatorReducer::Int32Mul(Node* lhs, Node* rhs) { } -Node* MachineOperatorReducer::TruncatingDiv(Node* dividend, int32_t divisor) { +Node* MachineOperatorReducer::Int32Div(Node* dividend, int32_t divisor) { + DCHECK_NE(0, divisor); DCHECK_NE(std::numeric_limits::min(), divisor); base::MagicNumbersForDivision const mag = base::SignedDivisionByConstant(bit_cast(divisor)); @@ -89,10 +92,25 @@ Node* MachineOperatorReducer::TruncatingDiv(Node* dividend, int32_t divisor) { } else if (divisor < 0 && bit_cast(mag.multiplier) > 0) { quotient = Int32Sub(quotient, dividend); } - if (mag.shift) { - quotient = Word32Sar(quotient, mag.shift); + return Int32Add(Word32Sar(quotient, mag.shift), Word32Shr(dividend, 31)); +} + + +Node* MachineOperatorReducer::Uint32Div(Node* dividend, uint32_t divisor) { + DCHECK_LT(0, divisor); + base::MagicNumbersForDivision const mag = + base::UnsignedDivisionByConstant(bit_cast(divisor)); + Node* quotient = graph()->NewNode(machine()->Uint32MulHigh(), dividend, + Uint32Constant(mag.multiplier)); + if (mag.add) { + DCHECK_LE(1, mag.shift); + quotient = Word32Shr( + Int32Add(Word32Shr(Int32Sub(dividend, quotient), 1), quotient), + mag.shift - 1); + } else { + quotient = Word32Shr(quotient, mag.shift); } - return Int32Add(quotient, Word32Shr(dividend, 31)); + return quotient; } @@ -572,7 +590,7 @@ Reduction MachineOperatorReducer::ReduceInt32Div(Node* node) { quotient = Int32Add(Word32Shr(quotient, 32u - shift), dividend); quotient = Word32Sar(quotient, shift); } else { - quotient = TruncatingDiv(quotient, Abs(divisor)); + quotient = Int32Div(quotient, Abs(divisor)); } if (divisor < 0) { node->set_op(machine()->Int32Sub()); @@ -600,11 +618,17 @@ Reduction MachineOperatorReducer::ReduceUint32Div(Node* node) { Node* const zero = Int32Constant(0); return Replace(Word32Equal(Word32Equal(m.left().node(), zero), zero)); } - if (m.right().IsPowerOf2()) { // x / 2^n => x >> n - node->TrimInputCount(2); - node->set_op(machine()->Word32Shr()); - node->ReplaceInput(1, Uint32Constant(WhichPowerOf2(m.right().Value()))); - return Changed(node); + if (m.right().HasValue()) { + Node* const dividend = m.left().node(); + uint32_t const divisor = m.right().Value(); + if (base::bits::IsPowerOfTwo32(divisor)) { // x / 2^n => x >> n + node->set_op(machine()->Word32Shr()); + node->ReplaceInput(1, Uint32Constant(WhichPowerOf2(m.right().Value()))); + node->TrimInputCount(2); + return Changed(node); + } else { + return Replace(Uint32Div(dividend, divisor)); + } } return NoChange(); } @@ -640,17 +664,20 @@ Reduction MachineOperatorReducer::ReduceInt32Mod(Node* node) { Node* pos = Word32And(dividend, mask); Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false); - Node* phi = - graph()->NewNode(common()->Phi(kMachInt32, 2), neg, pos, merge); - return Replace(phi); + + DCHECK_EQ(3, node->InputCount()); + node->set_op(common()->Phi(kMachInt32, 2)); + node->ReplaceInput(0, neg); + node->ReplaceInput(1, pos); + node->ReplaceInput(2, merge); } else { - Node* quotient = TruncatingDiv(dividend, divisor); + Node* quotient = Int32Div(dividend, divisor); node->set_op(machine()->Int32Sub()); DCHECK_EQ(dividend, node->InputAt(0)); node->ReplaceInput(1, Int32Mul(quotient, Int32Constant(divisor))); node->TrimInputCount(2); - return Changed(node); } + return Changed(node); } return NoChange(); } @@ -666,10 +693,19 @@ Reduction MachineOperatorReducer::ReduceUint32Mod(Node* node) { return ReplaceUint32( base::bits::UnsignedMod32(m.left().Value(), m.right().Value())); } - if (m.right().IsPowerOf2()) { // x % 2^n => x & 2^n-1 + if (m.right().HasValue()) { + Node* const dividend = m.left().node(); + uint32_t const divisor = m.right().Value(); + if (base::bits::IsPowerOfTwo32(divisor)) { // x % 2^n => x & 2^n-1 + node->set_op(machine()->Word32And()); + node->ReplaceInput(1, Uint32Constant(m.right().Value() - 1)); + } else { + Node* quotient = Uint32Div(dividend, divisor); + node->set_op(machine()->Int32Sub()); + DCHECK_EQ(dividend, node->InputAt(0)); + node->ReplaceInput(1, Int32Mul(quotient, Uint32Constant(divisor))); + } node->TrimInputCount(2); - node->set_op(machine()->Word32And()); - node->ReplaceInput(1, Uint32Constant(m.right().Value() - 1)); return Changed(node); } return NoChange(); diff --git a/src/compiler/machine-operator-reducer.h b/src/compiler/machine-operator-reducer.h index 475ed2ecd..f19184b75 100644 --- a/src/compiler/machine-operator-reducer.h +++ b/src/compiler/machine-operator-reducer.h @@ -41,8 +41,8 @@ class MachineOperatorReducer FINAL : public Reducer { Node* Int32Add(Node* lhs, Node* rhs); Node* Int32Sub(Node* lhs, Node* rhs); Node* Int32Mul(Node* lhs, Node* rhs); - - Node* TruncatingDiv(Node* dividend, int32_t divisor); + Node* Int32Div(Node* dividend, int32_t divisor); + Node* Uint32Div(Node* dividend, uint32_t divisor); Reduction ReplaceBool(bool value) { return ReplaceInt32(value ? 1 : 0); } Reduction ReplaceFloat32(volatile float value) { diff --git a/src/compiler/machine-operator.cc b/src/compiler/machine-operator.cc index a4aaa66cf..2ea1bf3ba 100644 --- a/src/compiler/machine-operator.cc +++ b/src/compiler/machine-operator.cc @@ -84,6 +84,7 @@ StoreRepresentation const& StoreRepresentationOf(Operator const* op) { V(Uint32LessThan, Operator::kNoProperties, 2, 0, 1) \ V(Uint32LessThanOrEqual, Operator::kNoProperties, 2, 0, 1) \ V(Uint32Mod, Operator::kNoProperties, 2, 1, 1) \ + V(Uint32MulHigh, Operator::kAssociative | Operator::kCommutative, 2, 0, 1) \ V(Int64Add, Operator::kAssociative | Operator::kCommutative, 2, 0, 1) \ V(Int64Sub, Operator::kNoProperties, 2, 0, 1) \ V(Int64Mul, Operator::kAssociative | Operator::kCommutative, 2, 0, 1) \ diff --git a/src/compiler/machine-operator.h b/src/compiler/machine-operator.h index eff9b8b25..979a887ac 100644 --- a/src/compiler/machine-operator.h +++ b/src/compiler/machine-operator.h @@ -108,6 +108,7 @@ class MachineOperatorBuilder FINAL : public ZoneObject { const Operator* Uint32LessThan(); const Operator* Uint32LessThanOrEqual(); const Operator* Uint32Mod(); + const Operator* Uint32MulHigh(); bool Int32DivIsSafe() const { return flags_ & kInt32DivIsSafe; } bool Int32ModIsSafe() const { return flags_ & kInt32ModIsSafe; } bool Uint32DivIsSafe() const { return flags_ & kUint32DivIsSafe; } diff --git a/src/compiler/opcodes.h b/src/compiler/opcodes.h index ac3f0629b..132f9cc75 100644 --- a/src/compiler/opcodes.h +++ b/src/compiler/opcodes.h @@ -198,6 +198,7 @@ V(Uint32LessThan) \ V(Uint32LessThanOrEqual) \ V(Uint32Mod) \ + V(Uint32MulHigh) \ V(Int64Add) \ V(Int64Sub) \ V(Int64Mul) \ diff --git a/src/compiler/raw-machine-assembler.h b/src/compiler/raw-machine-assembler.h index 01fa509e2..b7b82b1d3 100644 --- a/src/compiler/raw-machine-assembler.h +++ b/src/compiler/raw-machine-assembler.h @@ -102,14 +102,15 @@ class RawMachineAssembler : public GraphBuilder { return Load(rep, base, Int32Constant(0)); } Node* Load(MachineType rep, Node* base, Node* index) { - return NewNode(machine()->Load(rep), base, index); + return NewNode(machine()->Load(rep), base, index, graph()->start(), + graph()->start()); } void Store(MachineType rep, Node* base, Node* value) { Store(rep, base, Int32Constant(0), value); } void Store(MachineType rep, Node* base, Node* index, Node* value) { NewNode(machine()->Store(StoreRepresentation(rep, kNoWriteBarrier)), base, - index, value); + index, value, graph()->start(), graph()->start()); } // Arithmetic Operations. Node* WordAnd(Node* a, Node* b) { @@ -231,10 +232,10 @@ class RawMachineAssembler : public GraphBuilder { return NewNode(machine()->Int32MulHigh(), a, b); } Node* Int32Div(Node* a, Node* b) { - return NewNode(machine()->Int32Div(), a, b); + return NewNode(machine()->Int32Div(), a, b, graph()->start()); } Node* Int32Mod(Node* a, Node* b) { - return NewNode(machine()->Int32Mod(), a, b); + return NewNode(machine()->Int32Mod(), a, b, graph()->start()); } Node* Int32LessThan(Node* a, Node* b) { return NewNode(machine()->Int32LessThan(), a, b); @@ -243,7 +244,7 @@ class RawMachineAssembler : public GraphBuilder { return NewNode(machine()->Int32LessThanOrEqual(), a, b); } Node* Uint32Div(Node* a, Node* b) { - return NewNode(machine()->Uint32Div(), a, b); + return NewNode(machine()->Uint32Div(), a, b, graph()->start()); } Node* Uint32LessThan(Node* a, Node* b) { return NewNode(machine()->Uint32LessThan(), a, b); @@ -252,7 +253,10 @@ class RawMachineAssembler : public GraphBuilder { return NewNode(machine()->Uint32LessThanOrEqual(), a, b); } Node* Uint32Mod(Node* a, Node* b) { - return NewNode(machine()->Uint32Mod(), a, b); + return NewNode(machine()->Uint32Mod(), a, b, graph()->start()); + } + Node* Uint32MulHigh(Node* a, Node* b) { + return NewNode(machine()->Uint32MulHigh(), a, b); } Node* Int32GreaterThan(Node* a, Node* b) { return Int32LessThan(b, a); } Node* Int32GreaterThanOrEqual(Node* a, Node* b) { diff --git a/src/compiler/simplified-lowering.cc b/src/compiler/simplified-lowering.cc index 0f784c650..3069865ad 100644 --- a/src/compiler/simplified-lowering.cc +++ b/src/compiler/simplified-lowering.cc @@ -857,11 +857,13 @@ class RepresentationSelector { case IrOpcode::kInt32Add: case IrOpcode::kInt32Sub: case IrOpcode::kInt32Mul: + case IrOpcode::kInt32MulHigh: case IrOpcode::kInt32Div: case IrOpcode::kInt32Mod: return VisitInt32Binop(node); case IrOpcode::kUint32Div: case IrOpcode::kUint32Mod: + case IrOpcode::kUint32MulHigh: return VisitUint32Binop(node); case IrOpcode::kInt32LessThan: case IrOpcode::kInt32LessThanOrEqual: diff --git a/src/compiler/typer.cc b/src/compiler/typer.cc index 6b32258d1..1c169c23a 100644 --- a/src/compiler/typer.cc +++ b/src/compiler/typer.cc @@ -1606,7 +1606,7 @@ Bounds Typer::Visitor::TypeInt32Mul(Node* node) { Bounds Typer::Visitor::TypeInt32MulHigh(Node* node) { - return Bounds(Type::Integral32()); + return Bounds(Type::Signed32()); } @@ -1650,6 +1650,11 @@ Bounds Typer::Visitor::TypeUint32Mod(Node* node) { } +Bounds Typer::Visitor::TypeUint32MulHigh(Node* node) { + return Bounds(Type::Unsigned32()); +} + + Bounds Typer::Visitor::TypeInt64Add(Node* node) { return Bounds(Type::Internal()); } diff --git a/src/compiler/verifier.cc b/src/compiler/verifier.cc index 29342763b..434b845ae 100644 --- a/src/compiler/verifier.cc +++ b/src/compiler/verifier.cc @@ -688,6 +688,7 @@ GenericGraphVisit::Control Verifier::Visitor::Pre(Node* node) { case IrOpcode::kInt32LessThanOrEqual: case IrOpcode::kUint32Div: case IrOpcode::kUint32Mod: + case IrOpcode::kUint32MulHigh: case IrOpcode::kUint32LessThan: case IrOpcode::kUint32LessThanOrEqual: case IrOpcode::kInt64Add: diff --git a/src/compiler/x64/code-generator-x64.cc b/src/compiler/x64/code-generator-x64.cc index b684bdf74..bb02441ff 100644 --- a/src/compiler/x64/code-generator-x64.cc +++ b/src/compiler/x64/code-generator-x64.cc @@ -289,7 +289,18 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ASSEMBLE_MULT(imulq); break; case kX64ImulHigh32: - __ imull(i.InputRegister(1)); + if (instr->InputAt(1)->IsRegister()) { + __ imull(i.InputRegister(1)); + } else { + __ imull(i.InputOperand(1)); + } + break; + case kX64UmulHigh32: + if (instr->InputAt(1)->IsRegister()) { + __ mull(i.InputRegister(1)); + } else { + __ mull(i.InputOperand(1)); + } break; case kX64Idiv32: __ cdq(); diff --git a/src/compiler/x64/instruction-codes-x64.h b/src/compiler/x64/instruction-codes-x64.h index 807fe1480..9d4f59ca7 100644 --- a/src/compiler/x64/instruction-codes-x64.h +++ b/src/compiler/x64/instruction-codes-x64.h @@ -29,6 +29,7 @@ namespace compiler { V(X64Imul) \ V(X64Imul32) \ V(X64ImulHigh32) \ + V(X64UmulHigh32) \ V(X64Idiv) \ V(X64Idiv32) \ V(X64Udiv) \ diff --git a/src/compiler/x64/instruction-selector-x64.cc b/src/compiler/x64/instruction-selector-x64.cc index 432b21cef..cd039ed28 100644 --- a/src/compiler/x64/instruction-selector-x64.cc +++ b/src/compiler/x64/instruction-selector-x64.cc @@ -405,6 +405,36 @@ void VisitMul(InstructionSelector* selector, Node* node, ArchOpcode opcode) { } } + +void VisitMulHigh(InstructionSelector* selector, Node* node, + ArchOpcode opcode) { + X64OperandGenerator g(selector); + Node* left = node->InputAt(0); + Node* right = node->InputAt(1); + if (selector->IsLive(left) && !selector->IsLive(right)) { + std::swap(left, right); + } + selector->Emit(opcode, g.DefineAsFixed(node, rdx), g.UseFixed(left, rax), + g.UseUnique(right)); +} + + +void VisitDiv(InstructionSelector* selector, Node* node, ArchOpcode opcode) { + X64OperandGenerator g(selector); + InstructionOperand* temps[] = {g.TempRegister(rdx)}; + selector->Emit( + opcode, g.DefineAsFixed(node, rax), g.UseFixed(node->InputAt(0), rax), + g.UseUniqueRegister(node->InputAt(1)), arraysize(temps), temps); +} + + +void VisitMod(InstructionSelector* selector, Node* node, ArchOpcode opcode) { + X64OperandGenerator g(selector); + selector->Emit(opcode, g.DefineAsFixed(node, rdx), + g.UseFixed(node->InputAt(0), rax), + g.UseUniqueRegister(node->InputAt(1))); +} + } // namespace @@ -419,20 +449,7 @@ void InstructionSelector::VisitInt64Mul(Node* node) { void InstructionSelector::VisitInt32MulHigh(Node* node) { - X64OperandGenerator g(this); - Emit(kX64ImulHigh32, g.DefineAsFixed(node, rdx), - g.UseFixed(node->InputAt(0), rax), - g.UseUniqueRegister(node->InputAt(1))); -} - - -static void VisitDiv(InstructionSelector* selector, Node* node, - ArchOpcode opcode) { - X64OperandGenerator g(selector); - InstructionOperand* temps[] = {g.TempRegister(rdx)}; - selector->Emit( - opcode, g.DefineAsFixed(node, rax), g.UseFixed(node->InputAt(0), rax), - g.UseUniqueRegister(node->InputAt(1)), arraysize(temps), temps); + VisitMulHigh(this, node, kX64ImulHigh32); } @@ -456,15 +473,6 @@ void InstructionSelector::VisitUint64Div(Node* node) { } -static void VisitMod(InstructionSelector* selector, Node* node, - ArchOpcode opcode) { - X64OperandGenerator g(selector); - selector->Emit(opcode, g.DefineAsFixed(node, rdx), - g.UseFixed(node->InputAt(0), rax), - g.UseUniqueRegister(node->InputAt(1))); -} - - void InstructionSelector::VisitInt32Mod(Node* node) { VisitMod(this, node, kX64Idiv32); } @@ -485,6 +493,11 @@ void InstructionSelector::VisitUint64Mod(Node* node) { } +void InstructionSelector::VisitUint32MulHigh(Node* node) { + VisitMulHigh(this, node, kX64UmulHigh32); +} + + void InstructionSelector::VisitChangeFloat32ToFloat64(Node* node) { X64OperandGenerator g(this); Emit(kSSECvtss2sd, g.DefineAsRegister(node), g.Use(node->InputAt(0))); @@ -544,7 +557,8 @@ void InstructionSelector::VisitChangeUint32ToUint64(Node* node) { case IrOpcode::kUint32Div: case IrOpcode::kUint32LessThan: case IrOpcode::kUint32LessThanOrEqual: - case IrOpcode::kUint32Mod: { + case IrOpcode::kUint32Mod: + case IrOpcode::kUint32MulHigh: { // These 32-bit operations implicitly zero-extend to 64-bit on x64, so the // zero-extension is a no-op. Emit(kArchNop, g.DefineSameAsFirst(node), g.Use(value)); diff --git a/src/x64/assembler-x64.cc b/src/x64/assembler-x64.cc index 6e12458f9..dfd51a4be 100644 --- a/src/x64/assembler-x64.cc +++ b/src/x64/assembler-x64.cc @@ -935,6 +935,14 @@ void Assembler::emit_imul(Register src, int size) { } +void Assembler::emit_imul(const Operand& src, int size) { + EnsureSpace ensure_space(this); + emit_rex(src, size); + emit(0xF7); + emit_operand(0x5, src); +} + + void Assembler::emit_imul(Register dst, Register src, int size) { EnsureSpace ensure_space(this); emit_rex(dst, src, size); @@ -1499,7 +1507,23 @@ void Assembler::emit_repmovs(int size) { } -void Assembler::mul(Register src) { +void Assembler::mull(Register src) { + EnsureSpace ensure_space(this); + emit_optional_rex_32(src); + emit(0xF7); + emit_modrm(0x4, src); +} + + +void Assembler::mull(const Operand& src) { + EnsureSpace ensure_space(this); + emit_optional_rex_32(src); + emit(0xF7); + emit_operand(0x4, src); +} + + +void Assembler::mulq(Register src) { EnsureSpace ensure_space(this); emit_rex_64(src); emit(0xF7); diff --git a/src/x64/assembler-x64.h b/src/x64/assembler-x64.h index d938046b1..3b55396c1 100644 --- a/src/x64/assembler-x64.h +++ b/src/x64/assembler-x64.h @@ -810,8 +810,11 @@ class Assembler : public AssemblerBase { // Sign-extends eax into edx:eax. void cdq(); + // Multiply eax by src, put the result in edx:eax. + void mull(Register src); + void mull(const Operand& src); // Multiply rax by src, put the result in rdx:rax. - void mul(Register src); + void mulq(Register src); #define DECLARE_SHIFT_INSTRUCTION(instruction, subcode) \ void instruction##p(Register dst, Immediate imm8) { \ @@ -1473,6 +1476,7 @@ class Assembler : public AssemblerBase { // Signed multiply instructions. // rdx:rax = rax * src when size is 64 or edx:eax = eax * src when size is 32. void emit_imul(Register src, int size); + void emit_imul(const Operand& src, int size); void emit_imul(Register dst, Register src, int size); void emit_imul(Register dst, const Operand& src, int size); void emit_imul(Register dst, Register src, Immediate imm, int size); diff --git a/test/cctest/compiler/test-run-machops.cc b/test/cctest/compiler/test-run-machops.cc index 271967d7c..9eb675373 100644 --- a/test/cctest/compiler/test-run-machops.cc +++ b/test/cctest/compiler/test-run-machops.cc @@ -61,16 +61,16 @@ TEST(CodeGenInt32Binop) { RawMachineAssemblerTester m; const Operator* kOps[] = { - m.machine()->Word32And(), m.machine()->Word32Or(), - m.machine()->Word32Xor(), m.machine()->Word32Shl(), - m.machine()->Word32Shr(), m.machine()->Word32Sar(), - m.machine()->Word32Equal(), m.machine()->Int32Add(), - m.machine()->Int32Sub(), m.machine()->Int32Mul(), - m.machine()->Int32MulHigh(), m.machine()->Int32Div(), - m.machine()->Uint32Div(), m.machine()->Int32Mod(), - m.machine()->Uint32Mod(), m.machine()->Int32LessThan(), - m.machine()->Int32LessThanOrEqual(), m.machine()->Uint32LessThan(), - m.machine()->Uint32LessThanOrEqual()}; + m.machine()->Word32And(), m.machine()->Word32Or(), + m.machine()->Word32Xor(), m.machine()->Word32Shl(), + m.machine()->Word32Shr(), m.machine()->Word32Sar(), + m.machine()->Word32Equal(), m.machine()->Int32Add(), + m.machine()->Int32Sub(), m.machine()->Int32Mul(), + m.machine()->Int32MulHigh(), m.machine()->Int32Div(), + m.machine()->Uint32Div(), m.machine()->Int32Mod(), + m.machine()->Uint32Mod(), m.machine()->Uint32MulHigh(), + m.machine()->Int32LessThan(), m.machine()->Int32LessThanOrEqual(), + m.machine()->Uint32LessThan(), m.machine()->Uint32LessThanOrEqual()}; for (size_t i = 0; i < arraysize(kOps); ++i) { for (int j = 0; j < 8; j++) { @@ -1500,6 +1500,20 @@ TEST(RunInt32MulAndInt32SubP) { } +TEST(RunUint32MulHighP) { + RawMachineAssemblerTester m; + Int32BinopTester bt(&m); + bt.AddReturn(m.Uint32MulHigh(bt.param0, bt.param1)); + FOR_UINT32_INPUTS(i) { + FOR_UINT32_INPUTS(j) { + int32_t expected = bit_cast(static_cast( + (static_cast(*i) * static_cast(*j)) >> 32)); + CHECK_EQ(expected, bt.call(bit_cast(*i), bit_cast(*j))); + } + } +} + + TEST(RunInt32DivP) { { RawMachineAssemblerTester m; @@ -2812,16 +2826,17 @@ TEST(RunDeadInt32Binops) { RawMachineAssemblerTester m; const Operator* kOps[] = { - m.machine()->Word32And(), m.machine()->Word32Or(), - m.machine()->Word32Xor(), m.machine()->Word32Shl(), - m.machine()->Word32Shr(), m.machine()->Word32Sar(), - m.machine()->Word32Ror(), m.machine()->Word32Equal(), - m.machine()->Int32Add(), m.machine()->Int32Sub(), - m.machine()->Int32Mul(), m.machine()->Int32MulHigh(), - m.machine()->Int32Div(), m.machine()->Uint32Div(), - m.machine()->Int32Mod(), m.machine()->Uint32Mod(), - m.machine()->Int32LessThan(), m.machine()->Int32LessThanOrEqual(), - m.machine()->Uint32LessThan(), m.machine()->Uint32LessThanOrEqual()}; + m.machine()->Word32And(), m.machine()->Word32Or(), + m.machine()->Word32Xor(), m.machine()->Word32Shl(), + m.machine()->Word32Shr(), m.machine()->Word32Sar(), + m.machine()->Word32Ror(), m.machine()->Word32Equal(), + m.machine()->Int32Add(), m.machine()->Int32Sub(), + m.machine()->Int32Mul(), m.machine()->Int32MulHigh(), + m.machine()->Int32Div(), m.machine()->Uint32Div(), + m.machine()->Int32Mod(), m.machine()->Uint32Mod(), + m.machine()->Uint32MulHigh(), m.machine()->Int32LessThan(), + m.machine()->Int32LessThanOrEqual(), m.machine()->Uint32LessThan(), + m.machine()->Uint32LessThanOrEqual()}; for (size_t i = 0; i < arraysize(kOps); ++i) { RawMachineAssemblerTester m(kMachInt32, kMachInt32); diff --git a/test/cctest/test-disasm-x64.cc b/test/cctest/test-disasm-x64.cc index 4ae360893..fac922627 100644 --- a/test/cctest/test-disasm-x64.cc +++ b/test/cctest/test-disasm-x64.cc @@ -179,7 +179,8 @@ TEST(DisasmX64) { __ nop(); __ idivq(rdx); - __ mul(rdx); + __ mull(rdx); + __ mulq(rdx); __ negq(rdx); __ notq(rdx); __ testq(Operand(rbx, rcx, times_4, 10000), rdx); diff --git a/test/mjsunit/asm/uint32div.js b/test/mjsunit/asm/uint32div.js new file mode 100644 index 000000000..54a213878 --- /dev/null +++ b/test/mjsunit/asm/uint32div.js @@ -0,0 +1,29 @@ +// 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. + +var stdlib = {}; +var foreign = {}; +var heap = new ArrayBuffer(64 * 1024); + +function Uint32Div(divisor) { + var name = "div_"; + name += divisor; + var m = eval("function Module(stdlib, foreign, heap) {\n" + + " \"use asm\";\n" + + " function " + name + "(dividend) {\n" + + " return ((dividend >>> 0) / " + divisor + ") >>> 0;\n" + + " }\n" + + " return { f: " + name + "}\n" + + "}; Module"); + return m(stdlib, foreign, heap).f; +} + +var divisors = [0, 1, 3, 4, 10, 42, 64, 100, 1024, 2147483647, 4294967295]; +for (var i in divisors) { + var divisor = divisors[i]; + var mod = Uint32Div(divisor); + for (var dividend = 0; dividend < 4294967296; dividend += 3999773) { + assertEquals((dividend / divisor) >>> 0, mod(dividend)); + } +} diff --git a/test/mjsunit/asm/uint32mod.js b/test/mjsunit/asm/uint32mod.js new file mode 100644 index 000000000..4ba94dad2 --- /dev/null +++ b/test/mjsunit/asm/uint32mod.js @@ -0,0 +1,29 @@ +// 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. + +var stdlib = {}; +var foreign = {}; +var heap = new ArrayBuffer(64 * 1024); + +function Uint32Mod(divisor) { + var name = "mod_"; + name += divisor; + var m = eval("function Module(stdlib, foreign, heap) {\n" + + " \"use asm\";\n" + + " function " + name + "(dividend) {\n" + + " return ((dividend >>> 0) % " + divisor + ") >>> 0;\n" + + " }\n" + + " return { f: " + name + "}\n" + + "}; Module"); + return m(stdlib, foreign, heap).f; +} + +var divisors = [0, 1, 3, 4, 10, 42, 64, 100, 1024, 2147483647, 4294967295]; +for (var i in divisors) { + var divisor = divisors[i]; + var mod = Uint32Mod(divisor); + for (var dividend = 0; dividend < 4294967296; dividend += 3999773) { + assertEquals((dividend % divisor) >>> 0, mod(dividend)); + } +} diff --git a/test/unittests/compiler/arm/instruction-selector-arm-unittest.cc b/test/unittests/compiler/arm/instruction-selector-arm-unittest.cc index 08a23e9f7..6e4306de0 100644 --- a/test/unittests/compiler/arm/instruction-selector-arm-unittest.cc +++ b/test/unittests/compiler/arm/instruction-selector-arm-unittest.cc @@ -1765,6 +1765,23 @@ TEST_F(InstructionSelectorTest, Int32MulHighWithParameters) { } +TEST_F(InstructionSelectorTest, Uint32MulHighWithParameters) { + StreamBuilder m(this, kMachUint32, kMachUint32, kMachUint32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const n = m.Uint32MulHigh(p0, p1); + m.Return(n); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArmUmull, s[0]->arch_opcode()); + ASSERT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); + ASSERT_EQ(2U, s[0]->OutputCount()); + EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->OutputAt(1))); +} + + TEST_F(InstructionSelectorTest, Uint32DivWithParameters) { StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); m.Return(m.Uint32Div(m.Parameter(0), m.Parameter(1))); diff --git a/test/unittests/compiler/change-lowering-unittest.cc b/test/unittests/compiler/change-lowering-unittest.cc index bfee7562d..5f14b8efc 100644 --- a/test/unittests/compiler/change-lowering-unittest.cc +++ b/test/unittests/compiler/change-lowering-unittest.cc @@ -456,7 +456,7 @@ TARGET_TEST_F(ChangeLowering64Test, ChangeUint32ToTagged) { IsMerge( IsIfTrue(AllOf(CaptureEq(&branch), IsBranch(IsUint32LessThanOrEqual( - val, IsInt64Constant(SmiMaxValue())), + val, IsInt32Constant(SmiMaxValue())), graph()->start()))), AllOf(CaptureEq(&if_false), IsIfFalse(CaptureEq(&branch)))))); } diff --git a/test/unittests/compiler/machine-operator-reducer-unittest.cc b/test/unittests/compiler/machine-operator-reducer-unittest.cc index be22bfdb3..51697163a 100644 --- a/test/unittests/compiler/machine-operator-reducer-unittest.cc +++ b/test/unittests/compiler/machine-operator-reducer-unittest.cc @@ -680,26 +680,26 @@ TEST_F(MachineOperatorReducerTest, Word32ShlWithWord32Shr) { TEST_F(MachineOperatorReducerTest, Int32DivWithConstant) { Node* const p0 = Parameter(0); { - Reduction const r = - Reduce(graph()->NewNode(machine()->Int32Div(), p0, Int32Constant(0))); + Reduction const r = Reduce(graph()->NewNode( + machine()->Int32Div(), p0, Int32Constant(0), graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsInt32Constant(0)); } { - Reduction const r = - Reduce(graph()->NewNode(machine()->Int32Div(), p0, Int32Constant(1))); + Reduction const r = Reduce(graph()->NewNode( + machine()->Int32Div(), p0, Int32Constant(1), graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_EQ(r.replacement(), p0); } { - Reduction const r = - Reduce(graph()->NewNode(machine()->Int32Div(), p0, Int32Constant(-1))); + Reduction const r = Reduce(graph()->NewNode( + machine()->Int32Div(), p0, Int32Constant(-1), graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsInt32Sub(IsInt32Constant(0), p0)); } { - Reduction const r = - Reduce(graph()->NewNode(machine()->Int32Div(), p0, Int32Constant(2))); + Reduction const r = Reduce(graph()->NewNode( + machine()->Int32Div(), p0, Int32Constant(2), graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT( r.replacement(), @@ -707,8 +707,8 @@ TEST_F(MachineOperatorReducerTest, Int32DivWithConstant) { IsInt32Constant(1))); } { - Reduction const r = - Reduce(graph()->NewNode(machine()->Int32Div(), p0, Int32Constant(-2))); + Reduction const r = Reduce(graph()->NewNode( + machine()->Int32Div(), p0, Int32Constant(-2), graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT( r.replacement(), @@ -718,8 +718,9 @@ TEST_F(MachineOperatorReducerTest, Int32DivWithConstant) { IsInt32Constant(1)))); } TRACED_FORRANGE(int32_t, shift, 2, 30) { - Reduction const r = Reduce( - graph()->NewNode(machine()->Int32Div(), p0, Int32Constant(1 << shift))); + Reduction const r = + Reduce(graph()->NewNode(machine()->Int32Div(), p0, + Int32Constant(1 << shift), graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT( r.replacement(), @@ -731,7 +732,8 @@ TEST_F(MachineOperatorReducerTest, Int32DivWithConstant) { TRACED_FORRANGE(int32_t, shift, 2, 31) { Reduction const r = Reduce(graph()->NewNode( machine()->Int32Div(), p0, - Uint32Constant(bit_cast(-1) << shift))); + Uint32Constant(bit_cast(-1) << shift), + graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT( r.replacement(), @@ -746,15 +748,15 @@ TEST_F(MachineOperatorReducerTest, Int32DivWithConstant) { TRACED_FOREACH(int32_t, divisor, kInt32Values) { if (divisor < 0) { if (base::bits::IsPowerOfTwo32(-divisor)) continue; - Reduction const r = Reduce( - graph()->NewNode(machine()->Int32Div(), p0, Int32Constant(divisor))); + Reduction const r = Reduce(graph()->NewNode( + machine()->Int32Div(), p0, Int32Constant(divisor), graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsInt32Sub(IsInt32Constant(0), IsTruncatingDiv(p0, -divisor))); } else if (divisor > 0) { if (base::bits::IsPowerOfTwo32(divisor)) continue; - Reduction const r = Reduce( - graph()->NewNode(machine()->Int32Div(), p0, Int32Constant(divisor))); + Reduction const r = Reduce(graph()->NewNode( + machine()->Int32Div(), p0, Int32Constant(divisor), graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsTruncatingDiv(p0, divisor)); } @@ -764,7 +766,8 @@ TEST_F(MachineOperatorReducerTest, Int32DivWithConstant) { TEST_F(MachineOperatorReducerTest, Int32DivWithParameters) { Node* const p0 = Parameter(0); - Reduction const r = Reduce(graph()->NewNode(machine()->Int32Div(), p0, p0)); + Reduction const r = + Reduce(graph()->NewNode(machine()->Int32Div(), p0, p0, graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT( r.replacement(), @@ -779,28 +782,28 @@ TEST_F(MachineOperatorReducerTest, Int32DivWithParameters) { TEST_F(MachineOperatorReducerTest, Uint32DivWithConstant) { Node* const p0 = Parameter(0); { - Reduction const r = - Reduce(graph()->NewNode(machine()->Uint32Div(), Int32Constant(0), p0)); + Reduction const r = Reduce(graph()->NewNode( + machine()->Uint32Div(), Int32Constant(0), p0, graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsInt32Constant(0)); } { - Reduction const r = - Reduce(graph()->NewNode(machine()->Uint32Div(), p0, Int32Constant(0))); + Reduction const r = Reduce(graph()->NewNode( + machine()->Uint32Div(), p0, Int32Constant(0), graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsInt32Constant(0)); } { - Reduction const r = - Reduce(graph()->NewNode(machine()->Uint32Div(), p0, Int32Constant(1))); + Reduction const r = Reduce(graph()->NewNode( + machine()->Uint32Div(), p0, Int32Constant(1), graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_EQ(r.replacement(), p0); } TRACED_FOREACH(uint32_t, dividend, kUint32Values) { TRACED_FOREACH(uint32_t, divisor, kUint32Values) { - Reduction const r = Reduce(graph()->NewNode(machine()->Uint32Div(), - Uint32Constant(dividend), - Uint32Constant(divisor))); + Reduction const r = Reduce( + graph()->NewNode(machine()->Uint32Div(), Uint32Constant(dividend), + Uint32Constant(divisor), graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsInt32Constant(bit_cast( @@ -808,8 +811,9 @@ TEST_F(MachineOperatorReducerTest, Uint32DivWithConstant) { } } TRACED_FORRANGE(uint32_t, shift, 1, 31) { - Reduction const r = Reduce(graph()->NewNode(machine()->Uint32Div(), p0, - Uint32Constant(1u << shift))); + Reduction const r = + Reduce(graph()->NewNode(machine()->Uint32Div(), p0, + Uint32Constant(1u << shift), graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsWord32Shr(p0, IsInt32Constant(bit_cast(shift)))); @@ -819,7 +823,8 @@ TEST_F(MachineOperatorReducerTest, Uint32DivWithConstant) { TEST_F(MachineOperatorReducerTest, Uint32DivWithParameters) { Node* const p0 = Parameter(0); - Reduction const r = Reduce(graph()->NewNode(machine()->Uint32Div(), p0, p0)); + Reduction const r = Reduce( + graph()->NewNode(machine()->Uint32Div(), p0, p0, graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT( r.replacement(), @@ -834,42 +839,43 @@ TEST_F(MachineOperatorReducerTest, Uint32DivWithParameters) { TEST_F(MachineOperatorReducerTest, Int32ModWithConstant) { Node* const p0 = Parameter(0); { - Reduction const r = - Reduce(graph()->NewNode(machine()->Int32Mod(), Int32Constant(0), p0)); + Reduction const r = Reduce(graph()->NewNode( + machine()->Int32Mod(), Int32Constant(0), p0, graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsInt32Constant(0)); } { - Reduction const r = - Reduce(graph()->NewNode(machine()->Int32Mod(), p0, Int32Constant(0))); + Reduction const r = Reduce(graph()->NewNode( + machine()->Int32Mod(), p0, Int32Constant(0), graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsInt32Constant(0)); } { - Reduction const r = - Reduce(graph()->NewNode(machine()->Int32Mod(), p0, Int32Constant(1))); + Reduction const r = Reduce(graph()->NewNode( + machine()->Int32Mod(), p0, Int32Constant(1), graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsInt32Constant(0)); } { - Reduction const r = - Reduce(graph()->NewNode(machine()->Int32Mod(), p0, Int32Constant(-1))); + Reduction const r = Reduce(graph()->NewNode( + machine()->Int32Mod(), p0, Int32Constant(-1), graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsInt32Constant(0)); } TRACED_FOREACH(int32_t, dividend, kInt32Values) { TRACED_FOREACH(int32_t, divisor, kInt32Values) { - Reduction const r = Reduce(graph()->NewNode(machine()->Int32Mod(), - Int32Constant(dividend), - Int32Constant(divisor))); + Reduction const r = Reduce( + graph()->NewNode(machine()->Int32Mod(), Int32Constant(dividend), + Int32Constant(divisor), graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsInt32Constant(base::bits::SignedMod32(dividend, divisor))); } } TRACED_FORRANGE(int32_t, shift, 1, 30) { - Reduction const r = Reduce( - graph()->NewNode(machine()->Int32Mod(), p0, Int32Constant(1 << shift))); + Reduction const r = + Reduce(graph()->NewNode(machine()->Int32Mod(), p0, + Int32Constant(1 << shift), graph()->start())); ASSERT_TRUE(r.Changed()); Capture branch; @@ -890,7 +896,8 @@ TEST_F(MachineOperatorReducerTest, Int32ModWithConstant) { TRACED_FORRANGE(int32_t, shift, 1, 31) { Reduction const r = Reduce(graph()->NewNode( machine()->Int32Mod(), p0, - Uint32Constant(bit_cast(-1) << shift))); + Uint32Constant(bit_cast(-1) << shift), + graph()->start())); ASSERT_TRUE(r.Changed()); Capture branch; @@ -910,8 +917,8 @@ TEST_F(MachineOperatorReducerTest, Int32ModWithConstant) { } TRACED_FOREACH(int32_t, divisor, kInt32Values) { if (divisor == 0 || base::bits::IsPowerOfTwo32(Abs(divisor))) continue; - Reduction const r = Reduce( - graph()->NewNode(machine()->Int32Mod(), p0, Int32Constant(divisor))); + Reduction const r = Reduce(graph()->NewNode( + machine()->Int32Mod(), p0, Int32Constant(divisor), graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsInt32Sub(p0, IsInt32Mul(IsTruncatingDiv(p0, Abs(divisor)), @@ -922,7 +929,8 @@ TEST_F(MachineOperatorReducerTest, Int32ModWithConstant) { TEST_F(MachineOperatorReducerTest, Int32ModWithParameters) { Node* const p0 = Parameter(0); - Reduction const r = Reduce(graph()->NewNode(machine()->Int32Mod(), p0, p0)); + Reduction const r = + Reduce(graph()->NewNode(machine()->Int32Mod(), p0, p0, graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsInt32Constant(0)); } @@ -935,28 +943,28 @@ TEST_F(MachineOperatorReducerTest, Int32ModWithParameters) { TEST_F(MachineOperatorReducerTest, Uint32ModWithConstant) { Node* const p0 = Parameter(0); { - Reduction const r = - Reduce(graph()->NewNode(machine()->Uint32Mod(), p0, Int32Constant(0))); + Reduction const r = Reduce(graph()->NewNode( + machine()->Uint32Mod(), p0, Int32Constant(0), graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsInt32Constant(0)); } { - Reduction const r = - Reduce(graph()->NewNode(machine()->Uint32Mod(), Int32Constant(0), p0)); + Reduction const r = Reduce(graph()->NewNode( + machine()->Uint32Mod(), Int32Constant(0), p0, graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsInt32Constant(0)); } { - Reduction const r = - Reduce(graph()->NewNode(machine()->Uint32Mod(), p0, Int32Constant(1))); + Reduction const r = Reduce(graph()->NewNode( + machine()->Uint32Mod(), p0, Int32Constant(1), graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsInt32Constant(0)); } TRACED_FOREACH(uint32_t, dividend, kUint32Values) { TRACED_FOREACH(uint32_t, divisor, kUint32Values) { - Reduction const r = Reduce(graph()->NewNode(machine()->Uint32Mod(), - Uint32Constant(dividend), - Uint32Constant(divisor))); + Reduction const r = Reduce( + graph()->NewNode(machine()->Uint32Mod(), Uint32Constant(dividend), + Uint32Constant(divisor), graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsInt32Constant(bit_cast( @@ -964,8 +972,9 @@ TEST_F(MachineOperatorReducerTest, Uint32ModWithConstant) { } } TRACED_FORRANGE(uint32_t, shift, 1, 31) { - Reduction const r = Reduce(graph()->NewNode(machine()->Uint32Mod(), p0, - Uint32Constant(1u << shift))); + Reduction const r = + Reduce(graph()->NewNode(machine()->Uint32Mod(), p0, + Uint32Constant(1u << shift), graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsWord32And(p0, IsInt32Constant( @@ -976,7 +985,8 @@ TEST_F(MachineOperatorReducerTest, Uint32ModWithConstant) { TEST_F(MachineOperatorReducerTest, Uint32ModWithParameters) { Node* const p0 = Parameter(0); - Reduction const r = Reduce(graph()->NewNode(machine()->Uint32Mod(), p0, p0)); + Reduction const r = Reduce( + graph()->NewNode(machine()->Uint32Mod(), p0, p0, graph()->start())); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsInt32Constant(0)); } diff --git a/test/unittests/compiler/x64/instruction-selector-x64-unittest.cc b/test/unittests/compiler/x64/instruction-selector-x64-unittest.cc index e51c86b40..fe50ca5e3 100644 --- a/test/unittests/compiler/x64/instruction-selector-x64-unittest.cc +++ b/test/unittests/compiler/x64/instruction-selector-x64-unittest.cc @@ -298,7 +298,27 @@ TEST_F(InstructionSelectorTest, Int32MulHigh) { EXPECT_TRUE(s.IsFixed(s[0]->InputAt(0), rax)); EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); EXPECT_TRUE(!s.IsUsedAtStart(s[0]->InputAt(1))); - ASSERT_EQ(1U, s[0]->OutputCount()); + ASSERT_LE(1U, s[0]->OutputCount()); + EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output())); + EXPECT_TRUE(s.IsFixed(s[0]->OutputAt(0), rdx)); +} + + +TEST_F(InstructionSelectorTest, Uint32MulHigh) { + StreamBuilder m(this, kMachUint32, kMachUint32, kMachUint32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const n = m.Uint32MulHigh(p0, p1); + m.Return(n); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kX64UmulHigh32, s[0]->arch_opcode()); + ASSERT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_TRUE(s.IsFixed(s[0]->InputAt(0), rax)); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); + EXPECT_TRUE(!s.IsUsedAtStart(s[0]->InputAt(1))); + ASSERT_LE(1U, s[0]->OutputCount()); EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output())); EXPECT_TRUE(s.IsFixed(s[0]->OutputAt(0), rdx)); }