[llvm-commits] [llvm-gcc-4.2] r79104 - in /llvm-gcc-4.2/trunk/gcc/config/arm: llvm-arm-target.h llvm-arm.cpp

Bob Wilson bob.wilson at apple.com
Sat Aug 15 08:15:34 PDT 2009


Author: bwilson
Date: Sat Aug 15 10:15:33 2009
New Revision: 79104

URL: http://llvm.org/viewvc/llvm-project?rev=79104&view=rev
Log:
Add support for the ARM AAPCS-VFP variant ABI.
Patch by Sandeep Patel.

Modified:
    llvm-gcc-4.2/trunk/gcc/config/arm/llvm-arm-target.h
    llvm-gcc-4.2/trunk/gcc/config/arm/llvm-arm.cpp

Modified: llvm-gcc-4.2/trunk/gcc/config/arm/llvm-arm-target.h
URL: http://llvm.org/viewvc/llvm-project/llvm-gcc-4.2/trunk/gcc/config/arm/llvm-arm-target.h?rev=79104&r1=79103&r2=79104&view=diff

==============================================================================
--- llvm-gcc-4.2/trunk/gcc/config/arm/llvm-arm-target.h (original)
+++ llvm-gcc-4.2/trunk/gcc/config/arm/llvm-arm-target.h Sat Aug 15 10:15:33 2009
@@ -38,5 +38,24 @@
       CC = CallingConv::ARM_APCS;				\
   }
 
+#ifdef LLVM_ABI_H
+
+extern bool
+llvm_arm_should_pass_aggregate_in_mixed_regs(tree, const Type *Ty,
+                                             CallingConv::ID&,
+                                             std::vector<const 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))
+
+extern
+bool llvm_arm_aggregate_partially_passed_in_regs(std::vector<const Type*>&,
+                                                 std::vector<const Type*>&,
+                                                 bool, CallingConv::ID&);
+
+#define LLVM_AGGREGATE_PARTIALLY_PASSED_IN_REGS(E, SE, ISR, CC)		\
+   llvm_arm_aggregate_partially_passed_in_regs((E), (SE), (ISR), (CC))
+
+#endif /* LLVM_ABI_H */
 #endif /* ENABLE_LLVM */
 /* LLVM LOCAL end (ENTIRE FILE!)  */

Modified: llvm-gcc-4.2/trunk/gcc/config/arm/llvm-arm.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm-gcc-4.2/trunk/gcc/config/arm/llvm-arm.cpp?rev=79104&r1=79103&r2=79104&view=diff

==============================================================================
--- llvm-gcc-4.2/trunk/gcc/config/arm/llvm-arm.cpp (original)
+++ llvm-gcc-4.2/trunk/gcc/config/arm/llvm-arm.cpp Sat Aug 15 10:15:33 2009
@@ -25,11 +25,13 @@
 
 #include "llvm-abi.h"
 #include "llvm-internal.h"
+#include "llvm/CallingConv.h"
 #include "llvm/DerivedTypes.h"
 #include "llvm/Instructions.h"
 #include "llvm/Intrinsics.h"
 #include "llvm/LLVMContext.h"
 #include "llvm/Module.h"
+#include "llvm-arm-target.h"
 
 extern "C" {
 #include "insn-codes.h"
@@ -2193,4 +2195,410 @@
   return true;
 }
 
+// "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_UNSIGNED_BYTE,
+  ARM_FDT_SIGNED_BYTE,
+  ARM_FDT_UNSIGNED_HALF_WORD,
+  ARM_FDT_SIGNED_HALF_WORD,
+  ARM_FDT_UNSIGNED_WORD,
+  ARM_FDT_SIGNED_WORD,
+  ARM_FDT_UNSIGNED_DOUBLE_WORD,
+  ARM_FDT_SIGNED_DOUBLE_WORD,
+  
+  ARM_FDT_HALF_FLOAT,
+  ARM_FDT_FLOAT,
+  ARM_FDT_DOUBLE,
+  
+  ARM_FDT_VECTOR_64,
+  ARM_FDT_VECTOR_128,
+  
+  ARM_FDT_POINTER_DATA,
+  ARM_FDT_POINTER_CODE,
+  
+  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++) {
+          int offset = tree_low_cst (BINFO_OFFSET (base_binfo), 0) * 4;
+          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 and decide if we're a homogeneous aggregate.
+    result = false;
+    
+    // Make sure that only one FDT is used.
+    cnt = 0;
+    for (i = 0; i < ARM_FDT_MAX; ++i) {
+      if (fdt_counts[i])
+        ++cnt;
+    
+      if (cnt > 1)
+        return false;
+    }
+
+    // Make sure that one FDT is 4 or less elements in size.
+    if (fdt_counts[ARM_FDT_HALF_FLOAT] > 1 &&
+        fdt_counts[ARM_FDT_HALF_FLOAT] <= 4)
+      result = true;
+    else if (fdt_counts[ARM_FDT_FLOAT] > 1 &&
+             fdt_counts[ARM_FDT_FLOAT] <= 4)
+      result = true;
+    else if (fdt_counts[ARM_FDT_DOUBLE] > 1 &&
+             fdt_counts[ARM_FDT_DOUBLE] <= 4)
+      result = true;
+    else if (fdt_counts[ARM_FDT_VECTOR_64] > 1 &&
+             fdt_counts[ARM_FDT_VECTOR_64] <= 4)
+      result = true;
+    else if (fdt_counts[ARM_FDT_VECTOR_128] > 1 &&
+             fdt_counts[ARM_FDT_VECTOR_128] <= 4)
+      result = true;
+    
+    return result;
+  }
+  
+  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(const Type *Ty, std::vector<const Type*> &Elts)
+{
+  for (Type::subtype_iterator I = Ty->subtype_begin(), E = Ty->subtype_end();
+       I != E; ++I) {
+    const Type *STy = I->get();
+    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 (const ArrayType *ATy = dyn_cast<ArrayType>(STy)) {
+      const 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);
+  }
+}
+
+// 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, const Type *Ty,
+                                             CallingConv::ID &CC,
+                                             std::vector<const Type*> &Elts) {
+  // 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);
+
+  // Walk Ty and push LLVM types corresponding to register types onto
+  // Elts.
+  if (result)
+    push_elts(Ty, Elts);
+  
+  return result;
+}  
+
+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<const Type*> &ScalarElts,
+                                     bool *SPRs) {
+  for (unsigned i = 0, e = ScalarElts.size(); i != e; ++i) {
+    const 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->isInteger() || isa<PointerType>(Ty) ||
+               Ty==Type::getVoidTy(Context)) {
+      ;
+    } else {
+      // Floating point scalar argument.
+      assert(Ty->isFloatingPoint() && 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<const Type*> &Elts,
+                                            std::vector<const Type*> &ScalarElts,
+                                            bool isShadowReturn,
+                                            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!
+}
+
 /* LLVM LOCAL end (ENTIRE FILE!)  */





More information about the llvm-commits mailing list