Added WASM ABI

This commit is contained in:
Christoffer Lerno
2020-11-28 20:48:47 +01:00
parent 08e4757ade
commit 49006217e8
11 changed files with 131 additions and 62 deletions

View File

@@ -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)

View File

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

View File

@@ -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();

View File

@@ -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

View File

@@ -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)

View File

@@ -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

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

View File

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

View File

@@ -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:

View File

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

View File

@@ -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;