require "../abi" # Based on # https://github.com/rust-lang/rust/blob/master/src/librustc_trans/cabi_aarch64.rs @[Deprecated("This API is now internal to the compiler and no longer updated publicly.")] class LLVM::ABI::AArch64 < LLVM::ABI def abi_info(atys : Array(Type), rty : Type, ret_def : Bool, context : Context) : LLVM::ABI::FunctionType ret_ty = compute_return_type(rty, ret_def, context) arg_tys = atys.map { |aty| compute_arg_type(aty, context) } FunctionType.new(arg_tys, ret_ty) end def align(type : Type) : Int32 align(type, 8) end def size(type : Type) : Int32 size(type, 8) end def homogeneous_aggregate?(type) homog_agg : {Type, UInt64}? = case type.kind when Type::Kind::Float return {type, 1_u64} when Type::Kind::Double return {type, 1_u64} when Type::Kind::Array check_array(type) when Type::Kind::Struct check_struct(type) end # Ensure we have at most four uniquely addressable members if homog_agg if 0 < homog_agg[1] <= 4 return homog_agg end end end private def check_array(type) len = type.array_size.to_u64 return if len == 0 element = type.element_type # if our element is an HFA/HVA, so are we; multiply members by our len if homog_agg = homogeneous_aggregate?(element) base_type, members = homog_agg {base_type, len * members} end end private def check_struct(type) elements = type.struct_element_types return if elements.empty? base_type = nil members = 0_u64 elements.each do |element| opt_homog_agg = homogeneous_aggregate?(element) # field isn't itself an HFA, so we aren't either return unless opt_homog_agg field_type, field_members = opt_homog_agg if !base_type # first field - store its type and number of members base_type = field_type members = field_members else # 2nd or later field - give up if it's a different type; otherwise incr. members return unless base_type == field_type members += field_members end end return unless base_type if size(type) == size(base_type) * members {base_type, members} end end private def compute_return_type(rty, ret_def, context) if !ret_def ArgType.direct(context.void) elsif register?(rty) non_struct(rty, context) elsif homog_agg = homogeneous_aggregate?(rty) base_type, members = homog_agg ArgType.direct(rty, base_type.array(members)) else size = size(rty) if size <= 16 cast = if size <= 1 context.int8 elsif size <= 2 context.int16 elsif size <= 4 context.int32 elsif size <= 8 context.int64 else context.int64.array(((size + 7) // 8).to_u64) end ArgType.direct(rty, cast) else ArgType.indirect(rty, LLVM::Attribute::StructRet) end end end private def compute_arg_type(aty, context) if register?(aty) non_struct(aty, context) elsif homog_agg = homogeneous_aggregate?(aty) base_type, members = homog_agg ArgType.direct(aty, base_type.array(members)) else size = size(aty) if size <= 16 cast = if size == 0 context.int64.array(0) elsif size <= 1 context.int8 elsif size <= 2 context.int16 elsif size <= 4 context.int32 elsif size <= 8 context.int64 else context.int64.array(((size + 7) // 8).to_u64) end ArgType.direct(aty, cast) else ArgType.indirect(aty, nil) end end end def register?(type) : Bool case type.kind when Type::Kind::Integer, Type::Kind::Float, Type::Kind::Double, Type::Kind::Pointer true else false end end private def non_struct(type, context) attr = type == context.int1 ? LLVM::Attribute::ZExt : nil ArgType.direct(type, attr: attr) end end