[llvm-commits] [dragonegg] r149508 - in /dragonegg/trunk: include/arm/ include/arm/dragonegg/ include/arm/dragonegg/Target.h src/arm/ src/arm/Target.cpp

Duncan Sands baldrick at free.fr
Wed Feb 1 06:50:58 PST 2012


Author: baldrick
Date: Wed Feb  1 08:50:58 2012
New Revision: 149508

URL: http://llvm.org/viewvc/llvm-project?rev=149508&view=rev
Log:
Support for ARM.  Based on a patch by Jin Gu Kang that ports the logic
from llvm-gcc-4.2.

Added:
    dragonegg/trunk/include/arm/
    dragonegg/trunk/include/arm/dragonegg/
    dragonegg/trunk/include/arm/dragonegg/Target.h
    dragonegg/trunk/src/arm/
    dragonegg/trunk/src/arm/Target.cpp

Added: dragonegg/trunk/include/arm/dragonegg/Target.h
URL: http://llvm.org/viewvc/llvm-project/dragonegg/trunk/include/arm/dragonegg/Target.h?rev=149508&view=auto
==============================================================================
--- dragonegg/trunk/include/arm/dragonegg/Target.h (added)
+++ dragonegg/trunk/include/arm/dragonegg/Target.h Wed Feb  1 08:50:58 2012
@@ -0,0 +1,281 @@
+//==----- Target.h - Target hooks for GCC to LLVM conversion -----*- C++ -*-==//
+//
+// Copyright (C) 2007 to 2012  Jin Gu Kang, Anton Korobeynikov, Duncan Sands
+// et al.
+//
+// This file is part of DragonEgg.
+//
+// DragonEgg is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free Software
+// Foundation; either version 2, or (at your option) any later version.
+//
+// DragonEgg is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+// A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// DragonEgg; see the file COPYING.  If not, write to the Free Software
+// Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
+//
+//===----------------------------------------------------------------------===//
+// This file declares some target-specific hooks for GCC to LLVM conversion.
+// It was derived from llvm-arm-target.h and arm.h on llvm-gcc.4.2.
+//===----------------------------------------------------------------------===//
+
+#ifndef DRAGONEGG_TARGET_H
+#define DRAGONEGG_TARGET_H
+
+/* LLVM specific code to select the calling conventions. The AAPCS
+   specification says that varargs functions must use the base standard
+   instead of the VFP hard float variant. We check for that with
+   (isVoid || hasArgList). */
+
+/* from  TARGET_AAPCS_BASED */
+#define DEFAULT_TARGET_AAPCS_BASED \
+  (ARM_DEFAULT_ABI != ARM_ABI_APCS && ARM_DEFAULT_ABI != ARM_ABI_ATPCS)
+
+#define TARGET_ADJUST_LLVM_CC(CC, type)                       \
+  {                                                           \
+    if (TARGET_AAPCS_BASED) {                                 \
+      if (TARGET_VFP && TARGET_HARD_FLOAT_ABI &&              \
+             ((TYPE_ARG_TYPES(type) == 0) ||                  \
+              (TREE_VALUE(tree_last(TYPE_ARG_TYPES(type))) == \
+               void_type_node)))                              \
+        CC = CallingConv::ARM_AAPCS_VFP;                      \
+      else if (!DEFAULT_TARGET_AAPCS_BASED)                   \
+        CC = CallingConv::ARM_AAPCS;                          \
+    } else if (DEFAULT_TARGET_AAPCS_BASED) {                  \
+        CC = CallingConv::ARM_APCS;                           \
+    }                                                         \
+  }
+
+#ifdef DRAGONEGG_ABI_H
+
+extern bool
+llvm_arm_should_pass_aggregate_in_mixed_regs(tree_node *, Type *Ty,
+                                             CallingConv::ID,
+                                             std::vector<Type*>&);
+
+#define LLVM_SHOULD_PASS_AGGREGATE_IN_MIXED_REGS(T, TY, CC, E)    \
+   llvm_arm_should_pass_aggregate_in_mixed_regs((T), (TY), (CC), (E))
+
+struct DefaultABIClient;
+extern bool
+llvm_arm_try_pass_aggregate_custom(tree_node *, std::vector<Type*>&,
+           CallingConv::ID CC,
+           struct DefaultABIClient*);
+
+#define LLVM_TRY_PASS_AGGREGATE_CUSTOM(T, E, CC, C) \
+  llvm_arm_try_pass_aggregate_custom((T), (E), (CC), (C))
+
+extern
+bool llvm_arm_aggregate_partially_passed_in_regs(std::vector<Type*>&,
+                                                 std::vector<Type*>&,
+                                                 CallingConv::ID CC);
+
+#define LLVM_AGGREGATE_PARTIALLY_PASSED_IN_REGS(E, SE, ISR, CC)   \
+   llvm_arm_aggregate_partially_passed_in_regs((E), (SE), (CC))
+
+extern Type *llvm_arm_aggr_type_for_struct_return(tree_node *type,
+                                                  CallingConv::ID CC);
+
+/* LLVM_AGGR_TYPE_FOR_STRUCT_RETURN - Return LLVM Type if X can be
+  returned as an aggregate, otherwise return NULL. */
+#define LLVM_AGGR_TYPE_FOR_STRUCT_RETURN(X, CC) \
+    llvm_arm_aggr_type_for_struct_return((X), (CC))
+
+extern void llvm_arm_extract_multiple_return_value(Value *Src, Value *Dest,
+                                                   bool isVolatile,
+                                                   LLVMBuilder &B);
+
+/* LLVM_EXTRACT_MULTIPLE_RETURN_VALUE - Extract multiple return value from
+  SRC and assign it to DEST. */
+#define LLVM_EXTRACT_MULTIPLE_RETURN_VALUE(Src,Dest,V,B)       \
+    llvm_arm_extract_multiple_return_value((Src),(Dest),(V),(B))
+
+extern
+bool llvm_arm_should_pass_or_return_aggregate_in_regs(tree_node *TreeType,
+                                                      CallingConv::ID CC);
+
+/* LLVM_SHOULD_NOT_USE_SHADOW_RETURN = Return true is the given type should
+  not be returned via a shadow parameter with the given calling conventions. */
+#define LLVM_SHOULD_NOT_USE_SHADOW_RETURN(X, CC) \
+    llvm_arm_should_pass_or_return_aggregate_in_regs((X), (CC))
+
+/* Vectors bigger than 128 are returned using sret.  */
+#define LLVM_SHOULD_RETURN_VECTOR_AS_SHADOW(X, isBuiltin) \
+  (TREE_INT_CST_LOW(TYPE_SIZE(X)) > 128)
+
+#endif /* DRAGONEGG_ABI_H */
+
+#define LLVM_TARGET_INTRINSIC_PREFIX "arm"
+
+/* LLVM_TARGET_NAME - This specifies the name of the target, which correlates to
+ * the llvm::InitializeXXXTarget() function.
+ */
+#define LLVM_TARGET_NAME ARM
+
+
+/* Turn -march=xx into a CPU type.
+ */
+#define LLVM_SET_SUBTARGET_FEATURES(C, F)        \
+  { switch (arm_tune) { \
+    case arm8:          C = ("arm8"); break;\
+    case arm810:        C = ("arm810"); break;\
+    case strongarm:     C = ("strongarm"); break;\
+    case strongarm110:  C = ("strongarm110"); break;\
+    case strongarm1100: C = ("strongarm1100"); break;\
+    case strongarm1110: C = ("strongarm1110"); break;\
+    case arm7tdmi:      C = ("arm7tdmi"); break;\
+    case arm7tdmis:     C = ("arm7tdmi-s"); break;\
+    case arm710t:       C = ("arm710t"); break;\
+    case arm720t:       C = ("arm720t"); break;\
+    case arm740t:       C = ("arm740t"); break;\
+    case arm9:          C = ("arm9"); break;\
+    case arm9tdmi:      C = ("arm9tdmi"); break;\
+    case arm920:        C = ("arm920"); break;\
+    case arm920t:       C = ("arm920t"); break;\
+    case arm922t:       C = ("arm922t"); break;\
+    case arm940t:       C = ("arm940t"); break;\
+    case ep9312:        C = ("ep9312"); break;\
+    case arm10tdmi:     C = ("arm10tdmi"); break;\
+    case arm1020t:      C = ("arm1020t"); break;\
+    case arm9e:         C = ("arm9e"); break;\
+    case arm946es:      C = ("arm946e-s"); break;\
+    case arm966es:      C = ("arm966e-s"); break;\
+    case arm968es:      C = ("arm968e-s"); break;\
+    case arm10e:        C = ("arm10e"); break;\
+    case arm1020e:      C = ("arm1020e"); break;\
+    case arm1022e:      C = ("arm1022e"); break;\
+    case xscale:        C = ("xscale"); break;\
+    case iwmmxt:        C = ("iwmmxt"); break;\
+    case arm926ejs:     C = ("arm926ej-s"); break;\
+    case arm1026ejs:    C = ("arm1026ej-s"); break;\
+    case arm1136js:     C = ("arm1136j-s"); break;\
+    case arm1136jfs:    C = ("arm1136jf-s"); break;\
+    case arm1176jzs:    C = ("arm1176jz-s"); break;\
+    case arm1176jzfs:   C = ("arm1176jzf-s"); break;\
+    case mpcorenovfp:   C = ("mpcorenovfp"); break;\
+    case mpcore:        C = ("mpcore"); break;\
+    case arm1156t2s:    C = ("arm1156t2-s"); break; \
+    case arm1156t2fs:   C = ("arm1156t2f-s"); break; \
+    case cortexa8:      C = ("cortex-a8"); break; \
+    case cortexa9:      C = ("cortex-a9"); break; \
+    case cortexr4:      C = ("cortex-r4"); break; \
+    case cortexm3:      C = ("cortex-m3"); break; \
+    case cortexm4:      C = ("cortex-m4"); break; \
+    case cortexm0:      C = ("cortex-m0"); break; \
+    default:            \
+      C = ("arm7tdmi"); \
+      break; \
+    } \
+    if (TARGET_VFP3)            \
+      F.AddFeature("vfp3");         \
+    else {              \
+      F.AddFeature("vfp3", false);        \
+      if (TARGET_VFP && TARGET_HARD_FLOAT)      \
+        F.AddFeature("vfp2");         \
+      else              \
+        F.AddFeature("vfp2", false);        \
+    }               \
+    if (TARGET_NEON)            \
+      F.AddFeature("neon");         \
+    else              \
+      F.AddFeature("neon", false);        \
+    if (TARGET_FP16)            \
+      F.AddFeature("fp16");         \
+    else              \
+      F.AddFeature("fp16", false);        \
+  }
+
+/* Encode arm / thumb modes and arm subversion number in the triplet. e.g.
+ * armv6-apple-darwin, thumbv5-apple-darwin. FIXME: Replace thumb triplets
+ * with function notes.
+ */
+#define LLVM_OVERRIDE_TARGET_ARCH()                                        \
+  (TARGET_THUMB                                                            \
+   ? (arm_arch7                                                           \
+      ? "thumbv7"                                                          \
+      : (arm_arch_thumb2                                                \
+        ? "thumbv6t2"                                                   \
+        : (arm_tune == cortexm0                                         \
+           ? "thumbv6m"                                                 \
+           : (arm_arch6                                                 \
+              ? "thumbv6"                                               \
+              : (arm_arch5e                                             \
+                 ? "thumbv5e"                                           \
+                 : (arm_arch5                                           \
+                    ? "thumbv5"                                         \
+                    : (arm_arch4t                                       \
+                       ? "thumbv4t" : "")))))))                        \
+   : (arm_arch7                                                           \
+      ? "armv7"                                                            \
+      : (arm_arch_thumb2                                                   \
+         ? "armv6t2"                                                       \
+         : (arm_arch6                                                      \
+            ? "armv6"                                                      \
+            : (arm_arch5e                                                  \
+               ? "armv5e"                                                  \
+               : (arm_arch5                                                \
+                  ? "armv5"                                                \
+                  : (arm_arch4t                                            \
+                     ? "armv4t"                                            \
+                     : (arm_arch4                                          \
+                        ? "armv4" : ""))))))))
+
+#if 0
+// Dragonegg should make flag_mkernel and flag_apple_kext option later on.
+// We didn't decide place to make these flags.
+#define LLVM_SET_MACHINE_OPTIONS(argvec)               \
+  if (flag_mkernel || flag_apple_kext) {               \
+    argvec.push_back("-arm-long-calls");               \
+    argvec.push_back("-arm-strict-align");             \
+  }
+#endif
+
+#define LLVM_SET_TARGET_MACHINE_OPTIONS(options)       \
+  options.UseSoftFloat = TARGET_SOFT_FLOAT;            \
+  if (TARGET_HARD_FLOAT_ABI)                           \
+    options.FloatABIType = llvm::FloatABI::Hard;
+
+
+/* Doing struct copy by partial-word loads and stores is not a good idea on ARM. */
+#define TARGET_LLVM_MIN_BYTES_COPY_BY_MEMCPY 4
+
+/* These are a couple of extensions to the asm formats
+     %@ prints out ASM_COMMENT_START
+     TODO: %r prints out REGISTER_PREFIX reg_names[arg]  */
+#define LLVM_ASM_EXTENSIONS(ESCAPED_CHAR, ASM, RESULT)  \
+  else if ((ESCAPED_CHAR) == '@') {           \
+    (RESULT) += ASM_COMMENT_START;                      \
+  }
+
+/* LLVM_TARGET_INTRINSIC_LOWER - To handle builtins, we want to expand the
+   invocation into normal LLVM code.  If the target can handle the builtin, this
+   macro should call the target TreeToLLVM::TargetIntrinsicLower method and
+   return true.  This macro is invoked from a method in the TreeToLLVM class. */
+#if 0
+// Because of data dependency, we will implement later on.
+#define LLVM_TARGET_INTRINSIC_LOWER(EXP, BUILTIN_CODE, DESTLOC, RESULT,       \
+                                    DESTTY, OPS)                              \
+        TargetIntrinsicLower(EXP, BUILTIN_CODE, DESTLOC, RESULT, DESTTY, OPS);
+#endif
+
+/* LLVM_GET_REG_NAME - The registers known to llvm as "r10", "r11", and "r12"
+   may have different names in GCC.  Register "r12" is called "ip", and on
+   non-Darwin OSs, "r10" is "sl" and "r11" is "fp".  Translate those names.
+   For VFP registers, GCC doesn't distinguish between the q and d registers
+   so use the incoming register name if it exists.  Otherwise, use the default
+   register names to match the backend.  */
+#define LLVM_GET_REG_NAME(REG_NAME, REG_NUM) \
+  ((REG_NUM) == 10 ? "r10" \
+   : (REG_NUM) == 11 ? "r11" \
+   : (REG_NUM) == 12 ? "r12" \
+   : (REG_NUM) >= FIRST_VFP_REGNUM && REG_NAME != 0 ? REG_NAME \
+   : reg_names[REG_NUM])
+
+/* Define a static enumeration of the NEON builtins to be used when
+   converting to LLVM intrinsics.  These names are derived from the
+   neon_builtin_data table in arm.c and should be kept in sync with that.  */
+
+#endif /* DRAGONEGG_TARGET_H */

