Handle variable arguments for ARM/Mips/X86.

Expand va_arg LLVM instruction in a target-specific manner.

Change-Id: Iddf071b7f4026f003df2ad129fd940c506e9ec99
diff --git a/lib/AndroidBitcode/ABCCompiler.cpp b/lib/AndroidBitcode/ABCCompiler.cpp
index 1b7294d..8393d48 100644
--- a/lib/AndroidBitcode/ABCCompiler.cpp
+++ b/lib/AndroidBitcode/ABCCompiler.cpp
@@ -21,9 +21,12 @@
 #include <llvm/Target/TargetData.h>
 #include <llvm/Target/TargetMachine.h>
 
+#include "bcc/AndroidBitcode/ABCCompilerDriver.h"
+#include "bcc/AndroidBitcode/ABCExpandVAArgPass.h"
 #include "bcc/Script.h"
 #include "bcc/Source.h"
 
+
 namespace bcc {
 
 bool ABCCompiler::beforeAddCodeGenPasses(Script &pScript,
@@ -39,6 +42,7 @@
   }
 
   pm.add(target_data);
+  pm.add(mDriver.createExpandVAArgPass());
   pm.run(module);
 
   return true;
diff --git a/lib/AndroidBitcode/ABCCompilerDriver.cpp b/lib/AndroidBitcode/ABCCompilerDriver.cpp
index f8ddfc4..4eb33e6 100644
--- a/lib/AndroidBitcode/ABCCompilerDriver.cpp
+++ b/lib/AndroidBitcode/ABCCompilerDriver.cpp
@@ -17,6 +17,7 @@
 #include "bcc/AndroidBitcode/ABCCompilerDriver.h"
 
 #include <llvm/Module.h>
+#include <llvm/Pass.h>
 #include <llvm/Support/MemoryBuffer.h>
 #include <llvm/Support/raw_ostream.h>
 #include <mcld/Config/Config.h>
@@ -44,7 +45,7 @@
 namespace bcc {
 
 ABCCompilerDriver::ABCCompilerDriver(const std::string &pTriple)
-  : mContext(), mCompiler(), mLinker(),
+  : mContext(), mCompiler(*this), mLinker(),
     mCompilerConfig(NULL), mLinkerConfig(NULL),
     mTriple(pTriple), mAndroidSysroot("/") {
 }
diff --git a/lib/AndroidBitcode/ABCExpandVAArgPass.cpp b/lib/AndroidBitcode/ABCExpandVAArgPass.cpp
new file mode 100644
index 0000000..7b89ced
--- /dev/null
+++ b/lib/AndroidBitcode/ABCExpandVAArgPass.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BCC_ABC_EXPAND_VAARG_H
+#define BCC_ABC_EXPAND_VAARG_H
+
+#include "bcc/AndroidBitcode/ABCExpandVAArgPass.h"
+
+#include <llvm/Instructions.h>
+#include <llvm/Support/InstIterator.h>
+
+namespace bcc {
+
+char ABCExpandVAArgPass::ID = 0;
+
+bool ABCExpandVAArgPass::runOnFunction(llvm::Function &pFunc) {
+  bool changed = false;
+
+  mContext = &pFunc.getContext();
+
+  // process va_arg inst
+  for (llvm::inst_iterator inst = llvm::inst_begin(pFunc),
+          inst_end = llvm::inst_end(pFunc); inst != inst_end; inst++) {
+    if (inst->getOpcode() == llvm::Instruction::VAArg) {
+      llvm::Value *v = expandVAArg(&*inst);
+      inst->replaceAllUsesWith(v);
+      inst->eraseFromParent();
+      changed = true;
+      continue;
+    }
+  }
+  return changed;
+}
+
+} // end bcc namespace
+
+#endif // BCC_ABC_EXPAND_VAARG_H
diff --git a/lib/AndroidBitcode/ARM/ARMABCCompilerDriver.h b/lib/AndroidBitcode/ARM/ARMABCCompilerDriver.h
index ff1c91e..29f4f0a 100644
--- a/lib/AndroidBitcode/ARM/ARMABCCompilerDriver.h
+++ b/lib/AndroidBitcode/ARM/ARMABCCompilerDriver.h
@@ -27,6 +27,9 @@
       : ABCCompilerDriver(pTriple) { }
 
   virtual ~ARMABCCompilerDriver() { }
+
+private:
+  virtual ABCExpandVAArgPass *createExpandVAArgPass() const;
 };
 
 } // end namespace bcc
