diff options
author | 2014-10-07 20:23:36 +0100 | |
---|---|---|
committer | 2014-10-17 11:46:45 +0100 | |
commit | 34bacdf7eb46c0ffbf24ba7aa14a904bc9176fb2 (patch) | |
tree | e8ed8e40c5f7896a9ac01bf7dcc2e56f40cfc804 /compiler/optimizing | |
parent | 7f758228f7904d2f65f06bfbd2b8ecbb8e8c6a9d (diff) |
Add multiplication for integral types
This also fixes an issue where we could allocate a pair register even if
one of its parts was already blocked.
Change-Id: I4869175933409add2a56f1ccfb369c3d3dd3cb01
Diffstat (limited to 'compiler/optimizing')
-rw-r--r-- | compiler/optimizing/builder.cc | 30 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm.cc | 105 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm.h | 4 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86.cc | 138 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86.h | 4 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86_64.cc | 64 | ||||
-rw-r--r-- | compiler/optimizing/codegen_test.cc | 45 | ||||
-rw-r--r-- | compiler/optimizing/nodes.h | 19 |
8 files changed, 381 insertions, 28 deletions
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 5bcc65b03b..2648d4d670 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -713,6 +713,16 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::MUL_INT: { + Binop_23x<HMul>(instruction, Primitive::kPrimInt); + break; + } + + case Instruction::MUL_LONG: { + Binop_23x<HMul>(instruction, Primitive::kPrimLong); + break; + } + case Instruction::ADD_LONG_2ADDR: { Binop_12x<HAdd>(instruction, Primitive::kPrimLong); break; @@ -738,6 +748,16 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::MUL_INT_2ADDR: { + Binop_12x<HMul>(instruction, Primitive::kPrimInt); + break; + } + + case Instruction::MUL_LONG_2ADDR: { + Binop_12x<HMul>(instruction, Primitive::kPrimLong); + break; + } + case Instruction::ADD_INT_LIT16: { Binop_22s<HAdd>(instruction, false); break; @@ -748,6 +768,11 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::MUL_INT_LIT16: { + Binop_22s<HMul>(instruction, false); + break; + } + case Instruction::ADD_INT_LIT8: { Binop_22b<HAdd>(instruction, false); break; @@ -758,6 +783,11 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::MUL_INT_LIT8: { + Binop_22b<HMul>(instruction, false); + break; + } + case Instruction::NEW_INSTANCE: { current_block_->AddInstruction( new (arena_) HNewInstance(dex_offset, instruction.VRegB_21c())); diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index cdee845343..a2cf670b0f 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -236,19 +236,12 @@ Location CodeGeneratorARM::AllocateFreeRegister(Primitive::Type type) const { size_t reg = FindFreeEntry(blocked_register_pairs_, kNumberOfRegisterPairs); ArmManagedRegister pair = ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(reg)); + DCHECK(!blocked_core_registers_[pair.AsRegisterPairLow()]); + DCHECK(!blocked_core_registers_[pair.AsRegisterPairHigh()]); + blocked_core_registers_[pair.AsRegisterPairLow()] = true; blocked_core_registers_[pair.AsRegisterPairHigh()] = true; - // Block all other register pairs that share a register with `pair`. - for (int i = 0; i < kNumberOfRegisterPairs; i++) { - ArmManagedRegister current = - ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i)); - if (current.AsRegisterPairLow() == pair.AsRegisterPairLow() - || current.AsRegisterPairLow() == pair.AsRegisterPairHigh() - || current.AsRegisterPairHigh() == pair.AsRegisterPairLow() - || current.AsRegisterPairHigh() == pair.AsRegisterPairHigh()) { - blocked_register_pairs_[i] = true; - } - } + UpdateBlockedPairRegisters(); return Location::RegisterPairLocation(pair.AsRegisterPairLow(), pair.AsRegisterPairHigh()); } @@ -294,7 +287,6 @@ void CodeGeneratorARM::SetupBlockedRegisters() const { // Reserve R4 for suspend check. blocked_core_registers_[R4] = true; - blocked_register_pairs_[R4_R5] = true; // Reserve thread register. blocked_core_registers_[TR] = true; @@ -318,6 +310,19 @@ void CodeGeneratorARM::SetupBlockedRegisters() const { blocked_fpu_registers_[D13] = true; blocked_fpu_registers_[D14] = true; blocked_fpu_registers_[D15] = true; + + UpdateBlockedPairRegisters(); +} + +void CodeGeneratorARM::UpdateBlockedPairRegisters() const { + for (int i = 0; i < kNumberOfRegisterPairs; i++) { + ArmManagedRegister current = + ArmManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i)); + if (blocked_core_registers_[current.AsRegisterPairLow()] + || blocked_core_registers_[current.AsRegisterPairHigh()]) { + blocked_register_pairs_[i] = true; + } + } } InstructionCodeGeneratorARM::InstructionCodeGeneratorARM(HGraph* graph, CodeGeneratorARM* codegen) @@ -1139,6 +1144,82 @@ void InstructionCodeGeneratorARM::VisitSub(HSub* sub) { } } +void LocationsBuilderARM::VisitMul(HMul* mul) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall); + switch (mul->GetResultType()) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: { + locations->SetInAt(0, Location::RequiresRegister(), Location::kDiesAtEntry); + locations->SetInAt(1, Location::RequiresRegister(), Location::kDiesAtEntry); + locations->SetOut(Location::RequiresRegister()); + break; + } + + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); + break; + + default: + LOG(FATAL) << "Unimplemented mul type " << mul->GetResultType(); + } +} + +void InstructionCodeGeneratorARM::VisitMul(HMul* mul) { + LocationSummary* locations = mul->GetLocations(); + Location out = locations->Out(); + Location first = locations->InAt(0); + Location second = locations->InAt(1); + switch (mul->GetResultType()) { + case Primitive::kPrimInt: { + __ mul(out.As<Register>(), first.As<Register>(), second.As<Register>()); + break; + } + case Primitive::kPrimLong: { + Register out_hi = out.AsRegisterPairHigh<Register>(); + Register out_lo = out.AsRegisterPairLow<Register>(); + Register in1_hi = first.AsRegisterPairHigh<Register>(); + Register in1_lo = first.AsRegisterPairLow<Register>(); + Register in2_hi = second.AsRegisterPairHigh<Register>(); + Register in2_lo = second.AsRegisterPairLow<Register>(); + + // Extra checks to protect caused by the existence of R1_R2. + // The algorithm is wrong if out.hi is either in1.lo or in2.lo: + // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2); + DCHECK_NE(out_hi, in1_lo); + DCHECK_NE(out_hi, in2_lo); + + // input: in1 - 64 bits, in2 - 64 bits + // output: out + // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo + // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32] + // parts: out.lo = (in1.lo * in2.lo)[31:0] + + // IP <- in1.lo * in2.hi + __ mul(IP, in1_lo, in2_hi); + // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo + __ mla(out_hi, in1_hi, in2_lo, IP); + // out.lo <- (in1.lo * in2.lo)[31:0]; + __ umull(out_lo, IP, in1_lo, in2_lo); + // out.hi <- in2.hi * in1.lo + in2.lo * in1.hi + (in1.lo * in2.lo)[63:32] + __ add(out_hi, out_hi, ShifterOperand(IP)); + break; + } + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); + break; + + default: + LOG(FATAL) << "Unimplemented mul type " << mul->GetResultType(); + } +} + void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 7c063f1728..57b289c801 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -164,6 +164,7 @@ class CodeGeneratorARM : public CodeGenerator { } virtual void SetupBlockedRegisters() const OVERRIDE; + virtual Location AllocateFreeRegister(Primitive::Type type) const OVERRIDE; virtual Location GetStackLocation(HLoadLocal* load) const OVERRIDE; @@ -171,6 +172,9 @@ class CodeGeneratorARM : public CodeGenerator { virtual void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE; virtual void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE; + // Blocks all register pairs made out of blocked core registers. + void UpdateBlockedPairRegisters() const; + ParallelMoveResolverARM* GetMoveResolver() { return &move_resolver_; } diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 98d3ad4185..041acdf91e 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -207,19 +207,11 @@ Location CodeGeneratorX86::AllocateFreeRegister(Primitive::Type type) const { size_t reg = FindFreeEntry(blocked_register_pairs_, kNumberOfRegisterPairs); X86ManagedRegister pair = X86ManagedRegister::FromRegisterPair(static_cast<RegisterPair>(reg)); + DCHECK(!blocked_core_registers_[pair.AsRegisterPairLow()]); + DCHECK(!blocked_core_registers_[pair.AsRegisterPairHigh()]); blocked_core_registers_[pair.AsRegisterPairLow()] = true; blocked_core_registers_[pair.AsRegisterPairHigh()] = true; - // Block all other register pairs that share a register with `pair`. - for (int i = 0; i < kNumberOfRegisterPairs; i++) { - X86ManagedRegister current = - X86ManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i)); - if (current.AsRegisterPairLow() == pair.AsRegisterPairLow() - || current.AsRegisterPairLow() == pair.AsRegisterPairHigh() - || current.AsRegisterPairHigh() == pair.AsRegisterPairLow() - || current.AsRegisterPairHigh() == pair.AsRegisterPairHigh()) { - blocked_register_pairs_[i] = true; - } - } + UpdateBlockedPairRegisters(); return Location::RegisterPairLocation(pair.AsRegisterPairLow(), pair.AsRegisterPairHigh()); } @@ -266,10 +258,19 @@ void CodeGeneratorX86::SetupBlockedRegisters() const { blocked_core_registers_[EBP] = true; blocked_core_registers_[ESI] = true; blocked_core_registers_[EDI] = true; - blocked_register_pairs_[EAX_EDI] = true; - blocked_register_pairs_[EDX_EDI] = true; - blocked_register_pairs_[ECX_EDI] = true; - blocked_register_pairs_[EBX_EDI] = true; + + UpdateBlockedPairRegisters(); +} + +void CodeGeneratorX86::UpdateBlockedPairRegisters() const { + for (int i = 0; i < kNumberOfRegisterPairs; i++) { + X86ManagedRegister current = + X86ManagedRegister::FromRegisterPair(static_cast<RegisterPair>(i)); + if (blocked_core_registers_[current.AsRegisterPairLow()] + || blocked_core_registers_[current.AsRegisterPairHigh()]) { + blocked_register_pairs_[i] = true; + } + } } InstructionCodeGeneratorX86::InstructionCodeGeneratorX86(HGraph* graph, CodeGeneratorX86* codegen) @@ -1118,6 +1119,113 @@ void InstructionCodeGeneratorX86::VisitSub(HSub* sub) { } } +void LocationsBuilderX86::VisitMul(HMul* mul) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall); + switch (mul->GetResultType()) { + case Primitive::kPrimInt: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::Any()); + locations->SetOut(Location::SameAsFirstInput()); + break; + case Primitive::kPrimLong: { + locations->SetInAt(0, Location::RequiresRegister()); + // TODO: Currently this handles only stack operands: + // - we don't have enough registers because we currently use Quick ABI. + // - by the time we have a working register allocator we will probably change the ABI + // and fix the above. + // - we don't have a way yet to request operands on stack but the base line compiler + // will leave the operands on the stack with Any(). + locations->SetInAt(1, Location::Any()); + locations->SetOut(Location::SameAsFirstInput()); + // Needed for imul on 32bits with 64bits output. + locations->AddTemp(Location::RegisterLocation(EAX)); + locations->AddTemp(Location::RegisterLocation(EDX)); + break; + } + + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); + break; + + default: + LOG(FATAL) << "Unimplemented mul type " << mul->GetResultType(); + } +} + +void InstructionCodeGeneratorX86::VisitMul(HMul* mul) { + LocationSummary* locations = mul->GetLocations(); + Location first = locations->InAt(0); + Location second = locations->InAt(1); + DCHECK(first.Equals(locations->Out())); + + switch (mul->GetResultType()) { + case Primitive::kPrimInt: { + if (second.IsRegister()) { + __ imull(first.As<Register>(), second.As<Register>()); + } else if (second.IsConstant()) { + Immediate imm(second.GetConstant()->AsIntConstant()->GetValue()); + __ imull(first.As<Register>(), imm); + } else { + DCHECK(second.IsStackSlot()); + __ imull(first.As<Register>(), Address(ESP, second.GetStackIndex())); + } + break; + } + + case Primitive::kPrimLong: { + DCHECK(second.IsDoubleStackSlot()); + + Register in1_hi = first.AsRegisterPairHigh<Register>(); + Register in1_lo = first.AsRegisterPairLow<Register>(); + Address in2_hi(ESP, second.GetHighStackIndex(kX86WordSize)); + Address in2_lo(ESP, second.GetStackIndex()); + Register eax = locations->GetTemp(0).As<Register>(); + Register edx = locations->GetTemp(1).As<Register>(); + + DCHECK_EQ(EAX, eax); + DCHECK_EQ(EDX, edx); + + // input: in1 - 64 bits, in2 - 64 bits + // output: in1 + // formula: in1.hi : in1.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo + // parts: in1.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32] + // parts: in1.lo = (in1.lo * in2.lo)[31:0] + + __ movl(eax, in2_hi); + // eax <- in1.lo * in2.hi + __ imull(eax, in1_lo); + // in1.hi <- in1.hi * in2.lo + __ imull(in1_hi, in2_lo); + // in1.hi <- in1.lo * in2.hi + in1.hi * in2.lo + __ addl(in1_hi, eax); + // move in1_lo to eax to prepare for double precision + __ movl(eax, in1_lo); + // edx:eax <- in1.lo * in2.lo + __ mull(in2_lo); + // in1.hi <- in2.hi * in1.lo + in2.lo * in1.hi + (in1.lo * in2.lo)[63:32] + __ addl(in1_hi, edx); + // in1.lo <- (in1.lo * in2.lo)[31:0]; + __ movl(in1_lo, eax); + + break; + } + + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); + break; + + default: + LOG(FATAL) << "Unimplemented mul type " << mul->GetResultType(); + } +} + void LocationsBuilderX86::VisitNewInstance(HNewInstance* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index aa5fee00e0..db8b9abd91 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -166,6 +166,7 @@ class CodeGeneratorX86 : public CodeGenerator { } virtual void SetupBlockedRegisters() const OVERRIDE; + virtual Location AllocateFreeRegister(Primitive::Type type) const OVERRIDE; virtual Location GetStackLocation(HLoadLocal* load) const OVERRIDE; @@ -173,6 +174,9 @@ class CodeGeneratorX86 : public CodeGenerator { virtual void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE; virtual void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE; + // Blocks all register pairs made out of blocked core registers. + void UpdateBlockedPairRegisters() const; + ParallelMoveResolverX86* GetMoveResolver() { return &move_resolver_; } diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 059ff3fa79..5fa930512b 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -1080,6 +1080,70 @@ void InstructionCodeGeneratorX86_64::VisitSub(HSub* sub) { } } +void LocationsBuilderX86_64::VisitMul(HMul* mul) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall); + switch (mul->GetResultType()) { + case Primitive::kPrimInt: { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::Any()); + locations->SetOut(Location::SameAsFirstInput()); + break; + } + case Primitive::kPrimLong: { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); + break; + } + + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); + break; + + default: + LOG(FATAL) << "Unimplemented mul type " << mul->GetResultType(); + } +} + +void InstructionCodeGeneratorX86_64::VisitMul(HMul* mul) { + LocationSummary* locations = mul->GetLocations(); + Location first = locations->InAt(0); + Location second = locations->InAt(1); + DCHECK(first.Equals(locations->Out())); + switch (mul->GetResultType()) { + case Primitive::kPrimInt: { + if (second.IsRegister()) { + __ imull(first.As<CpuRegister>(), second.As<CpuRegister>()); + } else if (second.IsConstant()) { + Immediate imm(second.GetConstant()->AsIntConstant()->GetValue()); + __ imull(first.As<CpuRegister>(), imm); + } else { + DCHECK(second.IsStackSlot()); + __ imull(first.As<CpuRegister>(), Address(CpuRegister(RSP), second.GetStackIndex())); + } + break; + } + case Primitive::kPrimLong: { + __ imulq(first.As<CpuRegister>(), second.As<CpuRegister>()); + break; + } + + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + LOG(FATAL) << "Unexpected mul type " << mul->GetResultType(); + break; + + default: + LOG(FATAL) << "Unimplemented mul type " << mul->GetResultType(); + } +} + void LocationsBuilderX86_64::VisitNewInstance(HNewInstance* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc index 3037f1c2e8..8bb12de387 100644 --- a/compiler/optimizing/codegen_test.cc +++ b/compiler/optimizing/codegen_test.cc @@ -349,4 +349,49 @@ TEST(CodegenTest, NonMaterializedCondition) { RunCodeOptimized(graph, hook_before_codegen, true, 0); } +#define MUL_TEST(TYPE, TEST_NAME) \ + TEST(CodegenTest, Return ## TEST_NAME) { \ + const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( \ + Instruction::CONST_4 | 3 << 12 | 0, \ + Instruction::CONST_4 | 4 << 12 | 1 << 8, \ + Instruction::MUL_ ## TYPE, 1 << 8 | 0, \ + Instruction::RETURN); \ + \ + TestCode(data, true, 12); \ + } \ + \ + TEST(CodegenTest, Return ## TEST_NAME ## 2addr) { \ + const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( \ + Instruction::CONST_4 | 3 << 12 | 0, \ + Instruction::CONST_4 | 4 << 12 | 1 << 8, \ + Instruction::MUL_ ## TYPE ## _2ADDR | 1 << 12, \ + Instruction::RETURN); \ + \ + TestCode(data, true, 12); \ + } + +MUL_TEST(INT, MulInt); +MUL_TEST(LONG, MulLong); +// MUL_TEST(FLOAT, Float); +// MUL_TEST(DOUBLE, Double); + +TEST(CodegenTest, ReturnMulIntLit8) { + const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + Instruction::CONST_4 | 4 << 12 | 0 << 8, + Instruction::MUL_INT_LIT8, 3 << 8 | 0, + Instruction::RETURN); + + TestCode(data, true, 12); +} + +TEST(CodegenTest, ReturnMulIntLit16) { + const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + Instruction::CONST_4 | 4 << 12 | 0 << 8, + Instruction::MUL_INT_LIT16, 3, + Instruction::RETURN); + + TestCode(data, true, 12); +} + + } // namespace art diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index e60a7e62db..ec26c4a4dc 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -502,11 +502,12 @@ class HBasicBlock : public ArenaObject { M(NullCheck, Instruction) \ M(Temporary, Instruction) \ M(SuspendCheck, Instruction) \ + M(Mul, BinaryOperation) \ #define FOR_EACH_INSTRUCTION(M) \ FOR_EACH_CONCRETE_INSTRUCTION(M) \ M(Constant, Instruction) \ - M(BinaryOperation, Instruction) \ + M(BinaryOperation, Instruction) \ M(Invoke, Instruction) #define FORWARD_DECLARATION(type, super) class H##type; @@ -1556,6 +1557,22 @@ class HSub : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HSub); }; +class HMul : public HBinaryOperation { + public: + HMul(Primitive::Type result_type, HInstruction* left, HInstruction* right) + : HBinaryOperation(result_type, left, right) {} + + virtual bool IsCommutative() { return true; } + + virtual int32_t Evaluate(int32_t x, int32_t y) const { return x * y; } + virtual int64_t Evaluate(int64_t x, int64_t y) const { return x * y; } + + DECLARE_INSTRUCTION(Mul); + + private: + DISALLOW_COPY_AND_ASSIGN(HMul); +}; + // The value of a parameter in this method. Its location depends on // the calling convention. class HParameterValue : public HExpression<0> { |