ef42402053b2 — Leonard Ritter a month ago
* ABI: initial work on aarch64
4 files changed, 222 insertions(+), 20 deletions(-)

M src/abi_aarch64.cpp
M src/gen_llvm.cpp
M src/platform_abi.cpp
M src/platform_abi.hpp
M src/abi_aarch64.cpp +209 -19
@@ 16,35 16,225 @@ 
 
 namespace scopes { namespace abi_aarch64 {
 
-size_t classify(const Type *T, ABIClass *classes) {
-    classes[0] = ABI_CLASS_NO_CLASS;
-    if (is_opaque(T))
+enum {
+    AARCH64_RET_VOID	= 0,
+    AARCH64_RET_INT64	= 1,
+    AARCH64_RET_INT128	= 2,
+
+    AARCH64_RET_UNUSED3	= 3,
+    AARCH64_RET_UNUSED4	= 4,
+    AARCH64_RET_UNUSED5	= 5,
+    AARCH64_RET_UNUSED6	= 6,
+    AARCH64_RET_UNUSED7	= 7,
+
+    // Note that FFI_TYPE_FLOAT == 2, _DOUBLE == 3, _LONGDOUBLE == 4,
+    // so _S4 through _Q1 are layed out as (TYPE * 4) + (4 - COUNT).
+    AARCH64_RET_S4		= 8,
+    AARCH64_RET_S3		= 9,
+    AARCH64_RET_S2		= 10,
+    AARCH64_RET_S1		= 11,
+
+    AARCH64_RET_D4		= 12,
+    AARCH64_RET_D3		= 13,
+    AARCH64_RET_D2		= 14,
+    AARCH64_RET_D1		= 15,
+
+    AARCH64_RET_Q4		= 16,
+    AARCH64_RET_Q3		= 17,
+    AARCH64_RET_Q2		= 18,
+    AARCH64_RET_Q1		= 19,
+
+    // Note that each of the sub-64-bit integers gets two entries.
+    AARCH64_RET_UINT8	= 20,
+    AARCH64_RET_UINT16	= 22,
+    AARCH64_RET_UINT32	= 24,
+
+    AARCH64_RET_SINT8	= 26,
+    AARCH64_RET_SINT16	= 28,
+    AARCH64_RET_SINT32	= 30,
+
+    AARCH64_RET_MASK	= 31,
+
+    AARCH64_RET_IN_MEM	= (1 << 5),
+    AARCH64_RET_NEED_COPY = (1 << 6),
+
+    AARCH64_FLAG_ARG_V_BIT	= 7,
+    AARCH64_FLAG_ARG_V	= (1 << AARCH64_FLAG_ARG_V_BIT),
+
+    N_X_ARG_REG		= 8,
+    N_V_ARG_REG		= 8,
+    CALL_CONTEXT_SIZE	= (N_V_ARG_REG * 16 + N_X_ARG_REG * 8),
+};
+
+#if 0
+// A subroutine of is_vfp_type.  Given a structure type, return the type
+// of the first non-structure element.  Recurse for structure elements.
+// Return NULL if the structure is in fact empty, i.e. no nested elements.
+static const Type *is_hfa0 (const Type *ST) {
+    switch (ST->kind()) {
+    case TK_Vector:
+    case TK_Array:
+    case TK_Matrix: {
+        auto tt = cast<ArrayLikeType>(ST);
+        auto T = is_hfa0(qualified_storage_type(tt->element_type).assert_ok());
+        if (T) return T;
+    } break;
+    case TK_Tuple: {
+        auto tt = cast<TupleType>(ST);
+        size_t count = tt->values.size();
+        for (size_t i = 0; i < count; ++i) {
+            auto T = is_hfa0(qualified_storage_type(tt->values[i]).assert_ok());
+            if (T) return T;
+        }
+    } break;
+    default: break;
+    }
+    return nullptr;
+}
+
+// A subroutine of is_vfp_type.  Given a structure type, return true if all
+// of the non-structure elements are the same as CANDIDATE.
+static bool is_hfa1 (const Type *ST, const Type *candidate) {
+    switch (ST->kind()) {
+    case TK_Vector:
+    case TK_Array:
+    case TK_Matrix: {
+        auto tt = cast<ArrayLikeType>(ST);
+        return is_hfa1(qualified_storage_type(tt->element_type).assert_ok(), candidate);
+    } break;
+    case TK_Tuple: {
+        auto tt = cast<TupleType>(ST);
+        size_t count = tt->values.size();
+        for (size_t i = 0; i < count; ++i) {
+            if (!is_hfa1(qualified_storage_type(tt->values[i]).assert_ok(), candidate))
+                return false;
+        }
+        return true;
+    } break;
+    default: break;
+    }
+    return (ST == candidate);
+}
+
+/* Determine if TY may be allocated to the FP registers.  This is both an
+   fp scalar type as well as an homogenous floating point aggregate (HFA).
+   That is, a structure consisting of 1 to 4 members of all the same type,
+   where that type is an fp scalar.
+   Returns non-zero iff TY is an HFA.  The result is the AARCH64_RET_*
+   constant for the type.  */
+static int is_vfp_type (const Type *ty, const Type *&candidate) {
+    size_t size, ele_count;
+
+    candidate = nullptr;
+    switch (ty->kind()) {
+    case TK_Real: {
+        candidate = ty;
         return 1;
-    T = storage_type(T).assert_ok();
-    size_t sz = size_of(T).assert_ok();
-    if (sz > 8)
-        return 0;
-    switch(T->kind()) {
+    } break;
+    case TK_Vector:
     case TK_Array:
     case TK_Matrix:
-    case TK_Tuple:
-        if (sz <= 1)
+    case TK_Tuple: {
+    } break;
+    default: return 0;
+    }
+
+    // No HFA types are smaller than 4 bytes, or larger than 64 bytes.
+    size = size_of(ty).assert_ok();
+    if (size < 4 || size > 64)
+        return 0;
+
+    // Find the type of the first non-structure member.
+    candidate = is_hfa0(ty);
+
+    if (!candidate)
+        return 0;
+
+    // If the first member is not a floating point type, it's not an HFA.
+    // Also quickly re-check the size of the structure.
+    switch (candidate->kind()) {
+    case TK_Real: {
+        size_t c_size = size_of(candidate).assert_ok();
+        ele_count = size / c_size;
+        if (size != ele_count * c_size)
+            return 0;
+    } break;
+    default: return 0;
+    }
+    if (ele_count > 4)
+        return 0;
+
+    if (!is_hfa1(ty, candidate))
+        return 0;
+
+    return ele_count;
+}
+#endif
+
+size_t classify(const Type *T, ABIClass *classes) {
+    switch(T->kind()) {
+    case TK_Integer:
+    case TK_Pointer: {
+        size_t size = size_of(T).assert_ok();
+        if (size <= 1) {
             classes[0] = ABI_CLASS_INTEGERSI8;
-        else if (sz <= 2)
+            return 1;
+        } else if (size <= 2) {
             classes[0] = ABI_CLASS_INTEGERSI16;
-        else if (sz <= 4)
+            return 1;
+        } else if (size <= 4) {
             classes[0] = ABI_CLASS_INTEGERSI;
-        else
+            return 1;
+        } else if (size <= 8) {
             classes[0] = ABI_CLASS_INTEGER;
-        return 1;
-    case TK_Integer:
-    case TK_Pointer:
+            return 1;
+        } else if (size <= 16) {
+            classes[0] = ABI_CLASS_INTEGER128;
+            return 1;
+        } else {
+            return 0;
+        }
+    } break;
+    case TK_Typename: {
+        if (is_opaque(T)) {
+            classes[0] = ABI_CLASS_NO_CLASS;
+            return 1;
+        } else {
+            return classify(storage_type(T).assert_ok(), classes);
+        }
+    } break;
     case TK_Real:
-    case TK_Typename:
     case TK_Vector:
-    default:
-        return 1;
+    case TK_Array:
+    case TK_Matrix:
+    case TK_Tuple: {
+        //const Type *ET = nullptr;
+        //int count = is_vfp_type(T, ET);
+        //if (!count) {
+            size_t s = size_of(T).assert_ok();
+            if (s > 16) {
+                classes[0] = ABI_CLASS_NO_CLASS;
+                return 1;
+            } else if (s == 16) {
+                classes[0] = ABI_CLASS_INTEGER128;
+                return 1;
+            } else if (s == 8) {
+                classes[0] = ABI_CLASS_INTEGER;
+                return 1;
+            } else {
+                classes[0] = ABI_CLASS_INTEGER128;
+                return 1;
+            }
+        //}
+    } break;
+    default: {
+        StyledStream ss;
+        ss << "internal error: type " << T << " unsupported in ABI" << std::endl;
+        assert(false && "not supported in ABI");
+        return 0;
+    } break;
     }
+    return 0;
 }
 
 }} // namespace scopes::abi_aarch64

          
M src/gen_llvm.cpp +8 -0
@@ 285,6 285,7 @@ struct LLVMIRGenerator {
     static LLVMTypeRef i16T;
     static LLVMTypeRef i32T;
     static LLVMTypeRef i64T;
+    static LLVMTypeRef i128T;
     static LLVMTypeRef f32T;
     static LLVMTypeRef f32x2T;
     static LLVMTypeRef f64T;

          
@@ 318,6 319,7 @@ struct LLVMIRGenerator {
     LLVMMetadataRef debug_i16T;
     LLVMMetadataRef debug_i32T;
     LLVMMetadataRef debug_i64T;
+    LLVMMetadataRef debug_i128T;
     LLVMMetadataRef debug_f32T;
     LLVMMetadataRef debug_f32x2T;
     LLVMMetadataRef debug_f64T;

          
@@ 667,6 669,7 @@ struct LLVMIRGenerator {
         i16T = LLVMInt16Type();
         i32T = LLVMInt32Type();
         i64T = LLVMInt64Type();
+        i128T = LLVMInt128Type();
         f32T = LLVMFloatType();
         f32x2T = LLVMVectorType(f32T, 2);
         f64T = LLVMDoubleType();

          
@@ 714,6 717,9 @@ struct LLVMIRGenerator {
             case ABI_CLASS_INTEGERSI8: {
                 types[i] = i8T; k++;
             } break;
+            case ABI_CLASS_INTEGER128: {
+                types[i] = i128T; k++;
+            } break;
             default: {
                 // do nothing
 #if 0

          
@@ 2900,6 2906,7 @@ struct LLVMIRGenerator {
         debug_i16T = LLVMDIBuilderCreateBasicType(di_builder, "i16", 3, 16, DW_ATE_signed, LLVMDIFlagZero);
         debug_i32T = LLVMDIBuilderCreateBasicType(di_builder, "i32", 3, 32, DW_ATE_signed, LLVMDIFlagZero);
         debug_i64T = LLVMDIBuilderCreateBasicType(di_builder, "i64", 3, 64, DW_ATE_signed, LLVMDIFlagZero);
+        debug_i128T = LLVMDIBuilderCreateBasicType(di_builder, "i128", 4, 128, DW_ATE_signed, LLVMDIFlagZero);
         debug_f32T = LLVMDIBuilderCreateBasicType(di_builder, "f32", 3, 32, DW_ATE_float, LLVMDIFlagZero);
         debug_f32x2T = LLVMDIBuilderCreateVectorType(di_builder, 2, 0, debug_f32T, nullptr, 0); // FIXME: No idea how subscript works
         debug_f64T = LLVMDIBuilderCreateBasicType(di_builder, "f64", 3, 32, DW_ATE_float, LLVMDIFlagZero);

          
@@ 3108,6 3115,7 @@ LLVMTypeRef LLVMIRGenerator::i8T = nullp
 LLVMTypeRef LLVMIRGenerator::i16T = nullptr;
 LLVMTypeRef LLVMIRGenerator::i32T = nullptr;
 LLVMTypeRef LLVMIRGenerator::i64T = nullptr;
+LLVMTypeRef LLVMIRGenerator::i128T = nullptr;
 LLVMTypeRef LLVMIRGenerator::f32T = nullptr;
 LLVMTypeRef LLVMIRGenerator::f32x2T = nullptr;
 LLVMTypeRef LLVMIRGenerator::f64T = nullptr;

          
M src/platform_abi.cpp +3 -0
@@ 84,9 84,12 @@ size_t abi_classify(const Type *T, ABICl
     sz = abi_windows_x64::classify(T, classes);
 #elif defined(__amd64__)
     sz = abi_x86_64::classify(T, classes);
+#elif defined(__aarch64__)
+    sz = abi_aarch64::classify(T, classes);
 #else
 #error unsupported platform ABI
 #endif
+
 #if 0
     if (sz) {
         StyledStream ss(std::cout);

          
M src/platform_abi.hpp +2 -1
@@ 23,9 23,10 @@ struct Type;
     /* purpose registers. */ \
     T(INTEGER) \
     T(INTEGERSI) \
-    /* special types for windows, not used anywhere else */ \
+    /* extra types not used by x86-64 PS */ \
     T(INTEGERSI16) \
     T(INTEGERSI8) \
+    T(INTEGER128) \
     /* The class consists of types that fit into a vector register. */ \
     T(SSE) \
     T(SSESF) \