Make "?:" lower in the frontend.

This commit is contained in:
Christoffer Lerno
2025-01-06 03:01:13 +01:00
parent 314c6f94f0
commit ea2dce0ab4
8 changed files with 142 additions and 289 deletions

View File

@@ -1371,99 +1371,6 @@ static void llvm_emit_bitstruct_to_bool(GenContext *c, BEValue *value, Type *to_
llvm_value_set(value, llvm_emit_char_array_zero(c, value, false), to_type);
}
void llvm_emit_bool_cast(GenContext *c, Expr *expr, BEValue *value)
{
switch (value->type->type_kind)
{
case FLATTENED_TYPES:
// These are not possible due to flattening.
UNREACHABLE
case TYPE_WILDCARD:
case TYPE_BOOL:
value->value = LLVMBuildTrunc(c->builder, value->value, c->bool_type, "boolbool");
value->kind = BE_BOOLEAN;
return;
case TYPE_FAULTTYPE:
case TYPE_ANYFAULT:
{
BEValue zero;
llvm_value_set_int(c, &zero, type_iptr, 0);
llvm_emit_int_comp(c, value, value, &zero, BINARYOP_NE);
return;
}
case TYPE_SLICE:
llvm_value_fold_optional(c, value);
if (llvm_value_is_addr(value))
{
value->value = llvm_emit_struct_gep_raw(c,
value->value,
llvm_get_type(c, value->type),
1,
value->alignment,
&value->alignment);
}
else
{
value->value = llvm_emit_extract_value(c, value->value, 1);
}
value->type = type_lowering(type_usz);
llvm_value_rvalue(c, value);
llvm_emit_int_comp_zero(c, value, value, BINARYOP_NE);
return;
case ALL_INTS:
{
llvm_value_rvalue(c, value);
value->value = LLVMBuildICmp(c->builder, LLVMIntNE, value->value, llvm_get_zero(c, value->type), "intbool");
value->kind = type_kind_is_any_vector(value->type->type_kind) ? BE_BOOLVECTOR : BE_BOOLEAN;
return;
}
case ALL_FLOATS:
llvm_value_rvalue(c, value);
value->value = LLVMBuildFCmp(c->builder, LLVMRealUNE, value->value, llvm_get_zero(c, value->type), "fpbool");
value->kind = BE_BOOLEAN;
return;
case TYPE_POINTER:
case TYPE_FUNC_PTR:
llvm_value_rvalue(c, value);
value->value = LLVMBuildIsNotNull(c->builder, value->value, "ptrbool");
value->kind = BE_BOOLEAN;
return;
case TYPE_ANY:
case TYPE_INTERFACE:
llvm_emit_any_pointer(c, value, value);
llvm_value_rvalue(c, value);
value->value = LLVMBuildIsNotNull(c->builder, value->value, "ptrbool");
value->kind = BE_BOOLEAN;
return;
case TYPE_BITSTRUCT:
llvm_emit_bitstruct_to_bool(c, value, type_bool, value->type);
return;
case TYPE_INFERRED_ARRAY:
case TYPE_INFERRED_VECTOR:
// These should never be here, type should already be known.
UNREACHABLE
case TYPE_POISONED:
case TYPE_VOID:
case TYPE_STRUCT:
case TYPE_UNION:
case TYPE_FUNC_RAW:
case TYPE_ARRAY:
case TYPE_TYPEID:
case TYPE_TYPEINFO:
case TYPE_VECTOR:
case TYPE_UNTYPED_LIST:
case TYPE_FLEXIBLE_ARRAY:
case TYPE_ENUM:
case TYPE_MEMBER:
// Everything else is an error
UNREACHABLE
}
UNREACHABLE
}
static LLVMValueRef llvm_recursive_set_value(GenContext *c, DesignatorElement **current_element_ptr, LLVMValueRef parent, DesignatorElement **last_element_ptr, Expr *value)
{
DesignatorElement *current_element = current_element_ptr[0];
@@ -4581,120 +4488,29 @@ static void llvm_emit_binary_expr(GenContext *c, BEValue *be_value, Expr *expr)
llvm_emit_binary(c, be_value, expr, NULL, binary_op);
}
static inline void llvm_emit_elvis_expr(GenContext *c, BEValue *value, Expr *expr)
{
// Generate condition and conditional branch
Expr *cond = exprptr(expr->ternary_expr.cond);
llvm_emit_expr(c, value, cond);
llvm_value_rvalue(c, value);
LLVMValueRef lhs_value = value->value;
if (value->kind != BE_BOOLEAN)
{
llvm_emit_bool_cast(c, cond, value);
}
Expr *else_expr = exprptr(expr->ternary_expr.else_expr);
if (!IS_OPTIONAL(expr) && expr_is_const(else_expr))
{
BEValue right;
llvm_emit_expr(c, &right, else_expr);
llvm_value_rvalue(c, &right);
LLVMValueRef val = LLVMBuildSelect(c->builder, value->value, lhs_value, right.value, "elvis");
llvm_value_set(value, val, right.type);
return;
}
LLVMBasicBlockRef lhs_exit = llvm_get_current_block_if_in_use(c);
if (!lhs_exit) return;
// Set up basic blocks, following Cone
LLVMBasicBlockRef phi_block = llvm_basic_block_new(c, "cond.phi");
LLVMBasicBlockRef rhs_block = llvm_basic_block_new(c, "cond.rhs");
llvm_emit_cond_br(c, value, phi_block, rhs_block);
llvm_emit_block(c, rhs_block);
BEValue rhs;
llvm_emit_expr(c, &rhs, else_expr);
llvm_value_rvalue(c, &rhs);
LLVMValueRef rhs_value = rhs.value;
if (rhs.type == type_bool && LLVMTypeOf(rhs_value) != c->bool_type)
{
llvm_emit_trunc_bool(c, rhs_value);
}
LLVMBasicBlockRef rhs_exit = llvm_get_current_block_if_in_use(c);
if (rhs_exit) llvm_emit_br(c, phi_block);
// Generate phi
llvm_emit_block(c, phi_block);
if (!rhs_exit)
{
if (!lhs_value) lhs_value = LLVMGetUndef(llvm_get_type(c, expr->type));
llvm_value_set(value, lhs_value, expr->type);
return;
}
if (!lhs_exit)
{
if (!rhs_value) rhs_value = LLVMGetUndef(llvm_get_type(c, expr->type));
llvm_value_set(value, rhs_value, expr->type);
return;
}
llvm_new_phi(c, value, "val", expr->type, lhs_value, lhs_exit, rhs_value, rhs_exit);
}
void gencontext_emit_ternary_expr(GenContext *c, BEValue *value, Expr *expr)
{
if (!expr->ternary_expr.then_expr)
{
llvm_emit_elvis_expr(c, value, expr);
return;
}
bool is_elvis = false;
ASSERT0(expr->ternary_expr.then_expr);
// Generate condition and conditional branch
Expr *cond = exprptr(expr->ternary_expr.cond);
llvm_emit_expr(c, value, cond);
llvm_value_rvalue(c, value);
LLVMValueRef lhs_value = is_elvis ? value->value : NULL;
if (value->kind != BE_BOOLEAN)
{
ASSERT0(is_elvis);
llvm_emit_bool_cast(c, cond, value);
}
Expr *else_expr;
Expr *then_expr;
if (is_elvis)
{
then_expr = NULL;
else_expr = exprptr(expr->ternary_expr.else_expr);
}
else
{
else_expr = exprptr(expr->ternary_expr.else_expr);
then_expr = exprptr(expr->ternary_expr.then_expr);
}
Expr *else_expr = exprptr(expr->ternary_expr.else_expr);
Expr *then_expr = exprptr(expr->ternary_expr.then_expr);
if (!IS_OPTIONAL(expr) && expr_is_const(else_expr)
&& (is_elvis || expr_is_const(then_expr)))
&& expr_is_const(then_expr))
{
if (!lhs_value)
{
BEValue left;
llvm_emit_expr(c, &left, then_expr);
llvm_value_rvalue(c, &left);
lhs_value = left.value;
}
BEValue left;
llvm_emit_expr(c, &left, then_expr);
llvm_value_rvalue(c, &left);
BEValue right;
llvm_emit_expr(c, &right, else_expr);
llvm_value_rvalue(c, &right);
LLVMValueRef val = LLVMBuildSelect(c->builder, value->value, lhs_value, right.value, "ternary");
LLVMValueRef val = LLVMBuildSelect(c->builder, value->value, left.value, right.value, "ternary");
llvm_value_set(value, val, right.type);
return;
}
@@ -4704,28 +4520,19 @@ void gencontext_emit_ternary_expr(GenContext *c, BEValue *value, Expr *expr)
LLVMBasicBlockRef rhs_block = llvm_basic_block_new(c, "cond.rhs");
LLVMBasicBlockRef lhs_exit;
if (is_elvis)
LLVMBasicBlockRef lhs_block = llvm_basic_block_new(c, "cond.lhs");
llvm_emit_cond_br(c, value, lhs_block, rhs_block);
llvm_emit_block(c, lhs_block);
BEValue lhs;
llvm_emit_expr(c, &lhs, then_expr);
llvm_value_rvalue(c, &lhs);
LLVMValueRef lhs_value = lhs.value;
lhs_exit = llvm_get_current_block_if_in_use(c);
if (lhs.type == type_bool && LLVMTypeOf(lhs_value) != c->bool_type)
{
lhs_exit = llvm_get_current_block_if_in_use(c);
if (!lhs_exit) return;
llvm_emit_cond_br(c, value, phi_block, rhs_block);
}
else
{
LLVMBasicBlockRef lhs_block = llvm_basic_block_new(c, "cond.lhs");
llvm_emit_cond_br(c, value, lhs_block, rhs_block);
llvm_emit_block(c, lhs_block);
BEValue lhs;
llvm_emit_expr(c, &lhs, then_expr);
llvm_value_rvalue(c, &lhs);
lhs_value = lhs.value;
lhs_exit = llvm_get_current_block_if_in_use(c);
if (lhs.type == type_bool && LLVMTypeOf(lhs_value) != c->bool_type)
{
llvm_emit_trunc_bool(c, lhs_value);
}
if (lhs_exit) llvm_emit_br(c, phi_block);
llvm_emit_trunc_bool(c, lhs_value);
}
if (lhs_exit) llvm_emit_br(c, phi_block);
llvm_emit_block(c, rhs_block);
BEValue rhs;

