// This file is a part of Julia. License is MIT: http://julialang.org/license //===----------------------------------------------------------------------===// // // The ABI implementation used for ARM targets. // //===----------------------------------------------------------------------===// // // The Procedure Call Standard can be found here: // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042f/IHI0042F_aapcs.pdf // //===----------------------------------------------------------------------===// #ifndef __ARM_EABI__ # error "the Julia ARM ABI implementation only supports EABI" #endif #ifndef __ARM_PCS_VFP # error "the Julia ARM ABI implementation requires VFP support" #endif namespace { typedef bool AbiState; AbiState default_abi_state = 0; void needPassByRef(AbiState *state,jl_value_t *ty, bool *byRef, bool *inReg, bool *byRefAttr) { return; } bool need_private_copy(jl_value_t *ty, bool byRef) { return false; } static Type *get_llvm_fptype(jl_datatype_t *dt) { // Assume jl_is_datatype(dt) && !jl_is_abstracttype(dt) if (dt->mutabl || jl_datatype_nfields(dt) != 0) return NULL; Type *lltype; // Check size first since it's cheaper. switch (dt->size) { case 2: lltype = T_float16; break; case 4: lltype = T_float32; break; case 8: lltype = T_float64; break; default: return NULL; } return jl_is_floattype((jl_value_t*)dt) ? lltype : NULL; } static size_t isLegalHA(jl_datatype_t *dt, Type *&base); // Check whether a type contained by a candidate homogeneous aggregate is valid // fundamental type. // // Returns the corresponding LLVM type. static Type *isLegalHAType(jl_datatype_t *dt) { // single- or double-precision floating-point type if (Type* fp = get_llvm_fptype(dt)) return fp; // NOT SUPPORTED: 64- or 128-bit containerized vectors return NULL; } // Check whether a type is a legal homogeneous aggregate. // Returns the number of fundamental members. // // Legality of the HA is determined by a nonzero return value. // In case of a non-legal HA, the value of 'base' is undefined. static size_t isLegalHA(jl_datatype_t *dt, Type *&base) { // Homogeneous aggregates are only used for VFP registers, // so use that definition of legality (section 6.1.2.1) if (jl_is_structtype(dt)) { // Fast path checks before descending the type hierarchy // (4 x 128b vector == 64B max size) if (dt->size > 64 || !dt->pointerfree || dt->haspadding) return 0; base = NULL; size_t total_members = 0; size_t parent_members = jl_datatype_nfields(dt); for (size_t i = 0; i < parent_members; ++i) { jl_datatype_t *fdt = (jl_datatype_t*)jl_field_type(dt,i); Type *T = isLegalHAType(fdt); if (T) total_members++; else if (size_t field_members = isLegalHA(fdt, T)) // recursive application (expanding nested composite types) total_members += field_members; else return 0; if (!base) base = T; else if (base != T) return 0; } // ... with one to four Elements. if (total_members < 1 || total_members > 4) return 0; return total_members; } return 0; } // Determine if an argument can be passed through a coprocessor register. // // All the out parameters should be default to `false`. static void classify_cprc(jl_datatype_t *dt, bool *vfp) { // Based on section 6.1 of the Procedure Call Standard // VFP: 6.1.2.1 // - A half-precision floating-point type. // - A single-precision floating-point type. // - A double-precision floating-point type. if (get_llvm_fptype(dt)) { *vfp = true; return; } // NOT SUPPORTED: A 64-bit or 128-bit containerized vector type. // - A Homogeneous Aggregate Type *base = NULL; if (isLegalHA(dt, base)) { *vfp = true; return; } } static void classify_return_arg(jl_value_t *ty, bool *reg, bool *onstack, bool *need_rewrite) { // Assume jl_is_datatype(ty) && !jl_is_abstracttype(ty) jl_datatype_t *dt = (jl_datatype_t*)ty; // Based on section 5.4 of the Procedure Call Standard // VFP standard variant: see 6.1.2.2 // Any result whose type would satisfy the conditions for a VFP CPRC is // returned in the appropriate number of consecutive VFP registers // starting with the lowest numbered register (s0, d0, q0). classify_cprc(dt, reg); if (*reg) return; // - A Half-precision Floating Point Type is returned in the least // significant 16 bits of r0. if (dt == jl_float16_type) { *reg = true; return; } // - A Fundamental Data Type that is smaller than 4 bytes is zero- or // sign-extended to a word and returned in r0. // - A double-word sized Fundamental Data Type (e.g., long long, double and // 64-bit containerized vectors) is returned in r0 and r1. // - A word-sized Fundamental Data Type (eg., int, float) is returned in r0. // NOTE: assuming "fundamental type" == jl_is_bitstype, might need exact def if (jl_is_bitstype(dt) && dt->size <= 8) { *reg = true; return; } // If we ever support containerized vectors on an ARMv7 without VFP, // these can be returned in r0-r3 as well. // NOTE: we don't check for jl_is_structtype below, because at this point // everything will be rewritten to look like a composite aggregate *need_rewrite = true; // - A Composite Type not larger than 4 bytes is returned in r0. The format // is as if the result had been stored in memory at a word-aligned address // and then loaded into r0 with an LDR instruction. Any bits in r0 that // lie outside the bounds of the result have unspecified values. // - A Composite Type larger than 4 bytes, or whose size cannot be // determined statically by both caller and callee, is stored in memory at // an address passed as an extra argument when the function was called // (ยง5.5, rule A.4). The memory to be used for the result may be modified // at any point during the function call. if (dt->size <= 4) *reg = true; else *onstack = true; } bool use_sret(AbiState *state, jl_value_t *ty) { // Assume (jl_is_datatype(ty) && !jl_is_abstracttype(ty) && // !jl_is_array_type(ty)) bool reg = false; bool onstack = false; bool need_rewrite = false; classify_return_arg(ty, ®, &onstack, &need_rewrite); return onstack; } // Determine which kind of register the argument will be passed in and // if the argument has to be passed on stack (including by reference). // // If the argument should be passed in SIMD and floating-point registers, // we may need to rewrite the argument types to [n x ftype]. // If the argument should be passed in general purpose registers, we may need // to rewrite the argument types to [n x i64]. // // If the argument has to be passed on stack, we need to use sret. // // All the out parameters should be default to `false`. static void classify_arg(jl_value_t *ty, bool *reg, bool *onstack, bool *need_rewrite) { // Assume jl_is_datatype(ty) && !jl_is_abstracttype(ty) jl_datatype_t *dt = (jl_datatype_t*)ty; // Based on section 5.5 of the Procedure Call Standard // C.1.cp // If the argument is a CPRC and there are sufficient unallocated // co-processor registers of the appropriate class, the argument is // allocated to co-processor registers. classify_cprc(dt, reg); if (*reg) return; // Handle fundamental types if (jl_is_bitstype(dt) && dt->size <= 8) { *reg = true; return; } *need_rewrite = true; } Type *preferred_llvm_type(jl_value_t *ty, bool isret) { if (!jl_is_datatype(ty) || jl_is_abstracttype(ty) || jl_is_array_type(ty)) return NULL; jl_datatype_t *dt = (jl_datatype_t*)ty; if (Type *fptype = get_llvm_fptype(dt)) return fptype; bool reg = false; bool onstack = false; bool need_rewrite = false; if (isret) classify_return_arg(ty, ®, &onstack, &need_rewrite); else classify_arg(ty, ®, &onstack, &need_rewrite); if (!need_rewrite) return NULL; // Based on section 4 of the Procedure Call Standard // If some type is illegal and needs to be rewritten, // represent it as an aggregate composite type. // 4.3.1: aggregates // - The alignment of an aggregate shall be the alignment of its // most-aligned component. // - The size of an aggregate shall be the smallest multiple of its // alignment that is sufficient to hold all of its members when they are // laid out according to these rules. // 5.5 B.5 // For a Composite Type, the alignment of the copy will have 4-byte // alignment if its natural alignment is <= 4 and 8-byte alignment if // its natural alignment is >= 8 size_t align = dt->alignment; if (align < 4) align = 4; if (align > 8) align = 8; Type* T = Type::getIntNTy(getGlobalContext(), align*8); return ArrayType::get(T, (dt->size + align - 1) / align); } }