From aa7da00323bdfa4a6bc04c22e3dc4b9ea5edc6d5 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Tue, 30 Nov 2021 15:27:03 +0100 Subject: [PATCH] Support for ranged case. --- src/compiler/compiler_internal.h | 7 +- src/compiler/copying.c | 5 +- src/compiler/llvm_codegen_c_abi_x64.c | 4 +- src/compiler/llvm_codegen_expr.c | 4 +- src/compiler/llvm_codegen_stmt.c | 32 ++- src/compiler/parse_stmt.c | 8 +- src/compiler/sema_stmts.c | 108 +++++++--- test/test_suite/statements/ranged_switch.c3t | 202 +++++++++++++++++++ 8 files changed, 332 insertions(+), 38 deletions(-) create mode 100644 test/test_suite/statements/ranged_switch.c3t diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 2edc868ac..f81b8003f 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1033,6 +1033,7 @@ typedef struct typedef struct { Expr *expr; + Expr *to_expr; Ast *body; void *backend_block; } AstCaseStmt; @@ -1147,11 +1148,7 @@ typedef struct { Label label; bool is_type; - union - { - Expr *target; - TypeInfo *type_info; - }; + void *expr_or_type_info; }; struct { diff --git a/src/compiler/copying.c b/src/compiler/copying.c index e53c6ce7d..9f459fb71 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -254,6 +254,7 @@ Ast *copy_ast(Ast *source) case AST_CASE_STMT: MACRO_COPY_AST(ast->case_stmt.body); MACRO_COPY_EXPR(ast->case_stmt.expr); + MACRO_COPY_EXPR(ast->case_stmt.to_expr); return ast; case AST_COMPOUND_STMT: MACRO_COPY_AST_LIST(ast->compound_stmt.stmts); @@ -330,11 +331,11 @@ Ast *copy_ast(Ast *source) case AST_NEXT_STMT: if (ast->next_stmt.is_type) { - MACRO_COPY_TYPE(ast->next_stmt.type_info); + MACRO_COPY_TYPE(ast->next_stmt.expr_or_type_info); } else { - MACRO_COPY_EXPR(ast->next_stmt.target); + MACRO_COPY_EXPR(ast->next_stmt.expr_or_type_info); } return ast; case AST_NOP_STMT: diff --git a/src/compiler/llvm_codegen_c_abi_x64.c b/src/compiler/llvm_codegen_c_abi_x64.c index c042dfd45..8caa6002d 100644 --- a/src/compiler/llvm_codegen_c_abi_x64.c +++ b/src/compiler/llvm_codegen_c_abi_x64.c @@ -101,7 +101,7 @@ ABIArgInfo *x64_indirect_result(Type *type, unsigned free_int_regs) { return abi_arg_new_direct_int_ext(type); } - // No change, just put it on the stack. + // No change, just put it on the stack return abi_arg_new_direct(); } @@ -370,7 +370,7 @@ void x64_classify_vector(Type *type, ByteSize offset_base, X64Class *current, X6 } -Decl *x64_get_member_at_offset(Decl *decl, unsigned offset) +static Decl *x64_get_member_at_offset(Decl *decl, unsigned offset) { if (type_size(decl->type) <= offset) return NULL; Decl **members = decl->strukt.members; diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index ab66c617b..a0558a087 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -2531,7 +2531,7 @@ void llvm_emit_int_comp(GenContext *c, BEValue *result, Type *lhs_type, Type *rh } else { - assert(type_is_integer(lhs_type)); + assert(type_is_integer_or_bool_kind(lhs_type)); lhs_signed = type_is_signed(lhs_type); rhs_signed = type_is_signed(rhs_type); } @@ -2901,7 +2901,7 @@ void llvm_emit_comparison(GenContext *c, BEValue *be_value, BEValue *lhs, BEValu assert(binary_op >= BINARYOP_GT && binary_op <= BINARYOP_EQ); llvm_value_rvalue(c, lhs); llvm_value_rvalue(c, rhs); - if (type_is_integer(lhs->type)) + if (type_is_integer_or_bool_kind(lhs->type)) { llvm_emit_int_comp(c, be_value, lhs->type, rhs->type, lhs->value, rhs->value, binary_op); return; diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 75bd8fc9e..af2b115ed 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -590,7 +590,22 @@ static void llvm_emit_switch_body_if_chain(GenContext *c, llvm_emit_expr(c, &be_value, case_stmt->case_stmt.expr); llvm_value_rvalue(c, &be_value); BEValue equals; - llvm_emit_comparison(c, &equals, &be_value, switch_value, BINARYOP_EQ); + Expr *to_expr = case_stmt->case_stmt.to_expr; + if (to_expr) + { + BEValue to_value; + llvm_emit_expr(c, &to_value, to_expr); + llvm_value_rvalue(c, &to_value); + BEValue le; + llvm_emit_comparison(c, &le, &be_value, switch_value, BINARYOP_LE); + BEValue ge; + llvm_emit_comparison(c, &ge, &to_value, switch_value, BINARYOP_GE); + llvm_value_set_bool(&equals, LLVMBuildAnd(c->builder, le.value, ge.value, "")); + } + else + { + llvm_emit_comparison(c, &equals, &be_value, switch_value, BINARYOP_EQ); + } next = llvm_basic_block_new(c, "next_if"); llvm_emit_cond_br(c, &equals, block, next); if (case_stmt->case_stmt.body) @@ -701,10 +716,25 @@ static void gencontext_emit_switch_body(GenContext *c, BEValue *switch_value, As { LLVMValueRef case_value; BEValue be_value; + Expr *from = case_stmt->case_stmt.expr; + assert(from->expr_kind == EXPR_CONST); llvm_emit_expr(c, &be_value, case_stmt->case_stmt.expr); llvm_value_rvalue(c, &be_value); case_value = be_value.value; LLVMAddCase(switch_stmt, case_value, block); + Expr *to = case_stmt->case_stmt.to_expr; + if (to) + { + BEValue to_value; + llvm_emit_expr(c, &to_value, case_stmt->case_stmt.to_expr); + assert(LLVMIsAConstant(to_value.value)); + LLVMValueRef one = llvm_const_int(c, to_value.type, 1); + while (LLVMConstIntGetZExtValue(LLVMConstICmp(LLVMIntEQ, to_value.value, case_value)) != 1) + { + case_value = LLVMConstAdd(case_value, one); + LLVMAddCase(switch_stmt, case_value, block); + } + } } // Skip fallthroughs. diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index 0fd6de8dc..369deccdb 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -222,6 +222,10 @@ static inline Ast *parse_case_stmt(Context *context, TokenType case_type, TokenT { ast->case_stmt.expr->expr_kind = EXPR_TYPEID; } + if (try_consume(context, TOKEN_DOTDOT)) + { + ASSIGN_EXPR_ELSE(ast->case_stmt.to_expr, parse_expr(context), poisoned_ast); + } TRY_CONSUME(TOKEN_COLON, "Missing ':' after case"); extend_ast_with_prev_token(context, ast); ASSIGN_AST_ELSE(ast->case_stmt.body, parse_case_stmts(context, case_type, default_type), poisoned_ast); @@ -449,11 +453,11 @@ static inline Ast* parse_next(Context *context) if (type) { ast->next_stmt.is_type = true; - ast->next_stmt.type_info = type; + ast->next_stmt.expr_or_type_info = type; } else { - ast->next_stmt.target = expr; + ast->next_stmt.expr_or_type_info = expr; } } return ast; diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 162805807..eb38e81eb 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -1541,13 +1541,21 @@ static bool sema_analyse_break_stmt(Context *context, Ast *statement) static bool sema_analyse_nextcase_stmt(Context *context, Ast *statement) { context->active_scope.jump_end = true; - if (!context->next_target && !statement->next_stmt.label.name) + + if (!context->next_target && !statement->next_stmt.label.name && !statement->next_stmt.expr_or_type_info) { - SEMA_ERROR(statement, "'nextcase' is not allowed here."); + if (context->next_switch) + { + SEMA_ERROR(statement, "A plain 'nextcase' is not allowed on the last case."); + } + else + { + SEMA_ERROR(statement, "'nextcase' can only be used inside of a switch."); + } return false; } - Ast *parent = context->next_switch; + Ast *parent = context->next_switch; if (statement->next_stmt.label.name) { Decl *target = sema_resolve_normal_symbol(context, statement->next_stmt.label.span, NULL, false); @@ -1575,26 +1583,24 @@ static bool sema_analyse_nextcase_stmt(Context *context, Ast *statement) SEMA_ERROR(statement, "This 'nextcase' would jump out of a defer which is not allowed."); return false; } - assert(statement->next_stmt.target); + assert(statement->next_stmt.expr_or_type_info); } statement->next_stmt.defers.start = context->active_scope.defer_last; statement->next_stmt.defers.end = parent->switch_stmt.defer; + // Plain next. - if (!statement->next_stmt.target) + if (!statement->next_stmt.expr_or_type_info) { - if (!context->next_target) - { - SEMA_ERROR(statement, "Unexpected 'nextcase' statement outside of a switch."); - return false; - } + assert(context->next_target); statement->next_stmt.case_switch_stmt = context->next_target; return true; } if (statement->next_stmt.is_type) { - if (!sema_resolve_type_info(context, statement->next_stmt.type_info)) return false; + TypeInfo *type_info = statement->next_stmt.expr_or_type_info; + if (!sema_resolve_type_info(context, type_info)) return false; Ast **cases; statement->next_stmt.defers.end = parent->switch_stmt.defer; if (parent->switch_stmt.cond->type->canonical != type_typeid) @@ -1606,7 +1612,7 @@ static bool sema_analyse_nextcase_stmt(Context *context, Ast *statement) cases = parent->switch_stmt.cases; Ast *default_stmt = NULL; - Type *type = statement->next_stmt.type_info->type->canonical; + Type *type = type_info->type->canonical; VECEACH(cases, i) { Ast *case_stmt = cases[i]; @@ -1627,11 +1633,11 @@ static bool sema_analyse_nextcase_stmt(Context *context, Ast *statement) statement->next_stmt.case_switch_stmt = astid(default_stmt); return true; } - SEMA_ERROR(statement->next_stmt.type_info, "There is no case for type '%s'.", type_to_error_string(statement->next_stmt.type_info->type)); + SEMA_ERROR(type_info, "There is no case for type '%s'.", type_to_error_string(type_info->type)); return false; } - Expr *target = statement->next_stmt.target; + Expr *target = statement->next_stmt.expr_or_type_info; Type *expected_type = parent->ast_kind == AST_SWITCH_STMT ? parent->switch_stmt.cond->type : type_anyerr; @@ -1649,7 +1655,10 @@ static bool sema_analyse_nextcase_stmt(Context *context, Ast *statement) default_stmt = case_stmt; break; } - if (expr_const_compare(&target->const_expr, &case_stmt->case_stmt.expr->const_expr, BINARYOP_EQ)) + ExprConst *const_expr = &case_stmt->case_stmt.expr->const_expr; + ExprConst *to_const_expr = case_stmt->case_stmt.to_expr ? &case_stmt->case_stmt.to_expr->const_expr : const_expr; + if (expr_const_compare(&target->const_expr, const_expr, BINARYOP_GE) && + expr_const_compare(&target->const_expr, to_const_expr, BINARYOP_LE)) { statement->next_stmt.case_switch_stmt = astid(case_stmt); return true; @@ -1660,7 +1669,7 @@ static bool sema_analyse_nextcase_stmt(Context *context, Ast *statement) statement->next_stmt.case_switch_stmt = astid(default_stmt); return true; } - SEMA_ERROR(statement, "The 'next' needs to jump to an exact case statement."); + SEMA_ERROR(statement, "'nextcase' needs to jump to an exact case statement."); return false; } @@ -1791,23 +1800,55 @@ static inline bool sema_check_type_case(Context *context, Type *switch_type, Ast return true; } -static inline bool sema_check_value_case(Context *context, Type *switch_type, Ast *case_stmt, Ast **cases, unsigned index, bool *if_chained) +static inline ExprConst *flatten_enum_const(Expr *expr) +{ + ExprConst *const_expr = &expr->const_expr; + if (const_expr->const_kind == CONST_ENUM) + { + const_expr->const_kind = CONST_INTEGER; + Decl *enum_val = const_expr->enum_val; + Expr *enum_expr = enum_val->enum_constant.expr; + assert(enum_expr->expr_kind == EXPR_CONST); + ExprConst *enum_const = &enum_expr->const_expr; + assert(enum_const->const_kind == CONST_INTEGER); + *const_expr = *enum_const; + } + return const_expr; +} +static inline bool sema_check_value_case(Context *context, Type *switch_type, Ast *case_stmt, Ast **cases, unsigned index, bool *if_chained, bool *max_ranged) { assert(switch_type); Expr *expr = case_stmt->case_stmt.expr; + Expr *to_expr = case_stmt->case_stmt.to_expr; // 1. Try to do implicit conversion to the correct type. if (!sema_analyse_expr_rhs(context, switch_type, expr, false)) return false; + if (to_expr && !sema_analyse_expr_rhs(context, switch_type, to_expr, false)) return false; - if (expr->expr_kind != EXPR_CONST) + if (expr->expr_kind != EXPR_CONST || (to_expr && to_expr->expr_kind != EXPR_CONST)) { *if_chained = true; return true; } + ExprConst *const_expr = flatten_enum_const(expr); + ExprConst *to_const_expr = to_expr ? flatten_enum_const(to_expr) : const_expr; + + if (!*max_ranged && type_is_integer(expr->type) && to_const_expr != const_expr) + { + Int128 range = int_sub(to_const_expr->ixx, const_expr->ixx).i; + Int128 max_range = { .low = 256 }; + if (i128_comp(range, max_range, type_i128) == CMP_GT) + { + *max_ranged = true; + } + } for (unsigned i = 0; i < index; i++) { Ast *other = cases[i]; - if (other->ast_kind == AST_CASE_STMT && expr_const_compare(&other->case_stmt.expr->const_expr, &expr->const_expr, BINARYOP_EQ)) + if (other->ast_kind != AST_CASE_STMT) continue; + ExprConst *other_const = &other->case_stmt.expr->const_expr; + ExprConst *other_to_const = other->case_stmt.to_expr ? &other->case_stmt.to_expr->const_expr : other_const; + if (expr_const_compare(const_expr, other_to_const, BINARYOP_LE) && expr_const_compare(to_const_expr, other_const, BINARYOP_GE)) { SEMA_ERROR(case_stmt, "The same case value appears more than once."); SEMA_PREV(other, "Here is the previous use of that value."); @@ -1834,9 +1875,12 @@ static bool sema_analyse_switch_body(Context *context, Ast *statement, SourceSpa bool exhaustive = false; unsigned case_count = vec_size(cases); bool success = true; + bool max_ranged = false; for (unsigned i = 0; i < case_count; i++) { Ast *stmt = cases[i]; + Ast *next = (i < case_count - 1) ? cases[i + 1] : NULL; + PUSH_NEXT(next, statement); switch (stmt->ast_kind) { case AST_CASE_STMT: @@ -1850,7 +1894,7 @@ static bool sema_analyse_switch_body(Context *context, Ast *statement, SourceSpa } else { - if (!sema_check_value_case(context, switch_type, stmt, cases, i, &if_chain)) + if (!sema_check_value_case(context, switch_type, stmt, cases, i, &if_chain, &max_ranged)) { success = false; break; @@ -1870,6 +1914,7 @@ static bool sema_analyse_switch_body(Context *context, Ast *statement, SourceSpa default: UNREACHABLE; } + POP_NEXT(); } bool all_jump_end = exhaustive; @@ -1888,7 +1933,7 @@ static bool sema_analyse_switch_body(Context *context, Ast *statement, SourceSpa SCOPE_END; } statement->flow.no_exit = all_jump_end; - statement->switch_stmt.if_chain = if_chain; + statement->switch_stmt.if_chain = if_chain || max_ranged; if (!success) return false; return success; } @@ -1911,6 +1956,7 @@ static bool sema_analyse_ct_switch_body(Context *context, Ast *statement) case AST_CASE_STMT: { Expr *expr = stmt->case_stmt.expr; + Expr *to_expr = stmt->case_stmt.to_expr; if (is_type) { if (!sema_analyse_expr(context, expr)) return false; @@ -1923,26 +1969,40 @@ static bool sema_analyse_ct_switch_body(Context *context, Ast *statement) else { if (!sema_analyse_expr_rhs(context, type, expr, false)) return false; + if (to_expr && !sema_analyse_expr_rhs(context, type, to_expr, false)) return false; } if (expr->expr_kind != EXPR_CONST) { SEMA_ERROR(expr, "The $case must have a constant expression."); return false; } + if (to_expr && to_expr->expr_kind != EXPR_CONST) + { + SEMA_ERROR(to_expr, "The $case must have a constant expression."); + return false; + } ExprConst *const_expr = &expr->const_expr; + ExprConst *const_to_expr = to_expr ? &to_expr->const_expr : const_expr; + if (to_expr && expr_const_compare(const_expr, const_to_expr, BINARYOP_GT)) + { + SEMA_ERROR(to_expr, "The end of a range must be less or equal to the beginning."); + return false; + } // Check that it is unique. for (unsigned j = 0; j < i; j++) { Ast *other_stmt = cases[j]; if (other_stmt->ast_kind == AST_DEFAULT_STMT) continue; - if (expr_const_compare(&expr->const_expr, &other_stmt->case_stmt.expr->const_expr, BINARYOP_EQ)) + ExprConst *other_const = &other_stmt->case_stmt.expr->const_expr; + ExprConst *other_const_to = other_stmt->case_stmt.to_expr ? &other_stmt->case_stmt.to_expr->const_expr : other_const; + if (expr_const_compare(const_expr, other_const_to, BINARYOP_LE) && expr_const_compare(const_to_expr, other_const, BINARYOP_GE)) { - SEMA_ERROR(stmt, "'%s' appears more than once.", expr_const_to_error_string(&expr->const_expr)); + SEMA_ERROR(stmt, "'%s' appears more than once.", expr_const_to_error_string(const_expr)); SEMA_PREV(cases[j]->case_stmt.expr, "The previous $case was here."); return false; } } - if (expr_const_compare(switch_expr_const, const_expr, BINARYOP_EQ)) + if (expr_const_compare(switch_expr_const, const_expr, BINARYOP_GE) && expr_const_compare(switch_expr_const, const_to_expr, BINARYOP_LE)) { matched_case = (int) i; } diff --git a/test/test_suite/statements/ranged_switch.c3t b/test/test_suite/statements/ranged_switch.c3t new file mode 100644 index 000000000..50531ce00 --- /dev/null +++ b/test/test_suite/statements/ranged_switch.c3t @@ -0,0 +1,202 @@ +// #target: x64-darwin + +module foo; + +extern fn void printf(char*, ...); + +fn void main() +{ + for (int i = 0; i < 12; i++) + { + switch (i) + { + case 1 .. 3: + printf("1-3\n"); + case 7 .. 277: + printf("7-277 %d\n", i); + case 4 .. 5: + printf("4-5 %d\n", i); + case 6: + printf("6 %d\n", i); + default: + printf("Something else: %d\n", i); + nextcase 5; + } + } + for (int i = 0; i < 12; i++) + { + switch (i) + { + case 1 .. 3: + printf("1-3\n"); + case 4 .. 6: + printf("4-6 %d\n", i); + default: + printf("Something else: %d\n", i); + nextcase 5; + } + } + bool x = false; + switch (x) + { + case true: + printf("Was true!\n"); + case false: + printf("Was false!\n"); + } +} + +/* #expect: foo.ll + +define void @main() #0 { +entry: + %i = alloca i32, align 4 + %switch = alloca i32, align 4 + %i11 = alloca i32, align 4 + %switch15 = alloca i32, align 4 + %x = alloca i8, align 1 + %switch23 = alloca i8, align 1 + store i32 0, i32* %i, align 4 + br label %loop.cond + +loop.cond: ; preds = %switch.exit, %entry + %0 = load i32, i32* %i, align 4 + %lt = icmp slt i32 %0, 12 + br i1 %lt, label %loop.body, label %loop.exit + +loop.body: ; preds = %loop.cond + %1 = load i32, i32* %i, align 4 + store i32 %1, i32* %switch, align 4 + br label %switch.entry + +switch.entry: ; preds = %loop.body + %2 = load i32, i32* %switch, align 4 + %le = icmp sle i32 1, %2 + %ge = icmp sge i32 3, %2 + %3 = and i1 %le, %ge + br i1 %3, label %switch.case, label %next_if + +switch.case: ; preds = %switch.entry + call void (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i32 0, i32 0)) + br label %switch.exit + +next_if: ; preds = %switch.entry + %le1 = icmp sle i32 7, %2 + %ge2 = icmp sge i32 277, %2 + %4 = and i1 %le1, %ge2 + br i1 %4, label %switch.case3, label %next_if4 + +switch.case3: ; preds = %next_if + %5 = load i32, i32* %i, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.1, i32 0, i32 0), i32 %5) + br label %switch.exit + +next_if4: ; preds = %next_if + %le5 = icmp sle i32 4, %2 + %ge6 = icmp sge i32 5, %2 + %6 = and i1 %le5, %ge6 + br i1 %6, label %switch.case7, label %next_if8 + +switch.case7: ; preds = %switch.default, %next_if4 + %7 = load i32, i32* %i, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.2, i32 0, i32 0), i32 %7) + br label %switch.exit + +next_if8: ; preds = %next_if4 + %eq = icmp eq i32 6, %2 + br i1 %eq, label %switch.case9, label %next_if10 + +switch.case9: ; preds = %next_if8 + %8 = load i32, i32* %i, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.3, i32 0, i32 0), i32 %8) + br label %switch.exit + +next_if10: ; preds = %next_if8 + br label %switch.default + +switch.default: ; preds = %next_if10 + %9 = load i32, i32* %i, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @.str.4, i32 0, i32 0), i32 %9) + br label %switch.case7 + +switch.exit: ; preds = %switch.case9, %switch.case7, %switch.case3, %switch.case + %10 = load i32, i32* %i, align 4 + %add = add i32 %10, 1 + store i32 %add, i32* %i, align 4 + br label %loop.cond + +loop.exit: ; preds = %loop.cond + store i32 0, i32* %i11, align 4 + br label %loop.cond12 + +loop.cond12: ; preds = %switch.exit20, %loop.exit + %11 = load i32, i32* %i11, align 4 + %lt13 = icmp slt i32 %11, 12 + br i1 %lt13, label %loop.body14, label %loop.exit22 + +loop.body14: ; preds = %loop.cond12 + %12 = load i32, i32* %i11, align 4 + store i32 %12, i32* %switch15, align 4 + br label %switch.entry16 + +switch.entry16: ; preds = %loop.body14 + %13 = load i32, i32* %switch15, align 4 + switch i32 %13, label %switch.default19 [ + i32 1, label %switch.case17 + i32 2, label %switch.case17 + i32 3, label %switch.case17 + i32 4, label %switch.case18 + i32 5, label %switch.case18 + i32 6, label %switch.case18 + ] + +switch.case17: ; preds = %switch.entry16, %switch.entry16, %switch.entry16 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str.5, i32 0, i32 0)) + br label %switch.exit20 + +switch.case18: ; preds = %switch.default19, %switch.entry16, %switch.entry16, %switch.entry16 + %14 = load i32, i32* %i11, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.6, i32 0, i32 0), i32 %14) + br label %switch.exit20 + +switch.default19: ; preds = %switch.entry16 + %15 = load i32, i32* %i11, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @.str.7, i32 0, i32 0), i32 %15) + br label %switch.case18 + +switch.exit20: ; preds = %switch.case18, %switch.case17 + %16 = load i32, i32* %i11, align 4 + %add21 = add i32 %16, 1 + store i32 %add21, i32* %i11, align 4 + br label %loop.cond12 + +loop.exit22: ; preds = %loop.cond12 + store i8 0, i8* %x, align 1 + %17 = load i8, i8* %x, align 1 + store i8 %17, i8* %switch23, align 1 + br label %switch.entry24 + +switch.entry24: ; preds = %loop.exit22 + %18 = load i8, i8* %switch23, align 1 + %19 = trunc i8 %18 to i1 + %eq25 = icmp eq i1 true, %19 + br i1 %eq25, label %switch.case26, label %next_if27 + +switch.case26: ; preds = %switch.entry24 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.8, i32 0, i32 0)) + br label %switch.exit31 + +next_if27: ; preds = %switch.entry24 + %eq28 = icmp eq i1 false, %19 + br i1 %eq28, label %switch.case29, label %next_if30 + +switch.case29: ; preds = %next_if27 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str.9, i32 0, i32 0)) + br label %switch.exit31 + +next_if30: ; preds = %next_if27 + br label %switch.exit31 + +switch.exit31: ; preds = %next_if30, %switch.case29, %switch.case26 + ret void +}