Cleanup and allow complex array length inference, e.g. "int[*][2][*] a = ..."

This commit is contained in:
Christoffer Lerno
2023-02-28 17:37:17 +01:00
parent cc19168c7b
commit 9db845903e
9 changed files with 289 additions and 68 deletions

View File

@@ -2080,7 +2080,7 @@ bool cast_explicit(SemaContext *context, Expr *expr, Type *to_type);
bool cast(Expr *expr, Type *to_type);
bool cast_may_bool_convert(Type *type);
Type *cast_infer_len(Type *to_infer, Type *actual_type);
Type *type_infer_len_from_actual_type(Type *to_infer, Type *actual_type);
bool cast_to_index(Expr *index);
bool cast_untyped_to_type(SemaContext *context, Expr *expr, Type *to_type);
@@ -2396,7 +2396,7 @@ INLINE bool type_is_integer_unsigned(Type *type);
INLINE bool type_is_integer_signed(Type *type);
INLINE bool type_is_integer_or_bool_kind(Type *type);
INLINE bool type_is_numeric(Type *type);
INLINE bool type_is_len_inferred(Type *type);
INLINE bool type_is_inferred(Type *type);
INLINE bool type_underlying_is_numeric(Type *type);
INLINE bool type_is_pointer(Type *type);
INLINE bool type_is_arraylike(Type *type);
@@ -2475,12 +2475,20 @@ INLINE Type *type_from_inferred(Type *flattened, Type *element_type, unsigned co
{
switch (flattened->type_kind)
{
case TYPE_POINTER:
assert(count == 0);
return type_get_ptr(element_type);
case TYPE_VECTOR:
assert(flattened->array.len == count);
FALLTHROUGH;
case TYPE_INFERRED_VECTOR:
return type_get_vector(element_type, count);
break;
case TYPE_ARRAY:
assert(flattened->array.len == count);
FALLTHROUGH;
case TYPE_INFERRED_ARRAY:
return type_get_array(element_type, count);
break;
default:
UNREACHABLE
}
@@ -2496,6 +2504,9 @@ INLINE bool type_len_is_inferred(Type *type)
case TYPE_TYPEDEF:
type = type->canonical;
continue;
case TYPE_OPTIONAL:
type = type->optional;
continue;
case TYPE_ARRAY:
case TYPE_SUBARRAY:
case TYPE_FLEXIBLE_ARRAY:
@@ -2833,7 +2844,7 @@ INLINE bool type_is_func_ptr(Type *fn_type)
return fn_type->pointer->type_kind == TYPE_FUNC;
}
INLINE bool type_is_len_inferred(Type *type)
INLINE bool type_is_inferred(Type *type)
{
TypeKind kind = type->type_kind;
return kind == TYPE_INFERRED_VECTOR || kind == TYPE_INFERRED_ARRAY;

View File

@@ -84,7 +84,7 @@ typedef enum
CAST_ARRVEC,
CAST_BOOLBOOL,
CAST_BOOLFP,
CAST_BOOLINT,
CAST_BOOLXI,
CAST_BOOLVECINT,
CAST_BSARRY,
CAST_BSINT,
@@ -100,7 +100,7 @@ typedef enum
CAST_FPFP,
CAST_FPSI,
CAST_FPUI,
CAST_INTBOOL,
CAST_XIBOOL,
CAST_INTENUM,
CAST_NUMVEC,
CAST_PTRANY,

View File

@@ -354,11 +354,11 @@ static inline bool expr_cast_is_constant_eval(Expr *expr, ConstantEvalKind eval_
case CAST_XIERR:
case CAST_STRPTR:
case CAST_PTRBOOL:
case CAST_BOOLINT:
case CAST_BOOLXI:
case CAST_BOOLFP:
case CAST_BOOLBOOL:
case CAST_FPBOOL:
case CAST_INTBOOL:
case CAST_XIBOOL:
case CAST_FPFP:
case CAST_FPSI:
case CAST_FPUI:

View File

@@ -1318,7 +1318,7 @@ void llvm_emit_cast(GenContext *c, CastKind cast_kind, Expr *expr, BEValue *valu
value->value = LLVMBuildIsNotNull(c->builder, value->value, "ptrbool");
value->kind = BE_BOOLEAN;
break;
case CAST_BOOLINT:
case CAST_BOOLXI:
llvm_value_rvalue(c, value);
value->value = LLVMBuildZExt(c->builder, value->value, llvm_get_type(c, to_type), "boolsi");
value->kind = BE_VALUE;
@@ -1337,7 +1337,7 @@ void llvm_emit_cast(GenContext *c, CastKind cast_kind, Expr *expr, BEValue *valu
value->value = LLVMBuildUIToFP(c->builder, value->value, llvm_get_type(c, to_type), "boolfp");
value->kind = BE_VALUE;
break;
case CAST_INTBOOL:
case CAST_XIBOOL:
llvm_value_rvalue(c, value);
value->value = LLVMBuildICmp(c->builder, LLVMIntNE, value->value, llvm_get_zero(c, from_type), "intbool");
value->kind = type_kind_is_any_vector(value->type->type_kind) ? BE_BOOLVECTOR : BE_BOOLEAN;

View File

@@ -18,7 +18,20 @@ static Expr *recursive_may_narrow_int(Expr *expr, Type *type);
static void recursively_rewrite_untyped_list(Expr *expr, Expr **list);
static inline bool cast_may_implicit_ptr(Type *from_pointee, Type *to_pointee);
static inline bool cast_may_array(Type *from, Type *to, bool is_explicit);
static inline bool insert_cast(Expr *expr, CastKind kind, Type *type);
static bool pointer_to_integer(Expr *expr, Type *type);
static bool pointer_to_bool(Expr *expr, Type *type);
static bool pointer_to_pointer(Expr* expr, Type *type);
static bool bool_to_int(Expr *expr, Type *canonical, Type *type);
static bool bool_to_float(Expr *expr, Type *canonical, Type *type);
static bool integer_to_bool(Expr *expr, Type *type);
static bool voidfail_to_error(Expr *expr, Type *type);
static void const_int_to_fp_cast(Expr *expr, Type *canonical, Type *type);
INLINE bool insert_runtime_cast_unless_const(Expr *expr, CastKind kind, Type *type);
/**
* Insert a cast. This will assume that the cast is valid. No typeinfo will be registered.
*/
static inline bool insert_cast(Expr *expr, CastKind kind, Type *type)
{
assert(expr->resolve_status == RESOLVE_DONE);
@@ -32,6 +45,9 @@ static inline bool insert_cast(Expr *expr, CastKind kind, Type *type)
return true;
}
/**
* General error due to casts.
*/
bool sema_error_failed_cast(Expr *expr, Type *from, Type *to)
{
SEMA_ERROR(expr, "The cast %s to %s is not allowed.", type_quoted_error_string(from), type_quoted_error_string(to));
@@ -39,45 +55,82 @@ bool sema_error_failed_cast(Expr *expr, Type *from, Type *to)
}
Type *cast_infer_len(Type *to_infer, Type *actual_type)
/**
* Create a type by inferring the length.
*/
Type *type_infer_len_from_actual_type(Type *to_infer, Type *actual_type)
{
Type *may_infer = to_infer->canonical;
Type *actual = actual_type->canonical;
if (may_infer == actual) return to_infer;
bool canonical_same_kind = may_infer->type_kind == to_infer->type_kind;
assert(type_is_arraylike(actual_type));
if (may_infer->type_kind == TYPE_INFERRED_ARRAY)
// This may be called on types not inferrable,
// if so we assume the original type
if (!type_len_is_inferred(to_infer)) return to_infer;
// Handle int[*]! a = { ... } by stripping the optional.
bool is_optional = type_is_optional(to_infer);
assert(is_optional || !type_is_optional(actual_type) && "int[*] x = { may_fail } should have been caught.");
// Strip the optional
if (is_optional) to_infer = to_infer->optional;
// And from the actual type.
actual_type = type_no_optional(actual_type);
// Grab the underlying indexed type,
// because we can only have [*] [] [<*>] [<>] * here
Type *indexed = type_get_indexed_type(to_infer);
Type *actual = type_get_indexed_type(actual_type);
// We should always have indexed types.
assert(indexed && actual);
// The underlying type may also be inferred.
// In this case, infer it.
if (type_len_is_inferred(indexed))
{
Type *base_type = cast_infer_len(canonical_same_kind ? to_infer->array.base :
may_infer->array.base, actual->array.base);
return type_get_array(base_type, actual->array.len);
// if we have int[*][*] => the inner is int[*], we cast it here.
indexed = type_infer_len_from_actual_type(indexed, actual);
}
if (may_infer->type_kind == TYPE_INFERRED_VECTOR)
// Construct the real type
switch (to_infer->type_kind)
{
Type *base_type = cast_infer_len(canonical_same_kind ? to_infer->array.base : may_infer->array.base, actual->array.base);
if (actual_type->type_kind == TYPE_SCALED_VECTOR)
{
return type_get_scaled_vector(base_type);
}
return type_get_vector(base_type, actual->array.len);
case TYPE_POINTER:
// The case of int[*]* x = ...
return type_add_optional(type_get_ptr(indexed), is_optional);
case TYPE_ARRAY:
// The case of int[*][2] x = ...
return type_add_optional(type_get_array(indexed, to_infer->array.len), is_optional);
case TYPE_INFERRED_ARRAY:
assert(type_is_arraylike(type_flatten(actual_type)));
return type_add_optional(type_get_array(indexed, type_flatten(actual_type)->array.len), is_optional);
case TYPE_INFERRED_VECTOR:
assert(type_is_arraylike(type_flatten(actual_type)));
return type_add_optional(type_get_vector(indexed, type_flatten(actual_type)->array.len), is_optional);
case TYPE_VECTOR:
// This is unreachable, because unlike arrays, there is no inner type that may be
// the inferred part.
UNREACHABLE
case TYPE_SUBARRAY:
// The case of int[*][] y = ... is disallowed
UNREACHABLE
default:
UNREACHABLE
}
if (may_infer->type_kind == TYPE_POINTER)
{
assert(actual->type_kind == TYPE_POINTER);
Type *base_type = cast_infer_len(canonical_same_kind ? to_infer->array.base : may_infer->pointer, actual->pointer);
return type_get_ptr(base_type);
}
UNREACHABLE
}
static inline bool insert_runtime_cast_unless_const(Expr *expr, CastKind kind, Type *type)
/**
* Insert a cast on non-const only
*/
INLINE bool insert_runtime_cast_unless_const(Expr *expr, CastKind kind, Type *type)
{
if (expr->expr_kind == EXPR_CONST) return false;
return insert_cast(expr, kind, type);
}
bool pointer_to_integer(Expr *expr, Type *type)
/**
* Insert the PTRXI cast, or on const do a rewrite.
*/
static bool pointer_to_integer(Expr *expr, Type *type)
{
if (insert_runtime_cast_unless_const(expr, CAST_PTRXI, type)) return true;
@@ -86,37 +139,49 @@ bool pointer_to_integer(Expr *expr, Type *type)
return true;
}
bool pointer_to_bool(Expr *expr, Type *type)
/**
* Insert the PTRBOOL cast or on const do a rewrite.
*/
static bool pointer_to_bool(Expr *expr, Type *type)
{
if (insert_runtime_cast_unless_const(expr, CAST_PTRBOOL, type)) return true;
// It may be a pointer
if (expr->const_expr.const_kind == CONST_POINTER)
{
expr_rewrite_const_bool(expr, type, expr->const_expr.ptr != 0);
return true;
}
// Or it's a string, in which case it is always true.
assert(expr->const_expr.const_kind == CONST_STRING);
expr_rewrite_const_bool(expr, type, true);
return true;
}
bool pointer_to_pointer(Expr* expr, Type *type)
/**
* Insert a PTRPTR cast or update the pointer type
*/
static bool pointer_to_pointer(Expr* expr, Type *type)
{
if (insert_runtime_cast_unless_const(expr, CAST_PTRPTR, type)) return true;
// Strings cannot be compile-time folded, so insert a runtime cast.
if (expr->const_expr.const_kind == CONST_STRING)
{
return insert_cast(expr, CAST_PTRPTR, type);
}
// Must have been a null
// Insert the cast, this removes the ability to narrow it.
expr->type = type;
expr->const_expr.narrowable = false;
expr->const_expr.is_hex = false;
return true;
}
/**
* Do a const int -> float cast.
*/
static void const_int_to_fp_cast(Expr *expr, Type *canonical, Type *type)
{
Real f = int_to_real(expr->const_expr.ixx);
@@ -129,9 +194,11 @@ static void const_int_to_fp_cast(Expr *expr, Type *canonical, Type *type)
expr->const_expr.fxx = (Float) { (double)f, TYPE_F64 };
break;
default:
REMINDER("Int to fp cast may be too wide.");
expr->const_expr.fxx = (Float) { f, canonical->type_kind };
break;
}
// It's not allowed to narrow after a cast.
expr->type = type;
expr->const_expr.const_kind = CONST_FLOAT;
expr->const_expr.narrowable = false;
@@ -140,20 +207,23 @@ static void const_int_to_fp_cast(Expr *expr, Type *canonical, Type *type)
/**
* Bool into a signed or unsigned int.
* Bool into a signed or unsigned int using CAST_BOOLXI
* or rewrite to 0 / 1 for false / true.
*/
bool bool_to_int(Expr *expr, Type *canonical, Type *type)
static bool bool_to_int(Expr *expr, Type *canonical, Type *type)
{
if (insert_runtime_cast_unless_const(expr, CAST_BOOLINT, type)) return true;
if (insert_runtime_cast_unless_const(expr, CAST_BOOLXI, type)) return true;
expr_rewrite_const_int(expr, type, expr->const_expr.b ? 1 : 0, false);
return true;
}
/**
* Cast bool to float.
* Cast bool to float using CAST_BOOLFP
* or rewrite to 0.0 / 1.0 for false / true
*/
bool bool_to_float(Expr *expr, Type *canonical, Type *type)
static bool bool_to_float(Expr *expr, Type *canonical, Type *type)
{
if (insert_runtime_cast_unless_const(expr, CAST_BOOLFP, type)) return true;
@@ -163,10 +233,12 @@ bool bool_to_float(Expr *expr, Type *canonical, Type *type)
}
/**
* Cast bool to float.
* Insert a cast from `void!` to some fault type by inserting a `catch`,
* so "anyerr a = returns_voidfail()" => "anyerr a = catch(returns_voidfail())"
*/
bool voidfail_to_error(Expr *expr, Type *type)
static bool voidfail_to_error(Expr *expr, Type *type)
{
assert(type->canonical->type_kind == TYPE_FAULTTYPE || type == type_anyerr);
Expr *inner = expr_copy(expr);
expr->expr_kind = EXPR_CATCH;
expr->inner_expr = inner;
@@ -175,19 +247,20 @@ bool voidfail_to_error(Expr *expr, Type *type)
}
/**
* Convert from any into to bool.
* @return true for any implicit conversion except assign and assign add.
* Cast int to bool using CAST_XIBOOL
* or rewrite 0 => false, any other value => true
*/
bool integer_to_bool(Expr *expr, Type *type)
static bool integer_to_bool(Expr *expr, Type *type)
{
if (insert_runtime_cast_unless_const(expr, CAST_INTBOOL, type)) return true;
if (insert_runtime_cast_unless_const(expr, CAST_XIBOOL, type)) return true;
expr_rewrite_const_bool(expr, type, !int_is_zero(expr->const_expr.ixx));
return true;
}
/**
* Convert from any float to bool
* Cast any float to bool using CAST_FPBOOL
* or rewrite 0.0 => false, any other value => true
*/
bool float_to_bool(Expr *expr, Type *type)
{
@@ -199,11 +272,17 @@ bool float_to_bool(Expr *expr, Type *type)
/**
* Convert from any fp to fp
* Convert any fp to another fp type using CAST_FPFP
*/
static bool float_to_float(Expr* expr, Type *canonical, Type *type)
{
// Change to same type should never enter here.
assert(type_flatten(canonical) != type_flatten(expr->type));
// Insert runtime cast if needed.
if (insert_runtime_cast_unless_const(expr, CAST_FPFP, type)) return true;
// Otherwise rewrite the const, which may cause rounding.
expr_rewrite_const_float(expr, type, expr->const_expr.fxx.f);
return true;
}
@@ -431,7 +510,7 @@ CastKind cast_to_bool_kind(Type *type)
case TYPE_SUBARRAY:
return CAST_SABOOL;
case ALL_INTS:
return CAST_INTBOOL;
return CAST_XIBOOL;
case ALL_FLOATS:
return CAST_FPBOOL;
case TYPE_POINTER:
@@ -1132,7 +1211,7 @@ CAST_ELEMENT:
CAST:
if (infer_type)
{
to_type = cast_infer_len(to_type, from);
to_type = type_infer_len_from_actual_type(to_type, from);
}
return cast_with_optional(expr, to_type, add_optional);
CAST_ILLEGAL:
@@ -1228,7 +1307,7 @@ TRY_CAST:
CAST_ILLEGAL:
return sema_error_cannot_convert(expr, to_type, false, silent);
CAST:
if (infer_len) to_type = cast_infer_len(to_type, from);
if (infer_len) to_type = type_infer_len_from_actual_type(to_type, from);
return cast_with_optional(expr, to_type, add_optional);
}
@@ -1763,14 +1842,14 @@ static bool vec_to_vec(Expr *expr, Type *to_type)
}
if (type_is_signed(from_element))
{
if (to_element == type_bool) return insert_cast(expr, CAST_INTBOOL, to_type);
if (to_element == type_bool) return insert_cast(expr, CAST_XIBOOL, to_type);
if (type_is_unsigned(to_element)) return insert_cast(expr, CAST_SIUI, to_type);
if (type_is_signed(to_element)) return insert_cast(expr, CAST_SISI, to_type);
if (type_is_float(to_element)) return insert_cast(expr, CAST_SIFP, to_type);
UNREACHABLE
}
assert(type_is_unsigned(from_element));
if (to_element == type_bool) return insert_cast(expr, CAST_INTBOOL, to_type);
if (to_element == type_bool) return insert_cast(expr, CAST_XIBOOL, to_type);
if (type_is_unsigned(to_element)) return insert_cast(expr, CAST_UIUI, to_type);
if (type_is_signed(to_element)) return insert_cast(expr, CAST_UISI, to_type);
if (type_is_float(to_element)) return insert_cast(expr, CAST_UIFP, to_type);

View File

@@ -2760,12 +2760,12 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local)
if (infer_len)
{
if (type_is_len_inferred(init->type))
if (type_is_inferred(init->type))
{
SEMA_ERROR(decl->var.type_info, "You cannot use [*] and [<*>] underlying types with initializers.");
return decl_poison(decl);
}
decl->type = cast_infer_len(decl->type, init->type);
decl->type = type_infer_len_from_actual_type(decl->type, init->type);
}
Expr *init_expr = decl->var.init_expr;

View File

@@ -251,6 +251,8 @@ static inline bool sema_expr_analyse_array_plain_initializer(SemaContext *contex
bool optional = false;
bool is_vector = type_flat_is_vector(assigned);
bool inner_is_inferred = type_len_is_inferred(inner_type);
Type *inferred_element = NULL;
for (unsigned i = 0; i < count; i++)
{
Expr *element = elements[i];
@@ -302,9 +304,34 @@ static inline bool sema_expr_analyse_array_plain_initializer(SemaContext *contex
else
{
if (!sema_analyse_expr_rhs(context, inner_type, element, true)) return false;
if (inner_is_inferred)
{
Type *element_type = type_no_optional(element->type);
if (inferred_element)
{
if (!cast_implicit(context, element, inferred_element))
{
SEMA_NOTE(elements[0], "Type inferred from here.");
return false;
}
}
else
{
inferred_element = type_infer_len_from_actual_type(inner_type, element_type);
}
}
}
optional = optional || IS_OPTIONAL(element);
}
if (inner_is_inferred)
{
if (!inferred_element)
{
SEMA_ERROR(initializer, "Zero sized elements are not allowed when inferring size.");
return false;
}
inner_type = inferred_element;
}
if (inferred_len)
{
initializer->type = type_from_inferred(flattened, inner_type, count);
@@ -377,7 +404,7 @@ static bool sema_expr_analyse_designated_initializer(SemaContext *context, Type
MemberIndex max_index = -1;
bool optional = false;
Type *inner_type = NULL;
bool is_inferred = type_is_len_inferred(flattened);
bool is_inferred = type_is_inferred(flattened);
VECEACH(init_expressions, i)
{
Expr *expr = init_expressions[i];
@@ -423,11 +450,6 @@ static inline bool sema_expr_analyse_initializer(SemaContext *context, Type *ass
// EXPR_DESIGNATED_INITIALIZER_LIST
// or EXPR_INITIALIZER_LIST
if (type_len_is_inferred(flattened) && type_len_is_inferred(type_get_indexed_type(flattened)))
{
SEMA_ERROR(expr, "Initializers cannot be used with inferred length element types (e.g. %s).", type_quoted_error_string(type_get_indexed_type(flattened)));
return false;
}
// 1. Designated initializer is separately evaluated.
if (expr->expr_kind == EXPR_DESIGNATED_INITIALIZER_LIST)
{

View File

@@ -0,0 +1,17 @@
// #target: macos-x64
module test;
fn void main()
{
int[*][*][2][*]! y = { {{{1}, {2}}, { {3}, {4}}}};
int[*][*][2][*] x = { {{{1}, {2}}, { {3}, {4}}}};
}
/* #expect: test.ll
@.__const = private unnamed_addr constant [1 x [2 x [2 x [1 x i32]]]] [[2 x [2 x [1 x i32]]] [[2 x [1 x i32]] [[1 x i32] [i32 1], [1 x i32] [i32 2]], [2 x [1 x i32]] [[1 x i32] [i32 3], [1 x i32] [i32 4]]]], align 16
@.__const.1 = private unnamed_addr constant [1 x [2 x [2 x [1 x i32]]]] [[2 x [2 x [1 x i32]]] [[2 x [1 x i32]] [[1 x i32] [i32 1], [1 x i32] [i32 2]], [2 x [1 x i32]] [[1 x i32] [i32 3], [1 x i32] [i32 4]]]], align 16
%y = alloca [1 x [2 x [2 x [1 x i32]]]], align 16
%x = alloca [1 x [2 x [2 x [1 x i32]]]], align 16

View File

@@ -0,0 +1,92 @@
// #target: macos-x64
module test;
fn int! foo() => 1;
fn int main()
{
int[*]! x = { 1, 2 };
int[*]! y = { foo(), foo() };
int[<*>]! x2 = { 1, 2 };
int[<*>]! y2 = { foo(), foo() };
return 1;
}
/* #expect: test.ll
define i32 @main() #0 {
entry:
%x = alloca [2 x i32], align 4
%x.f = alloca i64, align 8
%y = alloca [2 x i32], align 4
%y.f = alloca i64, align 8
%retparam = alloca i32, align 4
%retparam1 = alloca i32, align 4
%x2 = alloca <2 x i32>, align 8
%x2.f = alloca i64, align 8
%y2 = alloca <2 x i32>, align 8
%y2.f = alloca i64, align 8
%retparam5 = alloca i32, align 4
%retparam9 = alloca i32, align 4
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %x, ptr align 4
store i64 0, ptr %x.f, align 8
%0 = getelementptr inbounds [2 x i32], ptr %y, i64 0, i64 0
%1 = call i64 @test.foo(ptr %retparam)
%not_err = icmp eq i64 %1, 0
%2 = call i1 @llvm.expect.i1(i1 %not_err, i1 true)
br i1 %2, label %after_check, label %assign_optional
assign_optional: ; preds = %entry
store i64 %1, ptr %y.f, align 8
br label %after_assign
after_check: ; preds = %entry
%3 = load i32, ptr %retparam, align 4
store i32 %3, ptr %0, align 4
%4 = getelementptr inbounds [2 x i32], ptr %y, i64 0, i64 1
%5 = call i64 @test.foo(ptr %retparam1)
%not_err2 = icmp eq i64 %5, 0
%6 = call i1 @llvm.expect.i1(i1 %not_err2, i1 true)
br i1 %6, label %after_check4, label %assign_optional3
assign_optional3: ; preds = %after_check
store i64 %5, ptr %y.f, align 8
br label %after_assign
after_check4: ; preds = %after_check
%7 = load i32, ptr %retparam1, align 4
store i32 %7, ptr %4, align 4
store i64 0, ptr %y.f, align 8
br label %after_assign
after_assign: ; preds = %after_check4, %assign_optional3, %assign_optional
store <2 x i32> <i32 1, i32 2>, ptr %x2, align 8
store i64 0, ptr %x2.f, align 8
%8 = call i64 @test.foo(ptr %retparam5)
%not_err6 = icmp eq i64 %8, 0
%9 = call i1 @llvm.expect.i1(i1 %not_err6, i1 true)
br i1 %9, label %after_check8, label %assign_optional7
assign_optional7: ; preds = %after_assign
store i64 %8, ptr %y2.f, align 8
br label %after_assign13
after_check8: ; preds = %after_assign
%10 = load i32, ptr %retparam5, align 4
%11 = insertelement <2 x i32> undef, i32 %10, i64 0
%12 = call i64 @test.foo(ptr %retparam9)
%not_err10 = icmp eq i64 %12, 0
%13 = call i1 @llvm.expect.i1(i1 %not_err10, i1 true)
br i1 %13, label %after_check12, label %assign_optional11
assign_optional11: ; preds = %after_check8
store i64 %12, ptr %y2.f, align 8
br label %after_assign13
after_check12: ; preds = %after_check8
%14 = load i32, ptr %retparam9, align 4
%15 = insertelement <2 x i32> %11, i32 %14, i64 1
store <2 x i32> %15, ptr %y2, align 8
store i64 0, ptr %y2.f, align 8
br label %after_assign13