diff --git a/CMakeLists.txt b/CMakeLists.txt index 71abf5684..cdddc9c65 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_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) 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 bdd522fb6..98c5bceb1 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1459,7 +1459,7 @@ static inline bool type_is_pointer(Type *type); static inline bool type_is_promotable_integer(Type *type); static inline bool type_is_signed(Type *type); static inline bool type_is_structlike(Type *type); -static inline bool type_is_promotable_integer(Type *type); +static inline size_t type_min_alignment(size_t a, size_t b); bool type_is_subtype(Type *type, Type *possible_subtype); bool type_is_union_struct(Type *type); bool type_is_user_defined(Type *type); @@ -1635,7 +1635,7 @@ static inline bool type_is_signed(Type *type) { return type->type_kind >= TYPE_I static inline bool type_is_unsigned(Type *type) { return type->type_kind >= TYPE_U8 && type->type_kind <= TYPE_U64; } static inline bool type_ok(Type *type) { return !type || type->type_kind != TYPE_POISONED; } static inline bool type_info_ok(TypeInfo *type_info) { return !type_info || type_info->kind != TYPE_INFO_POISON; } - +bool type_is_scalar(Type *type); static inline bool type_kind_is_derived(TypeKind kind) { switch (kind) @@ -1700,6 +1700,15 @@ static inline bool type_is_promotable_integer(Type *type) return type_is_integer_kind(type) && type->builtin.bytesize < type_c_int->builtin.bytesize; } +/** + * Minimum alignment, values are either offsets or alignments. + * @return + */ +static inline size_t type_min_alignment(size_t a, size_t b) +{ + return (a | b) & (1 + ~(a | b)); +} + #define TRY_AST_OR(_ast_stmt, _res) ({ Ast* _ast = (_ast_stmt); if (!ast_ok(_ast)) return _res; _ast; }) #define TRY_EXPR_OR(_expr_stmt, _res) ({ Expr* _expr = (_expr_stmt); if (!expr_ok(_expr)) return _res; _expr; }) #define TRY_TYPE_OR(_type_stmt, _res) ({ TypeInfo* _type = (_type_stmt); if (!type_info_ok(_type)) return _res; _type; }) diff --git a/src/compiler/llvm_codegen_c_abi.c b/src/compiler/llvm_codegen_c_abi.c index e84062160..de613efbc 100644 --- a/src/compiler/llvm_codegen_c_abi.c +++ b/src/compiler/llvm_codegen_c_abi.c @@ -70,6 +70,7 @@ bool abi_arg_is_indirect(ABIArgInfo *info) case ABI_ARG_DIRECT_COERCE: case ABI_ARG_EXPAND: case ABI_ARG_DIRECT_PAIR: + case ABI_ARG_EXPAND_COERCE: return false; case ABI_ARG_INDIRECT: return true; @@ -96,7 +97,7 @@ ABIArgInfo *abi_arg_new_indirect_by_val(void) ABIArgInfo *abi_arg_new_indirect_not_by_val(void) { ABIArgInfo *info = abi_arg_new(ABI_ARG_INDIRECT); - info->indirect.by_val = true; + info->indirect.by_val = false; return info; } @@ -178,6 +179,30 @@ ABIArgInfo *abi_arg_new_direct(void) return abi_arg_new(ABI_ARG_DIRECT_COERCE); } +ABIArgInfo *abi_arg_new_expand_coerce(AbiType *target_type, unsigned offset) +{ + ABIArgInfo *arg = abi_arg_new(ABI_ARG_EXPAND_COERCE); + arg->coerce_expand.packed = offset > 0; + arg->coerce_expand.offset_lo = offset; + arg->coerce_expand.lo_index = offset > 0 ? 1 : 0; + arg->coerce_expand.lo = target_type; + return arg; +} + +ABIArgInfo *abi_arg_new_expand_coerce_pair(AbiType *first_element, unsigned initial_offset, AbiType *second_element, unsigned padding, bool is_packed) +{ + ABIArgInfo *arg = abi_arg_new(ABI_ARG_EXPAND_COERCE); + arg->coerce_expand.packed = is_packed; + arg->coerce_expand.offset_lo = initial_offset; + arg->coerce_expand.lo_index = initial_offset > 0 ? 1 : 0; + arg->coerce_expand.lo = first_element; + arg->coerce_expand.hi = second_element; + arg->coerce_expand.padding_hi = padding; + arg->coerce_expand.offset_hi = padding + initial_offset + abi_type_size(first_element); + arg->coerce_expand.hi_index = arg->coerce_expand.lo_index + (padding > 0 ? 1U : 0U); + return arg; +} + ABIArgInfo *abi_arg_new_direct_coerce(AbiType *target_type) { assert(target_type); diff --git a/src/compiler/llvm_codegen_c_abi_aarch64.c b/src/compiler/llvm_codegen_c_abi_aarch64.c index bf5e3ef92..1f67a02b8 100644 --- a/src/compiler/llvm_codegen_c_abi_aarch64.c +++ b/src/compiler/llvm_codegen_c_abi_aarch64.c @@ -9,6 +9,7 @@ ABIArgInfo *aarch64_illegal_vector(Type *type) // Need to look up SVE vectors. return false; } + ABIArgInfo *aarch64_coerce_illegal_vector(Type *type) { TODO diff --git a/src/compiler/llvm_codegen_c_abi_internal.h b/src/compiler/llvm_codegen_c_abi_internal.h index 0ac1001e0..240ef4894 100644 --- a/src/compiler/llvm_codegen_c_abi_internal.h +++ b/src/compiler/llvm_codegen_c_abi_internal.h @@ -30,6 +30,8 @@ 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); ABIArgInfo *abi_arg_new_direct_coerce(AbiType *target_type); +ABIArgInfo *abi_arg_new_expand_coerce(AbiType *target_type, unsigned offset); +ABIArgInfo *abi_arg_new_expand_coerce_pair(AbiType *first_element, unsigned initial_offset, AbiType *second_element, unsigned padding, bool is_packed); ABIArgInfo *abi_arg_new_expand_padded(Type *padding); ABIArgInfo *abi_arg_new_indirect_realigned(unsigned alignment); ABIArgInfo *abi_arg_new_indirect_by_val(void); @@ -47,7 +49,7 @@ 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(GenContext *context, FunctionSignature *signature); - +void c_abi_func_create_riscv(GenContext *context, 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 new file mode 100644 index 000000000..9576d5245 --- /dev/null +++ b/src/compiler/llvm_codegen_c_abi_riscv.c @@ -0,0 +1,333 @@ +// 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 *riscv_coerce_and_expand_fpcc_struct(AbiType *field1, unsigned field1_offset, AbiType *field2, unsigned field2_offset) +{ + if (!field2) + { + return abi_arg_new_expand_coerce(field1, field1_offset); + } + + unsigned field2_alignment = abi_type_abi_alignment(field2); + unsigned field1_size = abi_type_size(field1); + unsigned field2_offset_no_pad = aligned_offset(field1_size, field2_alignment); + + unsigned padding = 0; + + if (field2_offset > field2_offset_no_pad) + { + padding = field2_offset - field2_offset_no_pad; + } + else if (field2_offset != field2_alignment && field2_offset > field1_size) + { + padding = field2_offset - field1_size; + } + + bool is_packed = field2_offset % field2_alignment != 0; + + return abi_arg_new_expand_coerce_pair(field1, field1_offset, field2, padding, is_packed); +} + +static bool riscv_detect_fpcc_struct_internal(GenContext *c, Type *type, unsigned current_offset, AbiType **field1, unsigned *field1_offset, AbiType **field2, unsigned *field2_offset) +{ + bool is_int = type_is_integer(type); + bool is_float = type_is_float(type); + unsigned flen = build_target.riscv.flen; + unsigned size = type_size(type); + if (is_int || is_float) + { + if (is_int && size > build_target.riscv.xlen) return false; + // Can't be eligible if larger than the FP registers. Half precision isn't + // currently supported on RISC-V and the ABI hasn't been confirmed, so + // default to the integer ABI in that case. + if (is_float && (size > flen || size < 4)) return false; + // Can't be eligible if an integer type was already found (int+int pairs + // are not eligible). + if (is_int && *field1 && abi_type_is_integer(*field1)) return false; + if (!*field1) + { + *field1 = abi_type_new_plain(type); + *field1_offset = current_offset; + return true; + } + if (!*field2) + { + *field2 = abi_type_new_plain(type); + *field2_offset = current_offset; + return true; + } + return false; + } + + if (type->type_kind == TYPE_COMPLEX) + { + // Is the first field already occupied? + // Then fail because both needs to be available. + if (*field1) return false; + // If the element doesn't fit a register - bail. + Type *element_type = type->complex; + unsigned element_size = type_size(element_type); + if (element_size > flen) return false; + assert(!current_offset && "Expected zero offset"); + *field1 = abi_type_new_plain(element_type); + *field2 = abi_type_new_plain(element_type); + *field1_offset = current_offset; + *field2_offset = current_offset + element_size; + return true; + } + + if (type->type_kind == TYPE_ARRAY) + { + size_t array_len = type->array.len; + Type *element_type = type->array.base; + unsigned element_size = type_size(element_type); + for (size_t i = 0; i < array_len; i++) + { + if (!riscv_detect_fpcc_struct_internal(c, + element_type, + current_offset, + field1, + field1_offset, + field2, + field2_offset)) return false; + current_offset += element_size; + } + return true; + } + + if (type_is_structlike(type)) + { + if (type_is_empty_union_struct(type, true)) return true; + // Unions aren't eligible unless they're empty (which is caught above). + if (type->type_kind == TYPE_UNION) return false; + Decl **members = type->decl->strukt.members; + VECEACH(members, i) + { + Decl *member = members[i]; + if (!riscv_detect_fpcc_struct_internal(c, + member->type, + current_offset + member->offset, + field1, + field1_offset, + field2, + field2_offset)) return false; + + } + return *field1 != NULL; + } + return false; +} + +static bool riscv_detect_fpcc_struct(GenContext *c, Type *type, AbiType **field1, unsigned *field1_offset, AbiType **field2, unsigned *field2_offset, unsigned *gprs, unsigned *fprs) +{ + *field1 = NULL; + *field2 = NULL; + *gprs = 0; + *fprs = 0; + + bool is_candidate = riscv_detect_fpcc_struct_internal(c, type, 0, field1, field1_offset, field2, field2_offset); + + // Not really a candidate if we have a single int but no float. + if (*field1 && !*field2 && !abi_type_is_float(*field1)) return false; + if (!is_candidate) return false; + if (*field1) + { + if (abi_type_is_float(*field1)) + { + (*fprs)++; + } + else + { + (*gprs)++; + } + } + if (*field2) + { + if (abi_type_is_float(*field2)) + { + (*fprs)++; + } + else + { + (*gprs)++; + } + } + return true; +} + +static ABIArgInfo *riscv_classify_argument_type(GenContext *c, Type *type, bool is_fixed, unsigned *gprs, unsigned *fprs) +{ + + assert(type == type->canonical); + + 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); + + size_t size = type_size(type); + + // Pass floating point values via FPRs if possible. + if (is_fixed && type_is_float(type) && build_target.riscv.flen >= size && *fprs) + { + (*fprs)--; + return abi_arg_new_direct(); + } + + // Complex types for the hard float ABI must be passed direct rather than + // using CoerceAndExpand. + if (is_fixed && type->type_kind == TYPE_COMPLEX && *fprs >= 2) + { + Type *element_type = type->complex; + if (type_size(element_type) <= build_target.riscv.flen) + { + (*fprs) -= 2; + // TODO check that this will expand correctly. + return abi_arg_new_direct(); + } + } + + if (is_fixed && build_target.riscv.flen && (type->type_kind == TYPE_STRUCT || type->type_kind == TYPE_ERRTYPE)) + { + AbiType *field1 = NULL; + AbiType *field2 = NULL; + unsigned offset1 = 0; + unsigned offset2 = 0; + unsigned needed_gprs; + unsigned needed_fprs; + bool is_candidate = riscv_detect_fpcc_struct(c, + type, + &field1, + &offset1, + &field2, + &offset2, + &needed_gprs, + &needed_fprs); + if (is_candidate && needed_gprs <= *gprs && needed_fprs <= *fprs) + { + *gprs -= needed_gprs; + *fprs -= needed_fprs; + return riscv_coerce_and_expand_fpcc_struct(field1, offset1, field2, offset2); + } + } + + unsigned alignment = type_abi_alignment(type); + bool must_use_stack = false; + // Clang: Determine the number of GPRs needed to pass the current argument + // according to the ABI. 2*XLen-aligned varargs are passed in "aligned" + // register pairs, so may consume 3 registers. + unsigned needed_gprs = 1; + if (!is_fixed && alignment == 2 * xlen) + { + needed_gprs = 2 + (*gprs % 2U); + } + else if (size > xlen && size <= 2 * xlen) + { + needed_gprs = 2; + } + if (needed_gprs > *gprs) + { + must_use_stack = true; + needed_gprs = *gprs; + } + + *gprs -= needed_gprs; + + if (!type_is_abi_aggregate(type) && type->type_kind != TYPE_VECTOR) + { + // All integral types are promoted to XLen width, unless passed on the + // stack. + if (size < xlen && type_is_integer(type) && !must_use_stack) + { + return abi_arg_new_expand_padded(type_int_unsigned_by_bitsize(xlen * 8)); + } + if (size > 16 || (size > 8 && !build_target.int_128)) + { + return abi_arg_new_indirect_not_by_val(); + } + return abi_arg_new_direct(); + } + + // Aggregates which are <= 2*XLen will be passed in registers if possible, + // so coerce to integers. + if (size <= 2 * xlen) + { + // Use a single XLen int if possible, 2*XLen if 2*XLen alignment is + // required, and a 2-element XLen array if only XLen alignment is required. + if (size <= xlen) + { + return abi_arg_new_direct_coerce(abi_type_new_int_bits(xlen * 8)); + } + if (alignment == 2 * build_target.riscv.xlen) + { + return abi_arg_new_direct_coerce(abi_type_new_int_bits(xlen * 16)); + } + ABIArgInfo *info = abi_arg_new_direct_coerce(abi_type_new_int_bits(xlen)); + info->direct_coerce.elements = 2; + return info; + } + return abi_arg_new_indirect_not_by_val(); +} + +static ABIArgInfo *riscv_classify_return(GenContext *c, Type *return_type) +{ + if (return_type->type_kind == TYPE_VOID) return abi_arg_new(ABI_ARG_IGNORE); + + unsigned arg_gpr_left = 2; + unsigned arg_fpr_left = build_target.riscv.flen ? 2 : 0; + + // The rules for return and argument types are the same, so defer to + // classifyArgumentType. + return riscv_classify_argument_type(c, return_type, true, &arg_gpr_left, &arg_fpr_left); +} + +void c_abi_func_create_riscv(GenContext *context, FunctionSignature *signature) +{ + // Registers + unsigned gpr = 8; + unsigned fpr = 8; + + 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); + // TODO fixup of failable. + + // IsRetIndirect is true if classifyArgumentType indicated the value should + // be passed indirect, or if the type size is a scalar greater than 2*XLen + // and not a complex type with elements <= FLen. e.g. fp128 is passed direct + // in LLVM IR, relying on the backend lowering code to rewrite the argument + // list and pass indirectly on RV32. + bool is_ret_indirect = abi_arg_is_indirect(signature->failable ? signature->failable_abi_info : signature->ret_abi_info); + if (!is_ret_indirect && type_is_scalar(return_type) && type_size(return_type) > 2 * build_target.riscv.xlen) + { + if (return_type->type_kind == TYPE_COMPLEX && build_target.riscv.flen) + { + is_ret_indirect = type_size(return_type->complex) > build_target.riscv.flen; + } + else + { + // Normal scalar > 2 * XLen, e.g. f128 on RV32 + is_ret_indirect = true; + } + } + // Clang: We must track the number of GPRs used in order to conform to the RISC-V + // ABI, as integer scalars passed in registers should have signext/zeroext + // when promoted, but are anyext if passed on the stack. As GPR usage is + // different for variadic arguments, we must also track whether we are + // examining a vararg or not. + unsigned arg_gprs_left = is_ret_indirect ? gpr - 1 : gpr; + unsigned arg_fprs_left = build_target.riscv.flen ? fpr : 0; + + // unsigned fixed_arguments = vec_size(signature->params); todo + // TODO fix failable. + Decl **params = signature->params; + VECEACH(params, i) + { + bool is_fixed = true; + params[i]->var.abi_info = riscv_classify_argument_type(context, type_lowering(params[i]->type), is_fixed, &arg_gprs_left, &arg_fprs_left); + } +} diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index ce916cb02..7c647f23f 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -20,6 +20,7 @@ LLVMValueRef llvm_emit_is_no_error(GenContext *c, LLVMValueRef error) return LLVMBuildICmp(c->builder, LLVMIntEQ, domain, llvm_get_zero(c, type_usize), "noerr"); } + static inline LLVMValueRef gencontext_emit_add_int(GenContext *context, Type *type, bool use_mod, LLVMValueRef left, LLVMValueRef right) { if (use_mod) @@ -2110,8 +2111,31 @@ static void llvm_expand_type_to_args(GenContext *context, Type *param_type, LLVM } } +LLVMValueRef llvm_emit_struct_gep(GenContext *context, LLVMValueRef ptr, LLVMTypeRef struct_type, unsigned index, unsigned struct_alignment, unsigned offset, unsigned *alignment) +{ + *alignment = type_min_alignment(offset, struct_alignment); + LLVMValueRef addr = LLVMBuildStructGEP2(context->builder, struct_type, ptr, index, ""); + return addr; +} +void llvm_value_struct_gep(GenContext *c, BEValue *element, BEValue *struct_pointer, unsigned index) +{ + llvm_value_fold_failable(c, struct_pointer); + Decl *member = struct_pointer->type->decl->strukt.members[index]; + unsigned alignment; + LLVMValueRef ref = llvm_emit_struct_gep(c, + struct_pointer->value, + llvm_get_type(c, struct_pointer->type), + index, + struct_pointer->alignment, + member->offset, + &alignment); + llvm_value_set_address(element, ref, member->type); + element->alignment = alignment; +} + void gencontext_emit_call_expr(GenContext *context, BEValue *be_value, Expr *expr) { + printf("Optimize call return\n"); FunctionSignature *signature; LLVMTypeRef func_type; LLVMValueRef func; @@ -2162,6 +2186,7 @@ void gencontext_emit_call_expr(GenContext *context, BEValue *be_value, Expr *exp case ABI_ARG_DIRECT_PAIR: case ABI_ARG_IGNORE: case ABI_ARG_DIRECT_COERCE: + case ABI_ARG_EXPAND_COERCE: break; } unsigned param_index = 0; @@ -2198,7 +2223,6 @@ void gencontext_emit_call_expr(GenContext *context, BEValue *be_value, Expr *exp vec_add(values, llvm_value_rvalue_store(context, be_value)); break; } - if (!abi_info_should_flatten(info)) { vec_add(values, llvm_emit_coerce(context, coerce_type, be_value, param->type)); @@ -2237,35 +2261,42 @@ void gencontext_emit_call_expr(GenContext *context, BEValue *be_value, Expr *exp } case ABI_ARG_DIRECT_PAIR: { - printf("TODO: Optimize load\n"); - LLVMValueRef value = llvm_value_rvalue_store(context, be_value); + llvm_value_addr(context, be_value); + printf("Handle invalid alignment"); // Here we do the following transform: // struct -> { lo, hi } -> lo, hi LLVMTypeRef lo = llvm_abi_type(context, info->direct_pair.lo); LLVMTypeRef hi = llvm_abi_type(context, info->direct_pair.hi); LLVMTypeRef struct_type = llvm_get_coerce_type(context, info); - unsigned max_align = MAX(llvm_abi_alignment(struct_type), type_abi_alignment(param->type)); - // Create the alloca holding the struct. - LLVMValueRef temp = llvm_emit_alloca(context, struct_type, max_align, "temphold"); - // Bit cast to the original type type. - LLVMValueRef cast = LLVMBuildBitCast(context->builder, temp, llvm_get_ptr_type(context, param->type), "casttemp"); - // Store the value. - llvm_store_aligned(context, cast, value, max_align); + LLVMValueRef cast = LLVMBuildBitCast(context->builder, be_value->value, llvm_get_ptr_type(context, param->type), "casttemp"); // Get the lo value. - LLVMValueRef lo_ptr = LLVMBuildStructGEP2(context->builder, struct_type, temp, 0, "lo"); + LLVMValueRef lo_ptr = LLVMBuildStructGEP2(context->builder, struct_type, cast, 0, "lo"); vec_add(values, llvm_emit_load_aligned(context, lo, lo_ptr, llvm_abi_alignment(lo), "lo")); // Get the hi value. - LLVMValueRef hi_ptr = LLVMBuildStructGEP2(context->builder, struct_type, temp, 1, "hi"); + LLVMValueRef hi_ptr = LLVMBuildStructGEP2(context->builder, struct_type, cast, 1, "hi"); vec_add(values, llvm_emit_load_aligned(context, hi, hi_ptr, llvm_abi_alignment(hi), "hi")); break; } + case ABI_ARG_EXPAND_COERCE: + { + // Move this to an address (if needed) + llvm_value_addr(context, be_value); + LLVMTypeRef coerce_type = llvm_get_coerce_type(context, info); + LLVMValueRef temp = LLVMBuildBitCast(context->builder, be_value->value, LLVMPointerType(coerce_type, 0), "coerce"); + LLVMValueRef gep_first = LLVMBuildStructGEP2(context->builder, coerce_type, temp, info->coerce_expand.lo_index, "first"); + vec_add(values, LLVMBuildLoad2(context->builder, llvm_abi_type(context, info->coerce_expand.lo), gep_first, "")); + if (info->coerce_expand.hi) + { + LLVMValueRef gep_second = LLVMBuildStructGEP2(context->builder, coerce_type, temp, info->coerce_expand.hi_index, "second"); + vec_add(values, LLVMBuildLoad2(context->builder, llvm_abi_type(context, info->coerce_expand.hi), gep_second, "")); + } + break; + } case ABI_ARG_EXPAND: { - printf("TODO: Optimize load\n"); - LLVMValueRef value = llvm_value_rvalue_store(context, be_value); - LLVMValueRef expand = llvm_emit_alloca_aligned(context, param->type, "expand"); - llvm_store_aligned(context, expand, value, type_abi_alignment(param->type)); - llvm_expand_type_to_args(context, param->type, expand, &values); + // Move this to an address (if needed) + llvm_value_addr(context, be_value); + llvm_expand_type_to_args(context, param->type, be_value->value, &values); // Expand the padding here. if (info->expand.padding_type) { @@ -2305,11 +2336,51 @@ void gencontext_emit_call_expr(GenContext *context, BEValue *be_value, Expr *exp // TODO look at failable LLVMTypeRef lo = llvm_abi_type(context, ret_info->direct_pair.lo); LLVMTypeRef hi = llvm_abi_type(context, ret_info->direct_pair.hi); - LLVMTypeRef struct_type = gencontext_get_twostruct(context, lo, hi); + LLVMTypeRef struct_type = llvm_get_twostruct(context, lo, hi); call = llvm_emit_convert_value_from_coerced(context, struct_type, call, return_type); break; } + case ABI_ARG_EXPAND_COERCE: + { + LLVMValueRef ret = llvm_emit_alloca_aligned(context, return_type, ""); + LLVMTypeRef coerce_type = llvm_get_coerce_type(context, ret_info); + LLVMValueRef coerce = LLVMBuildBitCast(context->builder, ret, coerce_type, ""); + LLVMTypeRef lo_type = llvm_abi_type(context, ret_info->coerce_expand.lo); + + // Find the address to the low value + unsigned alignment; + LLVMValueRef lo = llvm_emit_struct_gep(context, coerce, coerce_type, ret_info->coerce_expand.lo_index, + type_abi_alignment(return_type), + ret_info->coerce_expand.offset_lo, &alignment); + + // If there is only a single element, we simply store the value. + if (!ret_info->coerce_expand.hi) + { + llvm_store_aligned(context, lo, call, alignment); + call = llvm_emit_load_aligned(context, llvm_get_type(context, return_type), ret, 0, ""); + break; + } + + LLVMTypeRef hi_type = llvm_abi_type(context, ret_info->coerce_expand.hi); + + LLVMValueRef lo_value = LLVMBuildExtractValue(context->builder, call, 0, ""); + LLVMValueRef hi_value = LLVMBuildExtractValue(context->builder, call, 1, ""); + + // Store the low value. + llvm_store_aligned(context, lo, lo_value, alignment); + + LLVMValueRef hi = llvm_emit_struct_gep(context, coerce, coerce_type, ret_info->coerce_expand.hi_index, + type_abi_alignment(return_type), + ret_info->coerce_expand.offset_hi, &alignment); + + // Store the high value. + llvm_store_aligned(context, lo, lo_value, alignment); + + // Now we can get the actual return value. + call = llvm_emit_load_aligned(context, llvm_get_type(context, return_type), ret, 0, ""); + break; + } case ABI_ARG_DIRECT_COERCE: { // TODO look at failable diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index f58c2d9e7..f81f9a445 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -135,13 +135,27 @@ static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, unsig llvm_emit_memcpy_to_decl(c, decl, pointer, info->indirect.realignment); return; } + case ABI_ARG_EXPAND_COERCE: + { + // Create the expand type: + LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info); + LLVMValueRef temp = LLVMBuildBitCast(c->builder, decl->backend_ref, LLVMPointerType(coerce_type, 0), "coerce"); + LLVMValueRef gep_first = LLVMBuildStructGEP2(c->builder, coerce_type, temp, info->coerce_expand.lo_index, "first"); + llvm_store_aligned(c, gep_first, llvm_get_next_param(c, index), 0); + if (info->coerce_expand.hi) + { + LLVMValueRef gep_second = LLVMBuildStructGEP2(c->builder, coerce_type, temp, info->coerce_expand.hi_index, "second"); + llvm_store_aligned(c, gep_second, llvm_get_next_param(c, index), 0); + } + break; + } case ABI_ARG_DIRECT_PAIR: { // Here we do the following transform: // lo, hi -> { lo, hi } -> struct LLVMTypeRef lo = llvm_abi_type(c, info->direct_pair.lo); LLVMTypeRef hi = llvm_abi_type(c, info->direct_pair.hi); - LLVMTypeRef struct_type = gencontext_get_twostruct(c, lo, hi); + LLVMTypeRef struct_type = llvm_get_twostruct(c, lo, hi); unsigned decl_alignment = decl_abi_alignment(decl); // Cast to { lo, hi } LLVMValueRef cast = LLVMBuildBitCast(c->builder, decl->backend_ref, LLVMPointerType(struct_type, 0), "pair"); @@ -264,6 +278,47 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl // Expands to multiple slots - // Not applicable to return values. UNREACHABLE + case ABI_ARG_EXPAND_COERCE: + { + // Pick the return as an address. + llvm_value_addr(c, return_value); + // Get the coerce type. + LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info); + // Create the new pointer + LLVMValueRef coerce = LLVMBuildBitCast(c->builder, return_value->value, coerce_type, ""); + // We might have only one value, in that case, build a GEP to that one. + LLVMValueRef lo_val; + unsigned alignment; + LLVMValueRef lo = llvm_emit_struct_gep(c, coerce, coerce_type, info->coerce_expand.lo_index, + return_value->alignment, + info->coerce_expand.offset_lo, &alignment); + LLVMTypeRef lo_type = llvm_abi_type(c, info->coerce_expand.hi); + lo_val = llvm_emit_load_aligned(c, lo_type, lo, alignment, ""); + + // We're done if there's a single element. + if (!info->coerce_expand.hi) + { + llvm_emit_return_value(c, lo_val); + return; + } + + // Let's make a first class aggregate + LLVMValueRef hi = llvm_emit_struct_gep(c, coerce, coerce_type, info->coerce_expand.hi_index, + return_value->alignment, + info->coerce_expand.offset_hi, &alignment); + LLVMTypeRef hi_type = llvm_abi_type(c, info->coerce_expand.hi); + LLVMValueRef hi_val = llvm_emit_load_aligned(c, hi_type, hi, alignment, ""); + + LLVMTypeRef unpadded_type = llvm_get_twostruct(c, lo_type, hi_type); + LLVMValueRef composite = LLVMGetUndef(unpadded_type); + + composite = LLVMBuildInsertValue(c->builder, composite, lo_val, 0, ""); + composite = LLVMBuildInsertValue(c->builder, composite, hi_val, 1, ""); + + // And return that unpadded result + llvm_emit_return_value(c, composite); + break; + } case ABI_ARG_DIRECT_PAIR: case ABI_ARG_DIRECT_COERCE: { @@ -427,6 +482,7 @@ static void llvm_emit_param_attributes(GenContext *context, LLVMValueRef functio case ABI_ARG_IGNORE: case ABI_ARG_DIRECT_COERCE: case ABI_ARG_DIRECT_PAIR: + case ABI_ARG_EXPAND_COERCE: break; case ABI_ARG_INDIRECT: if (info->indirect.realignment) diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index d3ed6640c..4d1ed27df 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -50,6 +50,7 @@ typedef enum ABI_ARG_IGNORE, ABI_ARG_DIRECT_PAIR, ABI_ARG_DIRECT_COERCE, + ABI_ARG_EXPAND_COERCE, ABI_ARG_INDIRECT, ABI_ARG_EXPAND, } ABIKind; @@ -94,6 +95,17 @@ typedef struct ABIArgInfo_ AbiType *hi; } direct_pair; struct + { + unsigned char offset_lo; + unsigned char padding_hi; + unsigned char lo_index; + unsigned char hi_index; + unsigned char offset_hi; + bool packed : 1; + AbiType *lo; + AbiType *hi; + } coerce_expand; + struct { AbiType *partial_type; }; @@ -210,6 +222,7 @@ void llvm_value_set(BEValue *value, LLVMValueRef llvm_value, Type *type); void llvm_value_set_address_align(BEValue *value, LLVMValueRef llvm_value, Type *type, unsigned alignment); void llvm_value_set_address(BEValue *value, LLVMValueRef llvm_value, Type *type); void llvm_value_fold_failable(GenContext *c, BEValue *value); +void llvm_value_struct_gep(GenContext *c, BEValue *element, BEValue *struct_pointer, unsigned index); LLVMValueRef llvm_value_rvalue_store(GenContext *c, BEValue *value); @@ -255,6 +268,7 @@ static inline LLVMValueRef llvm_emit_store(GenContext *context, Decl *decl, LLVM void llvm_emit_panic_on_true(GenContext *c, LLVMValueRef value, const char *panic_name); void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failable); void llvm_emit_return_implicit(GenContext *c); +LLVMValueRef llvm_emit_struct_gep(GenContext *context, LLVMValueRef ptr, LLVMTypeRef struct_type, unsigned index, unsigned struct_alignment, unsigned offset, unsigned *alignment); LLVMValueRef llvm_get_next_param(GenContext *context, unsigned *index); LLVMTypeRef llvm_get_coerce_type(GenContext *c, ABIArgInfo *arg_info); @@ -283,7 +297,7 @@ void llvm_store_self_aligned(GenContext *context, LLVMValueRef pointer, LLVMValu void llvm_store_aligned(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, unsigned alignment); void llvm_store_aligned_decl(GenContext *context, Decl *decl, LLVMValueRef value); -LLVMTypeRef gencontext_get_twostruct(GenContext *context, LLVMTypeRef lo, LLVMTypeRef hi); +LLVMTypeRef llvm_get_twostruct(GenContext *context, LLVMTypeRef lo, LLVMTypeRef hi); LLVMValueRef llvm_emit_coerce(GenContext *context, LLVMTypeRef coerced, BEValue *value, Type *original_type); static inline LLVMValueRef gencontext_emit_load(GenContext *c, Type *type, LLVMValueRef value) diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index 52c8f7117..a608fa92b 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -207,6 +207,13 @@ static inline void add_func_type_param(GenContext *context, Type *param_type, AB case ABI_ARG_INDIRECT: vec_add(*params, llvm_get_ptr_type(context, param_type)); break; + case ABI_ARG_EXPAND_COERCE: + vec_add(*params, llvm_abi_type(context, arg_info->coerce_expand.lo)); + if (arg_info->coerce_expand.hi) + { + vec_add(*params, llvm_abi_type(context, arg_info->coerce_expand.hi)); + } + break; case ABI_ARG_EXPAND: // Expanding a structs param_expand(context, params, param_type->canonical); @@ -267,6 +274,18 @@ LLVMTypeRef llvm_func_type(GenContext *context, Type *type) case ABI_ARG_INDIRECT: vec_add(params, llvm_get_ptr_type(context, real_return_type)); FALLTHROUGH; + case ABI_ARG_EXPAND_COERCE: + { + LLVMTypeRef lo = llvm_abi_type(context, ret_arg_info->direct_pair.lo); + if (!ret_arg_info->direct_pair.hi) + { + return_type = lo; + break; + } + LLVMTypeRef hi = llvm_abi_type(context, ret_arg_info->direct_pair.hi); + return_type = llvm_get_twostruct(context, lo, hi); + break; + } case ABI_ARG_IGNORE: return_type = llvm_get_type(context, type_void); break; @@ -274,7 +293,7 @@ LLVMTypeRef llvm_func_type(GenContext *context, Type *type) { LLVMTypeRef lo = llvm_abi_type(context, ret_arg_info->direct_pair.lo); LLVMTypeRef hi = llvm_abi_type(context, ret_arg_info->direct_pair.hi); - return_type = gencontext_get_twostruct(context, lo, hi); + return_type = llvm_get_twostruct(context, lo, hi); break; } case ABI_ARG_DIRECT_COERCE: @@ -369,7 +388,9 @@ LLVMTypeRef llvm_get_type(GenContext *c, Type *any_type) case TYPE_VECTOR: return any_type->backend_type = LLVMVectorType(llvm_get_type(c, any_type->vector.base), any_type->vector.len); case TYPE_COMPLEX: - return any_type->backend_type = gencontext_get_twostruct(c, llvm_get_type(c, any_type->complex), llvm_get_type(c, any_type->complex)); + return any_type->backend_type = llvm_get_twostruct(c, + llvm_get_type(c, any_type->complex), + llvm_get_type(c, any_type->complex)); } UNREACHABLE; } @@ -377,6 +398,27 @@ LLVMTypeRef llvm_get_type(GenContext *c, Type *any_type) LLVMTypeRef llvm_get_coerce_type(GenContext *c, ABIArgInfo *arg_info) { + if (arg_info->kind == ABI_ARG_EXPAND_COERCE) + { + unsigned element_index = 0; + LLVMTypeRef elements[4]; + // Add padding if needed. + if (arg_info->coerce_expand.offset_lo) + { + elements[element_index++] = LLVMArrayType(llvm_get_type(c, type_byte), arg_info->coerce_expand.offset_lo); + } + elements[element_index++] = llvm_abi_type(c, arg_info->coerce_expand.lo); + if (arg_info->coerce_expand.padding_hi) + { + elements[element_index++] = LLVMArrayType(llvm_get_type(c, type_byte), arg_info->coerce_expand.padding_hi); + } + if (arg_info->coerce_expand.hi) + { + elements[element_index++] = llvm_abi_type(c, arg_info->coerce_expand.hi); + } + return LLVMStructType(elements, element_index, arg_info->coerce_expand.packed); + } + if (arg_info->kind == ABI_ARG_DIRECT_COERCE) { if (!arg_info->direct_coerce.type) return NULL; @@ -393,12 +435,12 @@ LLVMTypeRef llvm_get_coerce_type(GenContext *c, ABIArgInfo *arg_info) { LLVMTypeRef lo = llvm_abi_type(c, arg_info->direct_pair.lo); LLVMTypeRef hi = llvm_abi_type(c, arg_info->direct_pair.hi); - return gencontext_get_twostruct(c, lo, hi); + return llvm_get_twostruct(c, lo, hi); } UNREACHABLE } -LLVMTypeRef gencontext_get_twostruct(GenContext *context, LLVMTypeRef lo, LLVMTypeRef hi) +LLVMTypeRef llvm_get_twostruct(GenContext *context, LLVMTypeRef lo, LLVMTypeRef hi) { LLVMTypeRef types[2] = { lo, hi }; return LLVMStructTypeInContext(context->context, types, 2, false); diff --git a/src/compiler/target.c b/src/compiler/target.c index 6e2f7aba2..11cb0e691 100644 --- a/src/compiler/target.c +++ b/src/compiler/target.c @@ -591,7 +591,7 @@ void target_setup(void) case ARCH_TYPE_RISCV64: case ARCH_TYPE_RISCV32: build_target.riscv.xlen = 0; // pointer width - build_target.riscv.abiflen = 32; // ends with f / d (64) + build_target.riscv.flen = 32; // ends with f / d (64) build_target.abi = ABI_RISCV; TODO case ARCH_TYPE_X86: diff --git a/src/compiler/target.h b/src/compiler/target.h index 3694e6990..826a187b3 100644 --- a/src/compiler/target.h +++ b/src/compiler/target.h @@ -281,7 +281,7 @@ typedef struct struct { unsigned xlen; - unsigned abiflen; + unsigned flen; } riscv; struct { diff --git a/src/compiler/types.c b/src/compiler/types.c index 199bedf6b..454f55395 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -913,6 +913,42 @@ type_create(#_name, &_shortname, _type, _bits, target->align_ ## _align, target- type_create("error", &t_error, TYPE_ERR_UNION, target->width_pointer * 2, target->align_pointer, target->align_pref_pointer); } +bool type_is_scalar(Type *type) +{ + RETRY: + switch (type->type_kind) + { + case TYPE_POISONED: + case TYPE_TYPEINFO: + case TYPE_MEMBER: + UNREACHABLE + case TYPE_VOID: + case TYPE_FUNC: + case TYPE_STRUCT: + case TYPE_UNION: + case TYPE_ARRAY: + case TYPE_SUBARRAY: + case TYPE_VECTOR: + return false; + case TYPE_BOOL: + case ALL_INTS: + case ALL_FLOATS: + case TYPE_TYPEID: + case TYPE_POINTER: + case TYPE_ENUM: + case TYPE_ERRTYPE: + case TYPE_ERR_UNION: + case TYPE_VARARRAY: + case TYPE_COMPLEX: + return true; + case TYPE_TYPEDEF: + type = type->canonical; + goto RETRY; + case TYPE_STRING: + TODO + } + UNREACHABLE +} /** * Check if a type is contained in another type. *