text: // This file is a part of Julia. License is MIT: https://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
  //
  //===----------------------------------------------------------------------===//
  
  #if defined _CPU_ARM_
  #ifndef __ARM_EABI__
  #  error "the Julia ARM ABI implementation only supports EABI"
  #endif
  #ifdef __ANDROID__
  #define __ARM_PCS_VFP
  #endif
  
  #ifndef __ARM_PCS_VFP
  #  error "the Julia ARM ABI implementation requires VFP support"
  #endif
  #endif
  
  struct ABI_ARMLayout : AbiLayout {
  
  bool needPassByRef(jl_datatype_t *dt, AttrBuilder &ab) override
  {
      return false;
  }
  
  #define jl_is_floattype(v)   jl_subtype(v,(jl_value_t*)jl_floatingpoint_type)
  
  Type *get_llvm_fptype(jl_datatype_t *dt) const
  {
      // 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 (jl_datatype_size(dt)) {
      case 2:
          lltype = T_float16;
          break;
      case 4:
          lltype = T_float32;
          break;
      case 8:
          lltype = T_float64;
          break;
      default:
          return NULL;
      }
      return ((jl_floatingpoint_type && jl_is_floattype((jl_value_t*)dt)) ?
              lltype : NULL);
  }
  
  // Check whether a type contained by a candidate homogeneous aggregate is valid
  // fundamental type.
  //
  // Returns the corresponding LLVM type.
  Type *isLegalHAType(jl_datatype_t *dt) const
  {
      // 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.
  size_t isLegalHA(jl_datatype_t *dt, Type *&base) const
  {
      // 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 (jl_datatype_size(dt) > 64 || dt->layout->npointers || dt->layout->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`.
  void classify_cprc(jl_datatype_t *dt, bool *vfp) const
  {
      // 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;
      }
  }
  
  void classify_return_arg(jl_datatype_t *dt, bool *reg,
                           bool *onstack, bool *need_rewrite) const
  {
      // 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_primitivetype, might need exact def
      if (jl_is_primitivetype(dt) && jl_datatype_size(dt) <= 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 (jl_datatype_size(dt) <= 4)
          *reg = true;
      else
          *onstack = true;
  }
  
  bool use_sret(jl_datatype_t *dt) override
  {
      bool reg = false;
      bool onstack = false;
      bool need_rewrite = false;
      classify_return_arg(dt, &reg, &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`.
  void classify_arg(jl_datatype_t *dt, bool *reg,
                    bool *onstack, bool *need_rewrite) const
  {
      // 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_primitivetype(dt) && jl_datatype_size(dt) <= 8) {
          *reg = true;
          return;
      }
  
      *need_rewrite = true;
  }
  
  Type *preferred_llvm_type(jl_datatype_t *dt, bool isret) const override
  {
      if (Type *fptype = get_llvm_fptype(dt))
          return fptype;
  
      bool reg = false;
      bool onstack = false;
      bool need_rewrite = false;
      if (isret)
          classify_return_arg(dt, &reg, &onstack, &need_rewrite);
      else
          classify_arg(dt, &reg, &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 = jl_datatype_align(dt);
      if (align < 4)
          align = 4;
      if (align > 8)
          align = 8;
  
      Type *T = Type::getIntNTy(jl_LLVMContext, align*8);
      return ArrayType::get(T, (jl_datatype_size(dt) + align - 1) / align);
  }
  
  };
  