mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
337 lines
7.9 KiB
C
337 lines
7.9 KiB
C
#include "codegen_internal.h"
|
|
|
|
bool type_is_empty_field(Type *type, bool allow_array)
|
|
{
|
|
type = type_lowering(type);
|
|
if (allow_array)
|
|
{
|
|
while (type->type_kind == TYPE_ARRAY)
|
|
{
|
|
if (type->array.len == 0) return true;
|
|
type = type_lowering(type->array.base);
|
|
}
|
|
}
|
|
return type_is_empty_record(type, allow_array);
|
|
}
|
|
|
|
bool type_is_empty_record(Type *type, bool allow_array)
|
|
{
|
|
if (!type_is_union_struct(type)) return false;
|
|
|
|
Decl *decl = type->decl;
|
|
if (decl->has_variable_array) return false;
|
|
|
|
Decl **members = decl->strukt.members;
|
|
VECEACH(members, i)
|
|
{
|
|
if (!type_is_empty_field(members[i]->type, allow_array)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Based on isSingleElementStruct in Clang
|
|
*/
|
|
Type *type_abi_find_single_struct_element(Type *type)
|
|
{
|
|
if (!type_is_structlike(type)) return NULL;
|
|
|
|
// Elements with a variable array? If so no.
|
|
if (type->decl->has_variable_array) return NULL;
|
|
|
|
Type *found = NULL;
|
|
Decl **members = type->decl->strukt.members;
|
|
VECEACH(members, i)
|
|
{
|
|
Type *field_type = type_lowering(members[i]->type);
|
|
|
|
// Ignore empty arrays
|
|
if (type_is_empty_field(field_type, true)) continue;
|
|
|
|
// Already one field found, not single field.
|
|
if (found) return NULL;
|
|
|
|
// Flatten single element arrays.
|
|
while (field_type->type_kind == TYPE_ARRAY)
|
|
{
|
|
if (field_type->array.len != 1) break;
|
|
field_type = field_type->array.base;
|
|
}
|
|
|
|
if (type_is_structlike(field_type))
|
|
{
|
|
field_type = type_abi_find_single_struct_element(field_type);
|
|
if (!field_type) return NULL;
|
|
}
|
|
found = field_type;
|
|
}
|
|
// If there is some padding? Then ignore.
|
|
if (found && type_size(type) != type_size(found)) found = NULL;
|
|
return found;
|
|
}
|
|
|
|
bool type_is_homogenous_base_type(Type *type)
|
|
{
|
|
type = type->canonical;
|
|
switch (platform_target.abi)
|
|
{
|
|
case ABI_PPC64_SVR4:
|
|
switch (type->type_kind)
|
|
{
|
|
case TYPE_F128:
|
|
if (!platform_target.float128) return false;
|
|
FALLTHROUGH;
|
|
case TYPE_F32:
|
|
case TYPE_F64:
|
|
return !platform_target.ppc64.is_softfp;
|
|
case TYPE_VECTOR:
|
|
return type_size(type) == 128 / 8;
|
|
default:
|
|
return false;
|
|
}
|
|
case ABI_X64:
|
|
case ABI_WIN64:
|
|
case ABI_X86:
|
|
switch (type->type_kind)
|
|
{
|
|
case TYPE_F64:
|
|
case TYPE_F32:
|
|
return true;
|
|
case TYPE_VECTOR:
|
|
switch (type_size(type))
|
|
{
|
|
case 16:
|
|
case 32:
|
|
case 64:
|
|
// vec128 256 512 ok
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
case ABI_AARCH64:
|
|
switch (type->type_kind)
|
|
{
|
|
case ALL_FLOATS:
|
|
return true;
|
|
case TYPE_VECTOR:
|
|
switch (type_size(type))
|
|
{
|
|
case 8:
|
|
case 16:
|
|
// vector 64, 128 => true
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
case ABI_ARM:
|
|
switch (type->type_kind)
|
|
{
|
|
case TYPE_F32:
|
|
case TYPE_F64:
|
|
case TYPE_F128:
|
|
return true;
|
|
case TYPE_VECTOR:
|
|
switch (type_size(type))
|
|
{
|
|
case 8:
|
|
case 16:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
FALLTHROUGH;
|
|
default:
|
|
return false;
|
|
}
|
|
case ABI_UNKNOWN:
|
|
case ABI_WASM:
|
|
case ABI_PPC32:
|
|
case ABI_RISCV:
|
|
return false;
|
|
}
|
|
UNREACHABLE
|
|
}
|
|
|
|
|
|
bool type_homogenous_aggregate_small_enough(Type *type, unsigned members)
|
|
{
|
|
switch (platform_target.abi)
|
|
{
|
|
case ABI_PPC64_SVR4:
|
|
if (type->type_kind == TYPE_F128 && platform_target.float128) return members <= 8;
|
|
if (type->type_kind == TYPE_VECTOR) return members <= 8;
|
|
// Use max 8 registers.
|
|
return ((type_size(type) + 7) / 8) * members <= 8;
|
|
case ABI_X64:
|
|
case ABI_WIN64:
|
|
case ABI_X86:
|
|
case ABI_AARCH64:
|
|
case ABI_ARM:
|
|
return members <= 4;
|
|
case ABI_UNKNOWN:
|
|
case ABI_WASM:
|
|
case ABI_PPC32:
|
|
case ABI_RISCV:
|
|
return false;
|
|
}
|
|
UNREACHABLE
|
|
}
|
|
|
|
|
|
/**
|
|
* Calculate whether this is a homogenous aggregate for the ABI.
|
|
* // Based on bool ABIInfo::isHomogeneousAggregate in Clang
|
|
* @param type the (flattened) type to check.
|
|
* @param base the base type of the aggregate
|
|
* @param elements the elements found
|
|
* @return true if it is an aggregate, false otherwise.
|
|
*/
|
|
bool type_is_homogenous_aggregate(Type *type, Type **base, unsigned *elements)
|
|
{
|
|
*elements = 0;
|
|
RETRY:
|
|
switch (type->type_kind)
|
|
{
|
|
case TYPE_FAILABLE:
|
|
type = type->failable;
|
|
goto RETRY;
|
|
case TYPE_DISTINCT:
|
|
type = type->decl->distinct_decl.base_type;
|
|
goto RETRY;
|
|
case TYPE_BITSTRUCT:
|
|
type = type->decl->bitstruct.base_type->type;
|
|
goto RETRY;
|
|
case TYPE_VOID:
|
|
case TYPE_TYPEID:
|
|
case TYPE_FUNC:
|
|
case TYPE_SUBARRAY:
|
|
case CT_TYPES:
|
|
case TYPE_FAILABLE_ANY:
|
|
return false;
|
|
case TYPE_ANY:
|
|
*base = type_iptr->canonical;
|
|
*elements = 2;
|
|
return true;
|
|
case TYPE_ANYERR:
|
|
case TYPE_ERRTYPE:
|
|
type = type_iptr;
|
|
goto RETRY;
|
|
case TYPE_TYPEDEF:
|
|
type = type->canonical;
|
|
goto RETRY;
|
|
case TYPE_STRUCT:
|
|
case TYPE_UNION:
|
|
if (type->decl->has_variable_array) return false;
|
|
*elements = 0;
|
|
{
|
|
Decl **members = type->decl->strukt.members;
|
|
VECEACH(members, i)
|
|
{
|
|
unsigned member_mult = 1;
|
|
// Flatten the type.
|
|
Type *member_type = type_lowering(members[i]->type);
|
|
// Go down deep into a nester array.
|
|
while (member_type->type_kind == TYPE_ARRAY)
|
|
{
|
|
// If we find a zero length array, this is not allowed.
|
|
if (member_type->array.len == 0) return false;
|
|
member_mult *= member_type->array.len;
|
|
member_type = member_type->array.base;
|
|
}
|
|
unsigned member_members = 0;
|
|
// Skip any empty record.
|
|
if (type_is_empty_record(member_type, true)) continue;
|
|
|
|
// Check recursively if the field member is homogenous
|
|
if (!type_is_homogenous_aggregate(member_type, base, &member_members)) return false;
|
|
member_members *= member_mult;
|
|
// In the case of a union, grab the bigger set of elements.
|
|
if (type->type_kind == TYPE_UNION)
|
|
{
|
|
*elements = MAX(*elements, member_members);
|
|
}
|
|
else
|
|
{
|
|
*elements += member_members;
|
|
}
|
|
}
|
|
assert(base);
|
|
if (!*base) return false;
|
|
|
|
// Ensure no padding
|
|
if (type_size(*base) * *elements != type_size(type)) return false;
|
|
}
|
|
goto TYPECHECK;
|
|
case TYPE_FLEXIBLE_ARRAY:
|
|
return false;
|
|
case TYPE_ARRAY:
|
|
// Empty arrays? Not homogenous.
|
|
if (type->array.len == 0) return false;
|
|
// Check the underlying type and multiply by length.
|
|
if (!type_is_homogenous_aggregate(type->array.base, base, elements)) return false;
|
|
*elements *= type->array.len;
|
|
goto TYPECHECK;
|
|
case TYPE_ENUM:
|
|
type = type->decl->enums.type_info->type;
|
|
goto RETRY;
|
|
case TYPE_BOOL:
|
|
// Lower bool to unsigned char
|
|
type = type_char;
|
|
break;
|
|
case ALL_SIGNED_INTS:
|
|
// Lower signed to unsigned
|
|
type = type_int_unsigned_by_bitsize(type->builtin.bitsize);
|
|
break;
|
|
case ALL_UNSIGNED_INTS:
|
|
case ALL_FLOATS:
|
|
case TYPE_VECTOR:
|
|
break;
|
|
case TYPE_POINTER:
|
|
// All pointers are the same.
|
|
type = type_voidptr;
|
|
break;
|
|
}
|
|
// The common case:
|
|
*elements = 1;
|
|
// Is it a valid base type?
|
|
if (!type_is_homogenous_base_type(type)) return false;
|
|
// If we don't have a base type yet, set it.
|
|
if (!*base)
|
|
{
|
|
*base = type;
|
|
// Special handling of non-power-of-2 vectors
|
|
if (type->type_kind == TYPE_VECTOR)
|
|
{
|
|
// Widen the type with elements.
|
|
unsigned vec_elements = type_size(type) / type_size(type->vector.base);
|
|
*base = type_get_vector(type->vector.base, vec_elements);
|
|
}
|
|
}
|
|
// One is vector - other isn't => failure
|
|
if (((*base)->type_kind == TYPE_VECTOR) != (type->type_kind == TYPE_VECTOR)) return false;
|
|
|
|
// Size does not match => failure
|
|
if (type_size(*base) != type_size(type)) return false;
|
|
|
|
TYPECHECK:
|
|
if (*elements == 0) return false;
|
|
return type_homogenous_aggregate_small_enough(type, *elements);
|
|
}
|
|
|
|
AlignSize type_alloca_alignment(Type *type)
|
|
{
|
|
if (platform_target.abi == ABI_X64 || platform_target.abi == ABI_WIN64)
|
|
{
|
|
type = type_lowering(type);
|
|
if (type->type_kind == TYPE_ARRAY && type_size(type) >= 16) return 16;
|
|
}
|
|
return type_abi_alignment(type);
|
|
}
|
|
|