mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 20:11:17 +00:00
1509 lines
54 KiB
C
1509 lines
54 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;
|
||
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_VARARRAY:
|
||
case TYPE_SUBARRAY:
|
||
case TYPE_STRING:
|
||
TODO
|
||
default:
|
||
UNREACHABLE
|
||
|
||
}
|
||
}
|
||
|
||
static LLVMValueRef gencontext_emit_member_addr(GenContext *context, LLVMValueRef value, Decl *parent, Decl *member)
|
||
{
|
||
unsigned index;
|
||
Decl *current_parent;
|
||
assert(member->resolve_status == RESOLVE_DONE);
|
||
if (decl_is_struct_type(member))
|
||
{
|
||
index = member->strukt.id;
|
||
current_parent = member->parent_struct;
|
||
}
|
||
else
|
||
{
|
||
index = member->var.id;
|
||
current_parent = member->var.parent;
|
||
}
|
||
assert(current_parent);
|
||
if (parent != current_parent)
|
||
{
|
||
value = gencontext_emit_member_addr(context, value, parent, current_parent);
|
||
}
|
||
|
||
if (current_parent->decl_kind == DECL_UNION)
|
||
{
|
||
return LLVMBuildBitCast(context->builder, value, LLVMPointerType(llvm_type(member->type), 0), member->name ?: "anon");
|
||
}
|
||
return LLVMBuildStructGEP2(context->builder, llvm_type(current_parent->type), value, index, member->name ?: "anon");
|
||
}
|
||
|
||
|
||
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_EXPR_BLOCK:
|
||
TODO
|
||
case EXPR_DESIGNATED_INITIALIZER:
|
||
// Should only appear when generating designated initializers.
|
||
UNREACHABLE
|
||
|
||
case EXPR_IDENTIFIER:
|
||
return expr->identifier_expr.decl->var.backend_ref;
|
||
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_TRY:
|
||
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_MACRO_EXPR:
|
||
case EXPR_TYPEOF:
|
||
UNREACHABLE
|
||
}
|
||
UNREACHABLE
|
||
}
|
||
|
||
static inline LLVMValueRef gencontext_emit_error_cast(GenContext *context, LLVMValueRef value, Type *type)
|
||
{
|
||
LLVMValueRef global = type->decl->error.start_value;
|
||
LLVMValueRef val = LLVMBuildBitCast(context->builder, global, llvm_type(type_usize), "");
|
||
LLVMValueRef extend = LLVMBuildZExtOrBitCast(context->builder, value, llvm_type(type_usize), "");
|
||
return LLVMBuildAdd(context->builder, val, extend, "");
|
||
}
|
||
|
||
LLVMValueRef gencontext_emit_cast(GenContext *context, CastKind cast_kind, LLVMValueRef value, Type *type, Type *target_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(type), "ptrptr");
|
||
case CAST_PTRXI:
|
||
return LLVMBuildPtrToInt(context->builder, value, llvm_type(type), "ptrxi");
|
||
case CAST_VARRPTR:
|
||
TODO
|
||
case CAST_ARRPTR:
|
||
TODO
|
||
case CAST_STRPTR:
|
||
TODO
|
||
case CAST_ERREU:
|
||
return gencontext_emit_error_cast(context, value, target_type);
|
||
case CAST_EUERR:
|
||
TODO
|
||
case CAST_EUBOOL:
|
||
return LLVMBuildICmp(context->builder, LLVMIntNE, value, llvm_int(type_usize, 0), "eubool");
|
||
case CAST_PTRBOOL:
|
||
return LLVMBuildICmp(context->builder, LLVMIntNE, value, LLVMConstPointerNull(llvm_type(type->canonical->pointer)), "ptrbool");
|
||
case CAST_BOOLINT:
|
||
return LLVMBuildTrunc(context->builder, value, llvm_type(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(type), "boolfp");
|
||
case CAST_INTBOOL:
|
||
return LLVMBuildICmp(context->builder, LLVMIntNE, value, LLVMConstNull(LLVMTypeOf(value)), "intbool");
|
||
case CAST_FPFP:
|
||
return type_convert_will_trunc(type, target_type)
|
||
? LLVMBuildFPTrunc(context->builder, value, llvm_type(type), "fpfptrunc")
|
||
: LLVMBuildFPExt(context->builder, value, llvm_type(type), "fpfpext");
|
||
case CAST_FPSI:
|
||
return LLVMBuildFPToSI(context->builder, value, llvm_type(type), "fpsi");
|
||
case CAST_FPUI:
|
||
return LLVMBuildFPToUI(context->builder, value, llvm_type(type), "fpui");
|
||
case CAST_SISI:
|
||
return type_convert_will_trunc(type, target_type)
|
||
? LLVMBuildTrunc(context->builder, value, llvm_type(type), "sisitrunc")
|
||
: LLVMBuildSExt(context->builder, value, llvm_type(type), "sisiext");
|
||
case CAST_SIUI:
|
||
return type_convert_will_trunc(type, target_type)
|
||
? LLVMBuildTrunc(context->builder, value, llvm_type(type), "siuitrunc")
|
||
: LLVMBuildZExt(context->builder, value, llvm_type(type), "siuiext");
|
||
case CAST_SIFP:
|
||
return LLVMBuildSIToFP(context->builder, value, llvm_type(type), "sifp");
|
||
case CAST_XIPTR:
|
||
return LLVMBuildIntToPtr(context->builder, value, llvm_type(type), "xiptr");
|
||
case CAST_UISI:
|
||
return type_convert_will_trunc(type, target_type)
|
||
? LLVMBuildTrunc(context->builder, value, llvm_type(type), "uisitrunc")
|
||
: LLVMBuildZExt(context->builder, value, llvm_type(type), "uisiext");
|
||
case CAST_UIUI:
|
||
return type_convert_will_trunc(type, target_type)
|
||
? LLVMBuildTrunc(context->builder, value, llvm_type(type), "uiuitrunc")
|
||
: LLVMBuildZExt(context->builder, value, llvm_type(type), "uiuiext");
|
||
case CAST_UIFP:
|
||
return LLVMBuildUIToFP(context->builder, value, llvm_type(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);
|
||
}
|
||
|
||
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, "");
|
||
LLVMBuildStore(context->builder, init_value, subref);
|
||
}
|
||
return ref;
|
||
}
|
||
|
||
VECEACH(elements, i)
|
||
{
|
||
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), "unionref");
|
||
}
|
||
else
|
||
{
|
||
sub_ref = LLVMBuildStructGEP2(context->builder, llvm_type(parent_type), sub_ref, path->index, "structref");
|
||
}
|
||
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:
|
||
return LLVMBuildXor(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr),
|
||
llvm_int(type_bool, 1), "not");
|
||
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);
|
||
gencontext_emit_br(context, phi_block);
|
||
|
||
// Generate phi
|
||
gencontext_emit_block(context, phi_block);
|
||
LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(type_bool), "val");
|
||
|
||
// Simplify for LLVM by entering the constants we already know of.
|
||
LLVMValueRef result_on_skip = llvm_int(type_bool, op == BINARYOP_AND ? 0 : 1);
|
||
LLVMValueRef logic_values[2] = { result_on_skip, rhs };
|
||
LLVMBasicBlockRef blocks[2] = { start_block, rhs_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
|
||
}
|
||
|
||
LLVMBasicBlockRef gencontext_get_try_target(GenContext *context, Expr *try_expr)
|
||
{
|
||
if (!try_expr->try_expr.jump_target)
|
||
{
|
||
try_expr->try_expr.jump_target = gencontext_create_free_block(context, "tryjump");
|
||
}
|
||
return try_expr->try_expr.jump_target;
|
||
}
|
||
|
||
|
||
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)
|
||
{
|
||
assert(expr->typeid_expr->type->backend_typeid);
|
||
return expr->typeid_expr->type->backend_typeid;
|
||
}
|
||
|
||
LLVMValueRef gencontext_emit_try_expr(GenContext *context, Expr *expr)
|
||
{
|
||
if (expr->try_expr.type == TRY_STMT)
|
||
{
|
||
gencontext_emit_stmt(context, expr->try_expr.stmt);
|
||
return NULL;
|
||
}
|
||
if (expr->try_expr.type == TRY_EXPR_ELSE_EXPR)
|
||
{
|
||
LLVMBasicBlockRef else_block = gencontext_get_try_target(context, expr);
|
||
LLVMBasicBlockRef after_catch = gencontext_create_free_block(context, "aftercatch");
|
||
LLVMValueRef res = gencontext_emit_alloca(context, llvm_type(expr->try_expr.else_expr->type), "catch");
|
||
LLVMValueRef normal_res = gencontext_emit_expr(context, expr->try_expr.expr);
|
||
LLVMBuildStore(context->builder, normal_res, res);
|
||
gencontext_emit_br(context, after_catch);
|
||
gencontext_emit_block(context, else_block);
|
||
LLVMValueRef catch_value = gencontext_emit_expr(context, expr->try_expr.else_expr);
|
||
LLVMBuildStore(context->builder, catch_value, res);
|
||
gencontext_emit_br(context, after_catch);
|
||
gencontext_emit_block(context, after_catch);
|
||
return gencontext_emit_load(context, expr->try_expr.else_expr->type, res);
|
||
}
|
||
if (expr->try_expr.type == TRY_EXPR_ELSE_JUMP)
|
||
{
|
||
LLVMBasicBlockRef else_block = gencontext_get_try_target(context, expr);
|
||
LLVMBasicBlockRef after_catch = gencontext_create_free_block(context, "aftercatch");
|
||
gencontext_emit_br(context, after_catch);
|
||
gencontext_emit_block(context, else_block);
|
||
gencontext_emit_stmt(context, expr->try_expr.else_stmt);
|
||
gencontext_emit_br(context, after_catch);
|
||
gencontext_emit_block(context, after_catch);
|
||
}
|
||
return gencontext_emit_expr(context, expr->try_expr.expr);
|
||
}
|
||
|
||
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);
|
||
return gencontext_emit_assign_expr(context, addr, expr->binary_expr.right);
|
||
}
|
||
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);
|
||
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, rhs_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);
|
||
gencontext_emit_br(context, phi_block);
|
||
|
||
gencontext_emit_block(context, rhs_block);
|
||
LLVMValueRef rhs = gencontext_emit_expr(context, expr->ternary_expr.else_expr);
|
||
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] = { lhs_block, rhs_block };
|
||
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, llvm_type(type), "string");
|
||
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_ERROR:
|
||
return llvm_int(type_error_base, expr->const_expr.error_constant->error_constant.value);
|
||
default:
|
||
UNREACHABLE
|
||
}
|
||
}
|
||
/*
|
||
static inline void gencontext_emit_throw_union_branch(GenContext *context, CatchInfo *catches, LLVMValueRef value)
|
||
{
|
||
LLVMBasicBlockRef after_block = gencontext_create_free_block(context, "throwafter");
|
||
|
||
type_error_union
|
||
value = LLVMBuildExtractValue(context->builder, value, 1, "");
|
||
LLVMValueRef comparison = LLVMBuildICmp(context->builder, LLVMIntNE, llvm_int(error_type, 0), value, "checkerr");
|
||
|
||
VECEACH(catches, i)
|
||
{
|
||
LLVMBasicBlockRef ret_err_block = gencontext_create_free_block(context, "ret_err");
|
||
gencontext_emit_cond_br(context, comparison, ret_err_block, after_block);
|
||
gencontext_emit_block(context, ret_err_block);
|
||
|
||
if (catches->kind == CATCH_TRY_ELSE)
|
||
{
|
||
|
||
}
|
||
}
|
||
|
||
if (error_type == type_error_union)
|
||
size_t catch_index = context->catch_stack_index;
|
||
while (catch_index > 0)
|
||
{
|
||
catch_index--;
|
||
// TODO check error
|
||
Catch *current_catch = &context->catch_stack[catch_index];
|
||
if (!current_catch->decl)
|
||
{
|
||
LLVMValueRef zero = llvm_int(type_reduced(type_error), 0);
|
||
// TODO emit defers.
|
||
if (error_type == type_error_union)
|
||
{
|
||
}
|
||
LLVMValueRef comparison = LLVMBuildICmp(context->builder, LLVMIntNE, zero, value, "checkerr");
|
||
gencontext_emit_cond_br(context, comparison, current_catch->catch_block, after_block);
|
||
gencontext_emit_block(context, after_block);
|
||
return;
|
||
}
|
||
}
|
||
// Fix defers gencontext_emit_defer(context, ast->throw_stmt.defers.start, ast->throw_stmt.defers.end);
|
||
|
||
if (error_type == type_error_union)
|
||
{
|
||
gencontext_emit_load(context, type_error_union, context->error_out);
|
||
TODO
|
||
}
|
||
LLVMBuildRet(context->builder, value);
|
||
context->current_block = NULL;
|
||
context->current_block_is_target = false;
|
||
gencontext_emit_block(context, after_block);
|
||
}
|
||
*/
|
||
|
||
|
||
static inline bool gencontext_emit_throw_branch_for_single_throw_catch(GenContext *context, Decl *throw, LLVMValueRef value, Ast *catch, bool single_throw)
|
||
{
|
||
gencontext_generate_catch_block_if_needed(context, catch);
|
||
|
||
// Catch any is simple.
|
||
if (catch->catch_stmt.error_param->type == type_error_union)
|
||
{
|
||
// If this was a single throw, then we do a cast first.
|
||
if (single_throw)
|
||
{
|
||
value = gencontext_emit_cast(context, CAST_ERREU, value, type_error_union, throw->type);
|
||
}
|
||
// Store the value and jump to the catch.
|
||
LLVMBuildStore(context->builder, value, catch->catch_stmt.error_param->var.backend_ref);
|
||
gencontext_emit_br(context, catch->catch_stmt.block);
|
||
return true;
|
||
}
|
||
|
||
// If we catch a single, the single throw is easy, it *must* be the same type.
|
||
if (single_throw)
|
||
{
|
||
assert(catch->catch_stmt.error_param->type->decl == throw);
|
||
// Store and jump.
|
||
LLVMBuildStore(context->builder, value, catch->catch_stmt.error_param->var.backend_ref);
|
||
gencontext_emit_br(context, catch->catch_stmt.block);
|
||
return true;
|
||
}
|
||
|
||
// Here instead we more than one throw and we're catching a single error type
|
||
LLVMValueRef offset = LLVMBuildBitCast(context->builder, catch->catch_stmt.error_param->type->decl->error.start_value, llvm_type(type_error_union), "");
|
||
LLVMValueRef check = LLVMBuildAnd(context->builder, offset, value, "");
|
||
LLVMValueRef match = LLVMBuildICmp(context->builder, LLVMIntEQ, offset, check, "");
|
||
LLVMBasicBlockRef catch_block_set = gencontext_create_free_block(context, "setval");
|
||
LLVMBasicBlockRef continue_block = gencontext_create_free_block(context, "");
|
||
gencontext_emit_cond_br(context, match, catch_block_set, continue_block);
|
||
value = LLVMBuildAnd(context->builder, LLVMBuildNeg(context->builder, offset, ""), value, "");
|
||
LLVMBuildStore(context->builder, value, catch->catch_stmt.error_param->var.backend_ref);
|
||
gencontext_emit_br(context, catch->catch_stmt.block);
|
||
gencontext_emit_block(context, continue_block);
|
||
return false;
|
||
}
|
||
|
||
static inline bool gencontext_emit_throw_branch_for_single_throw(GenContext *context, Decl *throw, LLVMValueRef value, CatchInfo *catch_infos, bool single_throw, bool union_return)
|
||
{
|
||
TODO
|
||
/*
|
||
VECEACH(catch_infos, i)
|
||
{
|
||
CatchInfo *info = &catch_infos[i];
|
||
switch (info->kind)
|
||
{
|
||
case CATCH_REGULAR:
|
||
if (gencontext_emit_throw_branch_for_single_throw_catch(context, throw, value, info->catch, single_throw))
|
||
{
|
||
// Catching all should only happen on the last branch.
|
||
assert(i == vec_size(catch_infos));
|
||
return true;
|
||
}
|
||
break;;
|
||
case CATCH_TRY_ELSE:
|
||
// Try else should only happen on the last branch.
|
||
assert(i == vec_size(catch_infos));
|
||
gencontext_emit_br(context, info->try_else->try_expr.jump_target);
|
||
return true;
|
||
}
|
||
}*/
|
||
|
||
// If this is not a single throw, then we need to check first.
|
||
if (!single_throw)
|
||
{
|
||
LLVMValueRef offset = LLVMBuildBitCast(context->builder, throw->error.start_value, llvm_type(type_error_union), "");
|
||
LLVMValueRef check = LLVMBuildAnd(context->builder, offset, value, "");
|
||
LLVMValueRef match = LLVMBuildICmp(context->builder, LLVMIntEQ, offset, check, "");
|
||
LLVMBasicBlockRef catch_block_set = gencontext_create_free_block(context, "setval");
|
||
LLVMBasicBlockRef continue_block = gencontext_create_free_block(context, "");
|
||
gencontext_emit_cond_br(context, match, catch_block_set, continue_block);
|
||
value = LLVMBuildAnd(context->builder, LLVMBuildNeg(context->builder, offset, ""), value, "");
|
||
//LLVMBuildStore(context->builder, value, catch->catch_stmt.error_param->var.backend_ref);
|
||
//gencontext_emit_br(context, catch->catch_stmt.block);
|
||
gencontext_emit_block(context, continue_block);
|
||
|
||
}
|
||
// Case (1) the error return is a normal return, in that case we send the unadorned type.
|
||
/* if (union_return)
|
||
{
|
||
|
||
|
||
assert(error_type == type_error_base);
|
||
gencontext_emit_cond_br(context, comparison, return_block, after_block);
|
||
gencontext_emit_block(context, return_block);
|
||
gencontext_emit_return_value(context, value);
|
||
gencontext_emit_block(context, after_block);
|
||
return;
|
||
}
|
||
|
||
assert(context->cur_func_decl->func.function_signature.error_return == ERROR_RETURN_UNION);
|
||
if (error_type == type_error_base)
|
||
{
|
||
value = gencontext_emit_cast(context, CAST_EREU, value, cuf, error_type)
|
||
|
||
gencontext_emit_cast_expr
|
||
|
||
|
||
return false;
|
||
*/
|
||
return false;
|
||
}
|
||
|
||
|
||
static inline void gencontext_emit_throw_branch(GenContext *context, LLVMValueRef value, TypeInfo** errors, ThrowInfo *throw_info, ErrorReturn error_return)
|
||
{
|
||
Type *call_error_type;
|
||
switch (error_return)
|
||
{
|
||
case ERROR_RETURN_NONE:
|
||
// If there is no error return, this is a no-op.
|
||
return;
|
||
case ERROR_RETURN_ONE:
|
||
call_error_type = type_error_base;
|
||
break;
|
||
case ERROR_RETURN_MANY:
|
||
case ERROR_RETURN_ANY:
|
||
call_error_type = type_error_union;
|
||
break;;
|
||
}
|
||
|
||
LLVMBasicBlockRef after_block = gencontext_create_free_block(context, "throwafter");
|
||
LLVMValueRef comparison = LLVMBuildICmp(context->builder, LLVMIntNE, llvm_int(call_error_type, 0), value, "checkerr");
|
||
|
||
unsigned catch_count = vec_size(throw_info->catches);
|
||
assert(throw_info->is_completely_handled && catch_count);
|
||
|
||
// Special handling for a single catch.
|
||
if (catch_count == 1)
|
||
{
|
||
CatchInfo *catch = &throw_info->catches[0];
|
||
switch (catch->kind)
|
||
{
|
||
case CATCH_RETURN_ONE:
|
||
{
|
||
LLVMBasicBlockRef else_block = gencontext_create_free_block(context, "erret_one");
|
||
gencontext_emit_cond_br(context, comparison, else_block, after_block);
|
||
gencontext_emit_block(context, else_block);
|
||
gencontext_emit_defer(context, throw_info->defer, NULL);
|
||
gencontext_emit_return_value(context, value);
|
||
gencontext_emit_block(context, after_block);
|
||
return;
|
||
}
|
||
case CATCH_RETURN_MANY:
|
||
{
|
||
LLVMBasicBlockRef else_block = gencontext_create_free_block(context, "erret_many");
|
||
gencontext_emit_cond_br(context, comparison, else_block, after_block);
|
||
gencontext_emit_block(context, else_block);
|
||
if (call_error_type == type_error_base)
|
||
{
|
||
value = gencontext_emit_cast(context, CAST_ERREU, value, type_error_union, call_error_type);
|
||
}
|
||
gencontext_emit_defer(context, throw_info->defer, NULL);
|
||
gencontext_emit_return_value(context, value);
|
||
gencontext_emit_block(context, after_block);
|
||
return;
|
||
}
|
||
case CATCH_TRY_ELSE:
|
||
{
|
||
LLVMBasicBlockRef else_block = gencontext_get_try_target(context, catch->try_else);
|
||
if (throw_info->defer != catch->defer)
|
||
{
|
||
LLVMBasicBlockRef defer_block = gencontext_create_free_block(context, "defer");
|
||
gencontext_emit_cond_br(context, comparison, defer_block, after_block);
|
||
gencontext_emit_defer(context, throw_info->defer, catch->defer);
|
||
gencontext_emit_br(context, else_block);
|
||
}
|
||
else
|
||
{
|
||
gencontext_emit_cond_br(context, comparison, else_block, after_block);
|
||
}
|
||
gencontext_emit_block(context, after_block);
|
||
return;
|
||
}
|
||
case CATCH_RETURN_ANY:
|
||
{
|
||
LLVMBasicBlockRef else_block = gencontext_create_free_block(context, "erret_any");
|
||
gencontext_emit_cond_br(context, comparison, else_block, after_block);
|
||
gencontext_emit_block(context, else_block);
|
||
if (call_error_type == type_error_base)
|
||
{
|
||
value = gencontext_emit_cast(context, CAST_ERREU, value, type_error_union, errors[0]->type);
|
||
}
|
||
gencontext_emit_defer(context, throw_info->defer, NULL);
|
||
gencontext_emit_return_value(context, value);
|
||
gencontext_emit_block(context, after_block);
|
||
return;
|
||
}
|
||
case CATCH_REGULAR:
|
||
{
|
||
gencontext_generate_catch_block_if_needed(context, catch->catch);
|
||
LLVMBasicBlockRef else_block = gencontext_create_free_block(context, "catch_regular");
|
||
gencontext_emit_cond_br(context, comparison, else_block, after_block);
|
||
gencontext_emit_block(context, else_block);
|
||
Decl *error_param = catch->catch->catch_stmt.error_param;
|
||
if (call_error_type == type_error_base)
|
||
{
|
||
if (error_param->type == type_error_union)
|
||
{
|
||
value = gencontext_emit_cast(context, CAST_ERREU, value, type_error_union, errors[0]->type);
|
||
}
|
||
}
|
||
LLVMBuildStore(context->builder, value, error_param->var.backend_ref);
|
||
gencontext_emit_defer(context, throw_info->defer, catch->defer);
|
||
gencontext_emit_br(context, catch->catch->catch_stmt.block);
|
||
gencontext_emit_block(context, after_block);
|
||
return;
|
||
}
|
||
}
|
||
UNREACHABLE
|
||
}
|
||
// Here we handle multiple branches.
|
||
|
||
LLVMBasicBlockRef err_handling_block = gencontext_create_free_block(context, "errhandlingblock");
|
||
gencontext_emit_cond_br(context, comparison, err_handling_block, after_block);
|
||
gencontext_emit_block(context, err_handling_block);
|
||
err_handling_block = NULL;
|
||
|
||
assert(error_return != ERROR_RETURN_ONE && "Should already be handled.");
|
||
|
||
// Below here we can assume we're handling error unions.
|
||
VECEACH(throw_info->catches, i)
|
||
{
|
||
if (err_handling_block == NULL)
|
||
{
|
||
err_handling_block = gencontext_create_free_block(context, "thrownext");
|
||
}
|
||
CatchInfo *catch = &throw_info->catches[i];
|
||
switch (catch->kind)
|
||
{
|
||
case CATCH_RETURN_ONE:
|
||
{
|
||
// This is always the last catch, so we can assume that we have the correct error
|
||
// type already.
|
||
LLVMValueRef offset = LLVMBuildBitCast(context->builder, catch->error->error.start_value, llvm_type(type_error_union), "");
|
||
LLVMValueRef negated = LLVMBuildNeg(context->builder, offset, "");
|
||
LLVMValueRef final_value = LLVMBuildAnd(context->builder, negated, value, "");
|
||
gencontext_emit_defer(context, throw_info->defer, NULL);
|
||
gencontext_emit_return_value(context, final_value);
|
||
gencontext_emit_block(context, after_block);
|
||
assert(i == vec_size(throw_info->catches) - 1);
|
||
return;
|
||
}
|
||
case CATCH_RETURN_MANY:
|
||
case CATCH_RETURN_ANY:
|
||
{
|
||
// This is simple, just return our value.
|
||
gencontext_emit_defer(context, throw_info->defer, NULL);
|
||
gencontext_emit_return_value(context, value);
|
||
gencontext_emit_block(context, after_block);
|
||
assert(i == vec_size(throw_info->catches) - 1);
|
||
return;
|
||
}
|
||
case CATCH_TRY_ELSE:
|
||
{
|
||
// This should be the last catch.
|
||
gencontext_emit_defer(context, throw_info->defer, catch->defer);
|
||
LLVMBasicBlockRef else_block = gencontext_get_try_target(context, catch->try_else);
|
||
gencontext_emit_br(context, else_block);
|
||
gencontext_emit_block(context, after_block);
|
||
assert(i == vec_size(throw_info->catches) - 1);
|
||
return;
|
||
}
|
||
case CATCH_REGULAR:
|
||
{
|
||
Decl *param = catch->catch->catch_stmt.error_param;
|
||
gencontext_generate_catch_block_if_needed(context, catch->catch);
|
||
Decl *error_param = catch->catch->catch_stmt.error_param;
|
||
|
||
// The wildcard catch is always the last one.
|
||
if (param->type == type_error_union)
|
||
{
|
||
// Store the value, then jump
|
||
LLVMBuildStore(context->builder, value, error_param->var.backend_ref);
|
||
gencontext_emit_defer(context, throw_info->defer, catch->defer);
|
||
gencontext_emit_br(context, catch->catch->catch_stmt.block);
|
||
gencontext_emit_block(context, after_block);
|
||
assert(i == vec_size(throw_info->catches) - 1);
|
||
return;
|
||
}
|
||
|
||
// Here we have a normal catch.
|
||
|
||
// Find the offset.
|
||
LLVMValueRef offset = LLVMBuildBitCast(context->builder, param->type->decl->error.start_value, llvm_type(type_error_union), "");
|
||
|
||
// wrapping(value - offset) < entries + 1 – this handles both cases since wrapping will make
|
||
// values below the offset big.
|
||
LLVMValueRef comp_value = LLVMBuildSub(context->builder, value, offset, "");
|
||
LLVMValueRef entries_value = llvm_int(type_error_union, vec_size(param->type->decl->error.error_constants) + 1);
|
||
LLVMValueRef match = LLVMBuildICmp(context->builder, LLVMIntULT, comp_value, entries_value, "matcherr");
|
||
|
||
LLVMBasicBlockRef match_block = gencontext_create_free_block(context, "match");
|
||
gencontext_emit_cond_br(context, match, match_block, err_handling_block);
|
||
gencontext_emit_block(context, match_block);
|
||
gencontext_emit_defer(context, throw_info->defer, catch->defer);
|
||
|
||
LLVMBuildStore(context->builder, comp_value, error_param->var.backend_ref);
|
||
gencontext_emit_br(context, catch->catch->catch_stmt.block);
|
||
gencontext_emit_block(context, err_handling_block);
|
||
err_handling_block = NULL;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
gencontext_emit_br(context, after_block);
|
||
gencontext_emit_block(context, after_block);
|
||
}
|
||
|
||
LLVMValueRef gencontext_emit_call_expr(GenContext *context, Expr *expr)
|
||
{
|
||
size_t args = vec_size(expr->call_expr.arguments);
|
||
Decl *function_decl = expr->call_expr.function->identifier_expr.decl;
|
||
FunctionSignature *signature = &function_decl->func.function_signature;
|
||
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]);
|
||
}
|
||
|
||
Decl *function = expr->call_expr.function->identifier_expr.decl;
|
||
|
||
LLVMValueRef func = function->func.backend_value;
|
||
LLVMTypeRef func_type = llvm_type(function->type);
|
||
LLVMValueRef call = LLVMBuildCall2(context->builder, func_type, func, values, args, "call");
|
||
|
||
gencontext_emit_throw_branch(context, call, function->func.function_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;
|
||
}
|
||
|
||
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)
|
||
{
|
||
switch (expr->expr_kind)
|
||
{
|
||
case EXPR_INITIALIZER_LIST:
|
||
return gencontext_emit_load(context,
|
||
expr->type,
|
||
gencontext_emit_initializer_list_expr_addr(context, expr, ref));
|
||
default:
|
||
break;
|
||
}
|
||
LLVMValueRef value = gencontext_emit_expr(context, expr);
|
||
LLVMBuildStore(context->builder, value, ref);
|
||
return value;
|
||
}
|
||
|
||
|
||
LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr)
|
||
{
|
||
NESTED_RETRY:
|
||
switch (expr->expr_kind)
|
||
{
|
||
case EXPR_RANGE:
|
||
TODO
|
||
case EXPR_POISONED:
|
||
UNREACHABLE
|
||
case EXPR_DESIGNATED_INITIALIZER:
|
||
// Should only appear when generating designated initializers.
|
||
UNREACHABLE
|
||
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_TRY:
|
||
return gencontext_emit_try_expr(context, expr);
|
||
case EXPR_TYPEID:
|
||
return gencontext_emit_typeid(context, expr);
|
||
case EXPR_TYPE_ACCESS:
|
||
case EXPR_MACRO_EXPR:
|
||
case EXPR_TYPEOF:
|
||
// These are folded in the semantic analysis step.
|
||
UNREACHABLE
|
||
case EXPR_IDENTIFIER:
|
||
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
|
||
} |