mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Added WASM ABI
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
81
src/compiler/llvm_codegen_c_abi_wasm.c
Normal file
81
src/compiler/llvm_codegen_c_abi_wasm.c
Normal file
@@ -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<RecordType>();
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user