diff --git a/lib/AndroidBitcode/ARM/ARMABCExpandVAArg.cpp b/lib/AndroidBitcode/ARM/ARMABCExpandVAArg.cpp
new file mode 100644
index 0000000..7f154af
--- /dev/null
+++ b/lib/AndroidBitcode/ARM/ARMABCExpandVAArg.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <llvm/ADT/Triple.h>
+#include <llvm/DerivedTypes.h>
+#include <llvm/Function.h>
+#include <llvm/Instructions.h>
+#include <llvm/IRBuilder.h>
+#include <llvm/Module.h>
+#include <llvm/Pass.h>
+#include <llvm/Type.h>
+#include <llvm/Target/TargetData.h>
+
+#include "bcc/AndroidBitcode/ABCExpandVAArgPass.h"
+
+#include "ARM/ARMABCCompilerDriver.h"
+
+namespace {
+
+class ARMABCExpandVAArg : public bcc::ABCExpandVAArgPass {
+public:
+  virtual const char *getPassName() const {
+    return "ARM LLVM va_arg Instruction Expansion Pass";
+  }
+
+private:
+  // Derivative work from external/clang/lib/CodeGen/TargetInfo.cpp.
+  llvm::Value *expandVAArg(llvm::Instruction *pInst) {
+    llvm::Type *pty = pInst->getType();
+    llvm::Type *ty = pty->getContainedType(0);
+    llvm::Value *va_list_addr = pInst->getOperand(0);
+    llvm::IRBuilder<> builder(pInst);
+    const llvm::TargetData *td = getAnalysisIfAvailable<llvm::TargetData>();
+
+    llvm::Type *bp = llvm::Type::getInt8PtrTy(*mContext);
+    llvm::Type *bpp = bp->getPointerTo(0);
+
+    llvm::Value *va_list_addr_bpp =
+        builder.CreateBitCast(va_list_addr, bpp, "ap");
+    llvm::Value *addr = builder.CreateLoad(va_list_addr_bpp, "ap.cur");
+    // Handle address alignment for type alignment > 32 bits.
+    uint64_t ty_align = td->getABITypeAlignment(ty);
+
+    if (ty_align > 4) {
+      assert((ty_align & (ty_align - 1)) == 0 &&
+        "Alignment is not power of 2!");
+      llvm::Value *addr_as_int =
+        builder.CreatePtrToInt(addr, llvm::Type::getInt32Ty(*mContext));
+      addr_as_int = builder.CreateAdd(addr_as_int,
+        builder.getInt32(ty_align-1));
+      addr_as_int = builder.CreateAnd(addr_as_int,
+        builder.getInt32(~(ty_align-1)));
+      addr = builder.CreateIntToPtr(addr_as_int, bp);
+    }
+    llvm::Value *addr_typed = builder.CreateBitCast(addr, pty);
+
+    uint64_t offset = llvm::RoundUpToAlignment(td->getTypeSizeInBits(ty)/8, 4);
+    llvm::Value *next_addr = builder.CreateGEP(addr,
+      llvm::ConstantInt::get(llvm::Type::getInt32Ty(*mContext), offset),
+      "ap.next");
+    builder.CreateStore(next_addr, va_list_addr_bpp);
+    return addr_typed;
+  }
+
+};
+
+} // end anonymous namespace
+
+namespace bcc {
+
+ABCExpandVAArgPass *ARMABCCompilerDriver::createExpandVAArgPass() const {
+  return new ARMABCExpandVAArg();
+}
+
+} // end namespace bcc
diff --git a/lib/AndroidBitcode/Android.mk b/lib/AndroidBitcode/Android.mk
index f736f58..806202c 100644
--- a/lib/AndroidBitcode/Android.mk
+++ b/lib/AndroidBitcode/Android.mk
@@ -23,15 +23,19 @@
 
 libbcc_androidbitcode_SRC_FILES := \
   ABCCompiler.cpp \
+  ABCExpandVAArgPass.cpp \
   ABCCompilerDriver.cpp
 
