From 9db845903e9217075aea9c5ba3ff13ddf8d72c88 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Tue, 28 Feb 2023 17:37:17 +0100 Subject: [PATCH] Cleanup and allow complex array length inference, e.g. "int[*][2][*] a = ..." --- src/compiler/compiler_internal.h | 19 +- src/compiler/enums.h | 4 +- src/compiler/expr.c | 4 +- src/compiler/llvm_codegen_expr.c | 4 +- src/compiler/sema_casts.c | 179 +++++++++++++----- src/compiler/sema_decls.c | 4 +- src/compiler/sema_initializers.c | 34 +++- .../arrays/complex_inferred_array.c3t | 17 ++ .../arrays/inferred_array_optional.c3t | 92 +++++++++ 9 files changed, 289 insertions(+), 68 deletions(-) create mode 100644 test/test_suite/arrays/complex_inferred_array.c3t create mode 100644 test/test_suite/arrays/inferred_array_optional.c3t diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index c8dc18597..4c609ef90 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -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; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 697db8d4e..479d9552f 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -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, diff --git a/src/compiler/expr.c b/src/compiler/expr.c index 11df4b13b..d4d2723df 100644 --- a/src/compiler/expr.c +++ b/src/compiler/expr.c @@ -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: diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index e2737c43c..a22f5a0d7 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -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; diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index da9706058..7584065d3 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -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); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index d9a74bcbe..7c3bd7cac 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -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; diff --git a/src/compiler/sema_initializers.c b/src/compiler/sema_initializers.c index 084415985..1a7b97be4 100644 --- a/src/compiler/sema_initializers.c +++ b/src/compiler/sema_initializers.c @@ -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) { diff --git a/test/test_suite/arrays/complex_inferred_array.c3t b/test/test_suite/arrays/complex_inferred_array.c3t new file mode 100644 index 000000000..6afe6566c --- /dev/null +++ b/test/test_suite/arrays/complex_inferred_array.c3t @@ -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 diff --git a/test/test_suite/arrays/inferred_array_optional.c3t b/test/test_suite/arrays/inferred_array_optional.c3t new file mode 100644 index 000000000..a31777408 --- /dev/null +++ b/test/test_suite/arrays/inferred_array_optional.c3t @@ -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> , 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