Add support for taking parameters in optimizing compiler.

- Fix stack layout to mimic Quick's.
- Implement some sub operations.

Change-Id: I8cf75a4d29b662381a64f02c0bc61d859482fc4e
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index d90405a..f89583d 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -25,7 +25,8 @@
 
 namespace art {
 
-void HGraphBuilder::InitializeLocals(int count) {
+void HGraphBuilder::InitializeLocals(uint16_t count) {
+  graph_->SetNumberOfVRegs(count);
   locals_.SetSize(count);
   for (int i = 0; i < count; i++) {
     HLocal* local = new (arena_) HLocal(i);
@@ -34,11 +35,54 @@
   }
 }
 
+bool HGraphBuilder::InitializeParameters(uint16_t number_of_parameters) {
+  // dex_compilation_unit_ is null only when unit testing.
+  if (dex_compilation_unit_ == nullptr) {
+    return true;
+  }
+
+  graph_->SetNumberOfInVRegs(number_of_parameters);
+  const char* shorty = dex_compilation_unit_->GetShorty();
+  int locals_index = locals_.Size() - number_of_parameters;
+  HBasicBlock* first_block = entry_block_->GetSuccessors()->Get(0);
+  int parameter_index = 0;
+
+  if (!dex_compilation_unit_->IsStatic()) {
+    // Add the implicit 'this' argument, not expressed in the signature.
+    HParameterValue* parameter = new (arena_) HParameterValue(parameter_index++);
+    first_block->AddInstruction(parameter);
+    HLocal* local = GetLocalAt(locals_index++);
+    first_block->AddInstruction(new (arena_) HStoreLocal(local, parameter));
+    number_of_parameters--;
+  }
+
+  uint32_t pos = 1;
+  for (int i = 0; i < number_of_parameters; i++) {
+    switch (shorty[pos++]) {
+      case 'F':
+      case 'D':
+      case 'J': {
+        return false;
+      }
+
+      default: {
+        // integer and reference parameters.
+        HParameterValue* parameter = new (arena_) HParameterValue(parameter_index++);
+        first_block->AddInstruction(parameter);
+        HLocal* local = GetLocalAt(locals_index++);
+        // Store the parameter value in the local that the dex code will use
+        // to reference that parameter.
+        first_block->AddInstruction(new (arena_) HStoreLocal(local, parameter));
+        break;
+      }
+    }
+  }
+  return true;
+}
+
 static bool CanHandleCodeItem(const DexFile::CodeItem& code_item) {
   if (code_item.tries_size_ > 0) {
     return false;
-  } else if (code_item.ins_size_ > 0) {
-    return false;
   }
   return true;
 }
@@ -66,6 +110,10 @@
   // start a new block, and create these blocks.
   ComputeBranchTargets(code_ptr, code_end);
 
+  if (!InitializeParameters(code_item.ins_size_)) {
+    return nullptr;
+  }
+
   size_t dex_offset = 0;
   while (code_ptr < code_end) {
     // Update the current block if dex_offset starts a new block.
@@ -139,6 +187,44 @@
   return branch_targets_.Get(index);
 }
 
+template<typename T>
+void HGraphBuilder::Binop_32x(const Instruction& instruction) {
+  HInstruction* first = LoadLocal(instruction.VRegB());
+  HInstruction* second = LoadLocal(instruction.VRegC());
+  current_block_->AddInstruction(new (arena_) T(Primitive::kPrimInt, first, second));
+  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+}
+
+template<typename T>
+void HGraphBuilder::Binop_12x(const Instruction& instruction) {
+  HInstruction* first = LoadLocal(instruction.VRegA());
+  HInstruction* second = LoadLocal(instruction.VRegB());
+  current_block_->AddInstruction(new (arena_) T(Primitive::kPrimInt, first, second));
+  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+}
+
+template<typename T>
+void HGraphBuilder::Binop_22s(const Instruction& instruction, bool reverse) {
+  HInstruction* first = LoadLocal(instruction.VRegB());
+  HInstruction* second = GetConstant(instruction.VRegC_22s());
+  if (reverse) {
+    std::swap(first, second);
+  }
+  current_block_->AddInstruction(new (arena_) T(Primitive::kPrimInt, first, second));
+  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+}
+
+template<typename T>
+void HGraphBuilder::Binop_22b(const Instruction& instruction, bool reverse) {
+  HInstruction* first = LoadLocal(instruction.VRegB());
+  HInstruction* second = GetConstant(instruction.VRegC_22b());
+  if (reverse) {
+    std::swap(first, second);
+  }
+  current_block_->AddInstruction(new (arena_) T(Primitive::kPrimInt, first, second));
+  UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+}
+
 bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, int32_t dex_offset) {
   if (current_block_ == nullptr) {
     return true;  // Dead code
@@ -185,7 +271,8 @@
       break;
     }
 
-    case Instruction::RETURN: {
+    case Instruction::RETURN:
+    case Instruction::RETURN_OBJECT: {
       HInstruction* value = LoadLocal(instruction.VRegA());
       current_block_->AddInstruction(new (arena_) HReturn(value));
       current_block_->AddSuccessor(exit_block_);
@@ -250,34 +337,42 @@
     }
 
     case Instruction::ADD_INT: {
-      HInstruction* first = LoadLocal(instruction.VRegB());
-      HInstruction* second = LoadLocal(instruction.VRegC());
-      current_block_->AddInstruction(new (arena_) HAdd(Primitive::kPrimInt, first, second));
-      UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+      Binop_32x<HAdd>(instruction);
+      break;
+    }
+
+    case Instruction::SUB_INT: {
+      Binop_32x<HSub>(instruction);
       break;
     }
 
     case Instruction::ADD_INT_2ADDR: {
-      HInstruction* first = LoadLocal(instruction.VRegA());
-      HInstruction* second = LoadLocal(instruction.VRegB());
-      current_block_->AddInstruction(new (arena_) HAdd(Primitive::kPrimInt, first, second));
-      UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+      Binop_12x<HAdd>(instruction);
+      break;
+    }
+
+    case Instruction::SUB_INT_2ADDR: {
+      Binop_12x<HSub>(instruction);
       break;
     }
 
     case Instruction::ADD_INT_LIT16: {
-      HInstruction* first = LoadLocal(instruction.VRegB());
-      HInstruction* second = GetConstant(instruction.VRegC_22s());
-      current_block_->AddInstruction(new (arena_) HAdd(Primitive::kPrimInt, first, second));
-      UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+      Binop_22s<HAdd>(instruction, false);
+      break;
+    }
+
+    case Instruction::RSUB_INT: {
+      Binop_22s<HSub>(instruction, true);
       break;
     }
 
     case Instruction::ADD_INT_LIT8: {
-      HInstruction* first = LoadLocal(instruction.VRegB());
-      HInstruction* second = GetConstant(instruction.VRegC_22b());
-      current_block_->AddInstruction(new (arena_) HAdd(Primitive::kPrimInt, first, second));
-      UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+      Binop_22b<HAdd>(instruction, false);
+      break;
+    }
+
+    case Instruction::RSUB_INT_LIT8: {
+      Binop_22b<HSub>(instruction, true);
       break;
     }
 
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 46ca9aa..df64d71 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -66,11 +66,20 @@
   HIntConstant* GetConstant0();
   HIntConstant* GetConstant1();
   HIntConstant* GetConstant(int constant);
-  void InitializeLocals(int count);
+  void InitializeLocals(uint16_t count);
   HLocal* GetLocalAt(int register_index) const;
   void UpdateLocal(int register_index, HInstruction* instruction) const;
   HInstruction* LoadLocal(int register_index) const;
 
+  // Temporarily returns whether the compiler supports the parameters
+  // of the method.
+  bool InitializeParameters(uint16_t number_of_parameters);
+
+  template<typename T> void Binop_32x(const Instruction& instruction);
+  template<typename T> void Binop_12x(const Instruction& instruction);
+  template<typename T> void Binop_22b(const Instruction& instruction, bool reverse);
+  template<typename T> void Binop_22s(const Instruction& instruction, bool reverse);
+
   ArenaAllocator* const arena_;
 
   // A list of the size of the dex code holding block information for
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index d6295db..40a7b6f 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -30,7 +30,6 @@
 namespace art {
 
 void CodeGenerator::Compile(CodeAllocator* allocator) {
-  frame_size_ = GetGraph()->GetMaximumNumberOfOutVRegs() * GetWordSize();
   const GrowableArray<HBasicBlock*>* blocks = GetGraph()->GetBlocks();
   DCHECK(blocks->Get(0) == GetGraph()->GetEntryBlock());
   DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks->Get(1)));
@@ -47,16 +46,14 @@
 void CodeGenerator::CompileEntryBlock() {
   HGraphVisitor* location_builder = GetLocationBuilder();
   HGraphVisitor* instruction_visitor = GetInstructionVisitor();
-  // The entry block contains all locals for this method. By visiting the entry block,
-  // we're computing the required frame size.
-  for (HInstructionIterator it(GetGraph()->GetEntryBlock()); !it.Done(); it.Advance()) {
-    HInstruction* current = it.Current();
-    // Instructions in the entry block should not generate code.
-    if (kIsDebugBuild) {
+  if (kIsDebugBuild) {
+    for (HInstructionIterator it(GetGraph()->GetEntryBlock()); !it.Done(); it.Advance()) {
+      HInstruction* current = it.Current();
+      // Instructions in the entry block should not generate code.
       current->Accept(location_builder);
       DCHECK(current->GetLocations() == nullptr);
+      current->Accept(instruction_visitor);
     }
-    current->Accept(instruction_visitor);
   }
   GenerateFrameEntry();
 }
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index cb77f57..2364bc8 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -38,9 +38,11 @@
   core_spill_mask_ |= (1 << LR);
   __ PushList((1 << LR));
 
-  // Add the current ART method to the frame size and the return PC.
-  SetFrameSize(RoundUp(GetFrameSize() + 2 * kArmWordSize, kStackAlignment));
-  // The retrn PC has already been pushed on the stack.
+  // Add the current ART method to the frame size, the return PC, and the filler.
+  SetFrameSize(RoundUp((
+      GetGraph()->GetMaximumNumberOfOutVRegs() + GetGraph()->GetNumberOfVRegs() + 3) * kArmWordSize,
+      kStackAlignment));
+  // The return PC has already been pushed on the stack.
   __ AddConstant(SP, -(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kArmWordSize));
   __ str(R0, Address(SP, 0));
 }
@@ -55,7 +57,20 @@
 }
 
 int32_t CodeGeneratorARM::GetStackSlot(HLocal* local) const {
-  return (GetGraph()->GetMaximumNumberOfOutVRegs() + local->GetRegNumber()) * kArmWordSize;
+  uint16_t reg_number = local->GetRegNumber();
+  uint16_t number_of_vregs = GetGraph()->GetNumberOfVRegs();
+  uint16_t number_of_in_vregs = GetGraph()->GetNumberOfInVRegs();
+  if (reg_number >= number_of_vregs - number_of_in_vregs) {
+    // Local is a parameter of the method. It is stored in the caller's frame.
+    return GetFrameSize() + kArmWordSize  // ART method
+                          + (reg_number - number_of_vregs + number_of_in_vregs) * kArmWordSize;
+  } else {
+    // Local is a temporary in this method. It is stored in this method's frame.
+    return GetFrameSize() - (kNumberOfPushedRegistersAtEntry * kArmWordSize)
+                          - kArmWordSize  // filler.
+                          - (number_of_vregs * kArmWordSize)
+                          + (reg_number * kArmWordSize);
+  }
 }
 
 void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstruction* move_for) {
@@ -187,18 +202,18 @@
 static constexpr Register kParameterCoreRegisters[] = { R1, R2, R3 };
 static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters);
 
-class InvokeStaticCallingConvention : public CallingConvention<Register> {
+class InvokeDexCallingConvention : public CallingConvention<Register> {
  public:
-  InvokeStaticCallingConvention()
+  InvokeDexCallingConvention()
       : CallingConvention(kParameterCoreRegisters, kParameterCoreRegistersLength) {}
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(InvokeStaticCallingConvention);
+  DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConvention);
 };
 
 void LocationsBuilderARM::VisitPushArgument(HPushArgument* argument) {
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(argument);
-  InvokeStaticCallingConvention calling_convention;
+  InvokeDexCallingConvention calling_convention;
   if (argument->GetArgumentIndex() < calling_convention.GetNumberOfRegisters()) {
     Location location = Location(calling_convention.GetRegisterAt(argument->GetArgumentIndex()));
     locations->SetInAt(0, location);
@@ -211,7 +226,7 @@
 
 void InstructionCodeGeneratorARM::VisitPushArgument(HPushArgument* argument) {
   uint8_t argument_index = argument->GetArgumentIndex();
-  InvokeStaticCallingConvention calling_convention;
+  InvokeDexCallingConvention calling_convention;
   size_t parameter_registers = calling_convention.GetNumberOfRegisters();
   LocationSummary* locations = argument->GetLocations();
   if (argument_index >= parameter_registers) {
@@ -287,6 +302,34 @@
   }
 }
 
+void LocationsBuilderARM::VisitSub(HSub* sub) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub);
+  switch (sub->GetResultType()) {
+    case Primitive::kPrimInt: {
+      locations->SetInAt(0, Location(R0));
+      locations->SetInAt(1, Location(R1));
+      locations->SetOut(Location(R0));
+      break;
+    }
+    default:
+      LOG(FATAL) << "Unimplemented";
+  }
+  sub->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorARM::VisitSub(HSub* sub) {
+  LocationSummary* locations = sub->GetLocations();
+  switch (sub->GetResultType()) {
+    case Primitive::kPrimInt:
+      __ sub(locations->Out().reg<Register>(),
+             locations->InAt(0).reg<Register>(),
+             ShifterOperand(locations->InAt(1).reg<Register>()));
+      break;
+    default:
+      LOG(FATAL) << "Unimplemented";
+  }
+}
+
 static constexpr Register kRuntimeParameterCoreRegisters[] = { R0, R1 };
 static constexpr size_t kRuntimeParameterCoreRegistersLength =
     arraysize(kRuntimeParameterCoreRegisters);
@@ -319,5 +362,27 @@
   codegen_->RecordPcInfo(instruction->GetDexPc());
 }
 
+void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  InvokeDexCallingConvention calling_convention;
+  uint32_t argument_index = instruction->GetIndex();
+  if (argument_index < calling_convention.GetNumberOfRegisters()) {
+    locations->SetOut(Location(calling_convention.GetRegisterAt(argument_index)));
+  } else {
+    locations->SetOut(Location(R0));
+  }
+  instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorARM::VisitParameterValue(HParameterValue* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  InvokeDexCallingConvention calling_convention;
+  uint8_t argument_index = instruction->GetIndex();
+  if (argument_index >= calling_convention.GetNumberOfRegisters()) {
+    uint8_t offset = calling_convention.GetStackOffsetOf(argument_index);
+    __ ldr(locations->Out().reg<Register>(), Address(SP, offset + codegen_->GetFrameSize()));
+  }
+}
+
 }  // namespace arm
 }  // namespace art
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index c695e26..540a72a 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -39,11 +39,13 @@
   static const int kFakeReturnRegister = 8;
   core_spill_mask_ |= (1 << kFakeReturnRegister);
 
