From 1b5472cc94f5254fbf284eb11ca4dd82d0dfb498 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sun, 15 Sep 2024 23:43:09 +0200 Subject: [PATCH] Add paramsof. --- lib/std/core/runtime.c3 | 6 + lib/std/io/formatter.c3 | 6 + lib/std/sort/countingsort.c3 | 2 +- lib/std/sort/insertionsort.c3 | 2 +- lib/std/sort/quicksort.c3 | 2 +- releasenotes.md | 1 + src/compiler/compiler_internal.h | 3 +- src/compiler/enums.h | 1 + src/compiler/sema_expr.c | 29 ++ src/compiler/sema_initializers.c | 28 +- src/compiler/sema_internal.h | 2 + src/compiler/symtab.c | 1 + src/compiler/types.c | 34 ++ .../compile_time_introspection/paramsof.c3t | 391 ++++++++++++++++++ 14 files changed, 501 insertions(+), 7 deletions(-) create mode 100644 test/test_suite/compile_time_introspection/paramsof.c3t diff --git a/lib/std/core/runtime.c3 b/lib/std/core/runtime.c3 index 70ca8bd92..e4b5ebd05 100644 --- a/lib/std/core/runtime.c3 +++ b/lib/std/core/runtime.c3 @@ -4,6 +4,12 @@ module std::core::runtime; import libc, std::time, std::io, std::sort; +struct ReflectedParam @if(!$defined(ReflectedParam)) +{ + String name; + typeid type; +} + struct AnyRaw { void* ptr; diff --git a/lib/std/io/formatter.c3 b/lib/std/io/formatter.c3 index 4b455e4bb..69cd8ebcb 100644 --- a/lib/std/io/formatter.c3 +++ b/lib/std/io/formatter.c3 @@ -158,6 +158,12 @@ fn usz! Formatter.out_str(&self, any arg) @private assert(i < arg.type.names.len, "Illegal enum value found, numerical value was %d.", i); return self.out_substr(arg.type.names[i]); case STRUCT: + if (arg.type == ReflectedParam.typeid) + { + ReflectedParam* param = arg.ptr; + return self.out_substr("[Parameter '") + + self.out_substr(param.name) + self.out_substr("']"); + } return self.out_substr(""); case UNION: return self.out_substr(""); diff --git a/lib/std/sort/countingsort.c3 b/lib/std/sort/countingsort.c3 index ed997541a..70521f219 100644 --- a/lib/std/sort/countingsort.c3 +++ b/lib/std/sort/countingsort.c3 @@ -32,7 +32,7 @@ def Indexs = char[256] @private; def ElementType = $typeof(Type{}[0]); const bool NO_KEY_FN @private = types::is_same(KeyFn, EmptySlot); -const bool KEY_BY_VALUE @private = NO_KEY_FN ||| $assignable(Type{}[0], $typefrom(KeyFn.params[0])); +const bool KEY_BY_VALUE @private = NO_KEY_FN ||| $assignable(Type{}[0], $typefrom(KeyFn.paramsof[0].type)); const bool LIST_HAS_REF @private = $defined(&Type{}[0]); def KeyFnReturnType = $typefrom(KeyFn.returns) @if(!NO_KEY_FN); diff --git a/lib/std/sort/insertionsort.c3 b/lib/std/sort/insertionsort.c3 index f3d535762..350e066d7 100644 --- a/lib/std/sort/insertionsort.c3 +++ b/lib/std/sort/insertionsort.c3 @@ -20,7 +20,7 @@ fn void isort(Type list, usz low, usz high, CmpFn comp, Context context) { var $has_cmp = @is_valid_macro_slot(comp); var $has_context = @is_valid_macro_slot(context); - var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom(CmpFn.params[0])); + var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom(CmpFn.paramsof[0].type)); var $has_get_ref = $defined(&list[0]); for (usz i = low; i < high; ++i) { diff --git a/lib/std/sort/quicksort.c3 b/lib/std/sort/quicksort.c3 index bb6ac7398..ec78dca65 100644 --- a/lib/std/sort/quicksort.c3 +++ b/lib/std/sort/quicksort.c3 @@ -31,7 +31,7 @@ fn void qsort(Type list, isz low, isz high, CmpFn cmp, Context context) { var $has_cmp = @is_valid_macro_slot(cmp); var $has_context = @is_valid_macro_slot(context); - var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom(CmpFn.params[0])); + var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom(CmpFn.paramsof[0].type)); if (low >= 0 && high >= 0 && low < high) { diff --git a/releasenotes.md b/releasenotes.md index d9eb8a630..fd1ef448a 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -8,6 +8,7 @@ - Allow `var` in lambdas in macros. - Support `int[*] { 1, 2, 3 }` expressions. - Support inline struct designated init as if inline was anonymous. +- Introduce the `.paramsof` property. ### Fixes - Issue where a lambda wasn't correctly registered as external. #1408 diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index b425f49cc..088dc96f0 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1794,7 +1794,7 @@ typedef struct Module **module_list; Module **generic_module_list; Type **type; - Decl** method_extensions; + Decl **method_extensions; const char *lib_dir; const char **sources; File **loaded_sources; @@ -1850,6 +1850,7 @@ extern Type *type_cuint; extern Type *type_chars; extern Type *type_wildcard_optional; extern Type *type_string; +extern Type *type_reflect_method; extern File stdin_file; extern const char *attribute_list[NUMBER_OF_ATTRIBUTES]; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 45087d8ec..d9ad7056c 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -1373,6 +1373,7 @@ typedef enum TYPE_PROPERTY_NAMES, TYPE_PROPERTY_NAMEOF, TYPE_PROPERTY_PARAMS, + TYPE_PROPERTY_PARAMSOF, TYPE_PROPERTY_PARENTOF, TYPE_PROPERTY_QNAMEOF, TYPE_PROPERTY_RETURNS, diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 9a03242a8..69e6c1f34 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -3601,6 +3601,7 @@ static inline bool sema_expr_analyse_member_access(SemaContext *context, Expr *e case TYPE_PROPERTY_ELEMENTS: case TYPE_PROPERTY_EXTNAMEOF: case TYPE_PROPERTY_PARAMS: + case TYPE_PROPERTY_PARAMSOF: case TYPE_PROPERTY_RETURNS: case TYPE_PROPERTY_INF: case TYPE_PROPERTY_LEN: @@ -3635,6 +3636,7 @@ static inline bool sema_expr_analyse_member_access(SemaContext *context, Expr *e RETURN_SEMA_ERROR(expr, "No member '%s' found.", name); } + expr->expr_kind = EXPR_CONST; expr->resolve_status = RESOLVE_DONE; expr->const_expr = (ExprConst) { @@ -3841,6 +3843,29 @@ static inline bool sema_create_const_params(SemaContext *context, Expr *expr, Ty return true; } +static inline bool sema_create_const_paramsof(SemaContext *context, Expr *expr, Type *type) +{ + ASSERT_SPAN(expr, type->type_kind == TYPE_FUNC_PTR); + type = type->pointer; + Signature *sig = type->function.signature; + unsigned params = vec_size(sig->params); + Expr **param_exprs = params ? VECNEW(Expr*, params) : NULL; + SourceSpan span = expr->span; + for (unsigned i = 0; i < params; i++) + { + Decl *decl = sig->params[i]; + Expr *name_expr = expr_new(EXPR_CONST, span); + expr_rewrite_to_string(name_expr, decl->name ? decl->name : ""); + Expr *type_expr = expr_new(EXPR_CONST, span); + expr_rewrite_const_typeid(type_expr, decl->type->canonical); + Expr *values[] = { name_expr, type_expr }; + Expr *struct_value = sema_create_struct_from_expressions(type_reflect_method->decl, expr->span, values); + vec_add(param_exprs, struct_value); + } + expr_rewrite_const_untyped_list(expr, param_exprs); + return true; +} + static inline bool sema_create_const_associated(SemaContext *context, Expr *expr, Type *type) { ASSERT_SPAN(expr, type->type_kind == TYPE_ENUM); @@ -4062,6 +4087,7 @@ static bool sema_expr_rewrite_to_typeid_property(SemaContext *context, Expr *exp case TYPE_PROPERTY_NAMEOF: case TYPE_PROPERTY_NAN: case TYPE_PROPERTY_PARAMS: + case TYPE_PROPERTY_PARAMSOF: case TYPE_PROPERTY_QNAMEOF: case TYPE_PROPERTY_RETURNS: case TYPE_PROPERTY_TAGOF: @@ -4350,6 +4376,7 @@ static bool sema_type_property_is_valid_for_type(Type *original_type, TypeProper default: return false; } + case TYPE_PROPERTY_PARAMSOF: case TYPE_PROPERTY_PARAMS: case TYPE_PROPERTY_RETURNS: return type_is_func_ptr(type); @@ -4433,6 +4460,8 @@ static bool sema_expr_rewrite_to_type_property(SemaContext *context, Expr *expr, sema_create_const_methodsof(context, expr, flat); return true; } + case TYPE_PROPERTY_PARAMSOF: + return sema_create_const_paramsof(context, expr, flat); case TYPE_PROPERTY_PARAMS: return sema_create_const_params(context, expr, flat); case TYPE_PROPERTY_RETURNS: diff --git a/src/compiler/sema_initializers.c b/src/compiler/sema_initializers.c index 6e240916e..319677d77 100644 --- a/src/compiler/sema_initializers.c +++ b/src/compiler/sema_initializers.c @@ -12,7 +12,6 @@ static bool sema_expr_analyse_designated_initializer(SemaContext *context, Type Expr *initializer); static inline void sema_not_enough_elements_error(SemaContext *context, Expr *initializer, int element); static inline bool sema_expr_analyse_initializer(SemaContext *context, Type *assigned_type, Type *flattened, Expr *expr); -static void sema_create_const_initializer_value(ConstInitializer *const_init, Expr *value); static void sema_create_const_initializer_from_designated_init(ConstInitializer *const_init, Expr *initializer); static Decl *sema_resolve_element_for_name(SemaContext *context, Decl **decls, DesignatorElement ***elements_ref, unsigned *index, bool is_substruct); static Type *sema_expr_analyse_designator(SemaContext *context, Type *current, Expr *expr, ArrayIndex *max_index, Decl **member_ptr); @@ -250,6 +249,30 @@ static inline bool sema_expr_analyse_struct_plain_initializer(SemaContext *conte } +Expr *sema_create_struct_from_expressions(Decl *struct_decl, SourceSpan span, Expr **exprs) +{ + Expr *expr_element = expr_calloc(); + expr_element->expr_kind = EXPR_CONST; + expr_element->span = span; + expr_element->type = struct_decl->type; + expr_element->const_expr.const_kind = CONST_INITIALIZER; + expr_element->resolve_status = RESOLVE_DONE; + ConstInitializer *init = CALLOCS(ConstInitializer); + expr_element->const_expr.initializer = init; + init->kind = CONST_INIT_STRUCT; + init->type = struct_decl->type; + unsigned params = vec_size(struct_decl->strukt.members); + ConstInitializer **struct_values = MALLOC(params * sizeof(ConstInitializer*)); + init->init_struct = struct_values; + for (unsigned i = 0; i < params; i++) + { + ConstInitializer *member_init = MALLOCS(ConstInitializer); + sema_create_const_initializer_value(member_init, exprs[i]); + struct_values[i] = member_init; + } + return expr_element; +} + /** * Perform analysis for a plain initializer, that is one initializing all fields. * @return true if analysis succeeds. @@ -799,8 +822,7 @@ bool sema_expr_analyse_initializer_list(SemaContext *context, Type *to, Expr *ex return false; } - -static void sema_create_const_initializer_value(ConstInitializer *const_init, Expr *value) +void sema_create_const_initializer_value(ConstInitializer *const_init, Expr *value) { // Possibly this is already a const initializers, in that case // overwrite what is inside, eg [1] = { .a = 1 } diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 2d984564b..cec0ea1f4 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -107,6 +107,8 @@ bool sema_bit_assignment_check(SemaContext *context, Expr *right, Decl *member); CondResult sema_check_comp_time_bool(SemaContext *context, Expr *expr); bool sema_expr_check_assign(SemaContext *context, Expr *expr); bool sema_analyse_function_signature(SemaContext *context, Decl *func_decl, TypeInfo *parent, CallABI abi, Signature *signature); +void sema_create_const_initializer_value(ConstInitializer *const_init, Expr *value); +Expr *sema_create_struct_from_expressions(Decl *struct_decl, SourceSpan span, Expr **exprs); ConstInitializer *sema_merge_bitstruct_const_initializers(ConstInitializer *lhs, ConstInitializer *rhs, BinaryOp op); void sema_invert_bitstruct_const_initializer(ConstInitializer *initializer); ArrayIndex sema_len_from_const(Expr *expr); diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index 1a781a27b..d94c833ef 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -173,6 +173,7 @@ void symtab_init(uint32_t capacity) type_property_list[TYPE_PROPERTY_NAMES] = KW_DEF("names"); type_property_list[TYPE_PROPERTY_NAN] = KW_DEF("nan"); type_property_list[TYPE_PROPERTY_PARAMS] = KW_DEF("params"); + type_property_list[TYPE_PROPERTY_PARAMSOF] = KW_DEF("paramsof"); type_property_list[TYPE_PROPERTY_PARENTOF] = KW_DEF("parentof"); type_property_list[TYPE_PROPERTY_QNAMEOF] = KW_DEF("qnameof"); type_property_list[TYPE_PROPERTY_RETURNS] = KW_DEF("returns"); diff --git a/src/compiler/types.c b/src/compiler/types.c index 181a8ccb5..f77add265 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -49,6 +49,7 @@ Type *type_member = &t.member; Type *type_chars = NULL; Type *type_wildcard_optional = NULL; Type *type_string = &t.string; +Type *type_reflect_method; Type *type_cint; Type *type_cuint; @@ -1286,6 +1287,34 @@ static inline void type_create_float(const char *name, Type *type, TypeKind kind type_init(name, type, kind, actual_bits, compiler.platform.floats[bits]); } +Type *type_create_struct(const char *name, Type **types, const char **names, int count) +{ + Decl *decl = decl_new_with_type(symtab_preset(name, TOKEN_TYPE_IDENT), INVALID_SPAN, DECL_STRUCT); + decl->unit = compiler.context.core_unit; + decl->extname = decl->name; + AlignSize offset = 0; + AlignSize max_align = 0; + for (int i = 0; i < count; i++) + { + Type *member_type = types[i]; + Decl *member = decl_new_var(symtab_preset(names[i], TOKEN_IDENT), INVALID_SPAN, type_info_new_base(member_type, INVALID_SPAN), VARDECL_MEMBER); + member->unit = compiler.context.core_unit; + member->type = member_type; + member->resolve_status = RESOLVE_DONE; + AlignSize align = type_abi_alignment(member_type); + if (align > max_align) max_align = align; + member->offset = aligned_offset(offset, align); + offset = member->offset + type_size(member_type); + member->alignment = align; + vec_add(decl->strukt.members, member); + } + decl->strukt.size = aligned_offset(offset, max_align); + decl->alignment = max_align; + decl->resolve_status = RESOLVE_DONE; + global_context_add_type(decl->type); + global_context_add_decl(decl); + return decl->type; +} void type_setup(PlatformTarget *target) { max_alignment_vector = (AlignSize)target->align_max_vector; @@ -1340,8 +1369,13 @@ void type_setup(PlatformTarget *target) string_decl->distinct = type_info_new_base(type_chars, INVALID_SPAN); string_decl->resolve_status = RESOLVE_DONE; type_string = string_decl->type; + global_context_add_type(string_decl->type); global_context_add_decl(string_decl); + + Type* types[2] = { type_string, type_typeid }; + const char* names[2] = { "name", "type" }; + type_reflect_method = type_create_struct("ReflectedParam", types, names, 2); } int type_kind_bitsize(TypeKind kind) diff --git a/test/test_suite/compile_time_introspection/paramsof.c3t b/test/test_suite/compile_time_introspection/paramsof.c3t new file mode 100644 index 000000000..8550d8d57 --- /dev/null +++ b/test/test_suite/compile_time_introspection/paramsof.c3t @@ -0,0 +1,391 @@ +// #target: macos-x64 +module test; +import std; +fn void testme(int a, double b) +{} + +fn void main() +{ + ReflectedParam[*] z = $typeof(testme).paramsof; + foreach (r : z) + { + io::printn(r); + } + $foreach ($r : $typeof(testme).paramsof) + io::printn($r.name); + io::printn($r.type.nameof); + $endforeach +} + +/* #expect: test.ll + + +; ModuleID = 'test' + +%ReflectedParam = type { %"char[]", i64 } + +@.str = private unnamed_addr constant [2 x i8] c"a\00", align 1 +@"$ct.int" = linkonce global %.introspect { i8 2, i64 0, ptr null, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@.str.1 = private unnamed_addr constant [2 x i8] c"b\00", align 1 +@"$ct.double" = linkonce global %.introspect { i8 4, i64 0, ptr null, i64 8, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@.__const = private unnamed_addr constant [2 x %ReflectedParam] [%ReflectedParam { %"char[]" { ptr @.str, i64 1 }, i64 ptrtoint (ptr @"$ct.int" to i64) }, %ReflectedParam { %"char[]" { ptr @.str.1, i64 1 }, i64 ptrtoint (ptr @"$ct.double" to i64) }], align 16 +@"$ct.std.io.File" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 8, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 +@.str.2 = private unnamed_addr constant [3 x i8] c"%s\00", align 1 +@"$ct.ReflectedParam" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 24, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 +@.str.3 = private unnamed_addr constant [2 x i8] c"a\00", align 1 +@.str.4 = private unnamed_addr constant [4 x i8] c"int\00", align 1 +@.str.5 = private unnamed_addr constant [2 x i8] c"b\00", align 1 +@.str.6 = private unnamed_addr constant [7 x i8] c"double\00", align 1 + +define void @test.main() #0 { +entry: + %z = alloca [2 x %ReflectedParam], align 16 + %.anon = alloca i64, align 8 + %r = alloca %ReflectedParam, align 8 + %x = alloca %ReflectedParam, align 8 + %x1 = alloca %ReflectedParam, align 8 + %len = alloca i64, align 8 + %error_var = alloca i64, align 8 + %x2 = alloca %ReflectedParam, align 8 + %varargslots = alloca [1 x %any], align 16 + %retparam = alloca i64, align 8 + %taddr = alloca %any, align 8 + %indirectarg = alloca %"any[]", align 8 + %error_var4 = alloca i64, align 8 + %error_var10 = alloca i64, align 8 + %len16 = alloca i64, align 8 + %error_var17 = alloca i64, align 8 + %retparam19 = alloca i64, align 8 + %error_var25 = alloca i64, align 8 + %error_var31 = alloca i64, align 8 + %len39 = alloca i64, align 8 + %error_var40 = alloca i64, align 8 + %retparam42 = alloca i64, align 8 + %error_var48 = alloca i64, align 8 + %error_var54 = alloca i64, align 8 + %len62 = alloca i64, align 8 + %error_var63 = alloca i64, align 8 + %retparam65 = alloca i64, align 8 + %error_var71 = alloca i64, align 8 + %error_var77 = alloca i64, align 8 + %len85 = alloca i64, align 8 + %error_var86 = alloca i64, align 8 + %retparam88 = alloca i64, align 8 + %error_var94 = alloca i64, align 8 + %error_var100 = alloca i64, align 8 + call void @llvm.memcpy.p0.p0.i32(ptr align 16 %z, ptr align 16 @.__const, i32 48, i1 false) + store i64 0, ptr %.anon, align 8 + br label %loop.cond + +loop.cond: ; preds = %voiderr, %entry + %0 = load i64, ptr %.anon, align 8 + %gt = icmp ugt i64 2, %0 + br i1 %gt, label %loop.body, label %loop.exit + +loop.body: ; preds = %loop.cond + %1 = load i64, ptr %.anon, align 8 + %ptroffset = getelementptr inbounds [24 x i8], ptr %z, i64 %1 + call void @llvm.memcpy.p0.p0.i32(ptr align 8 %r, ptr align 8 %ptroffset, i32 24, i1 false) + call void @llvm.memcpy.p0.p0.i32(ptr align 8 %x, ptr align 8 %r, i32 24, i1 false) + %2 = call ptr @std.io.stdout() + call void @llvm.memcpy.p0.p0.i32(ptr align 8 %x1, ptr align 8 %x, i32 24, i1 false) + call void @llvm.memcpy.p0.p0.i32(ptr align 8 %x2, ptr align 8 %x1, i32 24, i1 false) + %3 = insertvalue %any undef, ptr %2, 0 + %4 = insertvalue %any %3, i64 ptrtoint (ptr @"$ct.std.io.File" to i64), 1 + %5 = insertvalue %any undef, ptr %x2, 0 + %6 = insertvalue %any %5, i64 ptrtoint (ptr @"$ct.ReflectedParam" to i64), 1 + store %any %6, ptr %varargslots, align 16 + %7 = insertvalue %"any[]" undef, ptr %varargslots, 0 + %"$$temp" = insertvalue %"any[]" %7, i64 1, 1 + store %any %4, ptr %taddr, align 8 + %lo = load i64, ptr %taddr, align 8 + %ptradd = getelementptr inbounds i8, ptr %taddr, i64 8 + %hi = load ptr, ptr %ptradd, align 8 + store %"any[]" %"$$temp", ptr %indirectarg, align 8 + %8 = call i64 @std.io.fprintf(ptr %retparam, i64 %lo, ptr %hi, ptr @.str.2, i64 2, ptr byval(%"any[]") align 8 %indirectarg) + %not_err = icmp eq i64 %8, 0 + %9 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) + br i1 %9, label %after_check, label %assign_optional + +assign_optional: ; preds = %loop.body + store i64 %8, ptr %error_var, align 8 + br label %guard_block + +after_check: ; preds = %loop.body + br label %noerr_block + +guard_block: ; preds = %assign_optional + br label %voiderr + +noerr_block: ; preds = %after_check + %10 = load i64, ptr %retparam, align 8 + store i64 %10, ptr %len, align 8 + %11 = call i64 @std.io.File.write_byte(ptr %2, i8 zeroext 10) + %not_err5 = icmp eq i64 %11, 0 + %12 = call i1 @llvm.expect.i1(i1 %not_err5, i1 true) + br i1 %12, label %after_check7, label %assign_optional6 + +assign_optional6: ; preds = %noerr_block + store i64 %11, ptr %error_var4, align 8 + br label %guard_block8 + +after_check7: ; preds = %noerr_block + br label %noerr_block9 + +guard_block8: ; preds = %assign_optional6 + br label %voiderr + +noerr_block9: ; preds = %after_check7 + %13 = call i64 @std.io.File.flush(ptr %2) + %not_err11 = icmp eq i64 %13, 0 + %14 = call i1 @llvm.expect.i1(i1 %not_err11, i1 true) + br i1 %14, label %after_check13, label %assign_optional12 + +assign_optional12: ; preds = %noerr_block9 + store i64 %13, ptr %error_var10, align 8 + br label %guard_block14 + +after_check13: ; preds = %noerr_block9 + br label %noerr_block15 + +guard_block14: ; preds = %assign_optional12 + br label %voiderr + +noerr_block15: ; preds = %after_check13 + %15 = load i64, ptr %len, align 8 + %add = add i64 %15, 1 + br label %voiderr + +voiderr: ; preds = %noerr_block15, %guard_block14, %guard_block8, %guard_block + %16 = load i64, ptr %.anon, align 8 + %addnuw = add nuw i64 %16, 1 + store i64 %addnuw, ptr %.anon, align 8 + br label %loop.cond + +loop.exit: ; preds = %loop.cond + %17 = call ptr @std.io.stdout() + %18 = call i64 @std.io.File.write(ptr %retparam19, ptr %17, ptr @.str.3, i64 1) + %not_err20 = icmp eq i64 %18, 0 + %19 = call i1 @llvm.expect.i1(i1 %not_err20, i1 true) + br i1 %19, label %after_check22, label %assign_optional21 + +assign_optional21: ; preds = %loop.exit + store i64 %18, ptr %error_var17, align 8 + br label %guard_block23 + +after_check22: ; preds = %loop.exit + br label %noerr_block24 + +guard_block23: ; preds = %assign_optional21 + br label %voiderr38 + +noerr_block24: ; preds = %after_check22 + %20 = load i64, ptr %retparam19, align 8 + store i64 %20, ptr %len16, align 8 + %21 = call i64 @std.io.File.write_byte(ptr %17, i8 zeroext 10) + %not_err26 = icmp eq i64 %21, 0 + %22 = call i1 @llvm.expect.i1(i1 %not_err26, i1 true) + br i1 %22, label %after_check28, label %assign_optional27 + +assign_optional27: ; preds = %noerr_block24 + store i64 %21, ptr %error_var25, align 8 + br label %guard_block29 + +after_check28: ; preds = %noerr_block24 + br label %noerr_block30 + +guard_block29: ; preds = %assign_optional27 + br label %voiderr38 + +noerr_block30: ; preds = %after_check28 + %23 = call i64 @std.io.File.flush(ptr %17) + %not_err32 = icmp eq i64 %23, 0 + %24 = call i1 @llvm.expect.i1(i1 %not_err32, i1 true) + br i1 %24, label %after_check34, label %assign_optional33 + +assign_optional33: ; preds = %noerr_block30 + store i64 %23, ptr %error_var31, align 8 + br label %guard_block35 + +after_check34: ; preds = %noerr_block30 + br label %noerr_block36 + +guard_block35: ; preds = %assign_optional33 + br label %voiderr38 + +noerr_block36: ; preds = %after_check34 + %25 = load i64, ptr %len16, align 8 + %add37 = add i64 %25, 1 + br label %voiderr38 + +voiderr38: ; preds = %noerr_block36, %guard_block35, %guard_block29, %guard_block23 + %26 = call ptr @std.io.stdout() + %27 = call i64 @std.io.File.write(ptr %retparam42, ptr %26, ptr @.str.4, i64 3) + %not_err43 = icmp eq i64 %27, 0 + %28 = call i1 @llvm.expect.i1(i1 %not_err43, i1 true) + br i1 %28, label %after_check45, label %assign_optional44 + +assign_optional44: ; preds = %voiderr38 + store i64 %27, ptr %error_var40, align 8 + br label %guard_block46 + +after_check45: ; preds = %voiderr38 + br label %noerr_block47 + +guard_block46: ; preds = %assign_optional44 + br label %voiderr61 + +noerr_block47: ; preds = %after_check45 + %29 = load i64, ptr %retparam42, align 8 + store i64 %29, ptr %len39, align 8 + %30 = call i64 @std.io.File.write_byte(ptr %26, i8 zeroext 10) + %not_err49 = icmp eq i64 %30, 0 + %31 = call i1 @llvm.expect.i1(i1 %not_err49, i1 true) + br i1 %31, label %after_check51, label %assign_optional50 + +assign_optional50: ; preds = %noerr_block47 + store i64 %30, ptr %error_var48, align 8 + br label %guard_block52 + +after_check51: ; preds = %noerr_block47 + br label %noerr_block53 + +guard_block52: ; preds = %assign_optional50 + br label %voiderr61 + +noerr_block53: ; preds = %after_check51 + %32 = call i64 @std.io.File.flush(ptr %26) + %not_err55 = icmp eq i64 %32, 0 + %33 = call i1 @llvm.expect.i1(i1 %not_err55, i1 true) + br i1 %33, label %after_check57, label %assign_optional56 + +assign_optional56: ; preds = %noerr_block53 + store i64 %32, ptr %error_var54, align 8 + br label %guard_block58 + +after_check57: ; preds = %noerr_block53 + br label %noerr_block59 + +guard_block58: ; preds = %assign_optional56 + br label %voiderr61 + +noerr_block59: ; preds = %after_check57 + %34 = load i64, ptr %len39, align 8 + %add60 = add i64 %34, 1 + br label %voiderr61 + +voiderr61: ; preds = %noerr_block59, %guard_block58, %guard_block52, %guard_block46 + %35 = call ptr @std.io.stdout() + %36 = call i64 @std.io.File.write(ptr %retparam65, ptr %35, ptr @.str.5, i64 1) + %not_err66 = icmp eq i64 %36, 0 + %37 = call i1 @llvm.expect.i1(i1 %not_err66, i1 true) + br i1 %37, label %after_check68, label %assign_optional67 + +assign_optional67: ; preds = %voiderr61 + store i64 %36, ptr %error_var63, align 8 + br label %guard_block69 + +after_check68: ; preds = %voiderr61 + br label %noerr_block70 + +guard_block69: ; preds = %assign_optional67 + br label %voiderr84 + +noerr_block70: ; preds = %after_check68 + %38 = load i64, ptr %retparam65, align 8 + store i64 %38, ptr %len62, align 8 + %39 = call i64 @std.io.File.write_byte(ptr %35, i8 zeroext 10) + %not_err72 = icmp eq i64 %39, 0 + %40 = call i1 @llvm.expect.i1(i1 %not_err72, i1 true) + br i1 %40, label %after_check74, label %assign_optional73 + +assign_optional73: ; preds = %noerr_block70 + store i64 %39, ptr %error_var71, align 8 + br label %guard_block75 + +after_check74: ; preds = %noerr_block70 + br label %noerr_block76 + +guard_block75: ; preds = %assign_optional73 + br label %voiderr84 + +noerr_block76: ; preds = %after_check74 + %41 = call i64 @std.io.File.flush(ptr %35) + %not_err78 = icmp eq i64 %41, 0 + %42 = call i1 @llvm.expect.i1(i1 %not_err78, i1 true) + br i1 %42, label %after_check80, label %assign_optional79 + +assign_optional79: ; preds = %noerr_block76 + store i64 %41, ptr %error_var77, align 8 + br label %guard_block81 + +after_check80: ; preds = %noerr_block76 + br label %noerr_block82 + +guard_block81: ; preds = %assign_optional79 + br label %voiderr84 + +noerr_block82: ; preds = %after_check80 + %43 = load i64, ptr %len62, align 8 + %add83 = add i64 %43, 1 + br label %voiderr84 + +voiderr84: ; preds = %noerr_block82, %guard_block81, %guard_block75, %guard_block69 + %44 = call ptr @std.io.stdout() + %45 = call i64 @std.io.File.write(ptr %retparam88, ptr %44, ptr @.str.6, i64 6) + %not_err89 = icmp eq i64 %45, 0 + %46 = call i1 @llvm.expect.i1(i1 %not_err89, i1 true) + br i1 %46, label %after_check91, label %assign_optional90 + +assign_optional90: ; preds = %voiderr84 + store i64 %45, ptr %error_var86, align 8 + br label %guard_block92 + +after_check91: ; preds = %voiderr84 + br label %noerr_block93 + +guard_block92: ; preds = %assign_optional90 + br label %voiderr107 + +noerr_block93: ; preds = %after_check91 + %47 = load i64, ptr %retparam88, align 8 + store i64 %47, ptr %len85, align 8 + %48 = call i64 @std.io.File.write_byte(ptr %44, i8 zeroext 10) + %not_err95 = icmp eq i64 %48, 0 + %49 = call i1 @llvm.expect.i1(i1 %not_err95, i1 true) + br i1 %49, label %after_check97, label %assign_optional96 + +assign_optional96: ; preds = %noerr_block93 + store i64 %48, ptr %error_var94, align 8 + br label %guard_block98 + +after_check97: ; preds = %noerr_block93 + br label %noerr_block99 + +guard_block98: ; preds = %assign_optional96 + br label %voiderr107 + +noerr_block99: ; preds = %after_check97 + %50 = call i64 @std.io.File.flush(ptr %44) + %not_err101 = icmp eq i64 %50, 0 + %51 = call i1 @llvm.expect.i1(i1 %not_err101, i1 true) + br i1 %51, label %after_check103, label %assign_optional102 + +assign_optional102: ; preds = %noerr_block99 + store i64 %50, ptr %error_var100, align 8 + br label %guard_block104 + +after_check103: ; preds = %noerr_block99 + br label %noerr_block105 + +guard_block104: ; preds = %assign_optional102 + br label %voiderr107 + +noerr_block105: ; preds = %after_check103 + %52 = load i64, ptr %len85, align 8 + %add106 = add i64 %52, 1 + br label %voiderr107 + +voiderr107: ; preds = %noerr_block105, %guard_block104, %guard_block98, %guard_block92 + ret void +}