[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