-  // Add the current ART method to the frame size and the return PC.
-  SetFrameSize(RoundUp(GetFrameSize() + 2 * kX86WordSize, kStackAlignment));
+  // Add the current ART method to the frame size, the return PC, and the filler.
+  SetFrameSize(RoundUp((
+      GetGraph()->GetMaximumNumberOfOutVRegs() + GetGraph()->GetNumberOfVRegs() + 3) * kX86WordSize,
+      kStackAlignment));
   // The return PC has already been pushed on the stack.
   __ subl(ESP, Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize));
-  __ movl(Address(ESP, 0), EAX);
+  __ movl(Address(ESP, kCurrentMethodStackOffset), EAX);
 }
 
 void CodeGeneratorX86::GenerateFrameExit() {
@@ -59,7 +61,20 @@
 }
 
 int32_t CodeGeneratorX86::GetStackSlot(HLocal* local) const {
-  return (GetGraph()->GetMaximumNumberOfOutVRegs() + local->GetRegNumber()) * kX86WordSize;
+  uint16_t reg_number = local->GetRegNumber();
+  uint16_t number_of_vregs = GetGraph()->GetNumberOfVRegs();
+  uint16_t number_of_in_vregs = GetGraph()->GetNumberOfInVRegs();
+  if (reg_number >= number_of_vregs - number_of_in_vregs) {
+    // Local is a parameter of the method. It is stored in the caller's frame.
+    return GetFrameSize() + kX86WordSize  // ART method
+                          + (reg_number - number_of_vregs + number_of_in_vregs) * kX86WordSize;
+  } else {
+    // Local is a temporary in this method. It is stored in this method's frame.
+    return GetFrameSize() - (kNumberOfPushedRegistersAtEntry * kX86WordSize)
+                          - kX86WordSize  // filler.
+                          - (number_of_vregs * kX86WordSize)
+                          + (reg_number * kX86WordSize);
+  }
 }
 
 void CodeGeneratorX86::Move(HInstruction* instruction, Location location, HInstruction* move_for) {
@@ -122,7 +137,6 @@
 
 void InstructionCodeGeneratorX86::VisitLocal(HLocal* local) {
   DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock());
-  codegen_->SetFrameSize(codegen_->GetFrameSize() + kX86WordSize);
 }
 
 void LocationsBuilderX86::VisitLoadLocal(HLoadLocal* local) {
@@ -190,13 +204,13 @@
 static constexpr Register kParameterCoreRegisters[] = { ECX, EDX, EBX };
 static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters);
 