View File

@@ -544,7 +544,6 @@ void llvm_emit_initialize_reference_temporary_const(GenContext *c, BEValue *ref,
LLVMValueRef llvm_get_ref(GenContext *c, Decl *decl);
LLVMValueRef llvm_emit_call_intrinsic(GenContext *c, unsigned intrinsic, LLVMTypeRef *types, unsigned type_count, LLVMValueRef *values, unsigned arg_count);
void llvm_emit_bool_cast(GenContext *c, Expr *expr, BEValue *value);
void llvm_emit_local_var_alloca(GenContext *c, Decl *decl);
void llvm_emit_local_decl(GenContext *c, Decl *decl, BEValue *value);
void llvm_emit_builtin_call(GenContext *c, BEValue *result_value, Expr *expr);

View File

@@ -196,13 +196,7 @@ static void llvm_emit_cond(GenContext *c, BEValue *be_value, Expr *expr, bool bo
}
// Cast the result to bool if needed.
if (bool_cast && be_value->type != type_bool)
{
Type *type = be_value->type;
llvm_emit_bool_cast(c, last, be_value);
// llvm_emit_cast(c, cast, last, be_value, type_bool, type);
}
ASSERT0(!bool_cast || be_value->type == type_bool);
}
void llvm_emit_jmp(GenContext *context, LLVMBasicBlockRef block)