-libbcc_arm_androidbitcode_SRC_FILES :=
+libbcc_arm_androidbitcode_SRC_FILES := \
+  ARM/ARMABCExpandVAArg.cpp
 
 libbcc_mips_androidbitcode_SRC_FILES := \
-  Mips/MipsABCCompilerDriver.cpp
+  Mips/MipsABCCompilerDriver.cpp \
+  Mips/MipsABCExpandVAArg.cpp
 
 libbcc_x86_androidbitcode_SRC_FILES := \
-  X86/X86ABCCompilerDriver.cpp
+  X86/X86ABCCompilerDriver.cpp \
+  X86/X86ABCExpandVAArg.cpp
 
 
 #=====================================================================
diff --git a/lib/AndroidBitcode/Mips/MipsABCCompilerDriver.h b/lib/AndroidBitcode/Mips/MipsABCCompilerDriver.h
index 531fe83..7cfc34f 100644
--- a/lib/AndroidBitcode/Mips/MipsABCCompilerDriver.h
+++ b/lib/AndroidBitcode/Mips/MipsABCCompilerDriver.h
@@ -30,6 +30,8 @@
 
 private:
   virtual const char **getNonPortableList() const;
+
+  virtual ABCExpandVAArgPass *createExpandVAArgPass() const;
 };
 
 } // end namespace bcc
diff --git a/lib/AndroidBitcode/Mips/MipsABCExpandVAArg.cpp b/lib/AndroidBitcode/Mips/MipsABCExpandVAArg.cpp
new file mode 100644
index 0000000..495cef4
--- /dev/null
+++ b/lib/AndroidBitcode/Mips/MipsABCExpandVAArg.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <llvm/ADT/Triple.h>
+#include <llvm/DerivedTypes.h>
+#include <llvm/Function.h>
+#include <llvm/Instructions.h>
+#include <llvm/IRBuilder.h>
+#include <llvm/Module.h>
+#include <llvm/Pass.h>
+#include <llvm/Type.h>
+#include <llvm/Target/TargetData.h>
+
+#include "bcc/AndroidBitcode/ABCExpandVAArgPass.h"
+
+#include "Mips/MipsABCCompilerDriver.h"
+
+namespace {
+
+class MipsABCExpandVAArg : public bcc::ABCExpandVAArgPass {
+public:
+  virtual const char *getPassName() const {
+    return "Mips LLVM va_arg Instruction Expansion Pass";
+  }
+
+private:
+  // Derivative work from external/clang/lib/CodeGen/TargetInfo.cpp.
+  virtual llvm::Value *expandVAArg(llvm::Instruction *pInst) {
+    llvm::Type *pty = pInst->getType();
+    llvm::Type *ty = pty->getContainedType(0);
+    llvm::Value *va_list_addr = pInst->getOperand(0);
+    llvm::IRBuilder<> builder(pInst);
+    const llvm::TargetData *td = getAnalysisIfAvailable<llvm::TargetData>();
+
+    llvm::Type *bp = llvm::Type::getInt8PtrTy(*mContext);
+    llvm::Type *bpp = bp->getPointerTo(0);
+    llvm::Value *va_list_addr_bpp = builder.CreateBitCast(va_list_addr,
+                                                          bpp, "ap");
+    llvm::Value *addr = builder.CreateLoad(va_list_addr_bpp, "ap.cur");
+    int64_t type_align = td->getABITypeAlignment(ty);
+    llvm::Value *addr_typed;
+    llvm::IntegerType *int_ty = llvm::Type::getInt32Ty(*mContext);
+
+    if (type_align > 4) {
+      llvm::Value *addr_as_int = builder.CreatePtrToInt(addr, int_ty);
+      llvm::Value *inc = llvm::ConstantInt::get(int_ty, type_align - 1);
+      llvm::Value *mask = llvm::ConstantInt::get(int_ty, -type_align);
+      llvm::Value *add_v = builder.CreateAdd(addr_as_int, inc);
+      llvm::Value *and_v = builder.CreateAnd(add_v, mask);
+      addr_typed = builder.CreateIntToPtr(and_v, pty);
+    }
+    else {
+      addr_typed = builder.CreateBitCast(addr, pty);
+    }
+
+    llvm::Value *aligned_addr = builder.CreateBitCast(addr_typed, bp);
+    type_align = std::max((unsigned)type_align, (unsigned) 4);
+    uint64_t offset =
+      llvm::RoundUpToAlignment(td->getTypeSizeInBits(ty) / 8, type_align);
+    llvm::Value *next_addr =
+      builder.CreateGEP(aligned_addr, llvm::ConstantInt::get(int_ty, offset),
+                        "ap.next");
+    builder.CreateStore(next_addr, va_list_addr_bpp);
+
+    return addr_typed;
+  }
+
+};
+
+} // end anonymous namespace
+
+namespace bcc {
+
+ABCExpandVAArgPass *MipsABCCompilerDriver::createExpandVAArgPass() const {
+  return new MipsABCExpandVAArg();
+}
+
+} // end namespace bcc
diff --git a/lib/AndroidBitcode/X86/X86ABCCompilerDriver.h b/lib/AndroidBitcode/X86/X86ABCCompilerDriver.h
index 34e9603..af52866 100644
--- a/lib/AndroidBitcode/X86/X86ABCCompilerDriver.h
+++ b/lib/AndroidBitcode/X86/X86ABCCompilerDriver.h
@@ -30,6 +30,8 @@
 
 private:
   virtual const char **getNonPortableList() const;
