Merge "Structure layout logic cleanup." am: a71938846c
am: 21d87190ce

Change-Id: I5f63f1fe58d1d5ea90517cfc10c396844bc5a7e9
diff --git a/include/bcc/Compiler.h b/include/bcc/Compiler.h
index 73f8274..708e1b0 100644
--- a/include/bcc/Compiler.h
+++ b/include/bcc/Compiler.h
@@ -67,7 +67,9 @@
 
     kIllegalGlobalFunction,
 
-    kErrInvalidTargetMachine
+    kErrInvalidTargetMachine,
+
+    kErrInvalidLayout
   };
 
   static const char *GetErrorString(enum ErrorCode pErrCode);
diff --git a/include/bcc/Script.h b/include/bcc/Script.h
index b4135c5..3b10d82 100644
--- a/include/bcc/Script.h
+++ b/include/bcc/Script.h
@@ -17,6 +17,8 @@
 #ifndef BCC_SCRIPT_H
 #define BCC_SCRIPT_H
 
+#include "slang_version.h"
+
 #include <llvm/Support/CodeGen.h>
 #include "bcc/Source.h"
 
@@ -65,6 +67,10 @@
     return getSource().getCompilerVersion();
   }
 
+  bool isStructExplicitlyPaddedBySlang() const {
+    return getCompilerVersion() >= SlangVersion::N_STRUCT_EXPLICIT_PADDING;
+  }
+
   void setOptimizationLevel(llvm::CodeGenOpt::Level pOptimizationLevel) {
     mOptimizationLevel = pOptimizationLevel;
   }
diff --git a/lib/Android.bp b/lib/Android.bp
index e16b034..6ac3652 100644
--- a/lib/Android.bp
+++ b/lib/Android.bp
@@ -43,6 +43,8 @@
 
     shared_libs: ["libbcinfo"],
 
