Implement fill-array-data instruction.

Change-Id: I6182dc69af676986591e345078276b20cd22b9fc
diff --git a/src/compiler_llvm/method_compiler.cc b/src/compiler_llvm/method_compiler.cc
index eaaac72..88827e4 100644
--- a/src/compiler_llvm/method_compiler.cc
+++ b/src/compiler_llvm/method_compiler.cc
@@ -1661,7 +1661,84 @@
 
 void MethodCompiler::EmitInsn_FillArrayData(uint32_t dex_pc,
                                             Instruction const* insn) {
-  // UNIMPLEMENTED(WARNING);
+
+  Instruction::DecodedInstruction dec_insn(insn);
+
+  // Read the payload
+  struct PACKED Payload {
+    uint16_t ident_;
+    uint16_t elem_width_;
+    uint32_t num_elems_;
+    uint8_t data_[];
+  };
+
+  int32_t payload_offset = static_cast<int32_t>(dex_pc) +
+                           static_cast<int32_t>(dec_insn.vB_);
+
+  Payload const* payload =
+    reinterpret_cast<Payload const*>(code_item_->insns_ + payload_offset);
+
+  uint32_t size_in_bytes = payload->elem_width_ * payload->num_elems_;
+
+  // Load and check the array
+  llvm::Value* array_addr = EmitLoadDalvikReg(dec_insn.vA_, kObject, kAccurate);
+
+  EmitGuard_NullPointerException(dex_pc, array_addr);
+
+  if (payload->num_elems_ > 0) {
+    // Test: Is array length big enough?
+    llvm::Constant* last_index = irb_.getJInt(payload->num_elems_ - 1);
+
+    EmitGuard_ArrayIndexOutOfBoundsException(dex_pc, array_addr, last_index);
+
+    // Get array data field
+    llvm::Value* data_field_offset_value =
+      irb_.getPtrEquivInt(Array::DataOffset().Int32Value());
+
+    llvm::Value* data_field_addr =
+      irb_.CreatePtrDisp(array_addr, data_field_offset_value,
+                         irb_.getInt8Ty()->getPointerTo());
+
+    // Emit payload to bitcode constant pool
+    std::vector<llvm::Constant*> const_pool_data;
+    for (uint32_t i = 0; i < size_in_bytes; ++i) {
+      const_pool_data.push_back(irb_.getInt8(payload->data_[i]));
+    }
+
+    llvm::Constant* const_pool_data_array_value = llvm::ConstantArray::get(
+      llvm::ArrayType::get(irb_.getInt8Ty(), size_in_bytes), const_pool_data);
+
+    llvm::Value* const_pool_data_array_addr =
+      new llvm::GlobalVariable(*module_,
+                               const_pool_data_array_value->getType(),
+                               false, llvm::GlobalVariable::InternalLinkage,
+                               const_pool_data_array_value,
+                               "array_data_payload");
+
+    // Find the memcpy intrinsic
+    llvm::Type* memcpy_arg_types[] = {
+      llvm::Type::getInt8Ty(*context_)->getPointerTo(),
+      llvm::Type::getInt8Ty(*context_)->getPointerTo(),
+      llvm::Type::getInt32Ty(*context_)
+    };
+
+    llvm::Function* memcpy_intrinsic =
+      llvm::Intrinsic::getDeclaration(module_,
+                                      llvm::Intrinsic::memcpy,
+                                      memcpy_arg_types);
+
+    // Copy now!
+    llvm::Value *args[] = {
+      data_field_addr,
+      irb_.CreateConstGEP2_32(const_pool_data_array_addr, 0, 0),
+      irb_.getInt32(size_in_bytes),
+      irb_.getInt32(0), // alignment: no guarantee
+      irb_.getFalse() // is_volatile: false
+    };
+
+    irb_.CreateCall(memcpy_intrinsic, args);
+  }
+
   irb_.CreateBr(GetNextBasicBlock(dex_pc));
 }