-class InvokeStaticCallingConvention : public CallingConvention<Register> {
+class InvokeDexCallingConvention : public CallingConvention<Register> {
  public:
-  InvokeStaticCallingConvention()
+  InvokeDexCallingConvention()
       : CallingConvention(kParameterCoreRegisters, kParameterCoreRegistersLength) {}
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(InvokeStaticCallingConvention);
+  DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConvention);
 };
 
 static constexpr Register kRuntimeParameterCoreRegisters[] = { EAX, ECX, EDX };
@@ -215,7 +229,7 @@
 
 void LocationsBuilderX86::VisitPushArgument(HPushArgument* argument) {
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(argument);
-  InvokeStaticCallingConvention calling_convention;
+  InvokeDexCallingConvention calling_convention;
   if (argument->GetArgumentIndex() < calling_convention.GetNumberOfRegisters()) {
     Location location = Location(calling_convention.GetRegisterAt(argument->GetArgumentIndex()));
     locations->SetInAt(0, location);
@@ -228,7 +242,7 @@
 
 void InstructionCodeGeneratorX86::VisitPushArgument(HPushArgument* argument) {
   uint8_t argument_index = argument->GetArgumentIndex();
-  InvokeStaticCallingConvention calling_convention;
+  InvokeDexCallingConvention calling_convention;
   size_t parameter_registers = calling_convention.GetNumberOfRegisters();
   if (argument_index >= parameter_registers) {
     uint8_t offset = calling_convention.GetStackOffsetOf(argument_index);
@@ -298,6 +312,33 @@
   }
 }
 
+void LocationsBuilderX86::VisitSub(HSub* sub) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub);
+  switch (sub->GetResultType()) {
+    case Primitive::kPrimInt: {
+      locations->SetInAt(0, Location(EAX));
+      locations->SetInAt(1, Location(ECX));
+      locations->SetOut(Location(EAX));
+      break;
+    }
+    default:
+      LOG(FATAL) << "Unimplemented";
+  }
+  sub->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorX86::VisitSub(HSub* sub) {
+  LocationSummary* locations = sub->GetLocations();
+  switch (sub->GetResultType()) {
+    case Primitive::kPrimInt:
+      DCHECK_EQ(locations->InAt(0).reg<Register>(), locations->Out().reg<Register>());
+      __ subl(locations->InAt(0).reg<Register>(), locations->InAt(1).reg<Register>());
+      break;
+    default:
+      LOG(FATAL) << "Unimplemented";
+  }
+}
+
 void LocationsBuilderX86::VisitNewInstance(HNewInstance* instruction) {
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
   locations->SetOut(Location(EAX));
@@ -316,5 +357,27 @@
   codegen_->RecordPcInfo(instruction->GetDexPc());
 }
 
+void LocationsBuilderX86::VisitParameterValue(HParameterValue* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  InvokeDexCallingConvention calling_convention;
+  uint32_t argument_index = instruction->GetIndex();
+  if (argument_index < calling_convention.GetNumberOfRegisters()) {
+    locations->SetOut(Location(calling_convention.GetRegisterAt(argument_index)));
+  } else {
+    locations->SetOut(Location(EAX));
+  }
+  instruction->SetLocations(locations);
+}
+
+void InstructionCodeGeneratorX86::VisitParameterValue(HParameterValue* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  InvokeDexCallingConvention calling_convention;
+  uint32_t argument_index = instruction->GetIndex();
+  if (argument_index >= calling_convention.GetNumberOfRegisters()) {
+    uint8_t offset = calling_convention.GetStackOffsetOf(argument_index);
+    __ movl(locations->Out().reg<Register>(), Address(ESP, offset + codegen_->GetFrameSize()));
+  }
+}
+
 }  // namespace x86
 }  // namespace art
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 830d0c7..d1f672f 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -42,6 +42,8 @@
         blocks_(arena, kDefaultNumberOfBlocks),
         dominator_order_(arena, kDefaultNumberOfBlocks),
         maximum_number_of_out_vregs_(0),
