Files
c3c/src/compiler/codegen_general.c
2021-12-14 19:00:33 +01:00

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);
}