diff --git a/CMakeLists.txt b/CMakeLists.txt index cdddc9c65..d8dd5226b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,7 +106,7 @@ add_executable(c3c src/utils/vmem.c src/utils/vmem.h src/utils/whereami.c - src/compiler/llvm_codegen_c_abi_x86.c src/compiler/llvm_codegen_c_abi_internal.h src/compiler/llvm_codegen_c_abi_x64.c src/compiler/llvm_codegen_c_abi_win64.c src/compiler/llvm_codegen_c_abi_aarch64.c src/compiler/headers.c src/compiler/llvm_codegen_c_abi_riscv.c) + src/compiler/llvm_codegen_c_abi_x86.c src/compiler/llvm_codegen_c_abi_internal.h src/compiler/llvm_codegen_c_abi_x64.c src/compiler/llvm_codegen_c_abi_win64.c src/compiler/llvm_codegen_c_abi_aarch64.c src/compiler/headers.c src/compiler/llvm_codegen_c_abi_riscv.c src/compiler/llvm_codegen_c_abi_wasm.c) target_compile_options(c3c PRIVATE -Wimplicit-int -Werror -Wall -Wno-unknown-pragmas -Wextra -Wno-unused-function -Wno-unused-variable -Wno-unused-parameter) diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 98c5bceb1..95002fcfc 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1431,7 +1431,7 @@ static inline bool type_convert_will_trunc(Type *destination, Type *source); Type *type_find_common_ancestor(Type *left, Type *right); Type *type_find_largest_union_element(Type *type); Type *type_find_max_type(Type *type, Type *other); -Type *type_find_single_struct_element(Type *type); +Type *type_abi_find_single_struct_element(Type *type); const char *type_generate_qname(Type *type); Type *type_get_array(Type *arr_type, uint64_t len); Type *type_get_indexed_type(Type *type); diff --git a/src/compiler/llvm_codegen_c_abi.c b/src/compiler/llvm_codegen_c_abi.c index 2565e6e88..c7ce29461 100644 --- a/src/compiler/llvm_codegen_c_abi.c +++ b/src/compiler/llvm_codegen_c_abi.c @@ -12,6 +12,12 @@ ABIArgInfo *abi_arg_new(ABIKind kind) return info; } +ABIArgInfo *abi_arg_ignore(void) +{ + static ABIArgInfo info = { .kind = ABI_ARG_IGNORE }; + return &info; +} + AbiType *abi_type_new_plain(Type *type) { AbiType *abi_type = CALLOCS(AbiType); @@ -219,37 +225,6 @@ ABIArgInfo *abi_arg_new_expand_padded(Type *padding) return info; } -ABIArgInfo *classify_return_type_default(Type *type) -{ - if (type == type_void) - { - return abi_arg_new(ABI_ARG_IGNORE); - } - - // Struct-likes are returned by sret - if (type_is_abi_aggregate(type)) - { - return abi_arg_new(ABI_ARG_INDIRECT); - } - - // Otherwise do we have a type that needs promotion? - if (type_is_promotable_integer(type_lowering(type))) - { - ABIArgInfo *arg_info = abi_arg_new(ABI_ARG_DIRECT_COERCE); - if (type_is_signed(type)) - { - arg_info->attributes.signext = true; - } - else - { - arg_info->attributes.zeroext = true; - } - return arg_info; - } - - // No, then do a direct pass. - return abi_arg_new_direct(); -} void c_abi_func_create(GenContext *context, FunctionSignature *signature) { @@ -270,31 +245,33 @@ void c_abi_func_create(GenContext *context, FunctionSignature *signature) case ABI_RISCV: c_abi_func_create_riscv(context, signature); break; + case ABI_WASM: + c_abi_func_create_wasm(signature); + break; default: FATAL_ERROR("Unsupported ABI"); } } + +ABIArgInfo *c_abi_classify_return_type_default(Type *type) +{ + if (type->type_kind == TYPE_VOID) return abi_arg_ignore(); + return c_abi_classify_return_type_default(type); +} + ABIArgInfo *c_abi_classify_argument_type_default(Type *type) { + // Perform general lowering. type = type_lowering(type); // Struct-likes are returned by sret - if (type_is_abi_aggregate(type)) - { - return abi_arg_new(ABI_ARG_INDIRECT); - } + if (type_is_abi_aggregate(type)) return abi_arg_new_indirect_by_val(); - if (type_is_int128(type) && !build_target.int_128) - { - return abi_arg_new_indirect_by_val(); - } + if (type_is_int128(type) && !build_target.int_128) return abi_arg_new_indirect_by_val(); // Otherwise do we have a type that needs promotion? - if (type_is_promotable_integer(type)) - { - return abi_arg_new_direct_int_ext(type); - } + if (type_is_promotable_integer(type)) return abi_arg_new_direct_int_ext(type); // No, then do a direct pass. return abi_arg_new_direct(); diff --git a/src/compiler/llvm_codegen_c_abi_aarch64.c b/src/compiler/llvm_codegen_c_abi_aarch64.c index 9498d02ad..cf2d560af 100644 --- a/src/compiler/llvm_codegen_c_abi_aarch64.c +++ b/src/compiler/llvm_codegen_c_abi_aarch64.c @@ -19,7 +19,7 @@ ABIArgInfo *aarch64_classify_argument_type(Type *type) { type = type_lowering(type); - if (type->type_kind == TYPE_VOID) return abi_arg_new(ABI_ARG_IGNORE); + if (type->type_kind == TYPE_VOID) return abi_arg_ignore(); if (type->type_kind == TYPE_VECTOR && aarch64_illegal_vector(type)) { @@ -40,7 +40,7 @@ ABIArgInfo *aarch64_classify_argument_type(Type *type) } // Is empty - if (!size) return abi_arg_new(ABI_ARG_IGNORE); + if (!size) return abi_arg_ignore(); // Homogeneous Floating-point Aggregates (HFAs) need to be expanded. Type *base = NULL; @@ -83,7 +83,7 @@ ABIArgInfo *aarch64_classify_return_type(Type *type, bool variadic) { type = type_lowering(type); - if (type->type_kind == TYPE_VOID) return abi_arg_new(ABI_ARG_IGNORE); + if (type->type_kind == TYPE_VOID) return abi_arg_ignore(); if (type->type_kind == TYPE_VECTOR && aarch64_illegal_vector(type)) { @@ -110,14 +110,14 @@ ABIArgInfo *aarch64_classify_return_type(Type *type, bool variadic) // Abi aggregate: // Is empty - if (!size) return abi_arg_new(ABI_ARG_IGNORE); + if (!size) return abi_arg_ignore(); Type *base = NULL; unsigned members = 0; if (type_is_homogenous_aggregate(type, &base, &members) && !(build_target.arch == ARCH_TYPE_AARCH64_32 && variadic)) { - return abi_arg_new(ABI_ARG_DIRECT_COERCE); + return abi_arg_ignore(); } // Aggregates <= in registers diff --git a/src/compiler/llvm_codegen_c_abi_internal.h b/src/compiler/llvm_codegen_c_abi_internal.h index f10349570..60394ec4a 100644 --- a/src/compiler/llvm_codegen_c_abi_internal.h +++ b/src/compiler/llvm_codegen_c_abi_internal.h @@ -26,6 +26,7 @@ static inline ABIArgInfo *abi_arg_by_reg_attr(ABIArgInfo *info); size_t abi_arg_expanded_size(ABIArgInfo *type_info, Type *type); bool abi_arg_is_indirect(ABIArgInfo *info); ABIArgInfo *abi_arg_new(ABIKind kind); +ABIArgInfo *abi_arg_ignore(void); ABIArgInfo *abi_arg_new_direct_pair(AbiType *low_type, AbiType *high_type); ABIArgInfo *abi_arg_new_direct(void); ABIArgInfo *abi_arg_new_direct_int_ext(Type *type_to_extend); @@ -44,12 +45,14 @@ AbiType *abi_type_new_plain(Type *type); AbiType *abi_type_new_int_bits(unsigned bits); size_t abi_type_size(AbiType *type); +ABIArgInfo *c_abi_classify_return_type_default(Type *type); ABIArgInfo *c_abi_classify_argument_type_default(Type *type); void c_abi_func_create_win64(GenContext *context, FunctionSignature *signature); void c_abi_func_create_x86(GenContext *context, FunctionSignature *signature); void c_abi_func_create_x64(GenContext *context, FunctionSignature *signature); void c_abi_func_create_aarch64(FunctionSignature *signature); void c_abi_func_create_riscv(GenContext *context, FunctionSignature *signature); +void c_abi_func_create_wasm(FunctionSignature *signature); // Implementation static inline ABIArgInfo *abi_arg_by_reg_attr(ABIArgInfo *info) diff --git a/src/compiler/llvm_codegen_c_abi_riscv.c b/src/compiler/llvm_codegen_c_abi_riscv.c index e0775d44d..c6b49ec16 100644 --- a/src/compiler/llvm_codegen_c_abi_riscv.c +++ b/src/compiler/llvm_codegen_c_abi_riscv.c @@ -167,7 +167,7 @@ static ABIArgInfo *riscv_classify_argument_type(GenContext *c, Type *type, bool unsigned xlen = build_target.riscv.xlen; // Ignore empty structs/unions. - if (type_is_empty_union_struct(type, true)) return abi_arg_new(ABI_ARG_IGNORE); + if (type_is_empty_union_struct(type, true)) return abi_arg_ignore(); size_t size = type_size(type); @@ -275,7 +275,7 @@ static ABIArgInfo *riscv_classify_argument_type(GenContext *c, Type *type, bool static ABIArgInfo *riscv_classify_return(GenContext *c, Type *return_type) { - if (return_type->type_kind == TYPE_VOID) return abi_arg_new(ABI_ARG_IGNORE); + if (return_type->type_kind == TYPE_VOID) return abi_arg_ignore(); unsigned arg_gpr_left = 2; unsigned arg_fpr_left = build_target.riscv.flen ? 2 : 0; @@ -294,6 +294,14 @@ void c_abi_func_create_riscv(GenContext *context, FunctionSignature *signature) Type *return_type = signature->failable ? type_error : signature->rtype->type; return_type = type_lowering(return_type); ABIArgInfo *return_abi = riscv_classify_return(context, return_type); + if (signature->failable) + { + signature->failable_abi_info = return_abi; + } + else + { + signature->ret_abi_info = return_abi; + } // IsRetIndirect is true if classifyArgumentType indicated the value should // be passed indirect, or if the type size is a scalar greater than 2*XLen diff --git a/src/compiler/llvm_codegen_c_abi_wasm.c b/src/compiler/llvm_codegen_c_abi_wasm.c new file mode 100644 index 000000000..3c27a2f43 --- /dev/null +++ b/src/compiler/llvm_codegen_c_abi_wasm.c @@ -0,0 +1,81 @@ +// Copyright (c) 2020 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a LGPLv3.0 +// a copy of which can be found in the LICENSE file. + +#include "llvm_codegen_c_abi_internal.h" + +static ABIArgInfo *wasm_classify_argument_type(Type *type) +{ + type = type_lowering(type); + if (type_is_abi_aggregate(type)) + { + // Ignore empty structs/unions. + if (type_is_empty_union_struct(type, true)) return abi_arg_ignore(); + // Clang: Lower single-element structs to just pass a regular value. TODO: We + // could do reasonable-size multiple-element structs too, using getExpand(), + // though watch out for things like bitfields. + Type *single_type = type_abi_find_single_struct_element(type); + if (single_type) return abi_arg_new_direct_coerce(abi_type_new_plain(single_type)); + + // For the experimental multivalue ABI, fully expand all other aggregates + /*if (Kind == ABIKind::ExperimentalMV) { + const RecordType *RT = Ty->getAs(); + assert(RT); + bool HasBitField = false; + for (auto *Field : RT->getDecl()->fields()) { + if (Field->isBitField()) { + HasBitField = true; + break; + } + } + if (!HasBitField) + return ABIArgInfo::getExpand(); + }*/ + } + + // Otherwise just do the default thing. + return c_abi_classify_argument_type_default(type); +} + +static ABIArgInfo *wasm_classify_return(Type *type) +{ + if (type_is_abi_aggregate(type)) + { + // Ignore empty + if (type_is_empty_union_struct(type, true)) return abi_arg_ignore(); + + Type *single_type = type_abi_find_single_struct_element(type); + if (single_type) return abi_arg_new_direct_coerce(abi_type_new_plain(single_type)); + /* + * // For the experimental multivalue ABI, return all other aggregates + if (Kind == ABIKind::ExperimentalMV) + return ABIArgInfo::getDirect(); + */ + } + // Use default classification + return c_abi_classify_return_type_default(type); +} + +void c_abi_func_create_wasm(FunctionSignature *signature) +{ + if (signature->failable) + { + signature->failable_abi_info = wasm_classify_return(type_lowering(type_error)); + } + else + { + signature->ret_abi_info = wasm_classify_return(type_lowering(signature->rtype->type)); + } + + // If we have a failable, then the return type is a parameter. + if (signature->failable && signature->rtype->type->type_kind != TYPE_VOID) + { + signature->ret_abi_info = wasm_classify_argument_type(type_get_ptr(type_lowering(signature->rtype->type))); + } + + Decl **params = signature->params; + VECEACH(params, i) + { + params[i]->var.abi_info = wasm_classify_argument_type(type_lowering(params[i]->type)); + } +} \ No newline at end of file diff --git a/src/compiler/llvm_codegen_c_abi_win64.c b/src/compiler/llvm_codegen_c_abi_win64.c index 3559154bc..c029ccb6c 100644 --- a/src/compiler/llvm_codegen_c_abi_win64.c +++ b/src/compiler/llvm_codegen_c_abi_win64.c @@ -6,7 +6,7 @@ ABIArgInfo *win64_classify(GenContext *context, Type *type, bool is_return, bool is_vector, bool is_reg) { - if (type->type_kind == TYPE_VOID) return abi_arg_new(ABI_ARG_IGNORE); + if (type->type_kind == TYPE_VOID) return abi_arg_ignore(); // Lower enums etc. type = type_lowering(type); diff --git a/src/compiler/llvm_codegen_c_abi_x64.c b/src/compiler/llvm_codegen_c_abi_x64.c index 35823b01b..bfdad19e7 100644 --- a/src/compiler/llvm_codegen_c_abi_x64.c +++ b/src/compiler/llvm_codegen_c_abi_x64.c @@ -657,7 +657,7 @@ static AbiType *x64_get_byte_vector_type(Type *type) { // Wrapper structs/arrays that only contain vectors are passed just like // vectors; strip them off if present. - Type *inner_type = type_find_single_struct_element(type); + Type *inner_type = type_abi_find_single_struct_element(type); if (inner_type) type = inner_type; type = type_lowering(type); @@ -710,7 +710,7 @@ ABIArgInfo *x64_classify_return(Type *return_type) case CLASS_NO_CLASS: if (hi_class == CLASS_NO_CLASS) { - return abi_arg_new(ABI_ARG_IGNORE); + return abi_arg_ignore(); } // If low part is padding, keep type null assert(hi_class == CLASS_SSE || hi_class == CLASS_INTEGER); @@ -796,7 +796,7 @@ static ABIArgInfo *x64_classify_argument_type(Type *type, unsigned free_int_regs case CLASS_NO_CLASS: // Only C++ would leave 8 bytes of padding, so we can ignore that case. assert(hi_class == CLASS_NO_CLASS); - return abi_arg_new(ABI_ARG_IGNORE); + return abi_arg_ignore(); case CLASS_SSEUP: UNREACHABLE case CLASS_MEMORY: diff --git a/src/compiler/llvm_codegen_c_abi_x86.c b/src/compiler/llvm_codegen_c_abi_x86.c index 7f5d99707..500f76464 100644 --- a/src/compiler/llvm_codegen_c_abi_x86.c +++ b/src/compiler/llvm_codegen_c_abi_x86.c @@ -152,7 +152,7 @@ ABIArgInfo *x86_classify_return(GenContext *context, Type *type) { if (type == type_void) { - return abi_arg_new(ABI_ARG_IGNORE); + return abi_arg_ignore(); } type = type_lowering(type); @@ -199,7 +199,7 @@ ABIArgInfo *x86_classify_return(GenContext *context, Type *type) // Ignore empty struct/unions if (type_is_empty_union_struct(type, true)) { - return abi_arg_new(ABI_ARG_IGNORE); + return abi_arg_ignore(); } // Check if we can return it in a register. @@ -207,7 +207,7 @@ ABIArgInfo *x86_classify_return(GenContext *context, Type *type) { size_t size = type_size(type); // Special case is floats and pointers in single element structs (except for MSVC) - Type *single_element = type_find_single_struct_element(type); + Type *single_element = type_abi_find_single_struct_element(type); if (single_element) { if ((type_is_float(single_element) && !build_target.x86.is_win32_float_struct_abi)) @@ -481,7 +481,7 @@ static inline ABIArgInfo *x86_classify_aggregate(GenContext *context, Type *type // Ignore empty unions / structs on non-win. if (!build_target.x86.is_win32_float_struct_abi && type_is_empty_union_struct(type, true)) { - return abi_arg_new(ABI_ARG_IGNORE); + return abi_arg_ignore(); } unsigned size = type_size(type); diff --git a/src/compiler/types.c b/src/compiler/types.c index 454f55395..4a01acea8 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -258,7 +258,7 @@ bool type_is_int128(Type *type) return kind == TYPE_U128 || kind == TYPE_I128; } -Type *type_find_single_struct_element(Type *type) +Type *type_abi_find_single_struct_element(Type *type) { if (!type_is_union_struct(type)) return NULL; @@ -282,7 +282,7 @@ Type *type_find_single_struct_element(Type *type) if (type_is_union_struct(field_type)) { - field_type = type_find_single_struct_element(field_type); + field_type = type_abi_find_single_struct_element(field_type); if (!field_type) return NULL; } found = field_type;