mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 20:11:17 +00:00
2966 lines
96 KiB
C
2966 lines
96 KiB
C
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
|
// a copy of which can be found in the LICENSE file.
|
|
|
|
#include "llvm_codegen_internal.h"
|
|
#include "compiler_internal.h"
|
|
#include "bigint.h"
|
|
|
|
static void gencontext_emit_unary_expr(GenContext *context, BEValue *value, Expr *expr);
|
|
static inline void llvm_emit_post_inc_dec(GenContext *c, BEValue *value, Expr *expr, int diff, bool use_mod);
|
|
static inline void llvm_emit_pre_inc_dec(GenContext *c, BEValue *value, Expr *expr, int diff, bool use_mod);
|
|
static inline void llvm_emit_inc_dec_change(GenContext *c, bool use_mod, BEValue *addr, BEValue *after, BEValue *before, Expr *expr, int diff);
|
|
static void llvm_emit_post_unary_expr(GenContext *context, BEValue *be_value, Expr *expr);
|
|
static inline LLVMValueRef llvm_emit_subscript_addr_with_base_new(GenContext *c, BEValue *parent, BEValue *index);
|
|
static void llvm_emit_initialize_designated(GenContext *c, BEValue *ref, uint64_t offset, DesignatorElement** current, DesignatorElement **last, Expr *expr, BEValue *emitted_value);
|
|
|
|
LLVMValueRef llvm_emit_is_no_error(GenContext *c, LLVMValueRef error)
|
|
{
|
|
LLVMValueRef domain = LLVMBuildExtractValue(c->builder, error, 0, "");
|
|
return LLVMBuildICmp(c->builder, LLVMIntEQ, domain, llvm_get_zero(c, type_usize), "noerr");
|
|
}
|
|
|
|
LLVMTypeRef llvm_const_padding_type(GenContext *c, ByteSize size)
|
|
{
|
|
assert(size > 0);
|
|
if (size == 1) return llvm_get_type(c, type_char);
|
|
return LLVMArrayType(llvm_get_type(c, type_char), size);
|
|
}
|
|
|
|
LLVMValueRef llvm_emit_const_padding(GenContext *c, ByteSize size)
|
|
{
|
|
return LLVMGetUndef(llvm_const_padding_type(c, size));
|
|
}
|
|
|
|
static inline LLVMValueRef llvm_emit_add_int(GenContext *context, Type *type, LLVMValueRef left, LLVMValueRef right)
|
|
{
|
|
if (build_options.trap_wrapping)
|
|
{
|
|
LLVMTypeRef type_to_use = llvm_get_type(context, type->canonical);
|
|
LLVMValueRef args[2] = { left, right };
|
|
assert(type->canonical == type);
|
|
LLVMValueRef add_res;
|
|
if (type_is_unsigned(type))
|
|
{
|
|
add_res = llvm_emit_call_intrinsic(context, intrinsic_id_uadd_overflow, &type_to_use, 1, args, 2);
|
|
}
|
|
else
|
|
{
|
|
add_res = llvm_emit_call_intrinsic(context, intrinsic_id_sadd_overflow, &type_to_use, 1, args, 2);
|
|
}
|
|
LLVMValueRef result = LLVMBuildExtractValue(context->builder, add_res, 0, "");
|
|
LLVMValueRef ok = LLVMBuildExtractValue(context->builder, add_res, 1, "");
|
|
llvm_emit_panic_on_true(context, ok, "Addition overflow");
|
|
return result;
|
|
}
|
|
|
|
return LLVMBuildAdd(context->builder, left, right, "add");
|
|
}
|
|
|
|
LLVMValueRef llvm_emit_coerce(GenContext *context, LLVMTypeRef coerced, BEValue *value, Type *original_type)
|
|
{
|
|
LLVMValueRef cast;
|
|
AlignSize target_alignment = llvm_abi_alignment(coerced);
|
|
AlignSize max_align = MAX(value->alignment, llvm_abi_alignment(coerced));
|
|
|
|
// If we are loading something with greater alignment than what we have, we cannot directly memcpy.
|
|
if (llvm_value_is_addr(value) && value->alignment < target_alignment)
|
|
{
|
|
// So load it instead.
|
|
llvm_value_rvalue(context, value);
|
|
}
|
|
|
|
// In this case we have something nicely aligned, so we just do a cast.
|
|
if (llvm_value_is_addr(value))
|
|
{
|
|
cast = LLVMBuildBitCast(context->builder, value->value, LLVMPointerType(coerced, 0), "");
|
|
}
|
|
else
|
|
{
|
|
cast = llvm_emit_alloca(context, coerced, max_align, "coerce");
|
|
LLVMValueRef target = LLVMBuildBitCast(context->builder, cast, llvm_get_ptr_type(context, value->type), "");
|
|
llvm_store_bevalue_aligned(context, target, value, max_align);
|
|
}
|
|
return llvm_emit_load_aligned(context, coerced, cast, max_align, "coerced");
|
|
}
|
|
|
|
LLVMValueRef llvm_emit_convert_value_from_coerced(GenContext *context, LLVMTypeRef coerced, LLVMValueRef value, Type *original_type)
|
|
{
|
|
unsigned max_align = MAX(llvm_abi_alignment(coerced), type_abi_alignment(original_type));
|
|
LLVMValueRef temp = llvm_emit_alloca(context, coerced, max_align, "coerce_temp");
|
|
llvm_store_aligned(context, temp, value, max_align);
|
|
temp = LLVMBuildBitCast(context->builder, temp, llvm_get_type(context, type_get_ptr(original_type)), "");
|
|
return llvm_emit_load_aligned(context, llvm_get_type(context, original_type), temp, max_align, "coerced");
|
|
}
|
|
|
|
static inline LLVMValueRef
|
|
llvm_emit_sub_int(GenContext *context, Type *type, LLVMValueRef left, LLVMValueRef right)
|
|
{
|
|
if (build_options.trap_wrapping)
|
|
{
|
|
LLVMTypeRef type_to_use = llvm_get_type(context, type);
|
|
LLVMValueRef args[2] = { left, right };
|
|
assert(type->canonical == type);
|
|
LLVMValueRef add_res;
|
|
if (type_is_unsigned(type))
|
|
{
|
|
add_res = llvm_emit_call_intrinsic(context, intrinsic_id_usub_overflow, &type_to_use, 1, args, 2);
|
|
}
|
|
else
|
|
{
|
|
add_res = llvm_emit_call_intrinsic(context, intrinsic_id_ssub_overflow, &type_to_use, 1, args, 2);
|
|
}
|
|
LLVMValueRef result = LLVMBuildExtractValue(context->builder, add_res, 0, "");
|
|
LLVMValueRef ok = LLVMBuildExtractValue(context->builder, add_res, 1, "");
|
|
llvm_emit_panic_on_true(context, ok, "Subtraction overflow");
|
|
return result;
|
|
}
|
|
|
|
return LLVMBuildSub(context->builder, left, right, "sub");
|
|
}
|
|
|
|
static inline void llvm_emit_subscript_addr_base(GenContext *context, BEValue *value, Expr *parent)
|
|
{
|
|
LLVMValueRef parent_value;
|
|
Type *type = type_flatten(parent->type);
|
|
llvm_emit_expr(context, value, parent);
|
|
llvm_emit_ptr_from_array(context, value);
|
|
}
|
|
|
|
static inline LLVMValueRef llvm_emit_subscript_addr_with_base(GenContext *c, Type *parent_type, LLVMValueRef parent_value, LLVMValueRef index_value)
|
|
{
|
|
Type *type = parent_type;
|
|
switch (type->type_kind)
|
|
{
|
|
case TYPE_POINTER:
|
|
return LLVMBuildInBoundsGEP2(c->builder,
|
|
llvm_get_type(c, type->pointer),
|
|
parent_value, &index_value, 1, "ptridx");
|
|
case TYPE_ARRAY:
|
|
{
|
|
// TODO insert trap on overflow.
|
|
LLVMValueRef zero = llvm_get_zero(c, type_int);
|
|
LLVMValueRef indices[2] = {
|
|
zero,
|
|
index_value,
|
|
};
|
|
return LLVMBuildInBoundsGEP2(c->builder,
|
|
llvm_get_type(c, type),
|
|
parent_value, indices, 2, "arridx");
|
|
}
|
|
case TYPE_SUBARRAY:
|
|
{
|
|
// TODO insert trap on overflow.
|
|
return LLVMBuildInBoundsGEP2(c->builder,
|
|
llvm_get_type(c, type->array.base),
|
|
parent_value, &index_value, 1, "sarridx");
|
|
}
|
|
case TYPE_VARARRAY:
|
|
TODO
|
|
default:
|
|
UNREACHABLE
|
|
|
|
}
|
|
}
|
|
|
|
static void llvm_emit_array_bounds_check(GenContext *c, BEValue *index, LLVMValueRef array_max_index)
|
|
{
|
|
// Negative values are not allowed.
|
|
if (type_is_signed(index->type))
|
|
{
|
|
LLVMValueRef index_negative = llvm_emit_int_comparison(c, index->type, index->type, index->value,
|
|
llvm_get_zero(c, index->type), BINARYOP_LT);
|
|
llvm_emit_panic_on_true(c, index_negative, "Negative array indexing");
|
|
}
|
|
LLVMValueRef exceeds_max_index = llvm_emit_int_comparison(c, index->type, index->type,
|
|
index->value, array_max_index,
|
|
BINARYOP_GE);
|
|
llvm_emit_panic_on_true(c, exceeds_max_index, "Array index out of bounds");
|
|
}
|
|
|
|
static inline LLVMValueRef llvm_emit_subscript_addr_with_base_new(GenContext *c, BEValue *parent, BEValue *index)
|
|
{
|
|
assert(llvm_value_is_addr(parent));
|
|
Type *type = type_flatten(parent->type);
|
|
switch (type->type_kind)
|
|
{
|
|
case TYPE_POINTER:
|
|
return LLVMBuildInBoundsGEP(c->builder, parent->value, &index->value, 1, "ptridx");
|
|
case TYPE_ARRAY:
|
|
{
|
|
if (build_options.debug_mode)
|
|
{
|
|
llvm_emit_array_bounds_check(c, index, llvm_const_int(c, index->type, type->array.len));
|
|
}
|
|
LLVMValueRef zero = llvm_get_zero(c, index->type);
|
|
LLVMValueRef indices[2] = {
|
|
zero,
|
|
index->value,
|
|
};
|
|
return LLVMBuildInBoundsGEP2(c->builder,
|
|
llvm_get_type(c, type),
|
|
parent->value, indices, 2, "arridx");
|
|
}
|
|
case TYPE_SUBARRAY:
|
|
{
|
|
if (build_options.debug_mode)
|
|
{
|
|
// TODO insert trap on overflow.
|
|
}
|
|
return LLVMBuildInBoundsGEP2(c->builder,
|
|
llvm_get_type(c, type->array.base),
|
|
parent->value, &index->value, 1, "sarridx");
|
|
}
|
|
case TYPE_VARARRAY:
|
|
// TODO insert trap on overflow.
|
|
TODO
|
|
case TYPE_STRLIT:
|
|
// TODO insert trap on overflow.
|
|
return LLVMBuildInBoundsGEP(c->builder, parent->value, &index->value, 1, "ptridx");
|
|
default:
|
|
UNREACHABLE
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Expand foo[123] or someCall()[n] or some such.
|
|
* Evaluation order is left to right.
|
|
*/
|
|
static inline void gencontext_emit_subscript(GenContext *c, BEValue *value, Expr *expr)
|
|
{
|
|
BEValue ref;
|
|
// First, get thing being subscripted.
|
|
llvm_emit_subscript_addr_base(c, &ref, expr->subscript_expr.expr);
|
|
// It needs to be an address.
|
|
llvm_value_addr(c, &ref);
|
|
|
|
// Now calculate the index:
|
|
BEValue index;
|
|
llvm_emit_expr(c, &index, expr->subscript_expr.index);
|
|
// It needs to be an rvalue.
|
|
llvm_value_rvalue(c, &index);
|
|
|
|
// TODO set alignment
|
|
llvm_value_set_address(value, llvm_emit_subscript_addr_with_base_new(c, &ref, &index), expr->type);
|
|
}
|
|
|
|
|
|
static int find_member_index(Decl *parent, Decl *member)
|
|
{
|
|
VECEACH(parent->strukt.members, i)
|
|
{
|
|
Decl *maybe_member = parent->strukt.members[i];
|
|
if (member == maybe_member)
|
|
{
|
|
return (int)i;
|
|
}
|
|
if (!maybe_member->name)
|
|
{
|
|
if (find_member_index(maybe_member, member) != -1) return (int)i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static void gencontext_emit_member_addr(GenContext *c, BEValue *value, Decl *parent, Decl *member)
|
|
{
|
|
assert(member->resolve_status == RESOLVE_DONE);
|
|
|
|
Decl *found = NULL;
|
|
do
|
|
{
|
|
int index = find_member_index(parent, member);
|
|
assert(index > -1);
|
|
found = parent->strukt.members[index];
|
|
const char *name = found->name ? found->name : "anon";
|
|
switch (parent->type->canonical->type_kind)
|
|
{
|
|
case TYPE_UNION:
|
|
llvm_value_addr(c, value);
|
|
llvm_value_set_address_align(value, llvm_emit_bitcast(c, value->value, type_get_ptr(found->type)), found->type, value->alignment);
|
|
break;
|
|
case TYPE_ERRTYPE:
|
|
case TYPE_STRUCT:
|
|
llvm_value_struct_gep(c, value, value, index);
|
|
break;
|
|
default:
|
|
UNREACHABLE
|
|
}
|
|
parent = found;
|
|
} while (found != member);
|
|
}
|
|
|
|
|
|
static inline void gencontext_emit_access_addr(GenContext *context, BEValue *be_value, Expr *expr)
|
|
{
|
|
Expr *parent = expr->access_expr.parent;
|
|
llvm_emit_expr(context, be_value, parent);
|
|
Decl *member = expr->access_expr.ref;
|
|
|
|
gencontext_emit_member_addr(context, be_value, type_flatten(parent->type)->decl, member);
|
|
}
|
|
|
|
static void gencontext_emit_scoped_expr(GenContext *context, BEValue *value, Expr *expr)
|
|
{
|
|
llvm_emit_expr(context, value, expr->expr_scope.expr);
|
|
llvm_emit_defer(context, expr->expr_scope.defers.start, expr->expr_scope.defers.end);
|
|
}
|
|
|
|
|
|
static inline void llvm_emit_initialize_reference(GenContext *c, BEValue *value, Expr *expr);
|
|
|
|
|
|
/**
|
|
* Here we are converting an array to a subarray.
|
|
* int[] x = &the_array;
|
|
* @param c
|
|
* @param value
|
|
* @param to_type
|
|
* @param from_type
|
|
*/
|
|
static void gencontext_emit_arr_to_subarray_cast(GenContext *c, BEValue *value, Type *to_type, Type *from_type)
|
|
{
|
|
llvm_value_rvalue(c, value);
|
|
printf("TODO optimize subarray cast\n");
|
|
ByteSize size = from_type->pointer->array.len;
|
|
LLVMTypeRef subarray_type = llvm_get_type(c, to_type);
|
|
LLVMValueRef result = LLVMGetUndef(subarray_type);
|
|
value->value = llvm_emit_bitcast(c, value->value, type_get_ptr(from_type->pointer->array.base));
|
|
result = LLVMBuildInsertValue(c->builder, result, value->value, 0, "");
|
|
value->value = LLVMBuildInsertValue(c->builder, result, llvm_const_int(c, type_usize, size), 1, "");
|
|
}
|
|
|
|
|
|
LLVMValueRef gencontext_emit_value_bitcast(GenContext *context, LLVMValueRef value, Type *to_type, Type *from_type)
|
|
{
|
|
LLVMValueRef ptr = llvm_emit_alloca_aligned(context, from_type, "");
|
|
LLVMBuildStore(context->builder, value, ptr);
|
|
LLVMValueRef ptr_cast = llvm_emit_bitcast(context, ptr, type_get_ptr(to_type));
|
|
return gencontext_emit_load(context, to_type, ptr_cast);
|
|
}
|
|
|
|
|
|
void llvm_emit_cast(GenContext *c, CastKind cast_kind, BEValue *value, Type *to_type, Type *from_type)
|
|
{
|
|
to_type = type_flatten(to_type);
|
|
from_type = type_flatten(from_type);
|
|
|
|
switch (cast_kind)
|
|
{
|
|
case CAST_CXBOOL:
|
|
TODO
|
|
case CAST_XIERR:
|
|
// TODO Insert zero check.
|
|
llvm_value_rvalue(c, value);
|
|
break;
|
|
case CAST_ERROR:
|
|
UNREACHABLE
|
|
case CAST_STRPTR:
|
|
case CAST_PTRPTR:
|
|
llvm_value_rvalue(c, value);
|
|
if (c->builder)
|
|
{
|
|
value->value = LLVMBuildPointerCast(c->builder, value->value, llvm_get_type(c, to_type), "ptrptr");
|
|
}
|
|
else
|
|
{
|
|
value->value = LLVMConstPointerCast(value->value, llvm_get_type(c, to_type));
|
|
}
|
|
break;
|
|
case CAST_PTRXI:
|
|
llvm_value_rvalue(c, value);
|
|
if (c->builder)
|
|
{
|
|
value->value = LLVMBuildPtrToInt(c->builder, value->value, llvm_get_type(c, to_type), "ptrxi");
|
|
}
|
|
else
|
|
{
|
|
value->value = LLVMConstBitCast(value->value, llvm_get_type(c, to_type));
|
|
}
|
|
break;
|
|
case CAST_APTSA:
|
|
gencontext_emit_arr_to_subarray_cast(c, value, to_type, from_type);
|
|
break;
|
|
case CAST_SAPTR:
|
|
if (llvm_value_is_addr(value))
|
|
{
|
|
llvm_value_fold_failable(c, value);
|
|
value->value = llvm_emit_load_aligned(c, llvm_get_type(c, to_type),
|
|
LLVMBuildStructGEP(c->builder, value->value, 0, ""),
|
|
value->alignment, "");
|
|
}
|
|
else
|
|
{
|
|
value->value = LLVMBuildExtractValue(c->builder, value->value, 0, "");
|
|
}
|
|
break;
|
|
case CAST_VARPTR:
|
|
break;
|
|
case CAST_ARRPTR:
|
|
TODO
|
|
case CAST_EREU:
|
|
case CAST_EUER:
|
|
TODO // gencontext_emit_value_bitcast(c, value->value, to_type, from_type);
|
|
case CAST_EUBOOL:
|
|
if (value->kind == BE_VALUE)
|
|
{
|
|
value->value = LLVMBuildExtractValue(c->builder, value->value, 0, "");
|
|
}
|
|
else
|
|
{
|
|
value->value = LLVMBuildStructGEP2(c->builder, llvm_get_type(c, type_error), value->value, 0, "");
|
|
value->value = llvm_emit_load_aligned(c,
|
|
llvm_get_type(c, type_usize),
|
|
value->value,
|
|
type_abi_alignment(type_usize),
|
|
"");
|
|
}
|
|
value->value = LLVMBuildICmp(c->builder, LLVMIntNE, value->value, llvm_get_zero(c, type_usize), "eubool");
|
|
value->kind = BE_BOOLEAN;
|
|
break;
|
|
case CAST_PTRBOOL:
|
|
llvm_value_rvalue(c, value);
|
|
value->value = LLVMBuildIsNotNull(c->builder, value->value, "ptrbool");
|
|
value->kind = BE_BOOLEAN;
|
|
break;
|
|
case CAST_BOOLINT:
|
|
llvm_value_rvalue(c, value);
|
|
value->value = LLVMBuildZExt(c->builder, value->value, llvm_get_type(c, to_type), "boolsi");
|
|
value->kind = BE_VALUE;
|
|
break;
|
|
case CAST_FPBOOL:
|
|
llvm_value_rvalue(c, value);
|
|
value->value = LLVMBuildFCmp(c->builder, LLVMRealUNE, value->value, llvm_get_zero(c, from_type), "fpbool");
|
|
value->kind = BE_BOOLEAN;
|
|
break;
|
|
case CAST_BOOLFP:
|
|
llvm_value_rvalue(c, value);
|
|
value->value = LLVMBuildUIToFP(c->builder, value->value, llvm_get_type(c, to_type), "boolfp");
|
|
value->kind = BE_VALUE;
|
|
break;
|
|
case CAST_INTBOOL:
|
|
llvm_value_rvalue(c, value);
|
|
value->value = LLVMBuildICmp(c->builder, LLVMIntNE, value->value, llvm_get_zero(c, from_type), "intbool");
|
|
value->kind = BE_BOOLEAN;
|
|
break;
|
|
case CAST_FPFP:
|
|
llvm_value_rvalue(c, value);
|
|
value->value = type_convert_will_trunc(to_type, from_type)
|
|
? LLVMBuildFPTrunc(c->builder, value->value, llvm_get_type(c, to_type), "fpfptrunc")
|
|
: LLVMBuildFPExt(c->builder, value->value, llvm_get_type(c, to_type), "fpfpext");
|
|
break;
|
|
case CAST_FPSI:
|
|
llvm_value_rvalue(c, value);
|
|
value->value = LLVMBuildFPToSI(c->builder, value->value, llvm_get_type(c, to_type), "fpsi");
|
|
break;
|
|
case CAST_FPUI:
|
|
llvm_value_rvalue(c, value);
|
|
value->value = LLVMBuildFPToUI(c->builder, value->value, llvm_get_type(c, to_type), "fpui");
|
|
break;
|
|
case CAST_SISI:
|
|
llvm_value_rvalue(c, value);
|
|
value->value = type_convert_will_trunc(to_type, from_type)
|
|
? LLVMBuildTrunc(c->builder, value->value, llvm_get_type(c, to_type), "sisitrunc")
|
|
: LLVMBuildSExt(c->builder, value->value, llvm_get_type(c, to_type), "sisiext");
|
|
break;
|
|
case CAST_SIUI:
|
|
llvm_value_rvalue(c, value);
|
|
value->value = type_convert_will_trunc(to_type, from_type)
|
|
? LLVMBuildTrunc(c->builder, value->value, llvm_get_type(c, to_type), "siuitrunc")
|
|
: LLVMBuildZExt(c->builder, value->value, llvm_get_type(c, to_type), "siuiext");
|
|
break;
|
|
case CAST_SIFP:
|
|
llvm_value_rvalue(c, value);
|
|
value->value = LLVMBuildSIToFP(c->builder, value->value, llvm_get_type(c, to_type), "sifp");
|
|
break;
|
|
case CAST_XIPTR:
|
|
llvm_value_rvalue(c, value);
|
|
value->value = LLVMBuildIntToPtr(c->builder, value->value, llvm_get_type(c, to_type), "xiptr");
|
|
break;
|
|
case CAST_UISI:
|
|
llvm_value_rvalue(c, value);
|
|
value->value = type_convert_will_trunc(to_type, from_type)
|
|
? LLVMBuildTrunc(c->builder, value->value, llvm_get_type(c, to_type), "uisitrunc")
|
|
: LLVMBuildZExt(c->builder, value->value, llvm_get_type(c, to_type), "uisiext");
|
|
break;
|
|
case CAST_UIUI:
|
|
llvm_value_rvalue(c, value);
|
|
value->value = type_convert_will_trunc(to_type, from_type)
|
|
? LLVMBuildTrunc(c->builder, value->value, llvm_get_type(c, to_type), "uiuitrunc")
|
|
: LLVMBuildZExt(c->builder, value->value, llvm_get_type(c, to_type), "uiuiext");
|
|
break;
|
|
case CAST_UIFP:
|
|
llvm_value_rvalue(c, value);
|
|
value->value = LLVMBuildUIToFP(c->builder, value->value, llvm_get_type(c, to_type), "uifp");
|
|
break;
|
|
case CAST_ENUMLOW:
|
|
llvm_value_rvalue(c, value);
|
|
value->value = value->value;
|
|
break;
|
|
case CAST_STST:
|
|
llvm_value_addr(c, value);
|
|
value->value = LLVMBuildBitCast(c->builder, value->value, llvm_get_ptr_type(c, to_type), "");
|
|
value->type = to_type;
|
|
return;
|
|
case CAST_PTRVAR:
|
|
case CAST_VARSA:
|
|
case CAST_VARVAR:
|
|
case CAST_VARBOOL:
|
|
case CAST_BOOLBOOL:
|
|
case CAST_SABOOL:
|
|
TODO
|
|
break;
|
|
}
|
|
value->type = to_type;
|
|
}
|
|
|
|
static inline void gencontext_emit_cast_expr(GenContext *context, BEValue *be_value, Expr *expr)
|
|
{
|
|
llvm_emit_expr(context, be_value, expr->cast_expr.expr);
|
|
llvm_emit_cast(context,
|
|
expr->cast_expr.kind,
|
|
be_value,
|
|
expr->type,
|
|
expr->cast_expr.expr->type);
|
|
}
|
|
|
|
|
|
static LLVMValueRef llvm_recursive_set_value(GenContext *c, DesignatorElement **current_element_ptr, LLVMValueRef parent, DesignatorElement **last_element_ptr, Expr *value)
|
|
{
|
|
DesignatorElement *current_element = current_element_ptr[0];
|
|
if (current_element_ptr == last_element_ptr)
|
|
{
|
|
BEValue res;
|
|
llvm_emit_expr(c, &res, value);
|
|
unsigned index = current_element->index;
|
|
LLVMValueRef val = llvm_value_rvalue_store(c, &res);
|
|
switch (current_element->kind)
|
|
{
|
|
case DESIGNATOR_FIELD:
|
|
return LLVMConstInsertValue(parent, val, &index, 1);
|
|
case DESIGNATOR_ARRAY:
|
|
return LLVMConstInsertElement(parent, val, llvm_const_int(c, type_isize, current_element->index));
|
|
case DESIGNATOR_RANGE:
|
|
for (int64_t i = current_element->index; i <= current_element->index_end; i++)
|
|
{
|
|
parent = LLVMConstInsertElement(parent, val, llvm_const_int(c, type_isize, i));
|
|
}
|
|
return parent;
|
|
}
|
|
UNREACHABLE
|
|
}
|
|
LLVMValueRef current_val;
|
|
switch (current_element->kind)
|
|
{
|
|
case DESIGNATOR_FIELD:
|
|
{
|
|
unsigned index = current_element->index;
|
|
current_val = LLVMConstExtractValue(parent, &index, 1);
|
|
current_val = llvm_recursive_set_value(c, current_element_ptr + 1, current_val, last_element_ptr, value);
|
|
return LLVMConstInsertValue(parent, current_val, &index, 1);
|
|
}
|
|
case DESIGNATOR_ARRAY:
|
|
{
|
|
LLVMValueRef index = llvm_const_int(c, type_isize, current_element->index);
|
|
current_val = LLVMConstExtractElement(parent, index);
|
|
current_val = llvm_recursive_set_value(c, current_element_ptr + 1, current_val, last_element_ptr, value);
|
|
return LLVMConstInsertElement(parent, current_val, index);
|
|
}
|
|
case DESIGNATOR_RANGE:
|
|
for (int64_t i = current_element->index; i <= current_element->index_end; i++)
|
|
{
|
|
LLVMValueRef index = llvm_const_int(c, type_isize, i);
|
|
current_val = LLVMConstExtractElement(parent, index);
|
|
current_val = llvm_recursive_set_value(c, current_element_ptr + 1, current_val, last_element_ptr, value);
|
|
parent = LLVMConstInsertElement(parent, current_val, index);
|
|
}
|
|
return parent;
|
|
default:
|
|
UNREACHABLE
|
|
}
|
|
}
|
|
static LLVMValueRef llvm_recursive_set_const_value(GenContext *context, DesignatorElement **path, LLVMValueRef value, Type *parent_type, Expr *assign)
|
|
{
|
|
unsigned path_count = vec_size(path);
|
|
return llvm_recursive_set_value(context, path, value, path + (path_count - 1), assign);
|
|
}
|
|
|
|
|
|
static inline void llvm_emit_initialize_reference_temporary_const(GenContext *c, BEValue *ref, Expr *expr)
|
|
{
|
|
bool modified = false;
|
|
// First create the constant value.
|
|
|
|
Type *canonical = expr->type->canonical;
|
|
|
|
LLVMValueRef value = llvm_emit_const_aggregate(c, expr, &modified);
|
|
|
|
// Create a global const.
|
|
LLVMTypeRef type = modified ? LLVMTypeOf(value) : llvm_get_type(c, expr->type);
|
|
LLVMValueRef global_copy = LLVMAddGlobal(c->module, type, "");
|
|
|
|
// Set a nice alignment
|
|
unsigned alignment = type_alloca_alignment(expr->type);
|
|
llvm_set_alignment(global_copy, alignment);
|
|
|
|
// Set the value and make it constant
|
|
LLVMSetInitializer(global_copy, value);
|
|
LLVMSetGlobalConstant(global_copy, true);
|
|
|
|
// Ensure we have a reference.
|
|
llvm_value_addr(c, ref);
|
|
|
|
// Perform the memcpy.
|
|
llvm_emit_memcpy(c, ref->value, ref->alignment, global_copy, alignment, type_size(expr->type));
|
|
}
|
|
|
|
static void llvm_emit_inititialize_reference_const(GenContext *c, BEValue *ref, ConstInitializer *const_init)
|
|
{
|
|
switch (const_init->kind)
|
|
{
|
|
case CONST_INIT_ZERO:
|
|
if (type_is_builtin(ref->type->type_kind) || ref->type->type_kind == TYPE_ARRAY)
|
|
{
|
|
llvm_store_bevalue_raw(c, ref, llvm_get_zero(c, ref->type));
|
|
return;
|
|
}
|
|
llvm_emit_memclear(c, ref);
|
|
return;
|
|
case CONST_INIT_ARRAY_VALUE:
|
|
UNREACHABLE
|
|
case CONST_INIT_ARRAY_FULL:
|
|
{
|
|
LLVMValueRef array_ref = ref->value;
|
|
Type *array_type = const_init->type;
|
|
Type *element_type = array_type->array.base;
|
|
ArrayIndex size = array_type->array.len;
|
|
LLVMTypeRef array_type_llvm = llvm_get_type(c, array_type);
|
|
LLVMTypeRef element_type_llvm = llvm_get_type(c, element_type);
|
|
assert(size <= UINT32_MAX);
|
|
for (ArrayIndex i = 0; i < size; i++)
|
|
{
|
|
LLVMValueRef index = llvm_const_int(c, type_uint, i);
|
|
LLVMValueRef array_pointer = LLVMBuildInBoundsGEP2(c->builder, element_type_llvm, array_ref, &index, 1, "");
|
|
BEValue value;
|
|
llvm_value_set_address(&value, array_pointer, element_type);
|
|
llvm_emit_inititialize_reference_const(c, &value, const_init->init_array_full[i]);
|
|
}
|
|
return;
|
|
}
|
|
case CONST_INIT_ARRAY:
|
|
{
|
|
LLVMValueRef array_ref = ref->value;
|
|
llvm_emit_memclear(c, ref);
|
|
Type *array_type = const_init->type;
|
|
Type *element_type = array_type->array.base;
|
|
LLVMTypeRef element_type_llvm = llvm_get_type(c, element_type);
|
|
ConstInitializer **elements = const_init->init_array.elements;
|
|
unsigned element_count = vec_size(elements);
|
|
ArrayIndex current_index = 0;
|
|
LLVMValueRef *parts = NULL;
|
|
VECEACH(elements, i)
|
|
{
|
|
ConstInitializer *element = elements[i];
|
|
assert(element->kind == CONST_INIT_ARRAY_VALUE);
|
|
ArrayIndex element_index = element->init_array_value.index;
|
|
LLVMValueRef index = llvm_const_int(c, element_index <= UINT32_MAX ? type_uint : type_usize, element_index);
|
|
LLVMValueRef array_pointer = LLVMBuildInBoundsGEP2(c->builder, element_type_llvm, array_ref, &index, 1, "");
|
|
BEValue value;
|
|
llvm_value_set_address(&value, array_pointer, element_type);
|
|
llvm_emit_inititialize_reference_const(c, &value, element->init_array_value.element);
|
|
}
|
|
return;
|
|
}
|
|
case CONST_INIT_UNION:
|
|
{
|
|
Decl *decl = const_init->type->decl;
|
|
MemberIndex index = const_init->init_union.index;
|
|
Type *type = decl->strukt.members[index]->type->canonical;
|
|
// Bitcast.
|
|
BEValue value = *ref;
|
|
llvm_value_set_address(&value, llvm_emit_bitcast(c, ref->value, type_get_ptr(type)), type);
|
|
// Emit our value.
|
|
llvm_emit_inititialize_reference_const(c, &value, const_init->init_union.element);
|
|
return;
|
|
}
|
|
case CONST_INIT_STRUCT:
|
|
{
|
|
Decl *decl = const_init->type->decl;
|
|
Decl **members = decl->strukt.members;
|
|
MemberIndex count = vec_size(members);
|
|
LLVMValueRef *entries = NULL;
|
|
for (MemberIndex i = 0; i < count; i++)
|
|
{
|
|
BEValue value;
|
|
llvm_value_struct_gep(c, &value, ref, i);
|
|
llvm_emit_inititialize_reference_const(c, &value, const_init->init_struct[i]);
|
|
}
|
|
return;
|
|
}
|
|
case CONST_INIT_VALUE:
|
|
{
|
|
BEValue value;
|
|
llvm_emit_expr(c, &value, const_init->init_value);
|
|
llvm_store_bevalue(c, ref, &value);
|
|
return;
|
|
}
|
|
}
|
|
UNREACHABLE
|
|
|
|
}
|
|
static inline void llvm_emit_initialize_reference_const(GenContext *c, BEValue *ref, Expr *expr)
|
|
{
|
|
ConstInitializer *initializer = expr->initializer_expr.initializer;
|
|
|
|
// Make sure we have an address.
|
|
llvm_value_addr(c, ref);
|
|
|
|
llvm_emit_inititialize_reference_const(c, ref, initializer);
|
|
|
|
}
|
|
|
|
static inline void llvm_emit_initialize_reference_list(GenContext *c, BEValue *ref, Expr *expr)
|
|
{
|
|
// Getting ready to initialize, get the real type.
|
|
Type *real_type = type_flatten(ref->type);
|
|
Expr **elements = expr->initializer_expr.initializer_expr;
|
|
|
|
// Make sure we have an address.
|
|
llvm_value_addr(c, ref);
|
|
LLVMValueRef value = ref->value;
|
|
|
|
// If this is a union, we assume it's initializing the first element.
|
|
if (real_type->type_kind == TYPE_UNION)
|
|
{
|
|
assert(vec_size(elements) == 1);
|
|
real_type = type_lowering(real_type->decl->strukt.members[0]->type);
|
|
value = LLVMBuildBitCast(c->builder, ref->value, llvm_get_ptr_type(c, real_type), "");
|
|
}
|
|
|
|
LLVMTypeRef llvm_type = llvm_get_type(c, real_type);
|
|
bool is_struct = type_is_structlike(real_type);
|
|
bool is_array = real_type->type_kind == TYPE_ARRAY;
|
|
Type *array_element_type = is_array ? real_type->array.base : NULL;
|
|
LLVMTypeRef array_element_type_llvm = is_array ? llvm_get_type(c, real_type->array.base) : NULL;
|
|
// Now walk through the elements.
|
|
VECEACH(elements, i)
|
|
{
|
|
Expr *element = elements[i];
|
|
if (element->expr_kind == EXPR_COMPOUND_LITERAL)
|
|
{
|
|
element = element->expr_compound_literal.initializer;
|
|
}
|
|
unsigned offset = 0;
|
|
BEValue pointer;
|
|
if (is_struct)
|
|
{
|
|
Decl *member = real_type->decl->strukt.members[i];
|
|
offset = member->offset;
|
|
llvm_value_struct_gep(c, &pointer, ref, i);
|
|
}
|
|
else if (is_array)
|
|
{
|
|
// Todo optimize
|
|
offset = i * type_size(array_element_type);
|
|
LLVMValueRef index = llvm_const_int(c, type_uint, i);
|
|
LLVMValueRef ptr = LLVMBuildInBoundsGEP2(c->builder, array_element_type_llvm, value, &index, 1, "");
|
|
unsigned alignment = type_min_alignment(offset, ref->alignment);
|
|
llvm_value_set_address_align(&pointer, ptr, element->type, alignment);
|
|
}
|
|
else
|
|
{
|
|
llvm_value_set_address_align(&pointer, value, element->type, ref->alignment);
|
|
}
|
|
// If this is an initializer, we want to actually run the initialization recursively.
|
|
if (element->expr_kind == EXPR_INITIALIZER_LIST)
|
|
{
|
|
llvm_emit_initialize_reference(c, &pointer, element);
|
|
continue;
|
|
}
|
|
BEValue init_value;
|
|
llvm_emit_expr(c, &init_value, element);
|
|
llvm_store_bevalue(c, &pointer, &init_value);
|
|
}
|
|
}
|
|
|
|
static void llvm_emit_initialize_designated_const_range(GenContext *c, BEValue *ref, uint64_t offset, DesignatorElement** current, DesignatorElement **last, Expr *expr, BEValue *emitted_value)
|
|
{
|
|
DesignatorElement *curr = current[0];
|
|
llvm_value_addr(c, ref);
|
|
|
|
assert(curr->kind == DESIGNATOR_RANGE);
|
|
|
|
BEValue emitted_local;
|
|
if (!emitted_value)
|
|
{
|
|
llvm_emit_expr(c, &emitted_local, expr);
|
|
emitted_value = &emitted_local;
|
|
}
|
|
LLVMTypeRef ref_type = llvm_get_type(c, ref->type);
|
|
// Assign the index_ptr to the start value.
|
|
LLVMValueRef indices[2] = {
|
|
llvm_get_zero(c, curr->index_expr->type),
|
|
NULL
|
|
};
|
|
for (unsigned i = curr->index; i <= curr->index_end; i++)
|
|
{
|
|
indices[1] = llvm_const_int(c, curr->index_expr->type, i);
|
|
BEValue new_ref;
|
|
LLVMValueRef ptr = LLVMBuildInBoundsGEP2(c->builder, ref_type, ref->value, indices, 2, "");
|
|
llvm_value_set_address(&new_ref, ptr, type_get_indexed_type(ref->type));
|
|
llvm_emit_initialize_designated(c, &new_ref, offset, current + 1, last, expr, emitted_value);
|
|
}
|
|
}
|
|
|
|
static void llvm_emit_initialize_designated(GenContext *c, BEValue *ref, uint64_t offset, DesignatorElement** current,
|
|
DesignatorElement **last, Expr *expr, BEValue *emitted_value)
|
|
{
|
|
BEValue value;
|
|
if (current > last)
|
|
{
|
|
if (emitted_value)
|
|
{
|
|
llvm_store_bevalue(c, ref, emitted_value);
|
|
return;
|
|
}
|
|
if (expr->expr_kind == EXPR_INITIALIZER_LIST)
|
|
{
|
|
llvm_emit_initialize_reference(c, ref, expr);
|
|
return;
|
|
}
|
|
BEValue val;
|
|
llvm_emit_expr(c, &val, expr);
|
|
llvm_store_bevalue(c, ref, &val);
|
|
return;
|
|
}
|
|
DesignatorElement *curr = current[0];
|
|
switch (curr->kind)
|
|
{
|
|
case DESIGNATOR_FIELD:
|
|
{
|
|
Decl *decl = ref->type->canonical->decl->strukt.members[curr->index];
|
|
offset += decl->offset;
|
|
Type *type = decl->type->canonical;
|
|
unsigned decl_alignment = decl->alignment;
|
|
if (ref->type->type_kind == TYPE_UNION)
|
|
{
|
|
llvm_value_set_address_align(&value, llvm_emit_bitcast(c, ref->value, type_get_ptr(type)), type, type_min_alignment(offset, decl_alignment));
|
|
}
|
|
else
|
|
{
|
|
llvm_value_struct_gep(c, &value, ref, curr->index);
|
|
}
|
|
llvm_emit_initialize_designated(c, &value, offset, current + 1, last, expr, emitted_value);
|
|
break;
|
|
}
|
|
case DESIGNATOR_ARRAY:
|
|
{
|
|
Type *type = ref->type->array.base;
|
|
LLVMValueRef indices[2];
|
|
offset += curr->index * type_size(type);
|
|
Type *index_type = curr->index > UINT32_MAX ? type_uint : type_ulong;
|
|
indices[0] = llvm_const_int(c, index_type, 0);
|
|
indices[1] = llvm_const_int(c, index_type, curr->index);
|
|
LLVMValueRef ptr = LLVMBuildInBoundsGEP2(c->builder, llvm_get_type(c, ref->type), ref->value, indices, 2, "");
|
|
llvm_value_set_address_align(&value, ptr, type, type_min_alignment(offset, type_abi_alignment(type)));
|
|
llvm_emit_initialize_designated(c, &value, offset, current + 1, last, expr, emitted_value);
|
|
break;
|
|
}
|
|
case DESIGNATOR_RANGE:
|
|
llvm_emit_initialize_designated_const_range(c, ref, offset, current, last, expr, emitted_value);
|
|
break;
|
|
default:
|
|
UNREACHABLE
|
|
}
|
|
}
|
|
|
|
static inline void llvm_emit_initialize_reference_designated(GenContext *c, BEValue *ref, Expr *expr)
|
|
{
|
|
// Getting ready to initialize, get the real type.
|
|
Type *real_type = type_flatten(ref->type);
|
|
Expr **elements = expr->initializer_expr.initializer_expr;
|
|
assert(vec_size(elements));
|
|
|
|
// Make sure we have an address.
|
|
llvm_value_addr(c, ref);
|
|
|
|
// Clear the memory if not union.
|
|
if (real_type->type_kind != TYPE_UNION) llvm_emit_memclear(c, ref);
|
|
|
|
LLVMValueRef value = ref->value;
|
|
|
|
// Now walk through the elements.
|
|
VECEACH(elements, i)
|
|
{
|
|
Expr *designator = elements[i];
|
|
DesignatorElement **last_element = designator->designator_expr.path + vec_size(designator->designator_expr.path) - 1;
|
|
llvm_emit_initialize_designated(c, ref, 0, designator->designator_expr.path, last_element, designator->designator_expr.value, NULL);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize an aggregate type.
|
|
*
|
|
* There are three methods:
|
|
* 1. Create a constant and store it in a global, followed by a memcopy from this global.
|
|
* this is what Clang does for elements up to 4 pointers wide.
|
|
* 2. For empty elements, we do a memclear.
|
|
* 3. For the rest use GEP into the appropriate elements.
|
|
*/
|
|
static inline void llvm_emit_initialize_reference(GenContext *c, BEValue *ref, Expr *expr)
|
|
{
|
|
// In the case of a const, we have some optimizations:
|
|
if (expr->initializer_expr.init_type == INITIALIZER_CONST)
|
|
{
|
|
ConstInitializer *initializer = expr->initializer_expr.initializer;
|
|
if (initializer->kind == CONST_INIT_ZERO)
|
|
{
|
|
// In case of a zero, optimize.
|
|
llvm_emit_memclear(c, ref);
|
|
return;
|
|
}
|
|
// In case of small const initializers, or full arrays - use copy.
|
|
if (initializer->kind == CONST_INIT_ARRAY_FULL || type_size(expr->type) <= 32)
|
|
{
|
|
llvm_emit_initialize_reference_temporary_const(c, ref, expr);
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch (expr->initializer_expr.init_type)
|
|
{
|
|
case INITIALIZER_CONST:
|
|
// Just clear memory
|
|
llvm_emit_initialize_reference_const(c, ref, expr);
|
|
break;
|
|
case INITIALIZER_NORMAL:
|
|
llvm_emit_initialize_reference_list(c, ref, expr);
|
|
break;
|
|
case INITIALIZER_DESIGNATED:
|
|
llvm_emit_initialize_reference_designated(c, ref, expr);
|
|
break;
|
|
default:
|
|
UNREACHABLE
|
|
}
|
|
}
|
|
|
|
static inline void llvm_emit_inc_dec_change(GenContext *c, bool use_mod, BEValue *addr, BEValue *after, BEValue *before, Expr *expr, int diff)
|
|
{
|
|
EMIT_LOC(c, expr);
|
|
Type *type = type_reduced_from_expr(expr);
|
|
|
|
// Copy the address and make it a value.
|
|
BEValue value = *addr;
|
|
llvm_value_rvalue(c, &value);
|
|
|
|
// Store the original value if we want it
|
|
if (before) *before = value;
|
|
|
|
LLVMValueRef after_value;
|
|
|
|
switch (type->type_kind)
|
|
{
|
|
case TYPE_POINTER:
|
|
{
|
|
// Use byte here, we don't need a big offset.
|
|
LLVMValueRef add = LLVMConstInt(diff < 0 ? llvm_get_type(c, type_ichar) : llvm_get_type(c, type_char), diff, diff < 0);
|
|
after_value = LLVMBuildGEP2(c->builder, llvm_get_type(c, type->pointer), value.value, &add, 1, "ptrincdec");
|
|
break;
|
|
}
|
|
case ALL_FLOATS:
|
|
{
|
|
// We allow inc/dec on floats, which is same as f += 1.0 or f -= 1.0
|
|
LLVMTypeRef llvm_type = llvm_get_type(c, type);
|
|
LLVMValueRef add = LLVMConstReal(llvm_type, (double)diff);
|
|
after_value = LLVMBuildFAdd(c->builder, value.value, add, "fincdec");
|
|
break;
|
|
}
|
|
case ALL_INTS:
|
|
{
|
|
// Instead of negative numbers do dec/inc with a positive number.
|
|
LLVMTypeRef llvm_type = llvm_get_type(c, type);
|
|
LLVMValueRef diff_value = LLVMConstInt(llvm_type, 1, false);
|
|
after_value = diff > 0
|
|
? llvm_emit_add_int(c, type, value.value, diff_value)
|
|
: llvm_emit_sub_int(c, type, value.value, diff_value);
|
|
break;
|
|
}
|
|
default:
|
|
UNREACHABLE
|
|
}
|
|
|
|
// Store the result aligned.
|
|
llvm_store_bevalue_raw(c, addr, after_value);
|
|
if (after) llvm_value_set(after, after_value, addr->type);
|
|
}
|
|
|
|
/**
|
|
* This method implements the common ++x and --x operators, as well as the --%x and ++%x
|
|
* that have wrapping behaviour. See llvm_emit_post_inc_dec for more discussion.
|
|
*/
|
|
static inline void llvm_emit_pre_inc_dec(GenContext *c, BEValue *value, Expr *expr, int diff, bool use_mod)
|
|
{
|
|
// Pull out the address, also allowing temporaries.
|
|
BEValue addr;
|
|
llvm_emit_expr(c, &addr, expr);
|
|
llvm_value_addr(c, &addr);
|
|
|
|
// Set the value to the new value.
|
|
llvm_emit_inc_dec_change(c, use_mod, &addr, value, NULL, expr, diff);
|
|
}
|
|
|
|
|
|
/**
|
|
* Emit the common x++ and x-- operations.
|
|
* Normally overflow/underflow is considered UB, which is why C3 provides x%++ and x%-- for wrapping.
|
|
* We could also provide methods for the same but where it would cap on overflow.
|
|
*/
|
|
static inline void llvm_emit_post_inc_dec(GenContext *c, BEValue *value, Expr *expr, int diff, bool use_mod)
|
|
{
|
|
// Retrieve the address, creating a temp in case this is
|
|
// a temporary value (this gives us a lot of flexibility for temporaries)
|
|
BEValue addr;
|
|
llvm_emit_expr(c, &addr, expr);
|
|
llvm_value_addr(c, &addr);
|
|
|
|
// Perform the actual dec/inc to generate the new value.
|
|
llvm_emit_inc_dec_change(c, use_mod, &addr, NULL, value, expr, diff);
|
|
}
|
|
|
|
static void gencontext_emit_unary_expr(GenContext *c, BEValue *value, Expr *expr)
|
|
{
|
|
Type *type = type_reduced_from_expr(expr->unary_expr.expr);
|
|
LLVMValueRef llvm_value;
|
|
switch (expr->unary_expr.operator)
|
|
{
|
|
case UNARYOP_ERROR:
|
|
FATAL_ERROR("Illegal unary op %s", expr->unary_expr.operator);
|
|
case UNARYOP_TADDR:
|
|
llvm_value = llvm_emit_alloca_aligned(c, expr->unary_expr.expr->type, "taddr");
|
|
llvm_emit_expr(c, value, expr->unary_expr.expr);
|
|
llvm_store_bevalue_dest_aligned(c, llvm_value, value);
|
|
llvm_value_set(value, llvm_value, type);
|
|
return;
|
|
case UNARYOP_NOT:
|
|
llvm_emit_expr(c, value, expr->unary_expr.expr);
|
|
llvm_value_rvalue(c, value);
|
|
if (type_is_float(type))
|
|
{
|
|
llvm_value = LLVMBuildFCmp(c->builder, LLVMRealUNE, value->value, llvm_get_zero(c, type), "not");
|
|
}
|
|
else if (type->type_kind == TYPE_BOOL)
|
|
{
|
|
llvm_value = LLVMBuildNot(c->builder, value->value, "not");
|
|
}
|
|
else
|
|
{
|
|
llvm_value = LLVMBuildICmp(c->builder, LLVMIntEQ, value->value, llvm_get_zero(c, type), "not");
|
|
}
|
|
llvm_value_set_bool(value, llvm_value);
|
|
return;
|
|
case UNARYOP_BITNEG:
|
|
llvm_emit_expr(c, value, expr->unary_expr.expr);
|
|
llvm_value_rvalue(c, value);
|
|
value->value = LLVMBuildNot(c->builder, value->value, "bnot");
|
|
return;
|
|
case UNARYOP_NEG:
|
|
llvm_emit_expr(c, value, expr->unary_expr.expr);
|
|
llvm_value_rvalue(c, value);
|
|
if (type_is_float(type))
|
|
{
|
|
value->value = LLVMBuildFNeg(c->builder, value->value, "fneg");
|
|
return;
|
|
}
|
|
assert(type->canonical != type_bool);
|
|
assert(!type_is_unsigned(type));
|
|
{
|
|
LLVMValueRef zero = llvm_get_zero(c, expr->unary_expr.expr->type);
|
|
if (build_options.trap_wrapping)
|
|
{
|
|
// TODO
|
|
LLVMTypeRef type_to_use = llvm_get_type(c, type->canonical);
|
|
LLVMValueRef args[2] = { zero, value->value };
|
|
LLVMValueRef call_res = llvm_emit_call_intrinsic(c, intrinsic_id_ssub_overflow,
|
|
&type_to_use, 1, args, 2);
|
|
value->value = LLVMBuildExtractValue(c->builder, call_res, 0, "");
|
|
LLVMValueRef ok = LLVMBuildExtractValue(c->builder, call_res, 1, "");
|
|
llvm_emit_panic_on_true(c, ok, "Signed negation overflow");
|
|
return;
|
|
}
|
|
llvm_emit_expr(c, value, expr->unary_expr.expr);
|
|
llvm_value_rvalue(c, value);
|
|
value->value = LLVMBuildNeg(c->builder, value->value, "neg");
|
|
return;
|
|
}
|
|
case UNARYOP_ADDR:
|
|
llvm_emit_expr(c, value, expr->unary_expr.expr);
|
|
// Create an addr
|
|
llvm_value_addr(c, value);
|
|
// Transform to value
|
|
value->kind = BE_VALUE;
|
|
value->type = type_flatten(expr->type);
|
|
return;
|
|
case UNARYOP_DEREF:
|
|
llvm_emit_expr(c, value, expr->unary_expr.expr);
|
|
// Load the pointer value.
|
|
llvm_value_rvalue(c, value);
|
|
// Convert pointer to address
|
|
value->kind = BE_ADDRESS;
|
|
value->type = type_flatten(expr->type);
|
|
return;
|
|
case UNARYOP_INC:
|
|
llvm_emit_pre_inc_dec(c, value, expr->unary_expr.expr, 1, false);
|
|
return;
|
|
case UNARYOP_DEC:
|
|
llvm_emit_pre_inc_dec(c, value, expr->unary_expr.expr, -1, false);
|
|
return;
|
|
}
|
|
UNREACHABLE
|
|
}
|
|
|
|
void llvm_emit_len_for_expr(GenContext *c, BEValue *be_value, BEValue *expr_to_len)
|
|
{
|
|
llvm_value_addr(c, expr_to_len);
|
|
switch (expr_to_len->type->type_kind)
|
|
{
|
|
case TYPE_SUBARRAY:
|
|
{
|
|
LLVMTypeRef subarray_type = llvm_get_type(c, expr_to_len->type);
|
|
LLVMValueRef len_addr = LLVMBuildStructGEP2(c->builder, subarray_type, expr_to_len->value, 1, "len");
|
|
llvm_value_set_address(be_value, len_addr, type_usize);
|
|
break;
|
|
}
|
|
case TYPE_ARRAY:
|
|
llvm_value_set(be_value, llvm_const_int(c, type_usize, expr_to_len->type->array.len), type_usize);
|
|
break;
|
|
case TYPE_STRLIT:
|
|
TODO
|
|
break;
|
|
|
|
case TYPE_VARARRAY:
|
|
TODO
|
|
default:
|
|
UNREACHABLE
|
|
}
|
|
}
|
|
static void llvm_emit_len(GenContext *c, BEValue *be_value, Expr *expr)
|
|
{
|
|
llvm_emit_expr(c, be_value, expr->len_expr.inner);
|
|
llvm_emit_len_for_expr(c, be_value, be_value);
|
|
}
|
|
|
|
static void gencontext_emit_trap_negative(GenContext *context, Expr *expr, LLVMValueRef value, const char *error)
|
|
{
|
|
if (!build_options.debug_mode) return;
|
|
if (type_is_integer_unsigned(expr->type->canonical)) return;
|
|
|
|
LLVMValueRef zero = llvm_const_int(context, expr->type, 0);
|
|
LLVMValueRef ok = LLVMBuildICmp(context->builder, LLVMIntSLT, value, zero, "underflow");
|
|
llvm_emit_panic_on_true(context, ok, error);
|
|
}
|
|
|
|
static void
|
|
gencontext_emit_slice_values(GenContext *context, Expr *slice, Type **parent_type_ref, LLVMValueRef *parent_base_ref,
|
|
Type **start_type_ref, LLVMValueRef *start_index_ref, Type **end_type_ref,
|
|
LLVMValueRef *end_index_ref)
|
|
{
|
|
assert(slice->expr_kind == EXPR_SLICE);
|
|
|
|
Expr *parent_expr = slice->slice_expr.expr;
|
|
Type *parent_type = parent_expr->type->canonical;
|
|
BEValue parent_addr_x;
|
|
llvm_emit_expr(context, &parent_addr_x, parent_expr);
|
|
llvm_value_addr(context, &parent_addr_x);
|
|
LLVMValueRef parent_addr = parent_addr_x.value;
|
|
LLVMValueRef parent_load_value;
|
|
LLVMValueRef parent_base;
|
|
switch (parent_type->type_kind)
|
|
{
|
|
case TYPE_POINTER:
|
|
parent_load_value = parent_base = gencontext_emit_load(context, parent_type, parent_addr);
|
|
break;
|
|
case TYPE_SUBARRAY:
|
|
parent_load_value = gencontext_emit_load(context, parent_type, parent_addr);
|
|
parent_base = LLVMBuildExtractValue(context->builder, parent_load_value, 0, "");
|
|
break;
|
|
case TYPE_ARRAY:
|
|
parent_base = parent_addr;
|
|
break;
|
|
case TYPE_VARARRAY:
|
|
case TYPE_STRLIT:
|
|
TODO
|
|
default:
|
|
UNREACHABLE
|
|
}
|
|
// Endpoints
|
|
Expr *start = slice->slice_expr.start;
|
|
Expr *end = slice->slice_expr.end;
|
|
|
|
// Emit the start and end
|
|
Type *start_type = start->type->canonical;
|
|
BEValue start_index;
|
|
llvm_emit_expr(context, &start_index, start);
|
|
llvm_value_rvalue(context, &start_index);
|
|
|
|
LLVMValueRef len;
|
|
if (!end || slice->slice_expr.start_from_back || slice->slice_expr.end_from_back || build_options.debug_mode)
|
|
{
|
|
switch (parent_type->type_kind)
|
|
{
|
|
case TYPE_POINTER:
|
|
len = NULL;
|
|
break;
|
|
case TYPE_SUBARRAY:
|
|
len = LLVMBuildExtractValue(context->builder, parent_load_value, 1, "");
|
|
break;
|
|
case TYPE_ARRAY:
|
|
len = llvm_const_int(context, type_usize, parent_type->array.len);
|
|
break;
|
|
case TYPE_VARARRAY:
|
|
case TYPE_STRLIT:
|
|
TODO
|
|
default:
|
|
UNREACHABLE
|
|
}
|
|
}
|
|
|
|
// Walk from end if it is slice from the back.
|
|
if (slice->slice_expr.start_from_back)
|
|
{
|
|
start_index.value = llvm_emit_sub_int(context, start_type, len, start_index.value);
|
|
}
|
|
|
|
// Check that index does not extend beyond the length.
|
|
if (parent_type->type_kind != TYPE_POINTER && build_options.debug_mode)
|
|
{
|
|
|
|
LLVMValueRef exceeds_size = llvm_emit_int_comparison(context,
|
|
type_usize,
|
|
start_type,
|
|
start_index.value,
|
|
len,
|
|
BINARYOP_GE);
|
|
llvm_emit_panic_on_true(context, exceeds_size, "Index exceeds array length.");
|
|
}
|
|
|
|
// Insert trap for negative start offset for non pointers.
|
|
if (parent_type->type_kind != TYPE_POINTER)
|
|
{
|
|
gencontext_emit_trap_negative(context, start, start_index.value, "Negative index");
|
|
}
|
|
|
|
Type *end_type;
|
|
BEValue end_index;
|
|
|
|
if (end)
|
|
{
|
|
// Get the index.
|
|
llvm_emit_expr(context, &end_index, end);
|
|
llvm_value_rvalue(context, &end_index);
|
|
end_type = end->type->canonical;
|
|
|
|
// Reverse if it is "from back"
|
|
if (slice->slice_expr.end_from_back)
|
|
{
|
|
end_index.value = llvm_emit_sub_int(context, end_type, len, end_index.value);
|
|
llvm_value_rvalue(context, &end_index);
|
|
}
|
|
|
|
// This will trap any bad negative index, so we're fine.
|
|
if (build_options.debug_mode)
|
|
{
|
|
LLVMValueRef excess = llvm_emit_int_comparison(context,
|
|
start_type,
|
|
end_type,
|
|
start_index.value,
|
|
end_index.value,
|
|
BINARYOP_GT);
|
|
llvm_emit_panic_on_true(context, excess, "Negative size");
|
|
|
|
if (len)
|
|
{
|
|
LLVMValueRef exceeds_size = llvm_emit_int_comparison(context,
|
|
type_usize,
|
|
end_type,
|
|
len,
|
|
end_index.value,
|
|
BINARYOP_LT);
|
|
llvm_emit_panic_on_true(context, exceeds_size, "Size exceeds index");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
assert(len && "Pointer should never end up here.");
|
|
// Otherwise everything is fine and dandy. Our len - 1 is our end index.
|
|
end_index.value = LLVMBuildSub(context->builder, len, LLVMConstInt(LLVMTypeOf(len), 1, false), "");
|
|
end_type = type_usize;
|
|
}
|
|
|
|
*end_index_ref = end_index.value;
|
|
*end_type_ref = end_type;
|
|
*start_index_ref = start_index.value;
|
|
*start_type_ref = start_type;
|
|
*parent_base_ref = parent_base;
|
|
*parent_type_ref = parent_type;
|
|
}
|
|
|
|
static void gencontext_emit_slice(GenContext *context, BEValue *be_value, Expr *expr)
|
|
{
|
|
Type *parent_type;
|
|
Type *end_type;
|
|
LLVMValueRef end_index;
|
|
LLVMValueRef parent_base;
|
|
Type *start_type;
|
|
LLVMValueRef start_index;
|
|
// Use general function to get all the values we need (a lot!)
|
|
gencontext_emit_slice_values(context, expr, &parent_type,
|
|
&parent_base,
|
|
&start_type, &start_index, &end_type, &end_index);
|
|
|
|
|
|
// Calculate the size
|
|
LLVMValueRef size = LLVMBuildSub(context->builder, LLVMBuildAdd(context->builder, end_index, llvm_const_int(context, start_type, 1), ""), start_index, "size");
|
|
LLVMValueRef start_pointer;
|
|
switch (parent_type->type_kind)
|
|
{
|
|
case TYPE_ARRAY:
|
|
{
|
|
Type *pointer_type = type_get_ptr(parent_type->array.base);
|
|
// Change pointer from Foo[x] to Foo*
|
|
parent_base = llvm_emit_bitcast(context, parent_base, pointer_type);
|
|
// Move pointer
|
|
start_pointer = LLVMBuildInBoundsGEP2(context->builder, llvm_get_type(context, pointer_type->pointer), parent_base, &start_index, 1, "offset");
|
|
break;
|
|
}
|
|
case TYPE_SUBARRAY:
|
|
{
|
|
start_pointer = LLVMBuildInBoundsGEP(context->builder, parent_base, &start_index, 1, "offsetsub");
|
|
break;
|
|
}
|
|
case TYPE_POINTER:
|
|
{
|
|
// Move pointer
|
|
start_pointer = LLVMBuildInBoundsGEP2(context->builder, llvm_get_type(context, parent_type->pointer), parent_base, &start_index, 1, "offset");
|
|
break;
|
|
}
|
|
default:
|
|
TODO
|
|
}
|
|
|
|
// Create a new subarray type
|
|
LLVMValueRef result = LLVMGetUndef(llvm_get_type(context, expr->type));
|
|
result = LLVMBuildInsertValue(context->builder, result, start_pointer, 0, "");
|
|
llvm_value_set(be_value, LLVMBuildInsertValue(context->builder, result, size, 1, ""), expr->type);
|
|
}
|
|
|
|
static void gencontext_emit_slice_assign(GenContext *c, BEValue *be_value, Expr *expr)
|
|
{
|
|
// We will be replacing the slice assign with code that roughly looks like this:
|
|
// size_t end = slice_end;
|
|
// size_t slice_current = slice_start;
|
|
// while (slice_current <= end) pointer[slice_current++] = value;
|
|
|
|
// First, find the value assigned.
|
|
Expr *assigned_value = expr->slice_assign_expr.right;
|
|
llvm_emit_expr(c, be_value, assigned_value);
|
|
|
|
Type *parent_type;
|
|
Type *end_type;
|
|
LLVMValueRef end_index;
|
|
LLVMValueRef parent_base;
|
|
Type *start_type;
|
|
LLVMValueRef start_index;
|
|
// Use general function to get all the values we need (a lot!)
|
|
gencontext_emit_slice_values(c, expr->slice_assign_expr.left, &parent_type,
|
|
&parent_base,
|
|
&start_type, &start_index, &end_type, &end_index);
|
|
|
|
// We will need to iterate for the general case.
|
|
LLVMBasicBlockRef start_block = c->current_block;
|
|
LLVMBasicBlockRef cond_block = llvm_basic_block_new(c, "cond");
|
|
LLVMBasicBlockRef exit_block = llvm_basic_block_new(c, "exit");
|
|
LLVMBasicBlockRef assign_block = llvm_basic_block_new(c, "assign");
|
|
|
|
// First jump to the cond block.
|
|
llvm_emit_br(c, cond_block);
|
|
llvm_emit_block(c, cond_block);
|
|
|
|
// We emit a phi here: value is either the start value (start_offset) or the next value (next_offset)
|
|
// but we haven't generated the latter yet, so we defer that.
|
|
EMIT_LOC(c, expr);
|
|
LLVMValueRef offset = LLVMBuildPhi(c->builder, llvm_get_type(c, start_type), "");
|
|
|
|
// Check if we're not at the end.
|
|
LLVMValueRef not_at_end = llvm_emit_int_comparison(c, start_type, end_type, offset, end_index, BINARYOP_LE);
|
|
|
|
// If jump to the assign block if we're not at the end index.
|
|
BEValue value;
|
|
EMIT_LOC(c, expr);
|
|
llvm_value_set_bool(&value, not_at_end);
|
|
llvm_emit_cond_br(c, &value, assign_block, exit_block);
|
|
|
|
// Emit the assign.
|
|
llvm_emit_block(c, assign_block);
|
|
// Reuse this calculation
|
|
LLVMValueRef target = llvm_emit_subscript_addr_with_base(c, parent_type, parent_base, offset);
|
|
// And store the value.
|
|
// TODO correct alignment.
|
|
llvm_store_bevalue_aligned(c, target, be_value, 0);
|
|
|
|
// Create the new offset
|
|
LLVMValueRef next_offset = llvm_emit_add_int(c, start_type, offset, llvm_const_int(c, start_type, 1));
|
|
|
|
// And jump back
|
|
llvm_emit_br(c, cond_block);
|
|
|
|
// Finally set up our phi
|
|
LLVMValueRef logic_values[2] = { start_index, next_offset };
|
|
LLVMBasicBlockRef blocks[2] = { start_block, assign_block };
|
|
LLVMAddIncoming(offset, logic_values, blocks, 2);
|
|
|
|
// And emit the exit block.
|
|
llvm_emit_block(c, exit_block);
|
|
|
|
}
|
|
|
|
static void gencontext_emit_logical_and_or(GenContext *c, BEValue *be_value, Expr *expr, BinaryOp op)
|
|
{
|
|
// Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E)
|
|
// For vector implementation.
|
|
|
|
// Set up basic blocks, following Cone
|
|
LLVMBasicBlockRef start_block = LLVMGetInsertBlock(c->builder);
|
|
LLVMBasicBlockRef phi_block = llvm_basic_block_new(c, op == BINARYOP_AND ? "and.phi" : "or.phi");
|
|
LLVMBasicBlockRef rhs_block = llvm_basic_block_new(c, op == BINARYOP_AND ? "and.rhs" : "or.rhs");
|
|
|
|
// Generate left-hand condition and conditional branch
|
|
llvm_emit_expr(c, be_value, expr->binary_expr.left);
|
|
llvm_value_rvalue(c, be_value);
|
|
|
|
if (op == BINARYOP_AND)
|
|
{
|
|
llvm_emit_cond_br(c, be_value, rhs_block, phi_block);
|
|
}
|
|
else
|
|
{
|
|
llvm_emit_cond_br(c, be_value, phi_block, rhs_block);
|
|
}
|
|
|
|
llvm_emit_block(c, rhs_block);
|
|
BEValue rhs_value;
|
|
llvm_emit_expr(c, &rhs_value, expr->binary_expr.right);
|
|
llvm_value_rvalue(c, &rhs_value);
|
|
|
|
LLVMBasicBlockRef end_block = c->current_block;
|
|
llvm_emit_br(c, phi_block);
|
|
|
|
// Generate phi
|
|
llvm_emit_block(c, phi_block);
|
|
|
|
// Simplify for LLVM by entering the constants we already know of.
|
|
LLVMValueRef result_on_skip = LLVMConstInt(c->bool_type, op == BINARYOP_AND ? 0 : 1, 0);
|
|
|
|
// One possibility here is that a return happens inside of the expression.
|
|
if (!end_block)
|
|
{
|
|
llvm_value_set_bool(be_value, result_on_skip);
|
|
return;
|
|
}
|
|
LLVMValueRef phi = LLVMBuildPhi(c->builder, c->bool_type, "val");
|
|
LLVMValueRef logic_values[2] = { result_on_skip, rhs_value.value };
|
|
LLVMBasicBlockRef blocks[2] = { start_block, end_block };
|
|
LLVMAddIncoming(phi, logic_values, blocks, 2);
|
|
|
|
llvm_value_set_bool(be_value, phi);
|
|
}
|
|
|
|
|
|
|
|
LLVMValueRef llvm_emit_int_comparison(GenContext *c, Type *lhs_type, Type *rhs_type, LLVMValueRef lhs_value, LLVMValueRef rhs_value, BinaryOp binary_op)
|
|
{
|
|
bool lhs_signed = type_is_signed(lhs_type);
|
|
bool rhs_signed = type_is_signed(rhs_type);
|
|
if (lhs_signed != rhs_signed)
|
|
{
|
|
// Swap sides if needed.
|
|
if (!lhs_signed)
|
|
{
|
|
Type *temp = lhs_type;
|
|
lhs_type = rhs_type;
|
|
rhs_type = temp;
|
|
LLVMValueRef temp_val = lhs_value;
|
|
lhs_value = rhs_value;
|
|
rhs_value = temp_val;
|
|
switch (binary_op)
|
|
{
|
|
case BINARYOP_GE:
|
|
binary_op = BINARYOP_LE;
|
|
break;
|
|
case BINARYOP_GT:
|
|
binary_op = BINARYOP_LT;
|
|
break;
|
|
case BINARYOP_LE:
|
|
binary_op = BINARYOP_GE;
|
|
break;
|
|
case BINARYOP_LT:
|
|
binary_op = BINARYOP_GT;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
lhs_signed = true;
|
|
rhs_signed = false;
|
|
}
|
|
}
|
|
if (!lhs_signed)
|
|
{
|
|
assert(lhs_signed == rhs_signed);
|
|
// Right and left side are both unsigned.
|
|
switch (binary_op)
|
|
{
|
|
case BINARYOP_EQ:
|
|
return LLVMBuildICmp(c->builder, LLVMIntEQ, lhs_value, rhs_value, "eq");
|
|
case BINARYOP_NE:
|
|
return LLVMBuildICmp(c->builder, LLVMIntNE, lhs_value, rhs_value, "neq");
|
|
case BINARYOP_GE:
|
|
return LLVMBuildICmp(c->builder, LLVMIntUGE, lhs_value, rhs_value, "ge");
|
|
case BINARYOP_GT:
|
|
return LLVMBuildICmp(c->builder, LLVMIntUGT, lhs_value, rhs_value, "gt");
|
|
case BINARYOP_LE:
|
|
return LLVMBuildICmp(c->builder, LLVMIntULE, lhs_value, rhs_value, "le");
|
|
case BINARYOP_LT:
|
|
return LLVMBuildICmp(c->builder, LLVMIntULT, lhs_value, rhs_value, "lt");
|
|
default:
|
|
UNREACHABLE
|
|
}
|
|
}
|
|
|
|
// Left side is signed.
|
|
LLVMValueRef comp_value;
|
|
LLVMValueRef check_value;
|
|
switch (binary_op)
|
|
{
|
|
case BINARYOP_EQ:
|
|
comp_value = LLVMBuildICmp(c->builder, LLVMIntEQ, lhs_value, rhs_value, "eq");
|
|
break;
|
|
case BINARYOP_NE:
|
|
comp_value = LLVMBuildICmp(c->builder, LLVMIntNE, lhs_value, rhs_value, "neq");
|
|
break;
|
|
case BINARYOP_GE:
|
|
comp_value = LLVMBuildICmp(c->builder, LLVMIntSGE, lhs_value, rhs_value, "ge");
|
|
break;
|
|
case BINARYOP_GT:
|
|
comp_value = LLVMBuildICmp(c->builder, LLVMIntSGT, lhs_value, rhs_value, "gt");
|
|
break;
|
|
case BINARYOP_LE:
|
|
comp_value = LLVMBuildICmp(c->builder, LLVMIntSLE, lhs_value, rhs_value, "le");
|
|
break;
|
|
case BINARYOP_LT:
|
|
comp_value = LLVMBuildICmp(c->builder, LLVMIntSLT, lhs_value, rhs_value, "lt");
|
|
break;
|
|
default:
|
|
UNREACHABLE
|
|
}
|
|
|
|
// If right side is also signed then this is fine.
|
|
if (rhs_signed) return comp_value;
|
|
|
|
// Otherwise, special handling for left side signed, right side unsigned.
|
|
LLVMValueRef zero = llvm_get_zero(c, lhs_type);
|
|
switch (binary_op)
|
|
{
|
|
case BINARYOP_EQ:
|
|
// Only true if lhs >= 0
|
|
check_value = LLVMBuildICmp(c->builder, LLVMIntSGE, lhs_value, zero, "check");
|
|
return LLVMBuildAnd(c->builder, check_value, comp_value, "siui-eq");
|
|
case BINARYOP_NE:
|
|
// Always true if lhs < 0
|
|
check_value = LLVMBuildICmp(c->builder, LLVMIntSLT, lhs_value, zero, "check");
|
|
return LLVMBuildOr(c->builder, check_value, comp_value, "siui-ne");
|
|
case BINARYOP_GE:
|
|
// Only true if rhs >= 0 when regarded as a signed integer
|
|
check_value = LLVMBuildICmp(c->builder, LLVMIntSGE, rhs_value, zero, "check");
|
|
return LLVMBuildAnd(c->builder, check_value, comp_value, "siui-ge");
|
|
case BINARYOP_GT:
|
|
// Only true if rhs >= 0 when regarded as a signed integer
|
|
check_value = LLVMBuildICmp(c->builder, LLVMIntSGE, rhs_value, zero, "check");
|
|
return LLVMBuildAnd(c->builder, check_value, comp_value, "siui-gt");
|
|
case BINARYOP_LE:
|
|
// Always true if rhs < 0 when regarded as a signed integer
|
|
check_value = LLVMBuildICmp(c->builder, LLVMIntSLT, rhs_value, zero, "check");
|
|
return LLVMBuildOr(c->builder, check_value, comp_value, "siui-le");
|
|
case BINARYOP_LT:
|
|
// Always true if rhs < 0 when regarded as a signed integer
|
|
check_value = LLVMBuildICmp(c->builder, LLVMIntSLT, rhs_value, zero, "check");
|
|
return LLVMBuildOr(c->builder, check_value, comp_value, "siui-lt");
|
|
default:
|
|
UNREACHABLE
|
|
}
|
|
|
|
}
|
|
|
|
static inline LLVMValueRef llvm_fixup_shift_rhs(GenContext *c, LLVMValueRef left, LLVMValueRef right)
|
|
{
|
|
LLVMTypeRef left_type = LLVMTypeOf(left);
|
|
LLVMTypeRef right_type = LLVMTypeOf(right);
|
|
if (left_type == right_type) return right;
|
|
if (LLVMStoreSizeOfType(build_target.target, left_type) < LLVMStoreSizeOfType(build_target.target, right_type))
|
|
{
|
|
return LLVMBuildTrunc(c->builder, right, left_type, "");
|
|
}
|
|
else
|
|
{
|
|
return LLVMBuildZExt(c->builder, right, left_type, "");
|
|
}
|
|
}
|
|
|
|
static inline LLVMValueRef llvm_emit_mult_int(GenContext *c, Type *type, LLVMValueRef left, LLVMValueRef right)
|
|
{
|
|
if (build_options.trap_wrapping)
|
|
{
|
|
LLVMTypeRef type_to_use = llvm_get_type(c, type);
|
|
LLVMValueRef args[2] = { left, right };
|
|
LLVMTypeRef types[2] = { type_to_use, type_to_use };
|
|
unsigned operation = type_is_integer_unsigned(type) ? intrinsic_id_umul_overflow
|
|
: intrinsic_id_smul_overflow;
|
|
LLVMValueRef call_res = llvm_emit_call_intrinsic(c,
|
|
operation,
|
|
types,
|
|
1,
|
|
args,
|
|
2);
|
|
LLVMValueRef val = LLVMBuildExtractValue(c->builder, call_res, 0, "mul");
|
|
LLVMValueRef ok = LLVMBuildExtractValue(c->builder, call_res, 1, "");
|
|
llvm_emit_panic_on_true(c, ok, "Integer multiplication overflow");
|
|
return val;
|
|
}
|
|
return LLVMBuildMul(c->builder, left, right, "mul");
|
|
}
|
|
|
|
static void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValue *lhs_addr, BinaryOp binary_op)
|
|
{
|
|
|
|
if (binary_op == BINARYOP_AND || binary_op == BINARYOP_OR)
|
|
{
|
|
gencontext_emit_logical_and_or(c, be_value, expr, binary_op);
|
|
return;
|
|
}
|
|
BEValue lhs;
|
|
if (lhs_addr)
|
|
{
|
|
lhs = *lhs_addr;
|
|
}
|
|
else
|
|
{
|
|
llvm_emit_expr(c, &lhs, expr->binary_expr.left);
|
|
}
|
|
llvm_value_rvalue(c, &lhs);
|
|
|
|
BEValue rhs;
|
|
llvm_emit_expr(c, &rhs, expr->binary_expr.right);
|
|
llvm_value_rvalue(c, &rhs);
|
|
|
|
Type *lhs_type = type_lowering(lhs.type);
|
|
Type *rhs_type = type_lowering(rhs.type);
|
|
LLVMValueRef lhs_value = lhs.value;
|
|
LLVMValueRef rhs_value = rhs.value;
|
|
EMIT_LOC(c, expr);
|
|
if (type_is_integer(lhs_type) && binary_op >= BINARYOP_GT && binary_op <= BINARYOP_EQ)
|
|
{
|
|
llvm_value_set_bool(be_value,
|
|
llvm_emit_int_comparison(c,
|
|
lhs_type,
|
|
rhs_type,
|
|
lhs_value,
|
|
rhs_value,
|
|
binary_op));
|
|
return;
|
|
}
|
|
bool is_float = type_is_float(lhs_type);
|
|
LLVMValueRef val;
|
|
switch (binary_op)
|
|
{
|
|
case BINARYOP_ERROR:
|
|
UNREACHABLE
|
|
case BINARYOP_MULT:
|
|
if (is_float)
|
|
{
|
|
val = LLVMBuildFMul(c->builder, lhs_value, rhs_value, "fmul");
|
|
break;
|
|
}
|
|
val = llvm_emit_mult_int(c, lhs_type, lhs_value, rhs_value);
|
|
break;
|
|
case BINARYOP_SUB:
|
|
if (lhs_type->type_kind == TYPE_POINTER)
|
|
{
|
|
if (lhs_type == rhs_type)
|
|
{
|
|
val = LLVMBuildPtrDiff(c->builder, lhs_value, rhs_value, "ptrdiff");
|
|
break;
|
|
}
|
|
rhs_value = LLVMBuildNeg(c->builder, rhs_value, "");
|
|
val = LLVMBuildGEP2(c->builder, llvm_get_type(c, lhs_type->canonical->pointer), lhs_value, &rhs_value, 1, "ptrsub");
|
|
break;
|
|
}
|
|
if (is_float)
|
|
{
|
|
val = LLVMBuildFSub(c->builder, lhs_value, rhs_value, "fsub");
|
|
break;
|
|
}
|
|
val = llvm_emit_sub_int(c, lhs_type, lhs_value, rhs_value);
|
|
break;
|
|
case BINARYOP_ADD:
|
|
if (lhs_type->type_kind == TYPE_POINTER)
|
|
{
|
|
assert(type_is_integer(rhs_type));
|
|
val = LLVMBuildGEP2(c->builder, llvm_get_type(c, lhs_type->pointer), lhs_value, &rhs_value, 1, "ptradd");
|
|
break;
|
|
}
|
|
if (is_float)
|
|
{
|
|
val = LLVMBuildFAdd(c->builder, lhs_value, rhs_value, "fadd");
|
|
break;
|
|
}
|
|
val = llvm_emit_add_int(c, lhs_type, lhs_value, rhs_value);
|
|
break;
|
|
case BINARYOP_DIV:
|
|
if (is_float)
|
|
{
|
|
val = LLVMBuildFDiv(c->builder, lhs_value, rhs_value, "fdiv");
|
|
break;
|
|
}
|
|
val = type_is_unsigned(lhs_type)
|
|
? LLVMBuildUDiv(c->builder, lhs_value, rhs_value, "udiv")
|
|
: LLVMBuildSDiv(c->builder, lhs_value, rhs_value, "sdiv");
|
|
break;
|
|
case BINARYOP_MOD:
|
|
val = type_is_unsigned(lhs_type)
|
|
? LLVMBuildURem(c->builder, lhs_value, rhs_value, "umod")
|
|
: LLVMBuildSRem(c->builder, lhs_value, rhs_value, "smod");
|
|
break;
|
|
case BINARYOP_SHR:
|
|
rhs_value = llvm_fixup_shift_rhs(c, lhs_value, rhs_value);
|
|
val = type_is_unsigned(lhs_type)
|
|
? LLVMBuildLShr(c->builder, lhs_value, rhs_value, "lshr")
|
|
: LLVMBuildAShr(c->builder, lhs_value, rhs_value, "ashr");
|
|
break;
|
|
case BINARYOP_SHL:
|
|
rhs_value = llvm_fixup_shift_rhs(c, lhs_value, rhs_value);
|
|
val = LLVMBuildShl(c->builder, lhs_value, rhs_value, "shl");
|
|
break;
|
|
case BINARYOP_BIT_AND:
|
|
val = LLVMBuildAnd(c->builder, lhs_value, rhs_value, "and");
|
|
break;
|
|
case BINARYOP_BIT_OR:
|
|
val = LLVMBuildOr(c->builder, lhs_value, rhs_value, "or");
|
|
break;
|
|
case BINARYOP_BIT_XOR:
|
|
val = LLVMBuildXor(c->builder, lhs_value, rhs_value, "xor");
|
|
break;
|
|
case BINARYOP_EQ:
|
|
// Unordered?
|
|
assert(type_is_float(lhs_type));
|
|
llvm_value_set_bool(be_value, LLVMBuildFCmp(c->builder, LLVMRealUEQ, lhs_value, rhs_value, "eq"));
|
|
return;
|
|
case BINARYOP_NE:
|
|
// Unordered?
|
|
assert(type_is_float(lhs_type));
|
|
llvm_value_set_bool(be_value, LLVMBuildFCmp(c->builder, LLVMRealUNE, lhs_value, rhs_value, "neq"));
|
|
return;
|
|
case BINARYOP_GE:
|
|
assert(type_is_float(lhs_type));
|
|
llvm_value_set_bool(be_value, LLVMBuildFCmp(c->builder, LLVMRealUGE, lhs_value, rhs_value, "ge"));
|
|
return;
|
|
case BINARYOP_GT:
|
|
assert(type_is_float(lhs_type));
|
|
llvm_value_set_bool(be_value, LLVMBuildFCmp(c->builder, LLVMRealUGT, lhs_value, rhs_value, "gt"));
|
|
return;
|
|
case BINARYOP_LE:
|
|
assert(type_is_float(lhs_type));
|
|
llvm_value_set_bool(be_value, LLVMBuildFCmp(c->builder, LLVMRealULE, lhs_value, rhs_value, "le"));
|
|
return;
|
|
case BINARYOP_LT:
|
|
assert(type_is_float(lhs_type));
|
|
llvm_value_set_bool(be_value, LLVMBuildFCmp(c->builder, LLVMRealULT, lhs_value, rhs_value, "lt"));
|
|
return;
|
|
case BINARYOP_AND:
|
|
case BINARYOP_OR:
|
|
case BINARYOP_ASSIGN:
|
|
case BINARYOP_MULT_ASSIGN:
|
|
case BINARYOP_ADD_ASSIGN:
|
|
case BINARYOP_SUB_ASSIGN:
|
|
case BINARYOP_DIV_ASSIGN:
|
|
case BINARYOP_MOD_ASSIGN:
|
|
case BINARYOP_BIT_AND_ASSIGN:
|
|
case BINARYOP_BIT_OR_ASSIGN:
|
|
case BINARYOP_BIT_XOR_ASSIGN:
|
|
case BINARYOP_SHR_ASSIGN:
|
|
case BINARYOP_SHL_ASSIGN:
|
|
UNREACHABLE
|
|
}
|
|
llvm_value_set(be_value, val, expr->type);
|
|
}
|
|
|
|
|
|
static void llvm_emit_post_unary_expr(GenContext *context, BEValue *be_value, Expr *expr)
|
|
{
|
|
|
|
llvm_emit_post_inc_dec(context,
|
|
be_value,
|
|
expr->post_expr.expr,
|
|
expr->post_expr.operator == POSTUNARYOP_INC ? 1 : -1,
|
|
false);
|
|
}
|
|
|
|
static void gencontext_emit_typeid(GenContext *context, BEValue *be_value, Expr *expr)
|
|
{
|
|
LLVMValueRef value;
|
|
if (type_is_builtin(expr->typeid_expr->type->type_kind))
|
|
{
|
|
value = llvm_const_int(context, type_usize, expr->typeid_expr->type->type_kind);
|
|
}
|
|
else
|
|
{
|
|
assert(expr->typeid_expr->type->backend_typeid);
|
|
value = expr->typeid_expr->type->backend_typeid;
|
|
}
|
|
llvm_value_set(be_value, value, expr->type);
|
|
}
|
|
|
|
void gencontext_emit_trycatch_expr(GenContext *c, BEValue *value, Expr *expr)
|
|
{
|
|
|
|
LLVMBasicBlockRef error_block = llvm_basic_block_new(c, "error_block");
|
|
LLVMBasicBlockRef no_err_block = llvm_basic_block_new(c, "noerr_block");
|
|
LLVMBasicBlockRef phi_block = llvm_basic_block_new(c, "phi_block");
|
|
|
|
// Store catch/error var
|
|
PUSH_ERROR();
|
|
|
|
// Set the catch/error var
|
|
c->error_var = NULL;
|
|
c->catch_block = error_block;
|
|
|
|
llvm_emit_expr(c, value, expr->trycatch_expr);
|
|
|
|
// Restore.
|
|
POP_ERROR();
|
|
|
|
// Emit success and jump to phi.
|
|
llvm_emit_br(c, no_err_block);
|
|
llvm_emit_block(c, no_err_block);
|
|
llvm_emit_br(c, phi_block);
|
|
|
|
// Emit error and jump to phi
|
|
llvm_emit_block(c, error_block);
|
|
llvm_emit_br(c, phi_block);
|
|
|
|
llvm_emit_block(c, phi_block);
|
|
|
|
LLVMValueRef phi = LLVMBuildPhi(c->builder, llvm_get_type(c, expr->type), "val");
|
|
LLVMValueRef lhs = llvm_const_int(c, type_bool, expr->expr_kind == EXPR_TRY ? 1 : 0);
|
|
LLVMValueRef rhs = llvm_const_int(c, type_bool, expr->expr_kind == EXPR_TRY ? 0 : 1);
|
|
|
|
LLVMValueRef logic_values[2] = { lhs, rhs };
|
|
LLVMBasicBlockRef blocks[2] = { no_err_block, error_block };
|
|
LLVMAddIncoming(phi, logic_values, blocks, 2);
|
|
|
|
llvm_value_set(value, phi, expr->type);
|
|
}
|
|
|
|
static inline void gencontext_emit_else_jump_expr(GenContext *c, BEValue *be_value, Expr *expr)
|
|
{
|
|
LLVMBasicBlockRef else_block = llvm_basic_block_new(c, "else_block");
|
|
LLVMBasicBlockRef no_err_block = llvm_basic_block_new(c, "noerr_block");
|
|
|
|
// Store catch/error var
|
|
PUSH_ERROR();
|
|
|
|
// Set the catch/error var
|
|
c->error_var = NULL;
|
|
c->catch_block = else_block;
|
|
|
|
|
|
llvm_emit_expr(c, be_value, expr->else_expr.expr);
|
|
llvm_value_rvalue(c, be_value);
|
|
|
|
// Restore.
|
|
POP_ERROR();
|
|
|
|
// Emit success and to end.
|
|
llvm_emit_br(c, no_err_block);
|
|
|
|
// Emit else
|
|
llvm_emit_block(c, else_block);
|
|
llvm_emit_stmt(c, expr->else_expr.else_stmt);
|
|
llvm_emit_br(c, no_err_block);
|
|
|
|
llvm_emit_block(c, no_err_block);
|
|
}
|
|
|
|
|
|
static void gencontext_emit_else_expr(GenContext *c, BEValue *be_value, Expr *expr)
|
|
{
|
|
if (expr->else_expr.is_jump)
|
|
{
|
|
gencontext_emit_else_jump_expr(c, be_value, expr);
|
|
return;
|
|
}
|
|
LLVMBasicBlockRef else_block = llvm_basic_block_new(c, "else_block");
|
|
LLVMBasicBlockRef phi_block = llvm_basic_block_new(c, "phi_block");
|
|
|
|
// Store catch/error var
|
|
PUSH_ERROR();
|
|
|
|
// Set the catch/error var
|
|
c->error_var = NULL;
|
|
c->catch_block = else_block;
|
|
|
|
BEValue normal_value;
|
|
llvm_emit_expr(c, &normal_value, expr->else_expr.expr);
|
|
llvm_value_rvalue(c, &normal_value);
|
|
|
|
// Restore.
|
|
POP_ERROR();
|
|
|
|
// Emit success and jump to phi.
|
|
LLVMBasicBlockRef success_end_block = llvm_get_current_block_if_in_use(c);
|
|
|
|
if (success_end_block) llvm_emit_br(c, phi_block);
|
|
|
|
// Emit else
|
|
llvm_emit_block(c, else_block);
|
|
|
|
BEValue else_value;
|
|
llvm_emit_expr(c, &else_value, expr->else_expr.else_expr);
|
|
llvm_value_rvalue(c, &else_value);
|
|
|
|
LLVMBasicBlockRef else_block_exit = llvm_get_current_block_if_in_use(c);
|
|
|
|
if (else_block_exit) llvm_emit_br(c, phi_block);
|
|
|
|
llvm_emit_block(c, phi_block);
|
|
|
|
if (!else_block_exit)
|
|
{
|
|
*be_value = normal_value;
|
|
return;
|
|
}
|
|
if (!success_end_block)
|
|
{
|
|
*be_value = else_value;
|
|
return;
|
|
}
|
|
|
|
LLVMValueRef phi = LLVMBuildPhi(c->builder, llvm_get_type(c, expr->type), "val");
|
|
|
|
LLVMValueRef logic_values[2] = { normal_value.value, else_value.value };
|
|
LLVMBasicBlockRef blocks[2] = { success_end_block, else_block_exit };
|
|
LLVMAddIncoming(phi, logic_values, blocks, 2);
|
|
|
|
llvm_value_set(be_value, phi, expr->type);
|
|
|
|
}
|
|
|
|
/**
|
|
* This is the foo!! instruction.
|
|
*/
|
|
static inline void gencontext_emit_guard_expr(GenContext *c, BEValue *be_value, Expr *expr)
|
|
{
|
|
LLVMBasicBlockRef guard_block = llvm_basic_block_new(c, "guard_block");
|
|
LLVMBasicBlockRef no_err_block = llvm_basic_block_new(c, "noerr_block");
|
|
|
|
// Store catch/error var
|
|
PUSH_ERROR();
|
|
|
|
// Set the catch/error var
|
|
LLVMValueRef error_var = llvm_emit_alloca_aligned(c, type_error, "error_var");
|
|
|
|
c->error_var = error_var;
|
|
c->catch_block = guard_block;
|
|
|
|
llvm_emit_expr(c, be_value, expr->guard_expr.inner);
|
|
printf("TODO: Passed rvalue on guard, consider semantics.");
|
|
llvm_value_rvalue(c, be_value);
|
|
|
|
// Restore.
|
|
POP_ERROR();
|
|
|
|
// Emit success and to end.
|
|
llvm_emit_br(c, no_err_block);
|
|
|
|
POP_ERROR();
|
|
|
|
// Emit else
|
|
llvm_emit_block(c, guard_block);
|
|
|
|
// Ensure we are on a branch that is non empty.
|
|
if (llvm_emit_check_block_branch(c))
|
|
{
|
|
llvm_emit_defer(c, expr->guard_expr.defer, 0);
|
|
BEValue value;
|
|
llvm_value_set_address(&value, error_var, type_error);
|
|
llvm_emit_return_abi(c, NULL, &value);
|
|
c->current_block = NULL;
|
|
c->current_block_is_target = NULL;
|
|
}
|
|
|
|
llvm_emit_block(c, no_err_block);
|
|
|
|
}
|
|
|
|
static void gencontext_emit_binary_expr(GenContext *context, BEValue *be_value, Expr *expr)
|
|
{
|
|
BinaryOp binary_op = expr->binary_expr.operator;
|
|
if (binary_op > BINARYOP_ASSIGN)
|
|
{
|
|
BinaryOp base_op = binaryop_assign_base_op(binary_op);
|
|
assert(base_op != BINARYOP_ERROR);
|
|
BEValue addr;
|
|
llvm_emit_expr(context, &addr, expr->binary_expr.left);
|
|
llvm_value_addr(context, &addr);
|
|
gencontext_emit_binary(context, be_value, expr, &addr, base_op);
|
|
llvm_store_bevalue(context, &addr, be_value);
|
|
return;
|
|
}
|
|
if (binary_op == BINARYOP_ASSIGN)
|
|
{
|
|
llvm_emit_expr(context, be_value, expr->binary_expr.left);
|
|
assert(llvm_value_is_addr(be_value));
|
|
LLVMValueRef failable_ref = NULL;
|
|
if (expr->binary_expr.left->expr_kind == EXPR_IDENTIFIER)
|
|
{
|
|
failable_ref = decl_failable_ref(expr->binary_expr.left->identifier_expr.decl);
|
|
}
|
|
*be_value = llvm_emit_assign_expr(context, be_value, expr->binary_expr.right, failable_ref);
|
|
return;
|
|
}
|
|
|
|
gencontext_emit_binary(context, be_value, expr, NULL, binary_op);
|
|
}
|
|
|
|
void gencontext_emit_elvis_expr(GenContext *c, BEValue *value, Expr *expr)
|
|
{
|
|
LLVMBasicBlockRef current_block = c->current_block;
|
|
LLVMBasicBlockRef phi_block = llvm_basic_block_new(c, "cond.phi");
|
|
LLVMBasicBlockRef rhs_block = llvm_basic_block_new(c, "cond.rhs");
|
|
|
|
// Generate condition and conditional branch
|
|
llvm_emit_expr(c, value, expr->ternary_expr.cond);
|
|
// Get the Rvalue version (in case we have an address)
|
|
llvm_value_rvalue(c, value);
|
|
|
|
LLVMValueRef lhs = value->value;
|
|
Type *cond_type = expr->ternary_expr.cond->type;
|
|
|
|
// If the cond is not a boolean, we need to do the cast.
|
|
if (value->kind != BE_BOOLEAN)
|
|
{
|
|
CastKind cast = cast_to_bool_kind(cond_type);
|
|
llvm_emit_cast(c, cast, value, type_bool, cond_type);
|
|
}
|
|
|
|
llvm_emit_cond_br(c, value, phi_block, rhs_block);
|
|
|
|
llvm_emit_block(c, rhs_block);
|
|
// Emit right side:
|
|
llvm_emit_expr(c, value, expr->ternary_expr.else_expr);
|
|
// Lower to value.
|
|
llvm_value_rvalue(c, value);
|
|
|
|
LLVMBasicBlockRef end_block = c->current_block;
|
|
llvm_emit_br(c, phi_block);
|
|
|
|
// Generate phi
|
|
llvm_emit_block(c, phi_block);
|
|
|
|
// If both sides are bool we produce a bool as well.
|
|
LLVMTypeRef phi_type = expr->type->canonical->type_kind == TYPE_BOOL ? c->bool_type : llvm_get_type(c, expr->type);
|
|
LLVMValueRef phi = LLVMBuildPhi(c->builder, phi_type, "val");
|
|
|
|
LLVMValueRef logic_values[2] = { lhs, value->value };
|
|
LLVMBasicBlockRef blocks[2] = { current_block, end_block };
|
|
LLVMAddIncoming(phi, logic_values, blocks, 2);
|
|
|
|
// The rest of value should now be set to the right value.
|
|
value->value = phi;
|
|
}
|
|
|
|
void gencontext_emit_ternary_expr(GenContext *c, BEValue *value, Expr *expr)
|
|
{
|
|
if (expr->ternary_expr.then_expr == NULL) return gencontext_emit_elvis_expr(c, value, expr);
|
|
|
|
// Set up basic blocks, following Cone
|
|
LLVMBasicBlockRef phi_block = llvm_basic_block_new(c, "cond.phi");
|
|
LLVMBasicBlockRef lhs_block = llvm_basic_block_new(c, "cond.lhs");
|
|
LLVMBasicBlockRef rhs_block = llvm_basic_block_new(c, "cond.rhs");
|
|
|
|
// Generate condition and conditional branch
|
|
|
|
llvm_emit_expr(c, value, expr->ternary_expr.cond);
|
|
llvm_value_rvalue(c, value);
|
|
|
|
assert(value->kind == BE_BOOLEAN);
|
|
|
|
llvm_emit_cond_br(c, value, lhs_block, rhs_block);
|
|
|
|
llvm_emit_block(c, lhs_block);
|
|
BEValue lhs;
|
|
llvm_emit_expr(c, &lhs, expr->ternary_expr.then_expr);
|
|
llvm_value_rvalue(c, &lhs);
|
|
|
|
LLVMBasicBlockRef lhs_exit = llvm_get_current_block_if_in_use(c);
|
|
if (lhs_exit) llvm_emit_br(c, phi_block);
|
|
|
|
llvm_emit_block(c, rhs_block);
|
|
BEValue rhs;
|
|
llvm_emit_expr(c, &rhs, expr->ternary_expr.else_expr);
|
|
llvm_value_rvalue(c, &rhs);
|
|
|
|
LLVMBasicBlockRef rhs_exit = llvm_get_current_block_if_in_use(c);
|
|
if (rhs_exit) llvm_emit_br(c, phi_block);
|
|
|
|
// Generate phi
|
|
llvm_emit_block(c, phi_block);
|
|
if (!rhs_exit)
|
|
{
|
|
*value = lhs;
|
|
}
|
|
if (!lhs_exit)
|
|
{
|
|
*value = rhs;
|
|
}
|
|
LLVMValueRef phi = LLVMBuildPhi(c->builder, llvm_get_type(c, expr->type), "val");
|
|
LLVMValueRef logic_values[2] = { lhs.value, rhs.value };
|
|
LLVMBasicBlockRef blocks[2] = { lhs_exit, rhs_exit };
|
|
LLVMAddIncoming(phi, logic_values, blocks, 2);
|
|
|
|
llvm_value_set(value, phi, expr->type);
|
|
}
|
|
|
|
|
|
static void llvm_emit_const_expr(GenContext *c, BEValue *be_value, Expr *expr)
|
|
{
|
|
Type *type = type_reduced_from_expr(expr)->canonical;
|
|
switch (expr->const_expr.kind)
|
|
{
|
|
case ALL_INTS:
|
|
if (type_is_unsigned(type))
|
|
{
|
|
llvm_value_set(be_value, llvm_const_int(c, type, bigint_as_unsigned(&expr->const_expr.i)), type);
|
|
}
|
|
else
|
|
{
|
|
llvm_value_set(be_value, llvm_const_int(c, type, bigint_as_signed(&expr->const_expr.i)), type);
|
|
}
|
|
return;
|
|
case TYPE_COMPLEX:
|
|
{
|
|
LLVMTypeRef element_type = llvm_get_type(c, type->complex);
|
|
LLVMValueRef value = LLVMGetUndef(llvm_get_type(c, type));
|
|
unsigned id = 0;
|
|
value = LLVMConstInsertValue(value, LLVMConstReal(element_type, (double)expr->const_expr.complex.r), &id, 1);
|
|
id++;
|
|
value = LLVMConstInsertValue(value, LLVMConstReal(element_type, (double)expr->const_expr.complex.i), &id, 1);
|
|
llvm_value_set(be_value, value, type);
|
|
return;
|
|
}
|
|
case ALL_FLOATS:
|
|
llvm_value_set(be_value, LLVMConstReal(llvm_get_type(c, type), (double) expr->const_expr.f), type);
|
|
return;
|
|
case TYPE_POINTER:
|
|
llvm_value_set(be_value, LLVMConstNull(llvm_get_type(c, type)), type);
|
|
return;
|
|
case TYPE_BOOL:
|
|
llvm_value_set_bool(be_value, LLVMConstInt(c->bool_type, expr->const_expr.b ? 1 : 0, 0));
|
|
return;
|
|
case TYPE_STRLIT:
|
|
{
|
|
LLVMValueRef global_name = LLVMAddGlobal(c->module, LLVMArrayType(llvm_get_type(c, type_char), expr->const_expr.string.len + 1), "");
|
|
LLVMSetLinkage(global_name, LLVMInternalLinkage);
|
|
LLVMSetGlobalConstant(global_name, 1);
|
|
LLVMSetInitializer(global_name, LLVMConstStringInContext(c->context,
|
|
expr->const_expr.string.chars,
|
|
expr->const_expr.string.len,
|
|
0));
|
|
global_name = LLVMConstBitCast(global_name, LLVMPointerType(llvm_get_type(c, type_char), 0));
|
|
llvm_value_set(be_value, global_name, type);
|
|
return;
|
|
}
|
|
case TYPE_ERRTYPE:
|
|
TODO
|
|
default:
|
|
UNREACHABLE
|
|
}
|
|
}
|
|
|
|
static void llvm_expand_type_to_args(GenContext *context, Type *param_type, LLVMValueRef expand_ptr, LLVMValueRef **values);
|
|
|
|
static void gencontext_expand_array_to_args(GenContext *c, Type *param_type, LLVMValueRef expand_ptr, LLVMValueRef **values)
|
|
{
|
|
LLVMTypeRef element_type = llvm_get_type(c, param_type->array.base);
|
|
LLVMValueRef zero = llvm_get_zero(c, type_int);
|
|
LLVMValueRef indices[2] = { zero, zero, };
|
|
for (ByteSize i = 0; i < param_type->array.len; i++)
|
|
{
|
|
indices[1] = llvm_const_int(c, type_int, i);
|
|
LLVMValueRef element_ptr = LLVMBuildGEP2(c->builder, element_type, expand_ptr, indices, 2, "");
|
|
llvm_expand_type_to_args(c, param_type->array.base, element_ptr, values);
|
|
}
|
|
}
|
|
|
|
static void gencontext_expand_struct_to_args(GenContext *context, Type *param_type, LLVMValueRef expand_ptr, LLVMValueRef **values)
|
|
{
|
|
Decl **members = param_type->decl->strukt.members;
|
|
VECEACH(members, i)
|
|
{
|
|
Type *member_type = members[i]->type;
|
|
if (type_is_empty_field(member_type, true)) continue;
|
|
LLVMValueRef member_ptr = LLVMBuildStructGEP(context->builder, expand_ptr, i, "expandmember");
|
|
llvm_expand_type_to_args(context, member_type, member_ptr, values);
|
|
}
|
|
}
|
|
|
|
static void llvm_expand_type_to_args(GenContext *context, Type *param_type, LLVMValueRef expand_ptr, LLVMValueRef **values)
|
|
{
|
|
REDO:
|
|
switch (type_flatten(param_type)->type_kind)
|
|
{
|
|
case TYPE_POISONED:
|
|
case TYPE_VOID:
|
|
case TYPE_IXX:
|
|
case TYPE_FXX:
|
|
case TYPE_TYPEID:
|
|
case TYPE_FUNC:
|
|
case TYPE_TYPEINFO:
|
|
case TYPE_MEMBER:
|
|
case TYPE_DISTINCT:
|
|
case TYPE_STRLIT:
|
|
case TYPE_INFERRED_ARRAY:
|
|
UNREACHABLE
|
|
break;
|
|
case TYPE_BOOL:
|
|
case ALL_SIGNED_INTS:
|
|
case ALL_UNSIGNED_INTS:
|
|
case ALL_REAL_FLOATS:
|
|
case TYPE_POINTER:
|
|
case TYPE_ENUM:
|
|
case TYPE_ERRTYPE:
|
|
case TYPE_VARARRAY:
|
|
vec_add(*values, LLVMBuildLoad2(context->builder, llvm_get_type(context, param_type), expand_ptr, "loadexpanded"));
|
|
return;
|
|
case TYPE_TYPEDEF:
|
|
param_type = param_type->canonical;
|
|
goto REDO;
|
|
case TYPE_ERR_UNION:
|
|
TODO
|
|
case TYPE_STRUCT:
|
|
gencontext_expand_struct_to_args(context, param_type, expand_ptr, values);
|
|
break;
|
|
case TYPE_ARRAY:
|
|
gencontext_expand_array_to_args(context, param_type, expand_ptr, values);
|
|
break;
|
|
case TYPE_UNION:
|
|
case TYPE_COMPLEX:
|
|
case TYPE_SUBARRAY:
|
|
case TYPE_VECTOR:
|
|
TODO
|
|
break;
|
|
}
|
|
}
|
|
|
|
LLVMValueRef llvm_emit_struct_gep_raw(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);
|
|
ArrayIndex actual_index = -1;
|
|
Decl *member;
|
|
for (ArrayIndex i = 0; i <= index; i++)
|
|
{
|
|
member = struct_pointer->type->decl->strukt.members[i];
|
|
if (member->padding)
|
|
{
|
|
actual_index++;
|
|
}
|
|
actual_index++;
|
|
}
|
|
unsigned alignment;
|
|
LLVMValueRef ref = llvm_emit_struct_gep_raw(c,
|
|
struct_pointer->value,
|
|
llvm_get_type(c, struct_pointer->type),
|
|
actual_index,
|
|
struct_pointer->alignment,
|
|
member->offset,
|
|
&alignment);
|
|
llvm_value_set_address(element, ref, member->type);
|
|
element->alignment = alignment;
|
|
}
|
|
|
|
static void llvm_emit_fp_intrinsic_expr(GenContext *c, unsigned intrinsic_id, BEValue *be_value, Expr *expr)
|
|
{
|
|
unsigned arguments = vec_size(expr->call_expr.arguments);
|
|
llvm_emit_expr(c, be_value, expr->call_expr.arguments[0]);
|
|
llvm_value_rvalue(c, be_value);
|
|
LLVMTypeRef call_type = llvm_get_type(c, be_value->type);
|
|
LLVMValueRef result = llvm_emit_call_intrinsic(c, intrinsic_id, &call_type, 1, &be_value->value, 1);
|
|
be_value->value = result;
|
|
}
|
|
void gencontext_emit_call_intrinsic_expr(GenContext *c, BEValue *be_value, Expr *expr)
|
|
{
|
|
Decl *function_decl = expr->call_expr.function->identifier_expr.decl;
|
|
if (function_decl->name == kw___round)
|
|
{
|
|
llvm_emit_fp_intrinsic_expr(c, intrinsic_id_rint, be_value, expr);
|
|
return;
|
|
}
|
|
if (function_decl->name == kw___sqrt)
|
|
{
|
|
llvm_emit_fp_intrinsic_expr(c, intrinsic_id_sqrt, be_value, expr);
|
|
return;
|
|
}
|
|
if (function_decl->name == kw___trunc)
|
|
{
|
|
llvm_emit_fp_intrinsic_expr(c, intrinsic_id_trunc, be_value, expr);
|
|
return;
|
|
}
|
|
if (function_decl->name == kw___ceil)
|
|
{
|
|
llvm_emit_fp_intrinsic_expr(c, intrinsic_id_ceil, be_value, expr);
|
|
return;
|
|
}
|
|
UNREACHABLE
|
|
}
|
|
|
|
void llvm_emit_parameter(GenContext *context, LLVMValueRef **args, ABIArgInfo *info, BEValue *be_value, Type *type)
|
|
{
|
|
switch (info->kind)
|
|
{
|
|
case ABI_ARG_IGNORE:
|
|
// Skip.
|
|
return;
|
|
case ABI_ARG_INDIRECT:
|
|
{
|
|
// If we want we could optimize for structs by doing it by reference here.
|
|
unsigned alignment = info->indirect.realignment ?: type_abi_alignment(type);
|
|
LLVMValueRef indirect = llvm_emit_alloca(context,
|
|
llvm_get_type(context, type),
|
|
alignment,
|
|
"indirectarg");
|
|
llvm_store_bevalue_aligned(context, indirect, be_value, alignment);
|
|
vec_add(*args, indirect);
|
|
return;
|
|
}
|
|
case ABI_ARG_DIRECT_COERCE:
|
|
{
|
|
LLVMTypeRef coerce_type = llvm_get_coerce_type(context, info);
|
|
if (!coerce_type || coerce_type == llvm_get_type(context, type))
|
|
{
|
|
vec_add(*args, llvm_value_rvalue_store(context, be_value));
|
|
return;
|
|
}
|
|
if (!abi_info_should_flatten(info))
|
|
{
|
|
vec_add(*args, llvm_emit_coerce(context, coerce_type, be_value, type));
|
|
return;
|
|
}
|
|
LLVMValueRef cast;
|
|
AlignSize target_alignment = llvm_abi_alignment(coerce_type);
|
|
AlignSize max_align = MAX((be_value->alignment), llvm_abi_alignment(coerce_type));
|
|
|
|
// If we are loading something with greater alignment than what we have, we cannot directly memcpy.
|
|
if (llvm_value_is_addr(be_value) && be_value->alignment < target_alignment)
|
|
{
|
|
// So load it instead.
|
|
llvm_value_rvalue(context, be_value);
|
|
}
|
|
|
|
// In this case we have something nicely aligned, so we just do a cast.
|
|
if (llvm_value_is_addr(be_value))
|
|
{
|
|
cast = LLVMBuildBitCast(context->builder, be_value->value, LLVMPointerType(coerce_type, 0), "");
|
|
}
|
|
else
|
|
{
|
|
cast = llvm_emit_alloca(context, coerce_type, max_align, "coerce");
|
|
LLVMValueRef target = LLVMBuildBitCast(context->builder, cast, llvm_get_ptr_type(context, type), "");
|
|
llvm_store_bevalue_aligned(context, target, be_value, max_align);
|
|
}
|
|
LLVMTypeRef element = llvm_abi_type(context, info->direct_coerce.type);
|
|
for (unsigned idx = 0; idx < info->direct_coerce.elements; idx++)
|
|
{
|
|
LLVMValueRef element_ptr = LLVMBuildStructGEP2(context->builder, coerce_type, cast, idx, "");
|
|
vec_add(*args,
|
|
llvm_emit_load_aligned(context, element, element_ptr, llvm_abi_alignment(element), ""));
|
|
}
|
|
return;
|
|
}
|
|
case ABI_ARG_DIRECT_PAIR:
|
|
{
|
|
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);
|
|
LLVMValueRef cast = LLVMBuildBitCast(context->builder, be_value->value, llvm_get_ptr_type(context, type), "casttemp");
|
|
// Get the lo value.
|
|
LLVMValueRef lo_ptr = LLVMBuildStructGEP2(context->builder, struct_type, cast, 0, "lo");
|
|
vec_add(*args, 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, cast, 1, "hi");
|
|
vec_add(*args, llvm_emit_load_aligned(context, hi, hi_ptr, llvm_abi_alignment(hi), "hi"));
|
|
return;
|
|
}
|
|
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(*args, 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(*args, LLVMBuildLoad2(context->builder, llvm_abi_type(context, info->coerce_expand.hi), gep_second, ""));
|
|
}
|
|
return;
|
|
}
|
|
case ABI_ARG_EXPAND:
|
|
{
|
|
// Move this to an address (if needed)
|
|
llvm_value_addr(context, be_value);
|
|
llvm_expand_type_to_args(context, type, be_value->value, args);
|
|
// Expand the padding here.
|
|
if (info->expand.padding_type)
|
|
{
|
|
vec_add(*args, LLVMGetUndef(llvm_get_type(context, info->expand.padding_type)));
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
}
|
|
void llvm_emit_call_expr(GenContext *context, BEValue *be_value, Expr *expr)
|
|
{
|
|
printf("Optimize call return\n");
|
|
FunctionSignature *signature;
|
|
LLVMTypeRef func_type;
|
|
LLVMValueRef func;
|
|
if (expr->call_expr.is_pointer_call)
|
|
{
|
|
signature = expr->call_expr.function->type->canonical->pointer->func.signature;
|
|
BEValue func_value;
|
|
llvm_emit_expr(context, &func_value, expr->call_expr.function);
|
|
func = llvm_value_rvalue_store(context, &func_value);
|
|
func_type = llvm_get_type(context, expr->call_expr.function->type->canonical->pointer);
|
|
}
|
|
else if (expr->call_expr.is_struct_function)
|
|
{
|
|
Decl *function_decl = expr->call_expr.function->access_expr.ref;
|
|
signature = &function_decl->func.function_signature;
|
|
func = function_decl->backend_ref;
|
|
func_type = llvm_get_type(context, function_decl->type);
|
|
}
|
|
else
|
|
{
|
|
Decl *function_decl = expr->call_expr.function->identifier_expr.decl;
|
|
if (function_decl->func.is_builtin)
|
|
{
|
|
gencontext_emit_call_intrinsic_expr(context, be_value, expr);
|
|
return;
|
|
}
|
|
signature = &function_decl->func.function_signature;
|
|
func = function_decl->backend_ref;
|
|
func_type = llvm_get_type(context, function_decl->type);
|
|
}
|
|
|
|
|
|
LLVMValueRef return_param = NULL;
|
|
LLVMValueRef *values = NULL;
|
|
|
|
ABIArgInfo *ret_info = signature->ret_abi_info;
|
|
Type *return_type = signature->rtype->type->canonical;
|
|
|
|
if (signature->failable)
|
|
{
|
|
ret_info = signature->failable_abi_info;
|
|
return_type = type_error;
|
|
}
|
|
|
|
switch (ret_info->kind)
|
|
{
|
|
case ABI_ARG_INDIRECT:
|
|
// Create the return parameter
|
|
return_param = llvm_emit_alloca(context, llvm_get_type(context, return_type),
|
|
ret_info->indirect.realignment, "sretparam");
|
|
// Add the pointer to the list of arguments.
|
|
vec_add(values, return_param);
|
|
if (ret_info->indirect.realignment)
|
|
{
|
|
llvm_set_alignment(return_param, ret_info->indirect.realignment);
|
|
}
|
|
break;
|
|
case ABI_ARG_EXPAND:
|
|
UNREACHABLE
|
|
case ABI_ARG_DIRECT_PAIR:
|
|
case ABI_ARG_IGNORE:
|
|
case ABI_ARG_DIRECT_COERCE:
|
|
case ABI_ARG_EXPAND_COERCE:
|
|
break;
|
|
}
|
|
|
|
if (signature->failable && signature->ret_abi_info)
|
|
{
|
|
Type *actual_return_type = type_lowering(signature->rtype->type);
|
|
return_param = llvm_emit_alloca_aligned(context, actual_return_type, "retparam");
|
|
llvm_value_set(be_value, return_param, type_get_ptr(actual_return_type));
|
|
llvm_emit_parameter(context, &values, signature->ret_abi_info, be_value, be_value->type);
|
|
}
|
|
unsigned arguments = vec_size(expr->call_expr.arguments);
|
|
assert(arguments >= vec_size(signature->params));
|
|
VECEACH(signature->params, i)
|
|
{
|
|
Expr *arg_expr = expr->call_expr.arguments[i];
|
|
llvm_emit_expr(context, be_value, arg_expr);
|
|
Decl *param = signature->params[i];
|
|
ABIArgInfo *info = param->var.abi_info;
|
|
llvm_emit_parameter(context, &values, info, be_value, param->type);
|
|
}
|
|
for (unsigned i = vec_size(signature->params); i < arguments; i++)
|
|
{
|
|
Expr *arg_expr = expr->call_expr.arguments[i];
|
|
llvm_emit_expr(context, be_value, arg_expr);
|
|
printf("TODO: varargs should be expanded correctly\n");
|
|
vec_add(values, llvm_value_rvalue_store(context, be_value));
|
|
}
|
|
|
|
LLVMValueRef call = LLVMBuildCall2(context->builder, func_type, func, values, vec_size(values), "");
|
|
|
|
switch (ret_info->kind)
|
|
{
|
|
case ABI_ARG_EXPAND:
|
|
UNREACHABLE
|
|
case ABI_ARG_IGNORE:
|
|
// Default behaviour is fine.
|
|
break;
|
|
case ABI_ARG_INDIRECT:
|
|
// TODO look at failable
|
|
call = llvm_emit_load_aligned(context,
|
|
llvm_get_type(context, return_type),
|
|
return_param,
|
|
ret_info->indirect.realignment,
|
|
"");
|
|
break;
|
|
case ABI_ARG_DIRECT_PAIR:
|
|
{
|
|
// 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 = 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_raw(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 field, 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_raw(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
|
|
LLVMTypeRef coerce = llvm_get_coerce_type(context, ret_info);
|
|
// Default behaviour.
|
|
if (!coerce || coerce == llvm_get_type(context, return_type)) break;
|
|
assert(!abi_info_should_flatten(ret_info));
|
|
call = llvm_emit_convert_value_from_coerced(context, coerce, call, return_type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (signature->failable)
|
|
{
|
|
LLVMBasicBlockRef after_block = llvm_basic_block_new(context, "after_check");
|
|
BEValue no_err;
|
|
llvm_value_set_bool(&no_err, llvm_emit_is_no_error(context, call));
|
|
if (context->error_var)
|
|
{
|
|
LLVMBasicBlockRef error_block = llvm_basic_block_new(context, "error");
|
|
llvm_emit_cond_br(context, &no_err, after_block, error_block);
|
|
llvm_emit_block(context, error_block);
|
|
LLVMBuildStore(context->builder,
|
|
call,
|
|
llvm_emit_bitcast(context, context->error_var, type_get_ptr(type_error)));
|
|
llvm_emit_br(context, context->catch_block);
|
|
}
|
|
else
|
|
{
|
|
llvm_emit_cond_br(context, &no_err, after_block, context->catch_block);
|
|
}
|
|
llvm_emit_block(context, after_block);
|
|
// If void, be_value contents should be skipped.
|
|
if (!signature->ret_abi_info) return;
|
|
|
|
llvm_value_set_address(be_value, return_param, expr->type);
|
|
return;
|
|
}
|
|
|
|
llvm_value_set(be_value, call, expr->type);
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void gencontext_emit_expression_list_expr(GenContext *context, BEValue *be_value, Expr *expr)
|
|
{
|
|
VECEACH(expr->expression_list, i)
|
|
{
|
|
llvm_emit_expr(context, be_value, expr->expression_list[i]);
|
|
}
|
|
}
|
|
|
|
|
|
static inline void gencontext_emit_expr_block(GenContext *context, BEValue *be_value, Expr *expr)
|
|
{
|
|
LLVMValueRef old_ret_out = context->return_out;
|
|
LLVMBasicBlockRef saved_expr_block = context->expr_block_exit;
|
|
|
|
LLVMBasicBlockRef expr_block = llvm_basic_block_new(context, "expr_block.exit");
|
|
context->expr_block_exit = expr_block;
|
|
|
|
LLVMValueRef return_out = NULL;
|
|
if (expr->type != type_void)
|
|
{
|
|
return_out = llvm_emit_alloca_aligned(context, expr->type, "blockret");
|
|
}
|
|
context->return_out = return_out;
|
|
|
|
Ast **stmts = expr->expr_block.stmts;
|
|
VECEACH(stmts, i)
|
|
{
|
|
llvm_emit_stmt(context, stmts[i]);
|
|
}
|
|
llvm_emit_br(context, expr_block);
|
|
|
|
// Emit the exit block.
|
|
llvm_emit_block(context, expr_block);
|
|
|
|
context->return_out = old_ret_out;
|
|
context->expr_block_exit = saved_expr_block;
|
|
|
|
if (return_out)
|
|
{
|
|
llvm_value_set_address(be_value, return_out, expr->type);
|
|
}
|
|
else
|
|
{
|
|
llvm_value_set(be_value, NULL, type_void);
|
|
}
|
|
}
|
|
|
|
static inline void gencontext_emit_macro_block(GenContext *context, BEValue *be_value, Expr *expr)
|
|
{
|
|
LLVMValueRef old_ret_out = context->return_out;
|
|
LLVMBasicBlockRef saved_expr_block = context->expr_block_exit;
|
|
|
|
LLVMBasicBlockRef expr_block = llvm_basic_block_new(context, "expr_block.exit");
|
|
context->expr_block_exit = expr_block;
|
|
|
|
LLVMValueRef return_out = NULL;
|
|
if (expr->type != type_void)
|
|
{
|
|
return_out = llvm_emit_alloca_aligned(context, expr->type, "blockret");
|
|
}
|
|
context->return_out = return_out;
|
|
|
|
Ast **stmts = expr->macro_block.stmts;
|
|
VECEACH(expr->macro_block.params, i)
|
|
{
|
|
// In case we have a constant, we never do an emit. The value is already folded.
|
|
Decl *decl = expr->macro_block.params[i];
|
|
switch (decl->var.kind)
|
|
{
|
|
case VARDECL_CONST:
|
|
case VARDECL_GLOBAL:
|
|
case VARDECL_LOCAL:
|
|
case VARDECL_MEMBER:
|
|
case VARDECL_LOCAL_CT:
|
|
case VARDECL_LOCAL_CT_TYPE:
|
|
case VARDECL_ALIAS:
|
|
UNREACHABLE
|
|
case VARDECL_PARAM_REF:
|
|
{
|
|
BEValue addr;
|
|
llvm_emit_expr(context, &addr, decl->var.init_expr);
|
|
decl->backend_ref = addr.value;
|
|
continue;
|
|
}
|
|
case VARDECL_PARAM_CT:
|
|
case VARDECL_PARAM_CT_TYPE:
|
|
case VARDECL_PARAM_EXPR:
|
|
continue;
|
|
case VARDECL_PARAM:
|
|
break;
|
|
}
|
|
llvm_emit_and_set_decl_alloca(context, decl);
|
|
BEValue value;
|
|
llvm_emit_expr(context, &value, expr->macro_block.args[i]);
|
|
printf("TODO: unoptimized use of BEValue\n");
|
|
llvm_emit_store(context, decl, llvm_value_rvalue_store(context, &value));
|
|
}
|
|
|
|
VECEACH(stmts, i)
|
|
{
|
|
llvm_emit_stmt(context, stmts[i]);
|
|
}
|
|
llvm_emit_br(context, expr_block);
|
|
|
|
// Emit the exit block.
|
|
llvm_emit_block(context, expr_block);
|
|
|
|
context->return_out = old_ret_out;
|
|
context->expr_block_exit = saved_expr_block;
|
|
|
|
if (return_out)
|
|
{
|
|
llvm_value_set_address(be_value, return_out, expr->type);
|
|
}
|
|
else
|
|
{
|
|
llvm_value_set(be_value, NULL, type_void);
|
|
}
|
|
}
|
|
|
|
LLVMValueRef llvm_emit_call_intrinsic(GenContext *context, unsigned intrinsic_id, LLVMTypeRef *types, unsigned type_count,
|
|
LLVMValueRef *values, unsigned arg_count)
|
|
{
|
|
LLVMValueRef decl = LLVMGetIntrinsicDeclaration(context->module, intrinsic_id, types, type_count);
|
|
LLVMTypeRef type = LLVMIntrinsicGetType(context->context, intrinsic_id, types, arg_count);
|
|
return LLVMBuildCall2(context->builder, type, decl, values, arg_count, "");
|
|
}
|
|
|
|
BEValue llvm_emit_assign_expr(GenContext *c, BEValue *ref, Expr *expr, LLVMValueRef failable)
|
|
{
|
|
llvm_value_addr(c, ref);
|
|
LLVMBasicBlockRef assign_block = NULL;
|
|
|
|
PUSH_ERROR();
|
|
|
|
if (failable)
|
|
{
|
|
if (expr->failable)
|
|
{
|
|
assign_block = llvm_basic_block_new(c, "after_assign");
|
|
c->error_var = failable;
|
|
c->catch_block = assign_block;
|
|
}
|
|
else
|
|
{
|
|
c->error_var = NULL;
|
|
c->catch_block = NULL;
|
|
}
|
|
}
|
|
BEValue value;
|
|
if (expr->expr_kind == EXPR_COMPOUND_LITERAL) expr = expr->expr_compound_literal.initializer;
|
|
if (expr->expr_kind == EXPR_INITIALIZER_LIST)
|
|
{
|
|
llvm_emit_initialize_reference(c, ref, expr);
|
|
value = *ref;
|
|
}
|
|
else
|
|
{
|
|
llvm_emit_expr(c, &value, expr);
|
|
llvm_store_bevalue(c, ref, &value);
|
|
}
|
|
|
|
if (failable)
|
|
{
|
|
llvm_store_self_aligned(c, failable, llvm_get_zero(c, type_error), type_error);
|
|
}
|
|
POP_ERROR();
|
|
|
|
if (failable && expr->failable)
|
|
{
|
|
llvm_emit_br(c, assign_block);
|
|
llvm_emit_block(c, assign_block);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
|
|
|
|
static inline void gencontext_emit_failable(GenContext *context, BEValue *be_value, Expr *expr)
|
|
{
|
|
Expr *fail = expr->failable_expr;
|
|
if (context->error_var)
|
|
{
|
|
assert(context->error_var);
|
|
llvm_emit_expr(context, be_value, fail);
|
|
printf("TODO // fix failable \n");
|
|
LLVMBuildStore(context->builder, llvm_value_rvalue_store(context, be_value),
|
|
llvm_emit_bitcast(context, context->error_var, type_get_ptr(fail->type)));
|
|
}
|
|
llvm_emit_br(context, context->catch_block);
|
|
LLVMBasicBlockRef ignored_block = llvm_basic_block_new(context, "postfailed");
|
|
llvm_emit_block(context, ignored_block);
|
|
if (expr->type->canonical == type_void)
|
|
{
|
|
llvm_value_set(be_value, NULL, type_void);
|
|
return;
|
|
}
|
|
llvm_value_set(be_value, LLVMGetUndef(llvm_get_type(context, expr->type)), expr->type);
|
|
}
|
|
|
|
static inline void llvm_emit_initializer_list_expr(GenContext *c, BEValue *value, Expr *expr)
|
|
{
|
|
llvm_value_set_address(value, llvm_emit_alloca_aligned(c, expr->type, "literal"), expr->type);
|
|
llvm_emit_initialize_reference(c, value, expr);
|
|
}
|
|
|
|
void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr)
|
|
{
|
|
EMIT_LOC(c, expr);
|
|
switch (expr->expr_kind)
|
|
{
|
|
case EXPR_DESIGNATOR:
|
|
case EXPR_MEMBER_ACCESS:
|
|
case EXPR_POISONED:
|
|
case EXPR_DECL_LIST:
|
|
case EXPR_TYPEINFO:
|
|
case EXPR_ENUM_CONSTANT:
|
|
case EXPR_MACRO_IDENTIFIER:
|
|
case EXPR_MACRO_CT_IDENTIFIER:
|
|
case EXPR_CT_IDENT:
|
|
case EXPR_HASH_IDENT:
|
|
UNREACHABLE
|
|
case EXPR_UNDEF:
|
|
// Should never reach this.
|
|
UNREACHABLE
|
|
case EXPR_SLICE_ASSIGN:
|
|
gencontext_emit_slice_assign(c, value, expr);
|
|
return;
|
|
case EXPR_SLICE:
|
|
gencontext_emit_slice(c, value, expr);
|
|
return;
|
|
case EXPR_LEN:
|
|
llvm_emit_len(c, value, expr);
|
|
return;
|
|
case EXPR_FAILABLE:
|
|
gencontext_emit_failable(c, value, expr);
|
|
return;
|
|
case EXPR_TRY:
|
|
case EXPR_CATCH:
|
|
gencontext_emit_trycatch_expr(c, value, expr);
|
|
return;
|
|
case EXPR_ELSE:
|
|
gencontext_emit_else_expr(c, value, expr);
|
|
return;
|
|
case EXPR_MACRO_BLOCK:
|
|
gencontext_emit_macro_block(c, value, expr);
|
|
return;
|
|
case EXPR_COMPOUND_LITERAL:
|
|
UNREACHABLE
|
|
case EXPR_INITIALIZER_LIST:
|
|
llvm_emit_initializer_list_expr(c, value, expr);
|
|
return;
|
|
case EXPR_EXPR_BLOCK:
|
|
gencontext_emit_expr_block(c, value, expr);
|
|
return;
|
|
case EXPR_SCOPED_EXPR:
|
|
gencontext_emit_scoped_expr(c, value, expr);
|
|
return;
|
|
case EXPR_UNARY:
|
|
gencontext_emit_unary_expr(c, value, expr);
|
|
return;
|
|
case EXPR_CONST:
|
|
llvm_emit_const_expr(c, value, expr);
|
|
return;
|
|
case EXPR_BINARY:
|
|
gencontext_emit_binary_expr(c, value, expr);
|
|
return;
|
|
case EXPR_TERNARY:
|
|
gencontext_emit_ternary_expr(c, value, expr);
|
|
return;
|
|
case EXPR_POST_UNARY:
|
|
llvm_emit_post_unary_expr(c, value, expr);
|
|
return;
|
|
case EXPR_GUARD:
|
|
gencontext_emit_guard_expr(c, value, expr);
|
|
return;
|
|
case EXPR_TYPEID:
|
|
gencontext_emit_typeid(c, value, expr);
|
|
return;
|
|
case EXPR_TYPEOF:
|
|
// These are folded in the semantic analysis step.
|
|
UNREACHABLE
|
|
case EXPR_IDENTIFIER:
|
|
case EXPR_CONST_IDENTIFIER:
|
|
llvm_value_set_decl_address(value, expr->identifier_expr.decl);
|
|
return;
|
|
case EXPR_SUBSCRIPT:
|
|
gencontext_emit_subscript(c, value, expr);
|
|
return;
|
|
case EXPR_ACCESS:
|
|
gencontext_emit_access_addr(c, value, expr);
|
|
return;
|
|
case EXPR_CALL:
|
|
llvm_emit_call_expr(c, value, expr);
|
|
return;
|
|
case EXPR_GROUP:
|
|
expr = expr->group_expr;
|
|
return;
|
|
case EXPR_EXPRESSION_LIST:
|
|
gencontext_emit_expression_list_expr(c, value, expr);
|
|
return;
|
|
case EXPR_CAST:
|
|
gencontext_emit_cast_expr(c, value, expr);
|
|
return;
|
|
}
|
|
UNREACHABLE
|
|
} |