Add POP instruction to ARM integrated assembler.

BUG= https://bugs.chromium.org/p/nativeclient/issues/detail?id=4334
R=jpp@chromium.org, stichnot@chromium.org

Review URL: https://codereview.chromium.org/1433743002 .
diff --git a/src/DartARM32/assembler_arm.cc b/src/DartARM32/assembler_arm.cc
index 576ca76..44ce658 100644
--- a/src/DartARM32/assembler_arm.cc
+++ b/src/DartARM32/assembler_arm.cc
@@ -549,15 +549,15 @@
   }
 }
 
-
+#if 0
+// Folded into ARM32::AssemblerARM32::popList(), since it is its only
+// use (and doesn't implement ARM STM instruction).
 void Assembler::ldm(BlockAddressMode am, Register base, RegList regs,
                     Condition cond) {
   ASSERT(regs != 0);
   EmitMultiMemOp(cond, am, true, base, regs);
 }
 
-
-#if 0
 // Folded into ARM32::AssemblerARM32::pushList(), since it is its only
 // use (and doesn't implement ARM STM instruction).
 void Assembler::stm(BlockAddressMode am, Register base, RegList regs,
@@ -2568,27 +2568,26 @@
 }
 
 #if 0
-// Moved to ARM32::AssemblerARM32::push();
+// Moved to ARM32::AssemblerARM32::push().
 void Assembler::Push(Register rd, Condition cond) {
   str(rd, Address(SP, -kWordSize, Address::PreIndex), cond);
 }
-#endif
 
+// Moved to ARM32::AssemblerARM32::pop().
 void Assembler::Pop(Register rd, Condition cond) {
   ldr(rd, Address(SP, kWordSize, Address::PostIndex), cond);
 }
 
-#if 0
-// Moved to ARM32::AssemblerARM32::pushList();
+// Moved to ARM32::AssemblerARM32::pushList().
 void Assembler::PushList(RegList regs, Condition cond) {
   stm(DB_W, SP, regs, cond);
 }
-#endif
 
+// Moved to ARM32::AssemblerARM32::popList().
 void Assembler::PopList(RegList regs, Condition cond) {
   ldm(IA_W, SP, regs, cond);
 }