+
+  virtual ABCExpandVAArgPass *createExpandVAArgPass() const;
 };
 
 } // end namespace bcc
diff --git a/lib/AndroidBitcode/X86/X86ABCExpandVAArg.cpp b/lib/AndroidBitcode/X86/X86ABCExpandVAArg.cpp
new file mode 100644
index 0000000..6d9acbc
--- /dev/null
+++ b/lib/AndroidBitcode/X86/X86ABCExpandVAArg.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <llvm/ADT/Triple.h>
+#include <llvm/DerivedTypes.h>
+#include <llvm/Function.h>
+#include <llvm/Instructions.h>
+#include <llvm/IRBuilder.h>
+#include <llvm/Module.h>
+#include <llvm/Pass.h>
+#include <llvm/Type.h>
+#include <llvm/Target/TargetData.h>
+
+#include "bcc/AndroidBitcode/ABCExpandVAArgPass.h"
+
+#include "X86/X86ABCCompilerDriver.h"
+
+namespace {
+
+class X86ABCExpandVAArg : public bcc::ABCExpandVAArgPass {
+public:
+  virtual const char *getPassName() const {
+    return "X86 LLVM va_arg Instruction Expansion Pass";
+  }
+
+private:
+  // Derivative work from external/clang/lib/CodeGen/TargetInfo.cpp.
+  virtual llvm::Value *expandVAArg(llvm::Instruction *pInst) {
+    llvm::Type *pty = pInst->getType();
+    llvm::Type *ty = pty->getContainedType(0);
+    llvm::Value *va_list_addr = pInst->getOperand(0);
+    llvm::IRBuilder<> builder(pInst);
+    const llvm::TargetData *td = getAnalysisIfAvailable<llvm::TargetData>();
+
+    llvm::Type *bp = llvm::Type::getInt8PtrTy(*mContext);
+    llvm::Type *bpp = bp->getPointerTo(0);
+    llvm::Value *va_list_addr_bpp = builder.CreateBitCast(va_list_addr,
+                                                          bpp, "ap");
+    llvm::Value *addr = builder.CreateLoad(va_list_addr_bpp, "ap.cur");
+
+    llvm::Value *addr_typed = builder.CreateBitCast(addr, pty);
+
+    // X86-32 stack type alignment is always 4.
+    uint64_t offset = llvm::RoundUpToAlignment(td->getTypeSizeInBits(ty)/8, 4);
+    llvm::Value *next_addr = builder.CreateGEP(addr,
+      llvm::ConstantInt::get(llvm::Type::getInt32Ty(*mContext), offset),
+      "ap.next");
+    builder.CreateStore(next_addr, va_list_addr_bpp);
+
+    return addr_typed;
+  }
+
+}; // end X86ABCExpandVAArg
+
+} // end anonymous namespace
+
+namespace bcc {
+
+ABCExpandVAArgPass *X86ABCCompilerDriver::createExpandVAArgPass() const {
+  return new X86ABCExpandVAArg();
+}
+
+} // end namespace bcc