Files
c3c/src/compiler/llvm_codegen_expr.c
2020-07-15 15:42:54 +02:00

1462 lines
51 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 inline LLVMValueRef gencontext_emit_add_int(GenContext *context, Type *type, bool use_mod, LLVMValueRef left, LLVMValueRef right)
{
if (use_mod)
{
return LLVMBuildAdd(context->builder, left, right, "add_mod");
}
if (build_options.debug_mode)
{
LLVMTypeRef type_to_use = llvm_type(type->canonical);
LLVMTypeRef types[2] = { type_to_use, type_to_use };
LLVMValueRef args[2] = { left, right };
assert(type->canonical == type);
LLVMValueRef add_res;
if (type_is_unsigned(type))
{
add_res = gencontext_emit_call_intrinsic(context, uadd_overflow_intrinsic_id, types, args, 2);
}
else
{
add_res = gencontext_emit_call_intrinsic(context, sadd_overflow_intrinsic_id, types, args, 2);
}
LLVMValueRef result = LLVMBuildExtractValue(context->builder, add_res, 0, "");
LLVMValueRef ok = LLVMBuildExtractValue(context->builder, add_res, 1, "");
gencontext_emit_panic_on_true(context, ok, "Addition overflow");
return result;
}
return type_is_unsigned_integer(type)
? LLVMBuildNUWAdd(context->builder, left, right, "uadd")
: LLVMBuildNSWAdd(context->builder, left, right, "add");
}
static inline LLVMValueRef gencontext_emit_sub_int(GenContext *context, Type *type, bool use_mod, LLVMValueRef left, LLVMValueRef right)
{
if (use_mod)
{
return LLVMBuildSub(context->builder, left, right, "sub_mod");
}
if (build_options.debug_mode)
{
LLVMTypeRef type_to_use = llvm_type(type);
LLVMTypeRef types[2] = { type_to_use, type_to_use };
LLVMValueRef args[2] = { left, right };
assert(type->canonical == type);
LLVMValueRef add_res;
if (type_is_unsigned(type))
{
add_res = gencontext_emit_call_intrinsic(context, usub_overflow_intrinsic_id, types, args, 2);
}
else
{
add_res = gencontext_emit_call_intrinsic(context, ssub_overflow_intrinsic_id, types, args, 2);
}
LLVMValueRef result = LLVMBuildExtractValue(context->builder, add_res, 0, "");
LLVMValueRef ok = LLVMBuildExtractValue(context->builder, add_res, 1, "");
gencontext_emit_panic_on_true(context, ok, "Subtraction overflow");
return result;
}
return type_is_unsigned_integer(type)
? LLVMBuildNUWSub(context->builder, left, right, "usub")
: LLVMBuildNSWSub(context->builder, left, right, "sub");
}
static inline LLVMValueRef gencontext_emit_subscript_addr(GenContext *context, Expr *expr)
{
Expr *parent = expr->subscript_expr.expr;
Expr *index = expr->subscript_expr.index;
if (index->expr_kind == EXPR_RANGE) TODO;
LLVMValueRef index_value = gencontext_emit_expr(context, index);
LLVMValueRef parent_value;
Type *type = parent->type->canonical;
switch (type->type_kind)
{
case TYPE_POINTER:
parent_value = gencontext_emit_expr(context, expr->subscript_expr.expr);
return LLVMBuildInBoundsGEP2(context->builder,
llvm_type(type->pointer),
parent_value, &index_value, 1, "ptridx");
case TYPE_ARRAY:
{
// TODO insert trap on overflow.
LLVMValueRef zero = llvm_int(type_int, 0);
LLVMValueRef indices[2] = {
zero,
index_value,
};
parent_value = gencontext_emit_address(context, expr->subscript_expr.expr);
return LLVMBuildInBoundsGEP2(context->builder,
llvm_type(type),
parent_value, indices, 2, "arridx");
}
case TYPE_SUBARRAY:
{
// TODO insert trap on overflow.
LLVMTypeRef subarray_type = llvm_type(type);
parent_value = gencontext_emit_address(context, expr->subscript_expr.expr);
LLVMValueRef pointer_addr = LLVMBuildStructGEP2(context->builder, subarray_type, parent_value, 0, "");
LLVMTypeRef pointer_type = llvm_type(type_get_ptr(type->array.base));
LLVMValueRef pointer = LLVMBuildLoad2(context->builder, pointer_type, pointer_addr, "");
return LLVMBuildInBoundsGEP2(context->builder,
llvm_type(type->array.base),
pointer, &index_value, 1, "sarridx");
}
case TYPE_VARARRAY:
case TYPE_STRING:
TODO
default:
UNREACHABLE
}
}
static LLVMValueRef gencontext_emit_member_addr(GenContext *context, LLVMValueRef value, Decl *parent, Decl *member)
{
assert(member->resolve_status == RESOLVE_DONE);
Decl *current_parent = member->member_decl.parent;
if (current_parent->decl_kind == DECL_MEMBER && current_parent->member_decl.anonymous)
{
value = gencontext_emit_member_addr(context, value, parent, current_parent);
}
switch (current_parent->type->canonical->type_kind)
{
case TYPE_UNION:
return LLVMBuildBitCast(context->builder, value, LLVMPointerType(llvm_type(member->type), 0), member->name);
case TYPE_ERRTYPE:
return LLVMBuildStructGEP2(context->builder, llvm_type(current_parent->type), value, member->member_decl.index + 1, member->name);
case TYPE_STRUCT:
return LLVMBuildStructGEP2(context->builder, llvm_type(current_parent->type), value, member->member_decl.index, member->name);
default:
UNREACHABLE
}
}
static inline LLVMValueRef gencontext_emit_access_addr(GenContext *context, Expr *expr)
{
Expr *parent = expr->access_expr.parent;
LLVMValueRef value = gencontext_emit_address(context, parent);
Decl *member = expr->access_expr.ref;
return gencontext_emit_member_addr(context, value, parent->type->canonical->decl, member);
}
LLVMValueRef gencontext_emit_scoped_expr(GenContext *context, Expr *expr)
{
LLVMValueRef value = gencontext_emit_expr(context, expr->expr_scope.expr);
gencontext_emit_defer(context, expr->expr_scope.defers.start, expr->expr_scope.defers.end);
return value;
}
LLVMValueRef gencontext_emit_scoped_expr_address(GenContext *context, Expr *expr)
{
LLVMValueRef value = gencontext_emit_address(context, expr->expr_scope.expr);
gencontext_emit_defer(context, expr->expr_scope.defers.start, expr->expr_scope.defers.end);
return value;
}
static inline LLVMValueRef gencontext_emit_initializer_list_expr_addr(GenContext *context, Expr *expr, LLVMValueRef optional_ref);
LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr)
{
switch (expr->expr_kind)
{
case EXPR_RANGE:
TODO
case EXPR_DESIGNATED_INITIALIZER:
// Should only appear when generating designated initializers.
UNREACHABLE
case EXPR_MACRO_BLOCK:
TODO
case EXPR_FAIL_CHECK:
UNREACHABLE
case EXPR_IDENTIFIER:
return decl_ref(expr->identifier_expr.decl);
case EXPR_UNARY:
assert(expr->unary_expr.operator == UNARYOP_DEREF);
return gencontext_emit_expr(context, expr->unary_expr.expr);
case EXPR_COMPOUND_LITERAL:
return gencontext_emit_initializer_list_expr_addr(context, expr->expr_compound_literal.initializer, NULL);
case EXPR_ACCESS:
return gencontext_emit_access_addr(context, expr);
case EXPR_SUBSCRIPT:
return gencontext_emit_subscript_addr(context, expr);
case EXPR_SCOPED_EXPR:
return gencontext_emit_scoped_expr_address(context, expr);
case EXPR_GROUP:
return gencontext_emit_address(context, expr->group_expr);
case EXPR_CONST:
case EXPR_TYPEID:
case EXPR_POISONED:
case EXPR_GUARD:
case EXPR_BINARY:
case EXPR_TERNARY:
case EXPR_POST_UNARY:
case EXPR_TYPE_ACCESS:
case EXPR_CALL:
case EXPR_INITIALIZER_LIST:
case EXPR_EXPRESSION_LIST:
case EXPR_CAST:
case EXPR_TYPEOF:
case EXPR_FAILABLE:
case EXPR_CATCH:
case EXPR_TRY:
case EXPR_EXPR_BLOCK:
case EXPR_DECL_LIST:
case EXPR_ELSE:
UNREACHABLE
}
UNREACHABLE
}
LLVMValueRef gencontext_emit_arr_to_subarray_cast(GenContext *context, LLVMValueRef value, Type *to_type, Type *from_type)
{
size_t size = from_type->pointer->array.len;
LLVMTypeRef subarray_type = llvm_type(to_type);
LLVMValueRef result = LLVMGetUndef(subarray_type);
value = gencontext_emit_bitcast(context, value, type_get_ptr(from_type->pointer->array.base));
result = LLVMBuildInsertValue(context->builder, result, value, 0, "");
return LLVMBuildInsertValue(context->builder, result, llvm_int(type_usize, size), 1, "");
}
LLVMValueRef gencontext_emit_subarray_to_ptr_cast(GenContext *context, LLVMValueRef value, Type *to_type, Type *from_type)
{
return LLVMBuildExtractValue(context->builder, value, 0, "");
}
LLVMValueRef gencontext_emit_value_bitcast(GenContext *context, LLVMValueRef value, Type *to_type, Type *from_type)
{
LLVMValueRef ptr = gencontext_emit_alloca(context, llvm_type(from_type), "");
LLVMBuildStore(context->builder, value, ptr);
LLVMValueRef ptr_cast = gencontext_emit_bitcast(context, ptr, type_get_ptr(to_type));
return gencontext_emit_load(context, to_type, ptr_cast);
}
LLVMValueRef gencontext_emit_cast(GenContext *context, CastKind cast_kind, LLVMValueRef value, Type *to_type, Type *from_type)
{
switch (cast_kind)
{
case CAST_XIERR:
// TODO Insert zero check.
return value;
case CAST_ERROR:
UNREACHABLE
case CAST_PTRPTR:
return LLVMBuildPointerCast(context->builder, value, llvm_type(to_type), "ptrptr");
case CAST_PTRXI:
return LLVMBuildPtrToInt(context->builder, value, llvm_type(to_type), "ptrxi");
case CAST_APTSA:
return gencontext_emit_arr_to_subarray_cast(context, value, to_type, from_type);
case CAST_SAPTR:
return gencontext_emit_subarray_to_ptr_cast(context, value, to_type, from_type);
case CAST_VARRPTR:
TODO
case CAST_ARRPTR:
TODO
case CAST_STRPTR:
TODO
case CAST_EREU:
case CAST_EUER:
return gencontext_emit_value_bitcast(context, value, to_type, from_type);
case CAST_EUBOOL:
return LLVMBuildICmp(context->builder, LLVMIntNE, value, gencontext_emit_no_error_union(context), "eubool");
case CAST_PTRBOOL:
return LLVMBuildICmp(context->builder, LLVMIntNE, value, LLVMConstPointerNull(llvm_type(to_type->canonical)), "ptrbool");
case CAST_BOOLINT:
return LLVMBuildTrunc(context->builder, value, llvm_type(to_type), "boolsi");
case CAST_FPBOOL:
return LLVMBuildFCmp(context->builder, LLVMRealUNE, value, LLVMConstNull(LLVMTypeOf(value)), "fpbool");
case CAST_BOOLFP:
return LLVMBuildSIToFP(context->builder, value, llvm_type(to_type), "boolfp");
case CAST_INTBOOL:
return LLVMBuildICmp(context->builder, LLVMIntNE, value, LLVMConstNull(LLVMTypeOf(value)), "intbool");
case CAST_FPFP:
return type_convert_will_trunc(to_type, from_type)
? LLVMBuildFPTrunc(context->builder, value, llvm_type(to_type), "fpfptrunc")
: LLVMBuildFPExt(context->builder, value, llvm_type(to_type), "fpfpext");
case CAST_FPSI:
return LLVMBuildFPToSI(context->builder, value, llvm_type(to_type), "fpsi");
case CAST_FPUI:
return LLVMBuildFPToUI(context->builder, value, llvm_type(to_type), "fpui");
case CAST_SISI:
return type_convert_will_trunc(to_type, from_type)
? LLVMBuildTrunc(context->builder, value, llvm_type(to_type), "sisitrunc")
: LLVMBuildSExt(context->builder, value, llvm_type(to_type), "sisiext");
case CAST_SIUI:
return type_convert_will_trunc(to_type, from_type)
? LLVMBuildTrunc(context->builder, value, llvm_type(to_type), "siuitrunc")
: LLVMBuildZExt(context->builder, value, llvm_type(to_type), "siuiext");
case CAST_SIFP:
return LLVMBuildSIToFP(context->builder, value, llvm_type(to_type), "sifp");
case CAST_XIPTR:
return LLVMBuildIntToPtr(context->builder, value, llvm_type(to_type), "xiptr");
case CAST_UISI:
return type_convert_will_trunc(to_type, from_type)
? LLVMBuildTrunc(context->builder, value, llvm_type(to_type), "uisitrunc")
: LLVMBuildZExt(context->builder, value, llvm_type(to_type), "uisiext");
case CAST_UIUI:
return type_convert_will_trunc(to_type, from_type)
? LLVMBuildTrunc(context->builder, value, llvm_type(to_type), "uiuitrunc")
: LLVMBuildZExt(context->builder, value, llvm_type(to_type), "uiuiext");
case CAST_UIFP:
return LLVMBuildUIToFP(context->builder, value, llvm_type(to_type), "uifp");
case CAST_ENUMSI:
TODO
}
UNREACHABLE
}
static inline LLVMValueRef gencontext_emit_cast_expr(GenContext *context, Expr *expr)
{
LLVMValueRef rhs = gencontext_emit_expr(context, expr->cast_expr.expr);
return gencontext_emit_cast(context, expr->cast_expr.kind, rhs, expr->type->canonical, expr->cast_expr.expr->type->canonical);
}
/**
* Emit a Foo { .... } literal.
*
* Improve: Direct assign in the case where this is assigning to a variable.
* Improve: Create constant initializer for the constant case and do a memcopy
*/
static inline LLVMValueRef gencontext_emit_initializer_list_expr_addr(GenContext *context, Expr *expr, LLVMValueRef optional_ref)
{
LLVMTypeRef type = llvm_type(expr->type);
LLVMValueRef ref = optional_ref ?: gencontext_emit_alloca(context, type, "literal");
Type *canonical = expr->type->canonical;
if (expr->expr_initializer.init_type != INITIALIZER_NORMAL)
{
gencontext_emit_memclear(context, ref, canonical);
}
bool is_error = expr->type->canonical->type_kind == TYPE_ERRTYPE;
if (is_error)
{
LLVMValueRef err_type = LLVMBuildStructGEP2(context->builder, type, ref, 0, "");
LLVMBuildStore(context->builder, expr->type->canonical->backend_typeid, err_type);
}
if (expr->expr_initializer.init_type == INITIALIZER_ZERO)
{
return ref;
}
Expr **elements = expr->expr_initializer.initializer_expr;
bool is_union = expr->type->canonical->type_kind == TYPE_UNION;
if (expr->expr_initializer.init_type == INITIALIZER_NORMAL)
{
if (is_union)
{
assert(vec_size(elements) == 1);
LLVMValueRef init_value = gencontext_emit_expr(context, elements[0]);
LLVMValueRef u = LLVMBuildBitCast(context->builder, ref, LLVMPointerType(llvm_type(elements[0]->type->canonical), 0), "");
LLVMBuildStore(context->builder, init_value, u);
return ref;
}
VECEACH(elements, i)
{
Expr *element = elements[i];
LLVMValueRef init_value = gencontext_emit_expr(context, element);
LLVMValueRef subref = LLVMBuildStructGEP2(context->builder, type, ref, i + (int)is_error, "");
LLVMBuildStore(context->builder, init_value, subref);
}
return ref;
}
VECEACH(elements, i)
{
if (is_error) TODO
Expr *element = elements[i];
DesignatedPath *path = element->designated_init_expr.path;
LLVMValueRef sub_value = gencontext_emit_expr(context, element->designated_init_expr.value);
LLVMValueRef sub_ref = ref;
Type *parent_type = expr->type->canonical;
while (path)
{
switch (path->kind)
{
case DESIGNATED_IDENT:
if (parent_type->canonical->type_kind == TYPE_UNION)
{
sub_ref = LLVMBuildBitCast(context->builder, sub_ref, LLVMPointerType(llvm_type(path->type), 0), path->type->name);
}
else
{
sub_ref = LLVMBuildStructGEP2(context->builder, llvm_type(parent_type), sub_ref, path->index, path->type->name);
}
break;
case DESIGNATED_SUBSCRIPT:
{
// TODO range, more arrays
LLVMValueRef zero = llvm_int(type_int, 0);
LLVMValueRef index = gencontext_emit_expr(context, path->index_expr);
LLVMValueRef indices[2] = {
zero,
index,
};
sub_ref = LLVMBuildInBoundsGEP2(context->builder,
llvm_type(parent_type),
sub_ref, indices, 2, "arrsub");
break;
}
default:
UNREACHABLE;
}
parent_type = path->type;
path = path->sub_path;
}
LLVMBuildStore(context->builder, sub_value, sub_ref);
}
return ref;
}
static inline LLVMValueRef gencontext_emit_inc_dec_change(GenContext *context, bool use_mod, LLVMValueRef current_value, Expr *expr, int diff)
{
Type *type = type_reduced_from_expr(expr);
if (type->type_kind == TYPE_POINTER)
{
LLVMValueRef add = LLVMConstInt(diff < 0 ? llvm_type(type_isize) : llvm_type(type_usize), diff, diff < 0);
return LLVMBuildGEP2(context->builder, llvm_type(type->pointer), current_value, &add, 1, "ptrincdec");
}
LLVMTypeRef llvm_type = llvm_type(type);
if (type_is_float(type))
{
LLVMValueRef add = LLVMConstReal(llvm_type, (double)diff);
return LLVMBuildFAdd(context->builder, current_value, add, "fincdec");
}
LLVMValueRef diff_value = LLVMConstInt(llvm_type, 1, false);
return diff > 0
? gencontext_emit_add_int(context, type, use_mod, current_value, diff_value)
: gencontext_emit_sub_int(context, type, use_mod, current_value, diff_value);
}
static inline LLVMValueRef gencontext_emit_pre_inc_dec(GenContext *context, Expr *expr, int diff, bool use_mod)
{
LLVMValueRef addr = gencontext_emit_address(context, expr);
LLVMValueRef value = gencontext_emit_load(context, expr->type, addr);
LLVMValueRef result = gencontext_emit_inc_dec_change(context, use_mod, value, expr, diff);
LLVMBuildStore(context->builder, result, addr);
return result;
}
static inline LLVMValueRef gencontext_emit_post_inc_dec(GenContext *context, Expr *expr, int diff, bool use_mod)
{
LLVMValueRef addr = gencontext_emit_address(context, expr);
LLVMValueRef value = gencontext_emit_load(context, expr->type, addr);
LLVMValueRef result = gencontext_emit_inc_dec_change(context, use_mod, value, expr, diff);
LLVMBuildStore(context->builder, result, addr);
return value;
}
LLVMValueRef gencontext_emit_unary_expr(GenContext *context, Expr *expr)
{
Type *type = type_reduced_from_expr(expr->unary_expr.expr);
switch (expr->unary_expr.operator)
{
case UNARYOP_ERROR:
FATAL_ERROR("Illegal unary op %s", expr->unary_expr.operator);
case UNARYOP_NOT:
if (type_is_float(type))
{
LLVMValueRef val = gencontext_emit_expr(context, expr->unary_expr.expr);
return LLVMBuildFCmp(context->builder, LLVMRealUNE, val, LLVMConstNull(llvm_type(type)), "");
}
else
{
LLVMValueRef val = gencontext_emit_expr(context, expr->unary_expr.expr);
return LLVMBuildICmp(context->builder, LLVMIntEQ, val, LLVMConstNull(llvm_type(type)), "");
}
case UNARYOP_BITNEG:
return LLVMBuildNot(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "bnot");
case UNARYOP_NEGMOD:
return LLVMBuildNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "negmod");
case UNARYOP_NEG:
if (type_is_float(type))
{
return LLVMBuildFNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "fneg");
}
assert(!type_is_unsigned(type));
{
LLVMValueRef to_negate = gencontext_emit_expr(context, expr->unary_expr.expr);
LLVMValueRef zero = llvm_int(expr->unary_expr.expr->type, 0);
if (build_options.debug_mode)
{
LLVMTypeRef type_to_use = llvm_type(type->canonical);
LLVMValueRef args[2] = { zero, to_negate };
LLVMTypeRef types[2] = { type_to_use, type_to_use };
LLVMValueRef call_res = gencontext_emit_call_intrinsic(context, ssub_overflow_intrinsic_id, types, args, 2);
LLVMValueRef result = LLVMBuildExtractValue(context->builder, call_res, 0, "");
LLVMValueRef ok = LLVMBuildExtractValue(context->builder, call_res, 1, "");
gencontext_emit_panic_on_true(context, ok, "Signed negation overflow");
return result;
}
return LLVMBuildNSWSub(context->builder, zero, to_negate, "neg");
}
case UNARYOP_ADDR:
return gencontext_emit_address(context, expr->unary_expr.expr);
case UNARYOP_DEREF:
// TODO check on deref null
return gencontext_emit_load(context, expr->type, gencontext_emit_expr(context, expr->unary_expr.expr));
case UNARYOP_INC:
return gencontext_emit_pre_inc_dec(context, expr->unary_expr.expr, 1, false);
case UNARYOP_DEC:
return gencontext_emit_pre_inc_dec(context, expr->unary_expr.expr, -1, false);
}
UNREACHABLE
}
static LLVMValueRef gencontext_emit_logical_and_or(GenContext *context, Expr *expr, BinaryOp op)
{
// Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E)
// For vector implementation.
// Set up basic blocks, following Cone
LLVMBasicBlockRef start_block = LLVMGetInsertBlock(context->builder);
LLVMBasicBlockRef phi_block = LLVMCreateBasicBlockInContext(context->context, op == BINARYOP_AND ? "and.phi" : "or.phi");
LLVMBasicBlockRef rhs_block = LLVMCreateBasicBlockInContext(context->context, op == BINARYOP_AND ? "and.rhs" : "or.rhs");
// Generate left-hand condition and conditional branch
LLVMValueRef lhs = gencontext_emit_expr(context, expr->binary_expr.left);
if (op == BINARYOP_AND)
{
gencontext_emit_cond_br(context, lhs, rhs_block, phi_block);
}
else
{
gencontext_emit_cond_br(context, lhs, phi_block, rhs_block);
}
gencontext_emit_block(context, rhs_block);
LLVMValueRef rhs = gencontext_emit_expr(context, expr->binary_expr.right);
LLVMBasicBlockRef end_block = context->current_block;
gencontext_emit_br(context, phi_block);
// Generate phi
gencontext_emit_block(context, phi_block);
// Simplify for LLVM by entering the constants we already know of.
LLVMValueRef result_on_skip = llvm_int(type_bool, op == BINARYOP_AND ? 0 : 1);
// One possibility here is that a return happens inside of the expression.
if (!end_block)
{
return result_on_skip;
}
LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(type_bool), "val");
LLVMValueRef logic_values[2] = { result_on_skip, rhs };
LLVMBasicBlockRef blocks[2] = { start_block, end_block };
LLVMAddIncoming(phi, logic_values, blocks, 2);
return phi;
}
static LLVMValueRef gencontext_emit_int_comparison(GenContext *context, 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(context->builder, LLVMIntEQ, lhs_value, rhs_value, "eq");
case BINARYOP_NE:
return LLVMBuildICmp(context->builder, LLVMIntNE, lhs_value, rhs_value, "neq");
case BINARYOP_GE:
return LLVMBuildICmp(context->builder, LLVMIntUGE, lhs_value, rhs_value, "ge");
case BINARYOP_GT:
return LLVMBuildICmp(context->builder, LLVMIntUGT, lhs_value, rhs_value, "gt");
case BINARYOP_LE:
return LLVMBuildICmp(context->builder, LLVMIntULE, lhs_value, rhs_value, "le");
case BINARYOP_LT:
return LLVMBuildICmp(context->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(context->builder, LLVMIntEQ, lhs_value, rhs_value, "eq");
break;
case BINARYOP_NE:
comp_value = LLVMBuildICmp(context->builder, LLVMIntNE, lhs_value, rhs_value, "neq");
break;
case BINARYOP_GE:
comp_value = LLVMBuildICmp(context->builder, LLVMIntSGE, lhs_value, rhs_value, "ge");
break;
case BINARYOP_GT:
comp_value = LLVMBuildICmp(context->builder, LLVMIntSGT, lhs_value, rhs_value, "gt");
break;
case BINARYOP_LE:
comp_value = LLVMBuildICmp(context->builder, LLVMIntSLE, lhs_value, rhs_value, "le");
break;
case BINARYOP_LT:
comp_value = LLVMBuildICmp(context->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_int(lhs_type, 0);
switch (binary_op)
{
case BINARYOP_EQ:
// Only true if lhs >= 0
check_value = LLVMBuildICmp(context->builder, LLVMIntSGE, lhs_value, zero, "check");
return LLVMBuildAnd(context->builder, check_value, comp_value, "siui-eq");
case BINARYOP_NE:
// Always true if lhs < 0
check_value = LLVMBuildICmp(context->builder, LLVMIntSLT, lhs_value, zero, "check");
return LLVMBuildOr(context->builder, check_value, comp_value, "siui-ne");
case BINARYOP_GE:
// Only true if rhs >= 0 when regarded as a signed integer
check_value = LLVMBuildICmp(context->builder, LLVMIntSGE, rhs_value, zero, "check");
return LLVMBuildAnd(context->builder, check_value, comp_value, "siui-ge");
case BINARYOP_GT:
// Only true if rhs >= 0 when regarded as a signed integer
check_value = LLVMBuildICmp(context->builder, LLVMIntSGE, rhs_value, zero, "check");
return LLVMBuildAnd(context->builder, check_value, comp_value, "siui-gt");
case BINARYOP_LE:
// Always true if rhs < 0 when regarded as a signed integer
check_value = LLVMBuildICmp(context->builder, LLVMIntSLT, rhs_value, zero, "check");
return LLVMBuildOr(context->builder, check_value, comp_value, "siui-le");
case BINARYOP_LT:
// Always true if rhs < 0 when regarded as a signed integer
check_value = LLVMBuildICmp(context->builder, LLVMIntSLT, rhs_value, zero, "check");
return LLVMBuildOr(context->builder, check_value, comp_value, "siui-lt");
default:
UNREACHABLE
}
}
static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVMValueRef lhs_addr, BinaryOp binary_op)
{
if (binary_op == BINARYOP_AND || binary_op == BINARYOP_OR)
{
return gencontext_emit_logical_and_or(context, expr, binary_op);
}
Expr *lhs = expr->binary_expr.left;
Expr *rhs = expr->binary_expr.right;
LLVMValueRef lhs_value;
LLVMValueRef rhs_value;
if (lhs_addr)
{
lhs_value = gencontext_emit_load(context, lhs->type, lhs_addr);
}
else
{
lhs_value = gencontext_emit_expr(context, lhs);
}
rhs_value = gencontext_emit_expr(context, rhs);
Type *lhs_type = type_reduced_from_expr(lhs);
if (type_is_integer(lhs_type) && binary_op >= BINARYOP_GT && binary_op <= BINARYOP_EQ)
{
return gencontext_emit_int_comparison(context, lhs_type, type_reduced_from_expr(rhs), lhs_value, rhs_value, binary_op);
}
bool is_float = type_is_float(lhs_type);
switch (binary_op)
{
case BINARYOP_ERROR:
UNREACHABLE
case BINARYOP_MULT:
if (is_float) return LLVMBuildFMul(context->builder, lhs_value, rhs_value, "fmul");
if (type_is_unsigned_integer(lhs_type))
{
if (build_options.debug_mode)
{
LLVMTypeRef type_to_use = llvm_type(lhs_type);
LLVMValueRef args[2] = { lhs_value, rhs_value };
LLVMTypeRef types[2] = { type_to_use, type_to_use };
LLVMValueRef call_res = gencontext_emit_call_intrinsic(context, umul_overflow_intrinsic_id, types, args, 2);
LLVMValueRef result = LLVMBuildExtractValue(context->builder, call_res, 0, "");
LLVMValueRef ok = LLVMBuildExtractValue(context->builder, call_res, 1, "");
gencontext_emit_panic_on_true(context, ok, "Unsigned multiplication overflow");
return result;
}
return LLVMBuildNUWMul(context->builder, lhs_value, rhs_value, "umul");
}
if (build_options.debug_mode)
{
LLVMTypeRef type_to_use = llvm_type(lhs_type);
LLVMValueRef args[2] = { lhs_value, rhs_value };
LLVMTypeRef types[2] = { type_to_use, type_to_use };
LLVMValueRef call_res = gencontext_emit_call_intrinsic(context, smul_overflow_intrinsic_id, types, args, 2);
LLVMValueRef result = LLVMBuildExtractValue(context->builder, call_res, 0, "");
LLVMValueRef ok = LLVMBuildExtractValue(context->builder, call_res, 1, "");
gencontext_emit_panic_on_true(context, ok, "Signed multiplication overflow");
return result;
}
return LLVMBuildNSWMul(context->builder, lhs_value, rhs_value, "mul");
case BINARYOP_MULT_MOD:
return LLVMBuildMul(context->builder, lhs_value, rhs_value, "mul");
case BINARYOP_SUB:
case BINARYOP_SUB_MOD:
if (lhs_type->type_kind == TYPE_POINTER)
{
if (lhs->type->canonical == rhs->type->canonical) return LLVMBuildPtrDiff(context->builder, lhs_value, rhs_value, "ptrdiff");
rhs_value = LLVMBuildNeg(context->builder, rhs_value, "");
return LLVMBuildGEP2(context->builder, llvm_type(lhs_type->canonical->pointer), lhs_value, &rhs_value, 1, "ptrsub");
}
if (is_float) return LLVMBuildFSub(context->builder, lhs_value, rhs_value, "fsub");
return gencontext_emit_sub_int(context, lhs->type->canonical, binary_op == BINARYOP_SUB_MOD, lhs_value, rhs_value);
case BINARYOP_ADD:
case BINARYOP_ADD_MOD:
if (lhs_type->type_kind == TYPE_POINTER)
{
assert(type_is_integer(rhs->type->canonical));
return LLVMBuildGEP2(context->builder, llvm_type(lhs_type->canonical->pointer), lhs_value, &rhs_value, 1, "ptradd");
}
if (is_float) return LLVMBuildFAdd(context->builder, lhs_value, rhs_value, "fadd");
return gencontext_emit_add_int(context, lhs_type, binary_op == BINARYOP_ADD_MOD, lhs_value, rhs_value);
case BINARYOP_DIV:
if (is_float) return LLVMBuildFDiv(context->builder, lhs_value, rhs_value, "fdiv");
return type_is_unsigned(lhs_type)
? LLVMBuildUDiv(context->builder, lhs_value, rhs_value, "udiv")
: LLVMBuildSDiv(context->builder, lhs_value, rhs_value, "sdiv");
case BINARYOP_MOD:
return type_is_unsigned(lhs_type)
? LLVMBuildURem(context->builder, lhs_value, rhs_value, "umod")
: LLVMBuildSRem(context->builder, lhs_value, rhs_value, "smod");
case BINARYOP_SHR:
return type_is_unsigned(lhs_type)
? LLVMBuildLShr(context->builder, lhs_value, rhs_value, "lshr")
: LLVMBuildAShr(context->builder, lhs_value, rhs_value, "ashr");
case BINARYOP_SHL:
return LLVMBuildShl(context->builder, lhs_value, rhs_value, "shl");
case BINARYOP_BIT_AND:
return LLVMBuildAnd(context->builder, lhs_value, rhs_value, "and");
case BINARYOP_BIT_OR:
return LLVMBuildOr(context->builder, lhs_value, rhs_value, "or");
case BINARYOP_BIT_XOR:
return LLVMBuildXor(context->builder, lhs_value, rhs_value, "xor");
case BINARYOP_EQ:
// Unordered?
assert(type_is_float(lhs_type));
return LLVMBuildFCmp(context->builder, LLVMRealUEQ, lhs_value, rhs_value, "eq");
case BINARYOP_NE:
// Unordered?
assert(type_is_float(lhs_type));
return LLVMBuildFCmp(context->builder, LLVMRealUNE, lhs_value, rhs_value, "neq");
case BINARYOP_GE:
assert(type_is_float(lhs_type));
return LLVMBuildFCmp(context->builder, LLVMRealUGE, lhs_value, rhs_value, "ge");
case BINARYOP_GT:
assert(type_is_float(lhs_type));
return LLVMBuildFCmp(context->builder, LLVMRealUGT, lhs_value, rhs_value, "gt");
case BINARYOP_LE:
assert(type_is_float(lhs_type));
return LLVMBuildFCmp(context->builder, LLVMRealULE, lhs_value, rhs_value, "le");
case BINARYOP_LT:
assert(type_is_float(lhs_type));
return LLVMBuildFCmp(context->builder, LLVMRealULE, lhs_value, rhs_value, "lt");
case BINARYOP_AND:
case BINARYOP_OR:
case BINARYOP_ASSIGN:
case BINARYOP_MULT_ASSIGN:
case BINARYOP_MULT_MOD_ASSIGN:
case BINARYOP_ADD_ASSIGN:
case BINARYOP_ADD_MOD_ASSIGN:
case BINARYOP_SUB_ASSIGN:
case BINARYOP_SUB_MOD_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
}
UNREACHABLE
}
LLVMValueRef gencontext_emit_post_unary_expr(GenContext *context, Expr *expr)
{
return gencontext_emit_post_inc_dec(context, expr->post_expr.expr, expr->post_expr.operator == POSTUNARYOP_INC ? 1 : -1, false);
}
LLVMValueRef gencontext_emit_typeid(GenContext *context, Expr *expr)
{
if (type_is_builtin(expr->typeid_expr->type->type_kind))
{
return gencontext_emit_const_int(context, type_usize, expr->typeid_expr->type->type_kind);
}
assert(expr->typeid_expr->type->backend_typeid);
return expr->typeid_expr->type->backend_typeid;
}
LLVMValueRef gencontext_emit_trycatch_expr(GenContext *context, Expr *expr)
{
LLVMBasicBlockRef error_block = gencontext_create_free_block(context, "error_block");
LLVMBasicBlockRef no_err_block = gencontext_create_free_block(context, "noerr_block");
LLVMBasicBlockRef phi_block = gencontext_create_free_block(context, "phi_block");
// Store catch/error var
PUSH_ERROR();
// Set the catch/error var
context->error_var = NULL;
context->catch_block = error_block;
gencontext_emit_expr(context, expr->trycatch_expr);
// Restore.
POP_ERROR();
// Emit success and jump to phi.
gencontext_emit_br(context, no_err_block);
gencontext_emit_block(context, no_err_block);
gencontext_emit_br(context, phi_block);
// Emit error and jump to phi
gencontext_emit_block(context, error_block);
gencontext_emit_br(context, phi_block);
gencontext_emit_block(context, phi_block);
LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(expr->type), "val");
LLVMValueRef lhs = llvm_int(type_bool, expr->expr_kind == EXPR_TRY ? 1 : 0);
LLVMValueRef rhs = llvm_int(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);
return phi;
}
static inline LLVMValueRef gencontext_emit_else_jump_expr(GenContext *context, Expr *expr)
{
LLVMBasicBlockRef else_block = gencontext_create_free_block(context, "else_block");
LLVMBasicBlockRef no_err_block = gencontext_create_free_block(context, "noerr_block");
// Store catch/error var
PUSH_ERROR();
// Set the catch/error var
context->error_var = NULL;
context->catch_block = else_block;
LLVMValueRef value = gencontext_emit_expr(context, expr->else_expr.expr);
// Restore.
POP_ERROR();
// Emit success and to end.
gencontext_emit_br(context, no_err_block);
// Emit else
gencontext_emit_block(context, else_block);
gencontext_emit_stmt(context, expr->else_expr.else_stmt);
gencontext_emit_br(context, no_err_block);
gencontext_emit_block(context, no_err_block);
return value;
}
static LLVMValueRef gencontext_emit_else_expr(GenContext *context, Expr *expr)
{
if (expr->else_expr.is_jump) return gencontext_emit_else_jump_expr(context, expr);
LLVMBasicBlockRef else_block = gencontext_create_free_block(context, "else_block");
LLVMBasicBlockRef phi_block = gencontext_create_free_block(context, "phi_block");
// Store catch/error var
PUSH_ERROR();
// Set the catch/error var
context->error_var = NULL;
context->catch_block = else_block;
LLVMValueRef normal_value = gencontext_emit_expr(context, expr->else_expr.expr);
// Restore.
POP_ERROR();
// Emit success and jump to phi.
LLVMBasicBlockRef success_end_block = gencontext_current_block_if_in_use(context);
if (success_end_block) gencontext_emit_br(context, phi_block);
// Emit else
gencontext_emit_block(context, else_block);
LLVMValueRef else_value = gencontext_emit_expr(context, expr->else_expr.else_expr);
LLVMBasicBlockRef else_block_exit = gencontext_current_block_if_in_use(context);
if (else_block_exit) gencontext_emit_br(context, phi_block);
gencontext_emit_block(context, phi_block);
if (!else_block_exit)
{
return normal_value;
}
if (!success_end_block)
{
return else_value;
}
LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(expr->type), "val");
LLVMValueRef logic_values[2] = { normal_value, else_value };
LLVMBasicBlockRef blocks[2] = { success_end_block, else_block_exit };
LLVMAddIncoming(phi, logic_values, blocks, 2);
return phi;
}
static LLVMValueRef gencontext_emit_binary_expr(GenContext *context, 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);
LLVMValueRef addr = gencontext_emit_address(context, expr->binary_expr.left);
LLVMValueRef value = gencontext_emit_binary(context, expr, addr, base_op);
LLVMBuildStore(context->builder, value, addr);
return value;
}
if (binary_op == BINARYOP_ASSIGN)
{
LLVMValueRef addr = gencontext_emit_address(context, expr->binary_expr.left);
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);
}
return gencontext_emit_assign_expr(context, addr, expr->binary_expr.right, failable_ref);
}
return gencontext_emit_binary(context, expr, NULL, binary_op);
}
LLVMValueRef gencontext_emit_elvis_expr(GenContext *context, Expr *expr)
{
LLVMBasicBlockRef current_block = context->current_block;
LLVMBasicBlockRef phi_block = LLVMCreateBasicBlockInContext(context->context, "cond.phi");
LLVMBasicBlockRef rhs_block = LLVMCreateBasicBlockInContext(context->context, "cond.rhs");
// Generate condition and conditional branch
LLVMValueRef lhs = gencontext_emit_expr(context, expr->ternary_expr.cond);
LLVMValueRef cond = lhs;
Type *cond_type = expr->ternary_expr.cond->type->canonical;
if (cond_type != type_bool)
{
CastKind cast = cast_to_bool_kind(cond_type);
cond = gencontext_emit_cast(context, cast, cond, cond_type, type_bool);
}
gencontext_emit_cond_br(context, cond, phi_block, rhs_block);
gencontext_emit_block(context, rhs_block);
LLVMValueRef rhs = gencontext_emit_expr(context, expr->ternary_expr.else_expr);
LLVMBasicBlockRef end_block = context->current_block;
gencontext_emit_br(context, phi_block);
// Generate phi
gencontext_emit_block(context, phi_block);
LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(expr->type), "val");
LLVMValueRef logic_values[2] = { lhs, rhs };
LLVMBasicBlockRef blocks[2] = { current_block, end_block };
LLVMAddIncoming(phi, logic_values, blocks, 2);
return phi;
}
LLVMValueRef gencontext_emit_ternary_expr(GenContext *context, Expr *expr)
{
if (expr->ternary_expr.then_expr == NULL) return gencontext_emit_elvis_expr(context, expr);
// Set up basic blocks, following Cone
LLVMBasicBlockRef phi_block = LLVMCreateBasicBlockInContext(context->context, "cond.phi");
LLVMBasicBlockRef lhs_block = LLVMCreateBasicBlockInContext(context->context, "cond.lhs");
LLVMBasicBlockRef rhs_block = LLVMCreateBasicBlockInContext(context->context, "cond.rhs");
// Generate condition and conditional branch
LLVMValueRef cond = gencontext_emit_expr(context, expr->ternary_expr.cond);
gencontext_emit_cond_br(context, cond, lhs_block, rhs_block);
gencontext_emit_block(context, lhs_block);
LLVMValueRef lhs = gencontext_emit_expr(context, expr->ternary_expr.then_expr);
LLVMBasicBlockRef lhs_exit = gencontext_current_block_if_in_use(context);
if (lhs_exit) gencontext_emit_br(context, phi_block);
gencontext_emit_block(context, rhs_block);
LLVMValueRef rhs = gencontext_emit_expr(context, expr->ternary_expr.else_expr);
LLVMBasicBlockRef rhs_exit = gencontext_current_block_if_in_use(context);
if (rhs_exit) gencontext_emit_br(context, phi_block);
// Generate phi
gencontext_emit_block(context, phi_block);
if (!rhs_exit)
{
return lhs;
}
if (!lhs_exit)
{
return rhs;
}
LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(expr->type), "val");
LLVMValueRef logic_values[2] = { lhs, rhs };
LLVMBasicBlockRef blocks[2] = { lhs_exit, rhs_exit };
LLVMAddIncoming(phi, logic_values, blocks, 2);
return phi;
}
LLVMValueRef gencontext_emit_const_expr(GenContext *context, Expr *expr)
{
Type *type = type_reduced_from_expr(expr)->canonical;
switch (expr->const_expr.kind)
{
case ALL_INTS:
if (type_is_unsigned(type))
{
return llvm_int(type, bigint_as_unsigned(&expr->const_expr.i));
}
else
{
return llvm_int(type, bigint_as_signed(&expr->const_expr.i));
}
case ALL_FLOATS:
return LLVMConstReal(llvm_type(type), (double) expr->const_expr.f);
case TYPE_POINTER:
return LLVMConstNull(llvm_type(type));
case TYPE_BOOL:
return llvm_int(type, expr->const_expr.b ? 1 : 0);
case TYPE_STRING:
{
LLVMValueRef global_name = LLVMAddGlobal(context->module, LLVMArrayType(llvm_type(type_char), expr->const_expr.string.len + 1), "");
LLVMSetLinkage(global_name, LLVMInternalLinkage);
LLVMSetGlobalConstant(global_name, 1);
LLVMSetInitializer(global_name, LLVMConstStringInContext(context->context,
expr->const_expr.string.chars,
expr->const_expr.string.len,
0));
return global_name;
}
case TYPE_ERRTYPE:
TODO
default:
UNREACHABLE
}
}
LLVMValueRef gencontext_emit_call_expr(GenContext *context, Expr *expr)
{
size_t args = vec_size(expr->call_expr.arguments);
FunctionSignature *signature;
LLVMValueRef func;
LLVMTypeRef func_type;
if (expr->call_expr.is_pointer_call)
{
signature = expr->call_expr.function->type->canonical->pointer->func.signature;
func = gencontext_emit_expr(context, expr->call_expr.function);
func_type = llvm_type(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->ref;
func_type = llvm_type(function_decl->type);
}
else
{
Decl *function_decl = expr->call_expr.function->identifier_expr.decl;
signature = &function_decl->func.function_signature;
func = function_decl->ref;
func_type = llvm_type(function_decl->type);
}
LLVMValueRef return_param = NULL;
if (signature->return_param)
{
return_param = gencontext_emit_alloca(context, llvm_type(signature->rtype->type), "returnparam");
args++;
}
LLVMValueRef *values = args ? malloc_arena(args * sizeof(LLVMValueRef)) : NULL;
unsigned param_index = 0;
if (return_param)
{
values[param_index++] = return_param;
}
VECEACH(expr->call_expr.arguments, i)
{
values[param_index++] = gencontext_emit_expr(context, expr->call_expr.arguments[i]);
}
LLVMValueRef call = LLVMBuildCall2(context->builder, func_type, func, values, args, "");
//gencontext_emit_throw_branch(context, call, signature->throws, expr->call_expr.throw_info, signature->error_return);
// If we used a return param, then load that info here.
if (return_param)
{
call = gencontext_emit_load(context, signature->rtype->type, return_param);
}
/*
if (function->func.function_signature.convention)
{
LLVMSetFunctionCallConv(call, LLVMX86StdcallCallConv);
}*/
return call;
}
static inline LLVMValueRef gencontext_emit_expression_list_expr(GenContext *context, Expr *expr)
{
LLVMValueRef value = NULL;
VECEACH(expr->expression_list, i)
{
value = gencontext_emit_expr(context, expr->expression_list[i]);
}
return value;
}
static inline LLVMValueRef gencontext_emit_expr_block(GenContext *context, Expr *expr)
{
LLVMValueRef old_ret_out = context->return_out;
LLVMBasicBlockRef saved_expr_block = context->expr_block_exit;
LLVMBasicBlockRef expr_block = gencontext_create_free_block(context, "expr_block.exit");
context->expr_block_exit = expr_block;
LLVMValueRef return_out = NULL;
if (expr->type != type_void)
{
return_out = gencontext_emit_alloca(context, llvm_type(expr->type), "blockret");
}
context->return_out = return_out;
Ast **stmts = expr->expr_block.stmts;
VECEACH(stmts, i)
{
gencontext_emit_stmt(context, stmts[i]);
}
gencontext_emit_br(context, expr_block);
// Emit the exit block.
gencontext_emit_block(context, expr_block);
context->return_out = old_ret_out;
context->expr_block_exit = saved_expr_block;
return return_out ? gencontext_emit_load(context, expr->type, return_out) : NULL;
}
static inline LLVMValueRef gencontext_emit_macro_block(GenContext *context, Expr *expr)
{
LLVMValueRef old_ret_out = context->return_out;
LLVMBasicBlockRef saved_expr_block = context->expr_block_exit;
LLVMBasicBlockRef expr_block = gencontext_create_free_block(context, "expr_block.exit");
context->expr_block_exit = expr_block;
LLVMValueRef return_out = NULL;
if (expr->type != type_void)
{
return_out = gencontext_emit_alloca(context, llvm_type(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.
if (!expr->macro_block.args[i]) continue;
Decl *decl = expr->macro_block.params[i];
decl->ref = gencontext_emit_alloca(context, llvm_type(decl->type), decl->name);
LLVMValueRef value = gencontext_emit_expr(context, expr->macro_block.args[i]);
gencontext_emit_store(context, decl, value);
}
VECEACH(stmts, i)
{
gencontext_emit_stmt(context, stmts[i]);
}
gencontext_emit_br(context, expr_block);
// Emit the exit block.
gencontext_emit_block(context, expr_block);
context->return_out = old_ret_out;
context->expr_block_exit = saved_expr_block;
return return_out ? gencontext_emit_load(context, expr->type, return_out) : NULL;
}
LLVMValueRef gencontext_emit_call_intrinsic(GenContext *context, unsigned intrinsic_id, LLVMTypeRef *types,
LLVMValueRef *values, unsigned arg_count)
{
LLVMValueRef decl = LLVMGetIntrinsicDeclaration(context->module, intrinsic_id, types, arg_count);
LLVMTypeRef type = LLVMIntrinsicGetType(context->context, intrinsic_id, types, arg_count);
return LLVMBuildCall2(context->builder, type, decl, values, arg_count, "");
}
LLVMValueRef gencontext_emit_assign_expr(GenContext *context, LLVMValueRef ref, Expr *expr, LLVMValueRef failable_ref)
{
LLVMBasicBlockRef assign_block = NULL;
PUSH_ERROR();
if (failable_ref)
{
assign_block = gencontext_create_free_block(context, "after_assign");
context->error_var = failable_ref;
context->catch_block = assign_block;
}
LLVMValueRef value;
switch (expr->expr_kind)
{
case EXPR_INITIALIZER_LIST:
value = gencontext_emit_load(context,
expr->type,
gencontext_emit_initializer_list_expr_addr(context, expr, ref));
break;
default:
value = gencontext_emit_expr(context, expr);
LLVMBuildStore(context->builder, value, ref);
break;
}
if (failable_ref)
{
LLVMBuildStore(context->builder, gencontext_emit_no_error_union(context), failable_ref);
}
POP_ERROR();
if (failable_ref)
{
gencontext_emit_br(context, assign_block);
gencontext_emit_block(context, assign_block);
}
return value;
}
static inline LLVMValueRef gencontext_emit_identifier_rvalue(GenContext *context, Decl *decl)
{
if (decl->decl_kind != DECL_VAR || !decl->var.failable)
{
return gencontext_emit_load(context, decl->type, decl_ref(decl));
}
assert(context->catch_block);
LLVMBasicBlockRef after_block = gencontext_create_free_block(context, "after_check");
LLVMValueRef error_value = gencontext_emit_load(context, type_error, decl_failable_ref(decl));
LLVMValueRef comp = LLVMBuildICmp(context->builder, LLVMIntEQ, error_value,
gencontext_emit_no_error_union(context), "");
if (context->error_var)
{
LLVMBasicBlockRef error_block = gencontext_create_free_block(context, "error");
gencontext_emit_cond_br(context, comp, after_block, error_block);
gencontext_emit_block(context, error_block);
LLVMBuildStore(context->builder, error_value, gencontext_emit_bitcast(context, context->error_var, type_get_ptr(type_error)));
gencontext_emit_br(context, context->catch_block);
}
else
{
gencontext_emit_cond_br(context, comp, after_block, context->catch_block);
}
gencontext_emit_block(context, after_block);
return gencontext_emit_load(context, decl->type, decl_ref(decl));
}
static inline LLVMValueRef gencontext_emit_failable(GenContext *context, Expr *expr)
{
Expr *fail = expr->failable_expr;
if (context->error_var)
{
assert(context->error_var);
LLVMValueRef value = gencontext_emit_expr(context, fail);
LLVMBuildStore(context->builder, value, gencontext_emit_bitcast(context, context->error_var, type_get_ptr(fail->type)));
}
gencontext_emit_br(context, context->catch_block);
LLVMBasicBlockRef ignored_block = gencontext_create_free_block(context, "postfailed");
gencontext_emit_block(context, ignored_block);
if (expr->type->canonical == type_void) return NULL;
return LLVMGetUndef(llvm_type(expr->type));
}
LLVMValueRef gencontext_emit_check_failable(GenContext *context, Expr *expr)
{
PUSH_ERROR();
LLVMBasicBlockRef after_check_block = gencontext_create_free_block(context, "after_check");
context->error_var = NULL;
context->catch_block = after_check_block;
gencontext_emit_expr(context, expr->fail_check_expr);
POP_ERROR();
LLVMBasicBlockRef phi_block = gencontext_create_free_block(context, "checkphi");
LLVMBasicBlockRef normal_block = context->current_block;
gencontext_emit_br(context, phi_block);
gencontext_emit_block(context, after_check_block);
gencontext_emit_br(context, phi_block);
gencontext_emit_block(context, phi_block);
LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(type_bool), "val");
LLVMValueRef logic_values[2] = { llvm_int(type_bool, 0), llvm_int(type_bool, 1) };
LLVMBasicBlockRef blocks[2] = { after_check_block, normal_block };
LLVMAddIncoming(phi, logic_values, blocks, 2);
return phi;
}
LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr)
{
NESTED_RETRY:
switch (expr->expr_kind)
{
case EXPR_RANGE:
case EXPR_POISONED:
case EXPR_DECL_LIST:
UNREACHABLE
case EXPR_DESIGNATED_INITIALIZER:
// Should only appear when generating designated initializers.
UNREACHABLE
case EXPR_FAIL_CHECK:
return gencontext_emit_check_failable(context, expr);
case EXPR_FAILABLE:
return gencontext_emit_failable(context, expr);
case EXPR_TRY:
case EXPR_CATCH:
return gencontext_emit_trycatch_expr(context, expr);
case EXPR_ELSE:
return gencontext_emit_else_expr(context, expr);
case EXPR_MACRO_BLOCK:
return gencontext_emit_macro_block(context, expr);
case EXPR_COMPOUND_LITERAL:
expr = expr->expr_compound_literal.initializer;
goto NESTED_RETRY;
case EXPR_INITIALIZER_LIST:
return gencontext_emit_load(context, expr->type, gencontext_emit_initializer_list_expr_addr(context, expr, NULL));
case EXPR_EXPR_BLOCK:
return gencontext_emit_expr_block(context, expr);
case EXPR_SCOPED_EXPR:
return gencontext_emit_scoped_expr(context, expr);
case EXPR_UNARY:
return gencontext_emit_unary_expr(context, expr);
case EXPR_CONST:
return gencontext_emit_const_expr(context, expr);
case EXPR_BINARY:
return gencontext_emit_binary_expr(context, expr);
case EXPR_TERNARY:
return gencontext_emit_ternary_expr(context, expr);
case EXPR_POST_UNARY:
return gencontext_emit_post_unary_expr(context, expr);
case EXPR_GUARD:
return gencontext_emit_trycatch_expr(context, expr);
case EXPR_TYPEID:
return gencontext_emit_typeid(context, expr);
case EXPR_TYPE_ACCESS:
case EXPR_TYPEOF:
// These are folded in the semantic analysis step.
UNREACHABLE
case EXPR_IDENTIFIER:
return gencontext_emit_identifier_rvalue(context, expr->identifier_expr.decl);
case EXPR_SUBSCRIPT:
case EXPR_ACCESS:
return gencontext_emit_load(context, expr->type, gencontext_emit_address(context, expr));
case EXPR_CALL:
return gencontext_emit_call_expr(context, expr);
case EXPR_GROUP:
expr = expr->group_expr;
goto NESTED_RETRY;
case EXPR_EXPRESSION_LIST:
return gencontext_emit_expression_list_expr(context, expr);
case EXPR_CAST:
return gencontext_emit_cast_expr(context, expr);
}
UNREACHABLE
}