-
+#endif
 
 void Assembler::MoveRegister(Register rd, Register rm, Condition cond) {
   if (rd != rm) {
diff --git a/src/DartARM32/assembler_arm.h b/src/DartARM32/assembler_arm.h
index 63a1ef8..dbc1873 100644
--- a/src/DartARM32/assembler_arm.h
+++ b/src/DartARM32/assembler_arm.h
@@ -576,11 +576,14 @@
   void ldrd(Register rd, Register rn, int32_t offset, Condition cond = AL);
   void strd(Register rd, Register rn, int32_t offset, Condition cond = AL);
 
+#if 0
+  // Folded into ARM32::AssemblerARM32::popList(), since it is its only use (and
+  // doesn't implement ARM LDM instructions).
   void ldm(BlockAddressMode am, Register base,
            RegList regs, Condition cond = AL);
-#if 0
-  // Folded into ARM32::AssemblerARM32::pushList(), since it is its only
-  // use (and doesn't implement ARM STM instruction).
+
+  // Folded into ARM32::AssemblerARM32::pushList(), since it is its only use
+  // (and doesn't implement ARM STM instruction).
   void stm(BlockAddressMode am, Register base,
            RegList regs, Condition cond = AL);
 #endif
@@ -936,15 +939,16 @@
 #if 0
   // Moved to ARM32::AssemblerARM32::push().
   void Push(Register rd, Condition cond = AL);
-#endif
+
+  // Moved to ARM32::AssemblerARM32::pop().
   void Pop(Register rd, Condition cond = AL);
 
-#if 0
-  // Moved to ARM32::AssemblerARM32::pushList();
+  // Moved to ARM32::AssemblerARM32::pushList().
   void PushList(RegList regs, Condition cond = AL);
-#endif
-  void PopList(RegList regs, Condition cond = AL);
 
+  // Moved to ARM32::AssemblerARM32::popList().
+  void PopList(RegList regs, Condition cond = AL);
+#endif
   void MoveRegister(Register rd, Register rm, Condition cond = AL);
 
   // Convenience shift instructions. Use mov instruction with shifter operand
diff --git a/src/IceAssemblerARM32.cpp b/src/IceAssemblerARM32.cpp
index d849c54..0fdddfb 100644
--- a/src/IceAssemblerARM32.cpp
+++ b/src/IceAssemblerARM32.cpp
@@ -906,11 +906,38 @@
   emitType01(Orr, OpRd, OpRn, OpSrc1, SetFlags, Cond);
 }
 
+void AssemblerARM32::pop(const Operand *OpRt, CondARM32::Cond Cond) {
+  // POP - ARM section A8.8.132, encoding A2:
+  //   pop<c> {Rt}
+  //
+  // cccc010010011101dddd000000000100 where dddd=Rt and cccc=Cond.
+  IValueT Rt;
+  if (decodeOperand(OpRt, Rt) != DecodedAsRegister)
+    return setNeedsTextFixup();
+  assert(Rt != RegARM32::Encoded_Reg_sp);
+  // Same as load instruction.
+  constexpr bool IsLoad = true;
+  constexpr bool IsByte = false;
+  IValueT Address = decodeImmRegOffset(RegARM32::Encoded_Reg_sp, kWordSize,
+                                       OperandARM32Mem::PostIndex);
+  emitMemOp(Cond, kInstTypeMemImmediate, IsLoad, IsByte, Rt, Address);
+}
+
+void AssemblerARM32::popList(const IValueT Registers, CondARM32::Cond Cond) {
+  // POP - ARM section A8.*.131, encoding A1:
+  //   pop<c> <registers>
+  //
+  // cccc100010111101rrrrrrrrrrrrrrrr where cccc=Cond and
+  // rrrrrrrrrrrrrrrr=Registers (one bit for each GP register).
+  constexpr bool IsLoad = true;
+  emitMultiMemOp(Cond, IA_W, IsLoad, RegARM32::Encoded_Reg_sp, Registers);
+}
+
 void AssemblerARM32::push(const Operand *OpRt, CondARM32::Cond Cond) {
   // PUSH - ARM section A8.8.133, encoding A2:
   //   push<c> {Rt}
   //
-  // cccc010100101101dddd000000000100 where dddd=Rd and cccc=Cond.
+  // cccc010100101101dddd000000000100 where dddd=Rt and cccc=Cond.
   IValueT Rt;
   if (decodeOperand(OpRt, Rt) != DecodedAsRegister)
     return setNeedsTextFixup();
diff --git a/src/IceAssemblerARM32.h b/src/IceAssemblerARM32.h
index a7f8c20..96e800b 100644
--- a/src/IceAssemblerARM32.h
+++ b/src/IceAssemblerARM32.h
@@ -189,6 +189,11 @@
   void orr(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1,
            bool SetFlags, CondARM32::Cond Cond);
 
+  void pop(const Operand *OpRt, CondARM32::Cond Cond);
+
+  // Note: Registers is a bitset, where bit n corresponds to register Rn.
+  void popList(const IValueT Registers, CondARM32::Cond Cond);
+
   void push(const Operand *OpRt, CondARM32::Cond Cond);
 
   // Note: Registers is a bitset, where bit n corresponds to register Rn.
diff --git a/src/IceInstARM32.cpp b/src/IceInstARM32.cpp
index 7312d84..1edaa17 100644
--- a/src/IceInstARM32.cpp
+++ b/src/IceInstARM32.cpp
@@ -1116,6 +1116,45 @@
   }
 }
 