View File

@@ -905,8 +905,24 @@ static inline bool sema_expr_analyse_ternary(SemaContext *context, Type *infer_t
cast_no_check(context, copy, type_bool, false);
ASSERT_SPAN(expr, expr_is_const_bool(copy));
path = copy->const_expr.b ? COND_TRUE : COND_FALSE;
expr->ternary_expr.then_expr = exprid(cond);
expr->ternary_expr.cond = exprid(copy);
left = cond;
}
else
{
// From foo :? bar
// (bool)($temp = foo) ? $temp : bar
Decl *temp = decl_new_generated_var(cond->type, VARDECL_LOCAL, cond->span);
temp->var.init_expr = expr_copy(cond);
cond->expr_kind = EXPR_DECL;
cond->decl_expr = temp;
cond->resolve_status = RESOLVE_NOT_DONE;
if (!sema_analyse_expr(context, cond)) return false;
if (!cast_explicit(context, cond, type_bool)) return false;
expr->ternary_expr.then_expr = exprid(left = expr_variable(temp));
if (!sema_analyse_expr(context, left)) return false;
}
left = cond;
}
Expr *right = exprptr(expr->ternary_expr.else_expr);

View File

@@ -1159,14 +1159,19 @@ static inline bool sema_analyse_cond(SemaContext *context, Expr *expr, CondType
{
return sema_error_failed_cast(context, last, last->type, cast_to_bool ? type_bool : init->type);
}
if (cast_to_bool && !may_cast(context, init, type_bool, true, true))
if (cast_to_bool)
{
RETURN_SEMA_ERROR(last->decl_expr->var.init_expr, "The expression needs to be convertible to a boolean.");
if (!may_cast(context, init, type_bool, true, true))
{
RETURN_SEMA_ERROR(last->decl_expr->var.init_expr, "The expression needs to be convertible to a boolean.");
}
cast_no_check(context, last, type_bool, false);
}
if (cast_to_bool && expr_is_const_bool(init))
{
*result = init->const_expr.b ? COND_TRUE : COND_FALSE;
}
return true;
}

