Initial work on RISCV - completely untested.

This commit is contained in:
Christoffer Lerno
2020-11-28 19:02:55 +01:00
parent 0889a93c4e
commit 84eea12efe
13 changed files with 620 additions and 31 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -281,7 +281,7 @@ typedef struct
struct
{
unsigned xlen;
unsigned abiflen;
unsigned flen;
} riscv;
struct
{

View File

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