+void InstARM32Pop::emitIAS(const Cfg *Func) const {
+  SizeT IntegerCount = 0;
+  ARM32::IValueT GPRegisters = 0;
+  const Variable *LastDest = nullptr;
+  for (const Variable *Var : Dests) {
+    if (!isScalarIntegerType(Var->getType()))
+      // TODO(kschimpf) Implement vpush.
+      return emitUsingTextFixup(Func);
+    assert((Var && Var->hasReg()) && "pop only applies to registers");
+    int32_t Reg = Var->getRegNum();
+    assert(Reg != RegARM32::Encoded_Not_GPR);
+    LastDest = Var;
+    GPRegisters |= (1 << Reg);
+    ++IntegerCount;
+  }
+  auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>();
+  switch (IntegerCount) {
+  case 0:
+    return;
+  case 1:
+    // Note: Can only apply pop register if single register is not sp.
+    assert((RegARM32::Encoded_Reg_sp != LastDest->getRegNum()) &&
+           "Effects of pop register SP is undefined!");
+    // TODO(kschimpf) ARM sandbox does not allow the single register form of
+    // pop, and the popList form expects multiple registers. Convert this
+    // assert to a conditional check once it has been shown that popList
+    // works.
+    assert(!Func->getContext()->getFlags().getUseSandboxing() &&
+           "pop register not in ARM sandbox!");
+    Asm->pop(LastDest, CondARM32::AL);
+    break;
+  default:
+    Asm->popList(GPRegisters, CondARM32::AL);
+    break;
+  }
+  if (Asm->needsTextFixup())
+    emitUsingTextFixup(Func);
+}
+
 void InstARM32Pop::dump(const Cfg *Func) const {
   if (!BuildDefs::dump())
     return;
@@ -1197,19 +1236,19 @@
 }
 
 void InstARM32Push::emitIAS(const Cfg *Func) const {
-  SizeT SrcReg = 0;
   SizeT IntegerCount = 0;
-  ARM32::IValueT GPURegisters = 0;
-  for (SizeT i = 0; i < getSrcSize(); ++i) {
-    if (!isScalarIntegerType(getSrc(i)->getType()))
+  ARM32::IValueT GPRegisters = 0;
+  const Variable *LastSrc = nullptr;
+  for (SizeT Index = 0; Index < getSrcSize(); ++Index) {
+    if (!isScalarIntegerType(getSrc(Index)->getType()))
       // TODO(kschimpf) Implement vpush.
       return emitUsingTextFixup(Func);
-    auto *Var = llvm::dyn_cast<Variable>(getSrc(i));
+    const auto *Var = llvm::dyn_cast<Variable>(getSrc(Index));
     assert((Var && Var->hasReg()) && "push only applies to registers");
-    ARM32::IValueT Reg = Var->getRegNum();
-    assert(Reg != static_cast<ARM32::IValueT>(RegARM32::Encoded_Not_GPR));
-    SrcReg = i;
-    GPURegisters |= (1 << Reg);
+    int32_t Reg = Var->getRegNum();
+    assert(Reg != RegARM32::Encoded_Not_GPR);
+    LastSrc = Var;
+    GPRegisters |= (1 << Reg);
     ++IntegerCount;
   }
   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>();
@@ -1217,24 +1256,21 @@
   case 0:
     return;
   case 1: {
-    if (auto *Var = llvm::dyn_cast<Variable>(getSrc(SrcReg))) {
-      // Note: Can only apply push register if single register is not sp.
-      assert((RegARM32::Encoded_Reg_sp != Var->getRegNum()) &&
-             "Effects of push register SP is undefined!");
-      // TODO(kschimpf) ARM sandbox does not allow the single register form of
-      // push, and the pushList form expects multiple registers. Convert this
-      // assert to a conditional check once it has been shown that pushList
-      // works.
-      assert(!Func->getContext()->getFlags().getUseSandboxing() &&
-             "push register not in ARM sandbox!");
-      Asm->push(Var, CondARM32::AL);
-      break;
-    }
-    // Intentionally fall to next case.
+    // Note: Can only apply push register if single register is not sp.
+    assert((RegARM32::Encoded_Reg_sp != LastSrc->getRegNum()) &&
+           "Effects of push register SP is undefined!");
+    // TODO(kschimpf) ARM sandbox does not allow the single register form of
+    // push, and the pushList form expects multiple registers. Convert this
+    // assert to a conditional check once it has been shown that pushList
+    // works.
+    assert(!Func->getContext()->getFlags().getUseSandboxing() &&
+           "push register not in ARM sandbox!");
+    Asm->push(LastSrc, CondARM32::AL);
+    break;
   }
   default:
     // TODO(kschimpf) Implement pushList in assembler.
-    Asm->pushList(GPURegisters, CondARM32::AL);
+    Asm->pushList(GPRegisters, CondARM32::AL);
     break;
   }
   if (Asm->needsTextFixup())
diff --git a/src/IceInstARM32.h b/src/IceInstARM32.h
index 6d28208..e1199ee 100644
--- a/src/IceInstARM32.h
+++ b/src/IceInstARM32.h
@@ -948,6 +948,7 @@
     return new (Func->allocate<InstARM32Pop>()) InstARM32Pop(Func, Dests);
   }
   void emit(const Cfg *Func) const override;