+        number_of_vregs_(0),
+        number_of_in_vregs_(0),
         current_instruction_id_(0) { }
 
   ArenaAllocator* GetArena() const { return arena_; }
@@ -68,6 +70,23 @@
     maximum_number_of_out_vregs_ = std::max(new_value, maximum_number_of_out_vregs_);
   }
 
+  void SetNumberOfVRegs(uint16_t number_of_vregs) {
+    number_of_vregs_ = number_of_vregs;
+  }
+
+  uint16_t GetNumberOfVRegs() const {
+    return number_of_vregs_;
+  }
+
+  void SetNumberOfInVRegs(uint16_t value) {
+    number_of_in_vregs_ = value;
+  }
+
+  uint16_t GetNumberOfInVRegs() const {
+    return number_of_in_vregs_;
+  }
+
+
  private:
   HBasicBlock* FindCommonDominator(HBasicBlock* first, HBasicBlock* second) const;
   void VisitBlockForDominatorTree(HBasicBlock* block,
@@ -90,9 +109,15 @@
   HBasicBlock* entry_block_;
   HBasicBlock* exit_block_;
 
-  // The maximum number of arguments passed to a HInvoke in this graph.
+  // The maximum number of virtual registers arguments passed to a HInvoke in this graph.
   uint16_t maximum_number_of_out_vregs_;
 
+  // The number of virtual registers in this method. Contains the parameters.
+  uint16_t number_of_vregs_;
+
+  // The number of virtual registers used by parameters of this method.
+  uint16_t number_of_in_vregs_;
+
   // The current id to assign to a newly added instruction. See HInstruction.id_.
   int current_instruction_id_;
 
@@ -202,10 +227,12 @@
   M(LoadLocal)                                             \
   M(Local)                                                 \
   M(NewInstance)                                           \
+  M(ParameterValue)                                        \
   M(PushArgument)                                          \
   M(Return)                                                \
   M(ReturnVoid)                                            \
   M(StoreLocal)                                            \
+  M(Sub)                                                   \
 
 #define FORWARD_DECLARATION(type) class H##type;
 FOR_EACH_INSTRUCTION(FORWARD_DECLARATION)
@@ -682,6 +709,37 @@
   DISALLOW_COPY_AND_ASSIGN(HAdd);
 };
 