View File

@@ -20,49 +20,75 @@ fn bool elvis3(bool x, bool y)
define ptr @elvis.elvis(ptr %0, ptr %1) #0 {
entry:
%ptrbool = icmp ne ptr %0, null
br i1 %ptrbool, label %cond.phi, label %cond.rhs
%.anon = alloca ptr, align 8
store ptr %0, ptr %.anon, align 8
%i2b = icmp ne ptr %0, null
br i1 %i2b, label %cond.lhs, label %cond.rhs
cond.lhs: ; preds = %entry
%2 = load ptr, ptr %.anon, align 8
br label %cond.phi
cond.rhs: ; preds = %entry
br label %cond.phi
cond.phi: ; preds = %cond.rhs, %entry
%val = phi ptr [ %0, %entry ], [ %1, %cond.rhs ]
cond.phi: ; preds = %cond.rhs, %cond.lhs
%val = phi ptr [ %2, %cond.lhs ], [ %1, %cond.rhs ]
ret ptr %val
}
; Function Attrs: nounwind uwtable
define ptr @elvis.elvis2(ptr %0, ptr %1) #0 {
entry:
%ptrbool = icmp ne ptr %0, null
br i1 %ptrbool, label %cond.phi3, label %cond.rhs
%.anon = alloca ptr, align 8
%.anon1 = alloca ptr, align 8
store ptr %0, ptr %.anon, align 8
%i2b = icmp ne ptr %0, null
br i1 %i2b, label %cond.lhs, label %cond.rhs
cond.lhs: ; preds = %entry
%2 = load ptr, ptr %.anon, align 8
br label %cond.phi5
cond.rhs: ; preds = %entry
%ptrbool1 = icmp ne ptr %1, null
br i1 %ptrbool1, label %cond.phi, label %cond.rhs2
store ptr %1, ptr %.anon1, align 8
%i2b2 = icmp ne ptr %1, null
br i1 %i2b2, label %cond.lhs3, label %cond.rhs4
cond.rhs2: ; preds = %cond.rhs
cond.lhs3: ; preds = %cond.rhs
%3 = load ptr, ptr %.anon1, align 8
br label %cond.phi
cond.phi: ; preds = %cond.rhs2, %cond.rhs
%val = phi ptr [ %1, %cond.rhs ], [ %0, %cond.rhs2 ]
br label %cond.phi3
cond.rhs4: ; preds = %cond.rhs
br label %cond.phi
cond.phi3: ; preds = %cond.phi, %entry
%val4 = phi ptr [ %0, %entry ], [ %val, %cond.phi ]
ret ptr %val4
cond.phi: ; preds = %cond.rhs4, %cond.lhs3
%val = phi ptr [ %3, %cond.lhs3 ], [ %0, %cond.rhs4 ]
br label %cond.phi5
cond.phi5: ; preds = %cond.phi, %cond.lhs
%val6 = phi ptr [ %2, %cond.lhs ], [ %val, %cond.phi ]
ret ptr %val6
}
define zeroext i8 @elvis.elvis3(i8 zeroext %0, i8 zeroext %1) #0 {
entry:
%.anon = alloca i8, align 1
store i8 %0, ptr %.anon, align 1
%2 = trunc i8 %0 to i1
br i1 %2, label %cond.phi, label %cond.rhs
br i1 %2, label %cond.lhs, label %cond.rhs
cond.rhs: ; preds = %entry
%3 = trunc i8 %1 to i1
cond.lhs: ; preds = %entry
%3 = load i8, ptr %.anon, align 1
%4 = trunc i8 %3 to i1
br label %cond.phi
cond.phi: ; preds = %cond.rhs, %entry
%val = phi i1 [ %2, %entry ], [ %3, %cond.rhs ]
%4 = zext i1 %val to i8
ret i8 %4
cond.rhs: ; preds = %entry
%5 = trunc i8 %1 to i1
br label %cond.phi
cond.phi: ; preds = %cond.rhs, %cond.lhs
%val = phi i1 [ %4, %cond.lhs ], [ %5, %cond.rhs ]
%6 = zext i1 %val to i8
ret i8 %6
}

