Files
c3c/src/compiler/codegen_general.c
2025-02-26 02:51:42 +01:00

334 lines
8.7 KiB
C

#include "codegen_internal.h"
const char *benchmark_fns_var_name = "__$C3_BENCHMARK_FN_LIST";
const char *benchmark_names_var_name = "__$C3_BENCHMARK_NAMES_LIST";
const char *test_fns_var_name = "__$C3_TEST_FN_LIST";
const char *test_names_var_name = "__$C3_TEST_NAMES_LIST";
/**
* Based on isSingleElementStruct in Clang
*/
Type *type_abi_find_single_struct_element(Type *type)
{
if (!type_is_union_or_strukt(type)) return NULL;
// Elements with a variable array? If so no.
if (type->decl->has_variable_array) return NULL;
Type *found = NULL;
FOREACH(Decl *, member, type->decl->strukt.members)
{
Type *field_type = type_lowering(member->type);
// 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_union_or_strukt(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 (compiler.platform.abi)
{
case ABI_PPC64_SVR4:
switch (type->type_kind)
{
case TYPE_F128:
if (!compiler.platform.float128) return false;
FALLTHROUGH;
case TYPE_F32:
case TYPE_F64:
return !compiler.platform.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:
case ABI_XTENSA:
return false;
}
UNREACHABLE
}
bool type_homogenous_aggregate_small_enough(Type *type, unsigned members)
{
switch (compiler.platform.abi)
{
case ABI_PPC64_SVR4:
if (type->type_kind == TYPE_F128 && compiler.platform.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:
case ABI_XTENSA:
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)
{
ASSERT(base && type && elements);
ASSERT(type_lowering(type) == type);
*elements = 0;
switch (type->type_kind)
{
case LOWERED_TYPES:
UNREACHABLE;
case TYPE_VOID:
case TYPE_FUNC_RAW:
case TYPE_SLICE:
return false;
case TYPE_ANY:
*base = type_iptr->canonical;
*elements = 2;
return true;
case TYPE_STRUCT:
case TYPE_UNION:
if (type->decl->has_variable_array) return false;
*elements = 0;
{
FOREACH(Decl *, member, type->decl->strukt.members)
{
unsigned member_mult = 1;
// Flatten the type.
Type *member_type = type_lowering(member->type);
// Go down deep into a nester array.
while (member_type->type_kind == TYPE_ARRAY)
{
ASSERT(member_type->array.len && "Zero length arrays not allowed");
member_mult *= member_type->array.len;
member_type = member_type->array.base;
}
unsigned member_members = 0;
// Check recursively if the field member is homogenous
if (!type_is_homogenous_aggregate(type_lowering(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:
// Same with scaled vectors
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_lowering(type->array.base), base, elements)) return false;
*elements *= type->array.len;
goto TYPECHECK;
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:
case TYPE_FUNC_PTR:
// 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->array.base);
*base = type_get_vector(type->array.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)
{
AlignSize align = type_abi_alignment(type);
if (align < 16 && (compiler.platform.abi == ABI_X64 || compiler.platform.abi == ABI_WIN64))
{
type = type_flatten(type);
if (type->type_kind == TYPE_ARRAY && type_size(type) >= 16) return 16;
}
return align;
}
bool codegen_single_obj_output()
{
if (!compiler.build.output_name) return false;
if (compiler.build.type != TARGET_TYPE_OBJECT_FILES) return false;
return compiler.build.single_module == SINGLE_MODULE_ON;
}
void codegen_setup_object_names(Module *module, const char **ir_filename, const char **asm_filename, const char **object_filename)
{
const char *result = module_create_object_file_name(module);
assert(compiler.build.object_file_dir);
if (codegen_single_obj_output())
{
const char *res = compiler.build.output_name;
if (!res) res = compiler.build.name;
if (!res) res = result;
const char *ext = get_object_extension();
if (!str_has_suffix(res, ext))
{
res = str_printf("%s%s", result, ext);
}
compiler.obj_output = *object_filename = file_append_path(compiler.build.output_dir ? compiler.build.output_dir : ".", res);
char *dir_path = NULL;
char *filename = NULL;
file_get_dir_and_filename_from_full(compiler.obj_output, &filename, &dir_path);
if (dir_path && strlen(dir_path) && !file_is_dir(dir_path))
{
error_exit("Can't output '%s', the directory '%s' could not be found.", compiler.obj_output, dir_path);
}
}
else
{
*object_filename = file_append_path(compiler.build.object_file_dir, str_printf("%s%s", result, get_object_extension()));
}
*ir_filename = str_printf(compiler.build.backend == BACKEND_LLVM ? "%s.ll" : "%s.ir", result);
if (compiler.build.ir_file_dir) *ir_filename = file_append_path(compiler.build.ir_file_dir, *ir_filename);
if (compiler.build.emit_asm)
{
*asm_filename = str_printf("%s.s", result);
if (compiler.build.asm_file_dir) *asm_filename = file_append_path(compiler.build.asm_file_dir, *asm_filename);
}
}