mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 20:11:17 +00:00
334 lines
8.7 KiB
C
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);
|
|
}
|
|
}
|