+  void emitIAS(const Cfg *Func) const override;
   void dump(const Cfg *Func) const override;
   static bool classof(const Inst *Inst) { return isClassof(Inst, Pop); }
 
diff --git a/tests_lit/assembler/arm32/bic.ll b/tests_lit/assembler/arm32/bic.ll
index 3115d06..7943a66 100644
--- a/tests_lit/assembler/arm32/bic.ll
+++ b/tests_lit/assembler/arm32/bic.ll
@@ -86,7 +86,12 @@
 ; IASM-NEXT:    .byte 0xd0
 ; IASM-NEXT:    .byte 0xa0
 ; IASM-NEXT:    .byte 0xe1
-; IASM:         pop     {fp}
+
+; IASM-NEXT:    .byte 0x4
+; IASM-NEXT:    .byte 0xb0
+; IASM-NEXT:    .byte 0x9d
+; IASM-NEXT:    .byte 0xe4
+
 ; IASM:         .byte 0x1e
 ; IASM-NEXT:    .byte 0xff
 ; IASM-NEXT:    .byte 0x2f
diff --git a/tests_lit/assembler/arm32/push-pop.ll b/tests_lit/assembler/arm32/push-pop.ll
index 5bda8c7..c0238f9 100644
--- a/tests_lit/assembler/arm32/push-pop.ll
+++ b/tests_lit/assembler/arm32/push-pop.ll
@@ -61,7 +61,11 @@
 ; IASM-NEXT:    .byte 0xd0
 ; IASM-NEXT:    .byte 0x8d
 ; IASM-NEXT:    .byte 0xe2
-; IASM-NEXT:    pop     {lr}
+
+; IASM-NEXT:    .byte 0x4
+; IASM-NEXT:    .byte 0xe0
+; IASM-NEXT:    .byte 0x9d
+; IASM-NEXT:    .byte 0xe4
 
 ; IASM:         .byte 0x1e
 ; IASM-NEXT:    .byte 0xff
@@ -141,7 +145,11 @@
 ; IASM-NEXT:    .byte 0xd0
 ; IASM-NEXT:    .byte 0x8d
 ; IASM-NEXT:    .byte 0xe2
-; IASM-NEXT:    pop     {r4, r5, lr}
+
+; IASM-NEXT:    .byte 0x30
+; IASM-NEXT:    .byte 0x40
+; IASM-NEXT:    .byte 0xbd
+; IASM-NEXT:    .byte 0xe8
 
 ; IASM:         .byte 0x1e
 ; IASM-NEXT:    .byte 0xff