| //===---------------- Target.cpp - Implements the ARM ABI. ----------------===// |
| // |
| // Copyright (C) 2005 to 2013 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/IR/Module.h" |
| |
| // System headers |
| #include <gmp.h> |
| |
| // GCC headers |
| #include "auto-host.h" |
| #ifndef ENABLE_BUILD_WITH_CXX |
| #include <cstring> // Otherwise included by system.h with C linkage. |
| extern "C" { |
| #endif |
| #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" |
| #ifndef ENABLE_BUILD_WITH_CXX |
| } // extern "C" |
| #endif |
| |
| // Trees header. |
| #include "dragonegg/Trees.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 && isa<AGGREGATE_TYPE>(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 (isa<FIELD_DECL>(field)) { |
| 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 (isa<FIELD_DECL>(field)) { |
| 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: |
| llvm_unreachable("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 |
| llvm_unreachable("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.CreateAlignedStore(EVIElem, GEP, 1, isVolatile); |
| } else { |
| Builder.CreateAlignedStore(EVI, GEP, 1, 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.CreateAlignedStore(EVI, GEP, 1, 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); |
| } |