+    header_libs: ["slang_headers"],
+
     target: {
         windows: {
             enabled: true,
diff --git a/lib/Compiler.cpp b/lib/Compiler.cpp
index ca14d1f..5000fa1 100644
--- a/lib/Compiler.cpp
+++ b/lib/Compiler.cpp
@@ -45,6 +45,74 @@
 #include <string>
 #include <set>
 
+namespace {
+
+// Name of metadata node where list of exported types resides
+// (should be synced with slang_rs_metadata.h)
+static const llvm::StringRef ExportedTypeMetadataName = "#rs_export_type";
+
+// Every exported struct type must have the same layout according to
+// the Module's DataLayout that it does according to the
+// TargetMachine's DataLayout -- that is, the front end (represented
+// by Module) and back end (represented by TargetMachine) must agree.
+bool validateLayoutOfExportedTypes(const llvm::Module &module,
+                                   const llvm::DataLayout &moduleDataLayout,
+                                   const llvm::DataLayout &targetDataLayout) {
+  if (moduleDataLayout == targetDataLayout)
+    return true;
+
+  const llvm::NamedMDNode *const exportedTypesMD =
+      module.getNamedMetadata(ExportedTypeMetadataName);
+  if (!exportedTypesMD)
+    return true;
+
+  bool allOk = true;
+  for (const llvm::MDNode *const exportedTypeMD : exportedTypesMD->operands()) {
+    bccAssert(exportedTypeMD->getNumOperands() == 1);
+
+    // The name of the type in LLVM is the name of the type in the
+    // metadata with "struct." prepended.
+    std::string exportedTypeName =
+        "struct." +
+        llvm::cast<llvm::MDString>(exportedTypeMD->getOperand(0))->getString().str();
+
+    llvm::StructType *const exportedType = module.getTypeByName(exportedTypeName);
+
+    if (!exportedType) {
+      // presumably this means the type got optimized away
+      continue;
+    }
+
+    const llvm::StructLayout *const moduleStructLayout = moduleDataLayout.getStructLayout(exportedType);
+    const llvm::StructLayout *const targetStructLayout = targetDataLayout.getStructLayout(exportedType);
+
+    if (moduleStructLayout->getSizeInBits() != targetStructLayout->getSizeInBits()) {
+      ALOGE("%s: getSizeInBits() does not match (%u, %u)", exportedTypeName.c_str(),
+            unsigned(moduleStructLayout->getSizeInBits()), unsigned(targetStructLayout->getSizeInBits()));
+      allOk = false;
+    }
+
+    // We deliberately do not check alignment of the struct as a whole -- the explicit padding
+    // from slang doesn't force the alignment.
+
+    for (unsigned elementCount = exportedType->getNumElements(), elementIdx = 0;
+         elementIdx < elementCount; ++elementIdx) {
+      if (moduleStructLayout->getElementOffsetInBits(elementIdx) !=
+          targetStructLayout->getElementOffsetInBits(elementIdx)) {
+        ALOGE("%s: getElementOffsetInBits(%u) does not match (%u, %u)",
+              exportedTypeName.c_str(), elementIdx,
+              unsigned(moduleStructLayout->getElementOffsetInBits(elementIdx)),
+              unsigned(targetStructLayout->getElementOffsetInBits(elementIdx)));
+        allOk = false;
+      }
+    }
+  }
+
+  return allOk;
+}
+
+}  // end unnamed namespace
+
 using namespace bcc;
 
 const char *Compiler::GetErrorString(enum ErrorCode pErrCode) {
@@ -77,6 +145,8 @@
     return "Use of undefined external function";
   case kErrInvalidTargetMachine:
     return "Invalid/unexpected llvm::TargetMachine.";
+  case kErrInvalidLayout:
+    return "Invalid layout (RenderScript ABI and native ABI are incompatible)";
   }
 
   // This assert should never be reached as the compiler verifies that the
@@ -252,12 +322,17 @@
     return kErrInvalidSource;
   }
 
-  if (getTargetMachine().getTargetTriple().getArch() == llvm::Triple::x86) {
-    // Detect and fail if TargetMachine datalayout is different than what we
-    // expect.  This is to detect changes in default target layout for x86 and
-    // update X86_CUSTOM_DL_STRING in include/bcc/Config/Config.h appropriately.
-    if (dl.getStringRepresentation().compare(X86_DEFAULT_DL_STRING) != 0) {
-      return kErrInvalidTargetMachine;
+  if (script.isStructExplicitlyPaddedBySlang()) {
+    if (!validateLayoutOfExportedTypes(module, module.getDataLayout(), dl))
+      return kErrInvalidLayout;
+  } else {
+    if (getTargetMachine().getTargetTriple().getArch() == llvm::Triple::x86) {
+      // Detect and fail if TargetMachine datalayout is different than what we
+      // expect.  This is to detect changes in default target layout for x86 and
+      // update X86_CUSTOM_DL_STRING in include/bcc/Config/Config.h appropriately.
+      if (dl.getStringRepresentation().compare(X86_DEFAULT_DL_STRING) != 0) {
+        return kErrInvalidTargetMachine;
+      }
     }
   }
 
diff --git a/lib/RSCompilerDriver.cpp b/lib/RSCompilerDriver.cpp
index 6ed7400..be91343 100644
--- a/lib/RSCompilerDriver.cpp
+++ b/lib/RSCompilerDriver.cpp
@@ -20,6 +20,7 @@
 #include "FileMutex.h"
 #include "Log.h"
 #include "RSScriptGroupFusion.h"
+#include "slang_version.h"
 
 #include "bcc/BCCContext.h"
 #include "bcc/Compiler.h"
@@ -130,7 +131,8 @@
   // (during LinkRuntime below) to ensure that RenderScript-driver-provided
   // structs (like Allocation_t) don't get forced into using the ARM layout
   // rules.
-  if (mCompiler.getTargetMachine().getTargetTriple().getArch() == llvm::Triple::x86) {
+  if (!pScript.isStructExplicitlyPaddedBySlang() &&
+      (mCompiler.getTargetMachine().getTargetTriple().getArch() == llvm::Triple::x86)) {
     mCompiler.translateGEPs(pScript);
   }
 
@@ -275,7 +277,7 @@
 // Assertion-enabled builds can't compile legacy bitcode (due to the use of
 // getName() with anonymous structure definitions).
 #ifdef _DEBUG
-  static const uint32_t kSlangMinimumFixedStructureNames = 2310;
+  static const uint32_t kSlangMinimumFixedStructureNames = SlangVersion::M_RS_OBJECT;
   uint32_t version = wrapper.getCompilerVersion();
   if (version < kSlangMinimumFixedStructureNames) {
     ALOGE("Found invalid legacy bitcode compiled with a version %u llvm-rs-cc "
diff --git a/lib/RSKernelExpand.cpp b/lib/RSKernelExpand.cpp
index e2fb6f4..2f451d7 100644
--- a/lib/RSKernelExpand.cpp
+++ b/lib/RSKernelExpand.cpp
@@ -22,6 +22,8 @@
 #include "bcc/Config.h"
 #include "bcinfo/MetadataExtractor.h"
 
+#include "slang_version.h"
+
 #include <cstdlib>
 #include <functional>
 #include <unordered_set>
@@ -89,7 +91,7 @@
   static char ID;
 
 private:
-  static const size_t RS_KERNEL_INPUT_LIMIT = 8; // see frameworks/base/libs/rs/cpu_ref/rsCpuCoreRuntime.h
+  static const size_t RS_KERNEL_INPUT_LIMIT = 8;  // see frameworks/base/libs/rs/cpu_ref/rsCpuCoreRuntime.h
 
   typedef std::unordered_set<llvm::Function *> FunctionSet;
 
@@ -130,6 +132,8 @@
   llvm::FunctionType *ExpandedForEachType;
   llvm::Type *RsExpandKernelDriverInfoPfxTy;
 
+  // Initialized when we begin to process each Module
+  bool mStructExplicitlyPaddedBySlang;
   uint32_t mExportForEachCount;
   const char **mExportForEachNameList;
   const uint32_t *mExportForEachSignatureList;
@@ -674,7 +678,7 @@
       llvm::LoadInst *InBufPtr = Builder.CreateLoad(InBufPtrAddr, "input_buf");
 
       llvm::Value *CastInBufPtr = nullptr;
-      if (Module->getTargetTriple() != DEFAULT_X86_TRIPLE_STRING) {
+      if (mStructExplicitlyPaddedBySlang || (Module->getTargetTriple() != DEFAULT_X86_TRIPLE_STRING)) {
         CastInBufPtr = Builder.CreatePointerCast(InBufPtr, InType, "casted_in");
       } else {
         // The disagreement between module and x86 target machine datalayout
@@ -682,7 +686,7 @@
         // code and bcc codegen for GetElementPtr. To solve this issue, skip the
         // cast to InType and leave CastInBufPtr as an int8_t*.  The buffer is
         // later indexed with an explicit byte offset computed based on
-        // X86_CUSTOM_DL_STRING and then bitcast it to actual input type.
+        // X86_CUSTOM_DL_STRING and then bitcast to actual input type.
         CastInBufPtr = InBufPtr;
       }
 
@@ -728,7 +732,7 @@
     for (size_t Index = 0; Index < NumInputs; ++Index) {
 
       llvm::Value *InPtr = nullptr;
-      if (Module->getTargetTriple() != DEFAULT_X86_TRIPLE_STRING) {
+      if (mStructExplicitlyPaddedBySlang || (Module->getTargetTriple() != DEFAULT_X86_TRIPLE_STRING)) {
         InPtr = Builder.CreateInBoundsGEP(InBufPtrs[Index], Offset);
       } else {
         // Treat x86 input buffer as byte[], get indexed pointer with explicit
@@ -784,7 +788,7 @@
     }
 
     llvm::DataLayout DL(Module);
-    if (Module->getTargetTriple() == DEFAULT_X86_TRIPLE_STRING) {
+    if (!mStructExplicitlyPaddedBySlang && (Module->getTargetTriple() == DEFAULT_X86_TRIPLE_STRING)) {
       DL.reset(X86_CUSTOM_DL_STRING);
     }
 
@@ -917,7 +921,7 @@
 
     // TODO: Refactor this to share functionality with ExpandOldStyleForEach.
     llvm::DataLayout DL(Module);
-    if (Module->getTargetTriple() == DEFAULT_X86_TRIPLE_STRING) {
+    if (!mStructExplicitlyPaddedBySlang && (Module->getTargetTriple() == DEFAULT_X86_TRIPLE_STRING)) {
       DL.reset(X86_CUSTOM_DL_STRING);
     }
     llvm::Type *Int32Ty = llvm::Type::getInt32Ty(*Context);
@@ -1001,7 +1005,7 @@
         OutBasePtr->setMetadata("tbaa", TBAAPointer);
       }
 
-      if (Module->getTargetTriple() != DEFAULT_X86_TRIPLE_STRING) {
+      if (mStructExplicitlyPaddedBySlang || (Module->getTargetTriple() != DEFAULT_X86_TRIPLE_STRING)) {
         CastedOutBasePtr = Builder.CreatePointerCast(OutBasePtr, OutTy, "casted_out");
       } else {
         // The disagreement between module and x86 target machine datalayout
@@ -1009,7 +1013,7 @@
         // code and bcc codegen for GetElementPtr. To solve this issue, skip the
         // cast to OutTy and leave CastedOutBasePtr as an int8_t*.  The buffer
         // is later indexed with an explicit byte offset computed based on
-        // X86_CUSTOM_DL_STRING and then bitcast it to actual output type.
+        // X86_CUSTOM_DL_STRING and then bitcast to actual output type.
         CastedOutBasePtr = OutBasePtr;
       }
     }
@@ -1053,7 +1057,7 @@
     if (CastedOutBasePtr) {
       llvm::Value *OutOffset = Builder.CreateSub(IV, Arg_x1);
 
-      if (Module->getTargetTriple() != DEFAULT_X86_TRIPLE_STRING) {
+      if (mStructExplicitlyPaddedBySlang || (Module->getTargetTriple() != DEFAULT_X86_TRIPLE_STRING)) {
         OutPtr = Builder.CreateInBoundsGEP(CastedOutBasePtr, OutOffset);
       } else {
         // Treat x86 output buffer as byte[], get indexed pointer with explicit
@@ -1374,6 +1378,8 @@
       return false;
     }
 
+    mStructExplicitlyPaddedBySlang = (me.getCompilerVersion() >= SlangVersion::N_STRUCT_EXPLICIT_PADDING);
+
     // Expand forEach_* style kernels.
     mExportForEachCount = me.getExportForEachSignatureCount();
     mExportForEachNameList = me.getExportForEachNameList();
diff --git a/tools/bcc/Android.bp b/tools/bcc/Android.bp
index 791d498..507fdd9 100644
--- a/tools/bcc/Android.bp
+++ b/tools/bcc/Android.bp
@@ -27,6 +27,8 @@
         "libLLVM",
     ],
 
+    header_libs: ["slang_headers"],
+
     target: {
         host: {
             host_ldlibs: ["-ldl"],
diff --git a/tools/bcc_compat/Android.bp b/tools/bcc_compat/Android.bp
index 51f5a81..12e703e 100644
--- a/tools/bcc_compat/Android.bp
+++ b/tools/bcc_compat/Android.bp
@@ -33,12 +33,15 @@
             host_ldlibs: ["-ldl"],
         },
     },
+
     shared_libs: [
         "libbcc",
         "libbcinfo",
         "libLLVM",
     ],
 
+    header_libs: ["slang_headers"],
+
     product_variables: {
         unbundled_build: {
             // Don't build for unbundled branches