+class HSub : public HBinaryOperation {
+ public:
+  HSub(Primitive::Type result_type, HInstruction* left, HInstruction* right)
+      : HBinaryOperation(result_type, left, right) {}
+
+  virtual bool IsCommutative() { return false; }
+
+  DECLARE_INSTRUCTION(Sub);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HSub);
+};
+
+// The value of a parameter in this method. Its location depends on
+// the calling convention.
+class HParameterValue : public HTemplateInstruction<0> {
+ public:
+  explicit HParameterValue(uint8_t index) : index_(index) {}
+
+  uint8_t GetIndex() const { return index_; }
+
+  DECLARE_INSTRUCTION(ParameterValue);
+
+ private:
+  // The index of this parameter in the parameters list. Must be less
+  // than HGraph::number_of_in_vregs_;
+  const uint8_t index_;
+
+  DISALLOW_COPY_AND_ASSIGN(HParameterValue);
+};
+
 class HGraphVisitor : public ValueObject {
  public:
   explicit HGraphVisitor(HGraph* graph) : graph_(graph) { }
diff --git a/test/401-optimizing-compiler/expected.txt b/test/401-optimizing-compiler/expected.txt
index a65e544..97492a4 100644
--- a/test/401-optimizing-compiler/expected.txt
+++ b/test/401-optimizing-compiler/expected.txt
@@ -7,3 +7,7 @@
 Forced GC
 In static method with object arg class java.lang.Object
 Forced GC
+Forced GC
+Forced GC
+Forced GC
+Forced GC
diff --git a/test/401-optimizing-compiler/src/Main.java b/test/401-optimizing-compiler/src/Main.java
index aa08137..e5706a5 100644
--- a/test/401-optimizing-compiler/src/Main.java
+++ b/test/401-optimizing-compiler/src/Main.java
@@ -28,6 +28,84 @@
     System.out.println(error);
 
     $opt$TestInvokeNew();
+
+    int result = $opt$TestInvokeIntParameter(42);
+    if (result != 42) {
+      throw new Error("Different value returned: " + result);
+    }
+
+
+    $opt$TestInvokeObjectParameter(new Object());
+
+    Object a = new Object();
+    Object b = $opt$TestInvokeObjectParameter(a);
+    if (a != b) {
+      throw new Error("Different object returned " + a + " " + b);
+    }
+
+    result = $opt$TestInvokeWith2Parameters(10, 9);
+    if (result != 1) {
+      throw new Error("Unexpected result: " + result);
+    }
+
+    result = $opt$TestInvokeWith3Parameters(10, 9, 1);
+    if (result != 0) {
+      throw new Error("Unexpected result: " + result);
+    }
+
+    result = $opt$TestInvokeWith5Parameters(10000, 1000, 100, 10, 1);
+    if (result != 8889) {
+      throw new Error("Unexpected result: " + result);
+    }
+
+    result = $opt$TestInvokeWith7Parameters(100, 6, 5, 4, 3, 2, 1);
+    if (result != 79) {
+      throw new Error("Unexpected result: " + result);
+    }
+
+    Main m = new Main();
+    if (m.$opt$TestThisParameter(m) != m) {
+      throw new Error("Unexpected value returned");
+    }
+
+    if (m.$opt$TestOtherParameter(new Main()) == m) {
+      throw new Error("Unexpected value returned");
+    }
+  }
+
+  static int $opt$TestInvokeIntParameter(int param) {
+    return param;
+  }
+
+  static Object $opt$TestInvokeObjectParameter(Object a) {
+    forceGCStaticMethod();
+    return a;
+  }
+
+  static int $opt$TestInvokeWith2Parameters(int a, int b) {
+    return a - b;
+  }
+
+  static int $opt$TestInvokeWith3Parameters(int a, int b, int c) {
+    return a - b - c;
+  }
+
+  static int $opt$TestInvokeWith5Parameters(int a, int b, int c, int d, int e) {
+    return a - b - c - d - e;
+  }
+
+  static int $opt$TestInvokeWith7Parameters(int a, int b, int c, int d, int e, int f, int g) {
+    return a - b - c - d - e - f - g;
+  }
+
+  Object $opt$TestThisParameter(Object other) {
+    forceGCStaticMethod();
+    return other;
+  }
+
+  Object $opt$TestOtherParameter(Object other) {
+    forceGCStaticMethod();
+    return other;
   }
 
   public static void $opt$TestInvokeStatic() {