Added: dragonegg/trunk/src/arm/Target.cpp
URL: http://llvm.org/viewvc/llvm-project/dragonegg/trunk/src/arm/Target.cpp?rev=149508&view=auto
==============================================================================
--- dragonegg/trunk/src/arm/Target.cpp (added)
+++ dragonegg/trunk/src/arm/Target.cpp Wed Feb  1 08:50:58 2012
@@ -0,0 +1,623 @@
+//===---------------- Target.cpp - Implements the ARM ABI. ----------------===//
+//
+// Copyright (C) 2005 to 2012  Evan Cheng, Jin Gu Kang, Duncan Sands et al.
+//
+// This file is part of DragonEgg.
+//
+// DragonEgg is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free Software
+// Foundation; either version 2, or (at your option) any later version.
+//
+// DragonEgg is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+// A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// DragonEgg; see the file COPYING.  If not, write to the Free Software
+// Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
+//
+//===----------------------------------------------------------------------===//
+// This file implements specific LLVM ARM ABI.
+// It was derived from llvm-arm.cpp on llvm-gcc.4.2.
+//===----------------------------------------------------------------------===//
+
+// Plugin headers
+#include "dragonegg/ABI.h"
+#include "dragonegg/Target.h"
+
+// LLVM headers
+#include "llvm/Module.h"
+
+// System headers
+#include <gmp.h>
+
+// GCC headers
+extern "C" {
+#include "config.h"
+// Stop GCC declaring 'getopt' as it can clash with the system's declaration.
+#undef HAVE_DECL_GETOPT
+#include "system.h"
+#include "coretypes.h"
+#include "target.h"
+#include "tree.h"
+
+#include "diagnostic.h"
+#include "gimple.h"
+#include "toplev.h"
+}
+
+static LLVMContext &Context = getGlobalContext();
+
+// "Fundamental Data Types" according to the AAPCS spec.  These are used
+// to check that a given aggregate meets the criteria for a "homogeneous
+// aggregate."
+enum arm_fdts {
+  ARM_FDT_INVALID,
+
+  ARM_FDT_HALF_FLOAT,
+  ARM_FDT_FLOAT,
+  ARM_FDT_DOUBLE,
+
+  ARM_FDT_VECTOR_64,
+  ARM_FDT_VECTOR_128,
+
+  ARM_FDT_MAX
+};
+
+// Classify type according to the number of fundamental data types contained
+// among its members.  Returns true if type is a homogeneous aggregate.
+static bool
+vfp_arg_homogeneous_aggregate_p(enum machine_mode mode, tree type,
+                                int *fdt_counts)
+{
+  bool result = false;
+  HOST_WIDE_INT bytes =
+    (mode == BLKmode) ? int_size_in_bytes (type) : (int) GET_MODE_SIZE (mode);
+
+  if (type && AGGREGATE_TYPE_P (type))
+  {
+    int i;
+    int cnt = 0;
+    tree field;
+
+    // Zero sized arrays or structures are not homogeneous aggregates.
+    if (!bytes)
+      return 0;
+
+    // Classify each field of records.
+    switch (TREE_CODE (type))
+    {
+      case RECORD_TYPE:
+      // For classes first merge in the field of the subclasses.
+      if (TYPE_BINFO (type)) {
+        tree binfo, base_binfo;
+        int basenum;
+
+        for (binfo = TYPE_BINFO (type), basenum = 0;
+             BINFO_BASE_ITERATE (binfo, basenum, base_binfo); basenum++) {
+          tree type = BINFO_TYPE (base_binfo);
+
+          result = vfp_arg_homogeneous_aggregate_p(TYPE_MODE (type), type,
+                                                   fdt_counts);
+          if (!result)
+            return false;
+        }
+      }
+      // And now merge the fields of structure.
+      for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) {
+        if (TREE_CODE (field) == FIELD_DECL) {
+          if (TREE_TYPE (field) == error_mark_node)
+            continue;
+
+          result = vfp_arg_homogeneous_aggregate_p(TYPE_MODE(TREE_TYPE(field)),
+                                                   TREE_TYPE(field),
+                                                   fdt_counts);
+          if (!result)
+            return false;
+        }
+      }
+      break;
+
+      case ARRAY_TYPE:
+        // Arrays are handled as small records.
+        {
+          int array_fdt_counts[ARM_FDT_MAX] = { 0 };
+
+          result = vfp_arg_homogeneous_aggregate_p(TYPE_MODE(TREE_TYPE(type)),
+                                                   TREE_TYPE(type),
+                                                   array_fdt_counts);
+
+          cnt = bytes / int_size_in_bytes(TREE_TYPE(type));
+          for (i = 0; i < ARM_FDT_MAX; ++i)
+            fdt_counts[i] += array_fdt_counts[i] * cnt;
+
+          if (!result)
+            return false;
+        }
+      break;
+
+      case UNION_TYPE:
+      case QUAL_UNION_TYPE:
+        {
+          // Unions are similar to RECORD_TYPE.
+          int union_fdt_counts[ARM_FDT_MAX] = { 0 };
+
+          // Unions are not derived.
+          gcc_assert (!TYPE_BINFO (type)
+                      || !BINFO_N_BASE_BINFOS (TYPE_BINFO (type)));
+          for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) {
+            int union_field_fdt_counts[ARM_FDT_MAX] = { 0 };
+
+            if (TREE_CODE (field) == FIELD_DECL) {
+              if (TREE_TYPE (field) == error_mark_node)
+                continue;
+
+              result = vfp_arg_homogeneous_aggregate_p(
+                          TYPE_MODE(TREE_TYPE(field)),
+                          TREE_TYPE(field),
+                          union_field_fdt_counts);
+              if (!result)
+                return false;
+
+              // track largest union field
+              for (i = 0; i < ARM_FDT_MAX; ++i) {
+                if (union_field_fdt_counts[i] > 4)  // bail early if we can
+                  return false;
+
+                union_fdt_counts[i] = MAX(union_fdt_counts[i],
+                                          union_field_fdt_counts[i]);
+                union_field_fdt_counts[i] = 0;  // clear it out for next iter
+              }
+            }
+          }
+
+          // check for only one type across all union fields
+          cnt = 0;
+          for (i = 0; i < ARM_FDT_MAX; ++i) {
+            if (union_fdt_counts[i])
+              ++cnt;
+
+            if (cnt > 1)
+              return false;
+
+            fdt_counts[i] += union_fdt_counts[i];
+          }
+        }
+      break;
+
+      default:
+      assert(0 && "What type is this?");
+    }
+
+    // Walk through fdt_counts.  This is a homogeneous aggregate if
+    // only one FDT is used.
+    cnt = 0;
+    for (i = 0; i < ARM_FDT_MAX; ++i) {
+      if (fdt_counts[i]) {
+        // Make sure that one FDT is 4 or less elements in size.
+        if (fdt_counts[i] > 4)
+          return false;
+        ++cnt;
+      }
+
+      if (cnt > 1)
+        return false;
+    }
+
+    if (cnt == 0)
+      return false;
+
+    return true;
+  }
+
+  if (type)
+  {
+    int idx = 0;
+    int cnt = 0;
+
+    switch (TREE_CODE(type))
+    {
+    case REAL_TYPE:
+      idx = (TYPE_PRECISION(type) == 32) ?
+              ARM_FDT_FLOAT :
+              ((TYPE_PRECISION(type) == 64) ?
+                ARM_FDT_DOUBLE :
+                ARM_FDT_INVALID);
+      cnt = 1;
+      break;
+
+    case COMPLEX_TYPE:
+      {
+        tree subtype = TREE_TYPE(type);
+        idx = (TYPE_PRECISION(subtype) == 32) ?
+                ARM_FDT_FLOAT :
+                ((TYPE_PRECISION(subtype) == 64) ?
+                  ARM_FDT_DOUBLE :
+                  ARM_FDT_INVALID);
+        cnt = 2;
+      }
+      break;
+
+    case VECTOR_TYPE:
+      idx = (bytes == 8) ?
+              ARM_FDT_VECTOR_64 :
+              (bytes == 16) ?
+                ARM_FDT_VECTOR_128 :
+                ARM_FDT_INVALID;
+      cnt = 1;
+      break;
+
+    case INTEGER_TYPE:
+    case POINTER_TYPE:
+    case ENUMERAL_TYPE:
+    case BOOLEAN_TYPE:
+    case REFERENCE_TYPE:
+    case FUNCTION_TYPE:
+    case METHOD_TYPE:
+    default:
+      return false;     // All of these disqualify.
+    }
+
+    fdt_counts[idx] += cnt;
+    return true;
+  }
+  else
+    assert(0 && "what type was this?");
+
+  return false;
+}
+
+// Walk over an LLVM Type that we know is a homogeneous aggregate and
+// push the proper LLVM Types that represent the register types to pass
+// that struct member in.
+static void push_elts(Type *Ty, std::vector<Type*> &Elts)
+{
+  for (Type::subtype_iterator I = Ty->subtype_begin(), E = Ty->subtype_end();
+       I != E; ++I) {
+    Type *STy = *I;
+    if (const VectorType *VTy = dyn_cast<VectorType>(STy)) {
+      switch (VTy->getBitWidth())
+      {
+        case 64:  // v2f32
+          Elts.push_back(VectorType::get(Type::getFloatTy(Context), 2));
+          break;
+        case 128: // v2f64
+          Elts.push_back(VectorType::get(Type::getDoubleTy(Context), 2));
+          break;
+        default:
+          assert (0 && "invalid vector type");
+      }
+    } else if (ArrayType *ATy = dyn_cast<ArrayType>(STy)) {
+      Type *ETy = ATy->getElementType();
+
+      for (uint64_t i = ATy->getNumElements(); i > 0; --i)
+        Elts.push_back(ETy);
+    } else if (STy->getNumContainedTypes())
+      push_elts(STy, Elts);
+    else
+      Elts.push_back(STy);
+  }
+}
+
+static unsigned count_num_words(std::vector<Type*> &ScalarElts) {
+  unsigned NumWords = 0;
+  for (unsigned i = 0, e = ScalarElts.size(); i != e; ++i) {
+    Type *Ty = ScalarElts[i];
+    if (Ty->isPointerTy()) {
+      NumWords++;
+    } else if (Ty->isIntegerTy()) {
+      const unsigned TypeSize = Ty->getPrimitiveSizeInBits();
+      const unsigned NumWordsForType = (TypeSize + 31) / 32;
+
+      NumWords += NumWordsForType;
+    } else {
+      assert (0 && "Unexpected type.");
+    }
+  }
+  return NumWords;
+}
+
+// This function is used only on AAPCS. The difference from the generic
+// handling of arguments is that arguments larger than 32 bits are split
+// and padding arguments are added as necessary for alignment. This makes
+// the IL a bit more explicit about how arguments are handled.
+extern bool
+llvm_arm_try_pass_aggregate_custom(tree type,
+                                   std::vector<Type*>& ScalarElts,
+           CallingConv::ID CC,
+           struct DefaultABIClient* C) {
+  if (CC != CallingConv::ARM_AAPCS && CC != CallingConv::C)
+    return false;
+
+  if (CC == CallingConv::C && !TARGET_AAPCS_BASED)
+    return false;
+
+  if (TARGET_HARD_FLOAT_ABI)
+    return false;
+  Type *Ty = ConvertType(type);
+  if (Ty->isPointerTy())
+    return false;
+
+  const unsigned Size = TREE_INT_CST_LOW(TYPE_SIZE(type))/8;
+  const unsigned Alignment = TYPE_ALIGN(type)/8;
+  const unsigned NumWords = count_num_words(ScalarElts);
+  const bool AddPad = Alignment >= 8 && (NumWords % 2);
+
+  // First, build a type that will be bitcast to the original one and
+  // from where elements will be extracted.
+  std::vector<Type*> Elts;
+  Type* Int32Ty = Type::getInt32Ty(getGlobalContext());
+  const unsigned NumRegularArgs = Size / 4;
+  for (unsigned i = 0; i < NumRegularArgs; ++i) {
+    Elts.push_back(Int32Ty);
+  }
+  const unsigned RestSize = Size % 4;
+  llvm::Type *RestType = NULL;
+  if (RestSize> 2) {
+    RestType = Type::getInt32Ty(getGlobalContext());
+  } else if (RestSize > 1) {
+    RestType = Type::getInt16Ty(getGlobalContext());
+  } else if (RestSize > 0) {
+    RestType = Type::getInt8Ty(getGlobalContext());
+  }
+  if (RestType)
+    Elts.push_back(RestType);
+  StructType *STy = StructType::get(getGlobalContext(), Elts, false);
+
+  if (AddPad) {
+    ScalarElts.push_back(Int32Ty);
+    C->HandlePad(Int32Ty);
+  }
+
+  for (unsigned i = 0; i < NumRegularArgs; ++i) {
+    C->EnterField(i, STy);
+    C->HandleScalarArgument(Int32Ty, 0);
+    ScalarElts.push_back(Int32Ty);
+    C->ExitField();
+  }
+  if (RestType) {
+    C->EnterField(NumRegularArgs, STy);
+    C->HandleScalarArgument(RestType, 0, RestSize);
+    ScalarElts.push_back(RestType);
+    C->ExitField();
+  }
+  return true;
+}
+
+// Target hook for llvm-abi.h. It returns true if an aggregate of the
+// specified type should be passed in a number of registers of mixed types.
+// It also returns a vector of types that correspond to the registers used
+// for parameter passing. This only applies to AAPCS-VFP "homogeneous
+// aggregates" as specified in 4.3.5 of the AAPCS spec.
+bool
+llvm_arm_should_pass_aggregate_in_mixed_regs(tree TreeType, Type *Ty,
+                                             CallingConv::ID CC,
+                                             std::vector<Type*> &Elts) {
+  if (!llvm_arm_should_pass_or_return_aggregate_in_regs(TreeType, CC))
+    return false;
+
+  // Walk Ty and push LLVM types corresponding to register types onto
+  // Elts.
+  push_elts(Ty, Elts);
+
+  return true;
+}
+
+static bool alloc_next_spr(bool *SPRs)
+{
+  for (int i = 0; i < 16; ++i)
+    if (!SPRs[i]) {
+      SPRs[i] = true;
+      return true;
+    }
+  return false;
+}
+
+static bool alloc_next_dpr(bool *SPRs)
+{
+  for (int i = 0; i < 16; i += 2)
+    if (!SPRs[i]) {
+      SPRs[i] = SPRs[i+1] = true;
+      return true;
+    }
+  return false;
+}
+
+static bool alloc_next_qpr(bool *SPRs) {
+  for (int i = 0; i < 16; i += 4)
+    if (!SPRs[i]) {
+      SPRs[i] = SPRs[i+1] = SPRs[i+2] = SPRs[i+3] = true;
+      return true;
+    }
+  return false;
+}
+
+// count_num_registers_uses - Simulate argument passing reg allocation in SPRs.
+// Caller is expected to zero out SPRs.  Returns true if all of ScalarElts fit
+// in registers.
+static bool count_num_registers_uses(std::vector<Type*> &ScalarElts,
+                                     bool *SPRs) {
+  for (unsigned i = 0, e = ScalarElts.size(); i != e; ++i) {
+    Type *Ty = ScalarElts[i];
+    if (const VectorType *VTy = dyn_cast<VectorType>(Ty)) {
+      switch (VTy->getBitWidth())
+      {
+        case 64:
+          if (!alloc_next_dpr(SPRs))
+            return false;
+          break;
+        case 128:
+          if (!alloc_next_qpr(SPRs))
+            return false;
+          break;
+        default:
+          assert(0);
+      }
+    } else if (Ty->isIntegerTy() || Ty->isPointerTy() ||
+               Ty==Type::getVoidTy(Context)) {
+      ;
+    } else {
+      // Floating point scalar argument.
+      assert(Ty->isFloatingPointTy() && Ty->isPrimitiveType() &&
+             "Expecting a floating point primitive type!");
+      switch (Ty->getTypeID())
+      {
+        case Type::FloatTyID:
+          if (!alloc_next_spr(SPRs))
+            return false;
+          break;
+        case Type::DoubleTyID:
+          if (!alloc_next_spr(SPRs))
+            return false;
+          break;
+        default:
+          assert(0);
+      }
+    }
+  }
+  return true;
+}
+
+// Target hook for llvm-abi.h. This is called when an aggregate is being passed
+// in registers. If there are only enough available parameter registers to pass
+// part of the aggregate, return true. That means the aggregate should instead
+// be passed in memory.
+bool
+llvm_arm_aggregate_partially_passed_in_regs(std::vector<Type*> &Elts,
+                                            std::vector<Type*> &ScalarElts,
+                                            CallingConv::ID CC) {
+  // Homogeneous aggregates are an AAPCS-VFP feature.
+  if ((CC != CallingConv::ARM_AAPCS_VFP) ||
+      !(TARGET_AAPCS_BASED && TARGET_VFP && TARGET_HARD_FLOAT_ABI))
+    return true;
+
+  bool SPRs[16] = { 0 };                    // represents S0-S16
+
+  // Figure out which SPRs are available.
+  if (!count_num_registers_uses(ScalarElts, SPRs))
+    return true;
+
+  if (!count_num_registers_uses(Elts, SPRs))
+    return true;
+
+  return false;                            // it all fit in registers!
+}
+
+// Return LLVM Type if TYPE can be returned as an aggregate,
+// otherwise return NULL.
+Type *llvm_arm_aggr_type_for_struct_return(tree TreeType,
+                                           CallingConv::ID CC) {
+  if (!llvm_arm_should_pass_or_return_aggregate_in_regs(TreeType, CC))
+    return NULL;
+
+  // Walk Ty and push LLVM types corresponding to register types onto
+  // Elts.
+  std::vector<Type*> Elts;
+  Type *Ty = ConvertType(TreeType);
+  push_elts(Ty, Elts);
+
+  return StructType::get(Context, Elts, false);
+}
+
+// llvm_arm_extract_mrv_array_element - Helper function that helps extract
+// an array element from multiple return value.
+//
+// Here, SRC is returning multiple values. DEST's DESTFIELDNO field is an array.
+// Extract SRCFIELDNO's ELEMENO value and store it in DEST's FIELDNO field's
+// ELEMENTNO.
+//
+static void llvm_arm_extract_mrv_array_element(Value *Src, Value *Dest,
+                                               unsigned SrcFieldNo,
+                                               unsigned SrcElemNo,
+                                               unsigned DestFieldNo,
+                                               unsigned DestElemNo,
+                                               LLVMBuilder &Builder,
+                                               bool isVolatile) {
+  Value *EVI = Builder.CreateExtractValue(Src, SrcFieldNo, "mrv_gr");
+  const StructType *STy = cast<StructType>(Src->getType());
+  llvm::Value *Idxs[3];
+  Idxs[0] = ConstantInt::get(llvm::Type::getInt32Ty(Context), 0);
+  Idxs[1] = ConstantInt::get(llvm::Type::getInt32Ty(Context), DestFieldNo);
+  Idxs[2] = ConstantInt::get(llvm::Type::getInt32Ty(Context), DestElemNo);
+  Value *GEP = Builder.CreateGEP(Dest, Idxs, "mrv_gep");
+  if (STy->getElementType(SrcFieldNo)->isVectorTy()) {
+    Value *ElemIndex = ConstantInt::get(Type::getInt32Ty(Context), SrcElemNo);
+    Value *EVIElem = Builder.CreateExtractElement(EVI, ElemIndex, "mrv");
+    Builder.CreateStore(EVIElem, GEP, isVolatile);
+  } else {
+    Builder.CreateStore(EVI, GEP, isVolatile);
+  }
+}
+
+// llvm_arm_extract_multiple_return_value - Extract multiple values returned
+// by SRC and store them in DEST. It is expected that SRC and
+// DEST types are StructType, but they may not match.
+void llvm_arm_extract_multiple_return_value(Value *Src, Value *Dest,
+                                            bool isVolatile,
+                                            LLVMBuilder &Builder) {
+  const StructType *STy = cast<StructType>(Src->getType());
+  unsigned NumElements = STy->getNumElements();
+
+  const PointerType *PTy = cast<PointerType>(Dest->getType());
+  const StructType *DestTy = cast<StructType>(PTy->getElementType());
+
+  unsigned SNO = 0;
+  unsigned DNO = 0;
+
+  while (SNO < NumElements) {
+
+    Type *DestElemType = DestTy->getElementType(DNO);
+
+    // Directly access first class values.
+    if (DestElemType->isSingleValueType()) {
+      Value *GEP = Builder.CreateStructGEP(Dest, DNO, "mrv_gep");
+      Value *EVI = Builder.CreateExtractValue(Src, SNO, "mrv_gr");
+      Builder.CreateStore(EVI, GEP, isVolatile);
+      ++DNO; ++SNO;
+      continue;
+    }
+
+    // Access array elements individually. Note, Src and Dest type may
+    // not match. For example { <2 x float>, float } and { float[3]; }
+    const ArrayType *ATy = cast<ArrayType>(DestElemType);
+    unsigned ArraySize = ATy->getNumElements();
+    unsigned DElemNo = 0; // DestTy's DNO field's element number
+    while (DElemNo < ArraySize) {
+      unsigned i = 0;
+      unsigned Size = 1;
+
+      if (const VectorType *SElemTy =
+          dyn_cast<VectorType>(STy->getElementType(SNO))) {
+        Size = SElemTy->getNumElements();
+      }
+      while (i < Size) {
+        llvm_arm_extract_mrv_array_element(Src, Dest, SNO, i++,
+                                           DNO, DElemNo++,
+                                           Builder, isVolatile);
+      }
+      // Consumed this src field. Try next one.
+      ++SNO;
+    }
+    // Finished building current dest field.
+    ++DNO;
+  }
+}
+
+// Target hook for llvm-abi.h for LLVM_SHOULD_NOT_USE_SHADOW_RETURN and is
+// also a utility function used for other target hooks in this file. Returns
+// true if the aggregate should be passed or returned in registers.
+bool llvm_arm_should_pass_or_return_aggregate_in_regs(tree TreeType,
+                                                      CallingConv::ID CC) {
+  // Homogeneous aggregates are an AAPCS-VFP feature.
+  if ((CC != CallingConv::ARM_AAPCS_VFP) ||
+      !(TARGET_AAPCS_BASED && TARGET_VFP && TARGET_HARD_FLOAT_ABI))
+    return false;
+
+  // Alas, we can't use LLVM Types to figure this out because we need to
+  // examine unions closely.  We'll have to walk the GCC TreeType.
+  int fdt_counts[ARM_FDT_MAX] = { 0 };
+  bool result = false;
+  result = vfp_arg_homogeneous_aggregate_p(TYPE_MODE(TreeType), TreeType,
+                                           fdt_counts);
+  return result && !TREE_ADDRESSABLE(TreeType);
+}





More information about the llvm-commits mailing list