View File

@@ -19,94 +19,88 @@ define void @ternary_bool.main() #0 {
entry:
%b = alloca i8, align 1
%c = alloca ptr, align 8
%.anon = alloca i8, align 1
%.anon13 = alloca i8, align 1
store i8 1, ptr %b, align 1
store ptr %b, ptr %c, align 8
%0 = load ptr, ptr %c, align 8
%1 = load i8, ptr %0, align 1
%2 = trunc i8 %1 to i1
br i1 %2, label %cond.lhs, label %cond.rhs
cond.lhs: ; preds = %entry
%3 = load ptr, ptr %c, align 8
%4 = load i8, ptr %3, align 1
%5 = trunc i8 %4 to i1
br label %cond.phi
cond.rhs: ; preds = %entry
%6 = load ptr, ptr %c, align 8
%7 = load i8, ptr %6, align 1
%8 = trunc i8 %7 to i1
br label %cond.phi
cond.phi: ; preds = %cond.rhs, %cond.lhs
%val = phi i1 [ %5, %cond.lhs ], [ %8, %cond.rhs ]
br i1 %val, label %if.then, label %if.exit
if.then: ; preds = %cond.phi
call void (ptr, ...) @printf(ptr @.str)
br label %if.exit
if.exit: ; preds = %if.then, %cond.phi
%9 = load i8, ptr %b, align 1
%10 = trunc i8 %9 to i1
br i1 %10, label %cond.lhs1, label %cond.rhs2
cond.lhs1: ; preds = %if.exit
%11 = load i8, ptr %b, align 1
%12 = trunc i8 %11 to i1
br label %cond.phi3
cond.rhs2: ; preds = %if.exit
%13 = load i8, ptr %b, align 1
%14 = trunc i8 %13 to i1
br label %cond.phi3
cond.phi3: ; preds = %cond.rhs2, %cond.lhs1
%val4 = phi i1 [ %12, %cond.lhs1 ], [ %14, %cond.rhs2 ]
br i1 %val4, label %if.then5, label %if.exit6
if.then5: ; preds = %cond.phi3
call void (ptr, ...) @printf(ptr @.str.1)
br label %if.exit6
if.exit6: ; preds = %if.then5, %cond.phi3
%15 = load ptr, ptr %c, align 8
%16 = load i8, ptr %15, align 1
store i8 %16, ptr %.anon, align 1
%17 = trunc i8 %16 to i1
br i1 %17, label %cond.phi8, label %cond.rhs7
cond.rhs7: ; preds = %if.exit6
%18 = load ptr, ptr %c, align 8
%19 = load i8, ptr %18, align 1
%20 = trunc i8 %19 to i1
br label %cond.phi8
cond.phi8: ; preds = %cond.rhs7, %if.exit6
%val9 = phi i1 [ %17, %if.exit6 ], [ %20, %cond.rhs7 ]
br i1 %val9, label %if.then10, label %if.exit11
if.then10: ; preds = %cond.phi8
call void (ptr, ...) @printf(ptr @.str.2)
br label %if.exit11
if.exit11: ; preds = %if.then10, %cond.phi8
%21 = load i8, ptr %b, align 1
br i1 %17, label %cond.lhs7, label %cond.rhs8
cond.lhs7: ; preds = %if.exit6
%18 = load i8, ptr %.anon, align 1
%19 = trunc i8 %18 to i1
br label %cond.phi9
cond.rhs8: ; preds = %if.exit6
%20 = load ptr, ptr %c, align 8
%21 = load i8, ptr %20, align 1
%22 = trunc i8 %21 to i1
br i1 %22, label %cond.phi13, label %cond.rhs12
cond.rhs12: ; preds = %if.exit11
br label %cond.phi9
cond.phi9: ; preds = %cond.rhs8, %cond.lhs7
%val10 = phi i1 [ %19, %cond.lhs7 ], [ %22, %cond.rhs8 ]
br i1 %val10, label %if.then11, label %if.exit12
if.then11: ; preds = %cond.phi9
call void (ptr, ...) @printf(ptr @.str.2)
br label %if.exit12
if.exit12: ; preds = %if.then11, %cond.phi9
%23 = load i8, ptr %b, align 1
store i8 %23, ptr %.anon13, align 1
%24 = trunc i8 %23 to i1
br label %cond.phi13
cond.phi13: ; preds = %cond.rhs12, %if.exit11
%val14 = phi i1 [ %22, %if.exit11 ], [ %24, %cond.rhs12 ]
br i1 %val14, label %if.then15, label %if.exit16
if.then15: ; preds = %cond.phi13
br i1 %24, label %cond.lhs14, label %cond.rhs15
cond.lhs14: ; preds = %if.exit12
%25 = load i8, ptr %.anon13, align 1
%26 = trunc i8 %25 to i1
br label %cond.phi16
cond.rhs15: ; preds = %if.exit12
%27 = load i8, ptr %b, align 1
%28 = trunc i8 %27 to i1
br label %cond.phi16
cond.phi16: ; preds = %cond.rhs15, %cond.lhs14
%val17 = phi i1 [ %26, %cond.lhs14 ], [ %28, %cond.rhs15 ]
br i1 %val17, label %if.then18, label %if.exit19
if.then18: ; preds = %cond.phi16
call void (ptr, ...) @printf(ptr @.str.3)
br label %if.exit16
if.exit16: ; preds = %if.then15, %cond.phi13
br label %if.exit19
if.exit19: ; preds = %if.then18, %cond.phi16
ret void
}

View File

@@ -16,6 +16,7 @@ entry:
%b = alloca i32, align 4
%c = alloca i8, align 1
%d = alloca i32, align 4
%.anon = alloca i32, align 4
store i8 1, ptr %a, align 1
store i32 2, ptr %b, align 4
%0 = load i32, ptr %b, align 4
@@ -27,8 +28,19 @@ entry:
%ternary = select i1 %siui-gt, i8 1, i8 0
store i8 %ternary, ptr %c, align 1
%2 = load i32, ptr %b, align 4
%intbool = icmp ne i32 %2, 0
%elvis = select i1 %intbool, i32 %2, i32 1
store i32 %elvis, ptr %d, align 4
store i32 %2, ptr %.anon, align 4
%i2b = icmp ne i32 %2, 0
br i1 %i2b, label %cond.lhs, label %cond.rhs
cond.lhs: ; preds = %entry
%3 = load i32, ptr %.anon, align 4
br label %cond.phi
cond.rhs: ; preds = %entry
br label %cond.phi
cond.phi: ; preds = %cond.rhs, %cond.lhs
%val = phi i32 [ %3, %cond.lhs ], [ 1, %cond.rhs ]
store i32 %val, ptr %d, align 4
ret void
}