From 71d95c6597bbca6ef44ba8a2a401c28c19a32bbb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 21 Nov 2016 03:08:24 -0500 Subject: IR: support unwrap maybe operation --- src/all_types.hpp | 20 +++ src/ast_render.cpp | 22 ++- src/codegen.cpp | 53 ++++++ src/ir.cpp | 504 +++++++++++++++++++++-------------------------------- src/ir_print.cpp | 20 +++ 5 files changed, 317 insertions(+), 302 deletions(-) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index b28099ba2f..5212576c71 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1453,6 +1453,8 @@ enum IrInstructionId { IrInstructionIdAsm, IrInstructionIdCompileVar, IrInstructionIdSizeOf, + IrInstructionIdTestNull, + IrInstructionIdUnwrapMaybe, }; struct IrInstruction { @@ -1749,6 +1751,21 @@ struct IrInstructionSizeOf { IrInstruction *type_value; }; +// returns true if nonnull, returns false if null +// this is so that `zeroes` sets maybe values to null +struct IrInstructionTestNull { + IrInstruction base; + + IrInstruction *value; +}; + +struct IrInstructionUnwrapMaybe { + IrInstruction base; + + IrInstruction *value; + bool safety_check_on; +}; + enum LValPurpose { LValPurposeNone, LValPurposeAssign, @@ -1758,4 +1775,7 @@ enum LValPurpose { static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; +static const size_t maybe_child_index = 0; +static const size_t maybe_null_index = 1; + #endif diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 3060670841..37950b3b46 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -706,6 +706,27 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { fprintf(ar->f, "null"); break; } + case NodeTypeIfVarExpr: + { + AstNodeVariableDeclaration *var_decl = &node->data.if_var_expr.var_decl; + const char *var_str = var_decl->is_const ? "const" : "var"; + const char *var_name = buf_ptr(var_decl->symbol); + const char *ptr_str = node->data.if_var_expr.var_is_ptr ? "*" : ""; + fprintf(ar->f, "if (%s %s%s", var_str, ptr_str, var_name); + if (var_decl->type) { + fprintf(ar->f, ": "); + render_node_ungrouped(ar, var_decl->type); + } + fprintf(ar->f, " = "); + render_node_grouped(ar, var_decl->expr); + fprintf(ar->f, ") "); + render_node_grouped(ar, node->data.if_var_expr.then_block); + if (node->data.if_var_expr.else_node) { + fprintf(ar->f, " else "); + render_node_grouped(ar, node->data.if_var_expr.else_node); + } + break; + } case NodeTypeFnDecl: case NodeTypeParamDecl: case NodeTypeErrorValueDecl: @@ -715,7 +736,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { case NodeTypeStructValueField: case NodeTypeUse: case NodeTypeZeroesLiteral: - case NodeTypeIfVarExpr: case NodeTypeForExpr: case NodeTypeSwitchExpr: case NodeTypeSwitchProng: diff --git a/src/codegen.cpp b/src/codegen.cpp index 6ed09db852..661bf8183f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1427,7 +1427,56 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru return LLVMBuildCall(g->builder, asm_fn, param_values, input_and_output_count, ""); } +// 0 - null, 1 - non null +static LLVMValueRef gen_null_bit(CodeGen *g, TypeTableEntry *ptr_type, LLVMValueRef maybe_ptr) { + assert(ptr_type->id == TypeTableEntryIdPointer); + TypeTableEntry *maybe_type = ptr_type->data.pointer.child_type; + assert(maybe_type->id == TypeTableEntryIdMaybe); + TypeTableEntry *child_type = maybe_type->data.maybe.child_type; + LLVMValueRef maybe_struct_ref = get_handle_value(g, maybe_ptr, maybe_type); + bool maybe_is_ptr = (child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn); + if (maybe_is_ptr) { + return LLVMBuildICmp(g->builder, LLVMIntNE, maybe_struct_ref, LLVMConstNull(child_type->type_ref), ""); + } else { + LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, maybe_null_index, ""); + return LLVMBuildLoad(g->builder, maybe_field_ptr, ""); + } +} + +static LLVMValueRef ir_render_test_null(CodeGen *g, IrExecutable *executable, IrInstructionTestNull *instruction) { + TypeTableEntry *ptr_type = instruction->value->type_entry; + assert(ptr_type->id == TypeTableEntryIdPointer); + return gen_null_bit(g, ptr_type, ir_llvm_value(g, instruction->value)); +} + +static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable, + IrInstructionUnwrapMaybe *instruction) +{ + TypeTableEntry *ptr_type = instruction->value->type_entry; + assert(ptr_type->id == TypeTableEntryIdPointer); + TypeTableEntry *maybe_type = ptr_type->data.pointer.child_type; + assert(maybe_type->id == TypeTableEntryIdMaybe); + TypeTableEntry *child_type = maybe_type->data.maybe.child_type; + bool maybe_is_ptr = (child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn); + LLVMValueRef maybe_ptr = ir_llvm_value(g, instruction->value); + if (ir_want_debug_safety(g, &instruction->base) && instruction->safety_check_on) { + LLVMValueRef nonnull_bit = gen_null_bit(g, ptr_type, maybe_ptr); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapMaybeOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapMaybeFail"); + LLVMBuildCondBr(g->builder, nonnull_bit, ok_block, fail_block); + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_debug_safety_crash(g); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + if (maybe_is_ptr) { + return maybe_ptr; + } else { + LLVMValueRef maybe_struct_ref = get_handle_value(g, maybe_ptr, maybe_type); + return LLVMBuildStructGEP(g->builder, maybe_struct_ref, maybe_child_index, ""); + } +} static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, IrInstruction *instruction) { set_debug_source_node(g, instruction->source_node); @@ -1476,6 +1525,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_struct_field_ptr(g, executable, (IrInstructionStructFieldPtr *)instruction); case IrInstructionIdAsm: return ir_render_asm(g, executable, (IrInstructionAsm *)instruction); + case IrInstructionIdTestNull: + return ir_render_test_null(g, executable, (IrInstructionTestNull *)instruction); + case IrInstructionIdUnwrapMaybe: + return ir_render_unwrap_maybe(g, executable, (IrInstructionUnwrapMaybe *)instruction); case IrInstructionIdSwitchBr: case IrInstructionIdPhi: case IrInstructionIdContainerInitList: diff --git a/src/ir.cpp b/src/ir.cpp index 34b6bcaed9..02e9f6c226 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -226,6 +226,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSizeOf *) { return IrInstructionIdSizeOf; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionTestNull *) { + return IrInstructionIdTestNull; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapMaybe *) { + return IrInstructionIdUnwrapMaybe; +} + template static T *ir_create_instruction(IrExecutable *exec, AstNode *source_node) { T *special_instruction = allocate(1); @@ -878,6 +886,44 @@ static IrInstruction *ir_build_size_of(IrBuilder *irb, AstNode *source_node, IrI return &instruction->base; } +static IrInstruction *ir_build_test_null(IrBuilder *irb, AstNode *source_node, IrInstruction *value) { + IrInstructionTestNull *instruction = ir_build_instruction(irb, source_node); + instruction->value = value; + + ir_ref_instruction(value); + + return &instruction->base; +} + +static IrInstruction *ir_build_test_null_from(IrBuilder *irb, IrInstruction *old_instruction, + IrInstruction *value) +{ + IrInstruction *new_instruction = ir_build_test_null(irb, old_instruction->source_node, value); + ir_link_new_instruction(new_instruction, old_instruction); + return new_instruction; +} + +static IrInstruction *ir_build_unwrap_maybe(IrBuilder *irb, AstNode *source_node, IrInstruction *value, + bool safety_check_on) +{ + IrInstructionUnwrapMaybe *instruction = ir_build_instruction(irb, source_node); + instruction->value = value; + instruction->safety_check_on = safety_check_on; + + ir_ref_instruction(value); + + return &instruction->base; +} + +static IrInstruction *ir_build_unwrap_maybe_from(IrBuilder *irb, IrInstruction *old_instruction, + IrInstruction *value, bool safety_check_on) +{ + IrInstruction *new_instruction = ir_build_unwrap_maybe(irb, old_instruction->source_node, + value, safety_check_on); + ir_link_new_instruction(new_instruction, old_instruction); + return new_instruction; +} + static void ir_gen_defers_for_block(IrBuilder *irb, BlockContext *inner_block, BlockContext *outer_block, bool gen_error_defers, bool gen_maybe_defers) { @@ -1164,7 +1210,6 @@ static IrInstruction *ir_gen_null_literal(IrBuilder *irb, AstNode *node) { return ir_build_const_null(irb, node); } - static IrInstruction *ir_gen_decl_ref(IrBuilder *irb, AstNode *source_node, AstNode *decl_node, LValPurpose lval, BlockContext *scope) { @@ -1502,6 +1547,15 @@ static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, AstNode *node, IrUnOp return ir_gen_prefix_op_id_lval(irb, node, op_id, LValPurposeNone); } +static IrInstruction *ir_gen_prefix_op_unwrap_maybe(IrBuilder *irb, AstNode *node) { + AstNode *expr = node->data.prefix_op_expr.primary_expr; + IrInstruction *value = ir_gen_node(irb, expr, node->block_context); + if (value == irb->codegen->invalid_instruction) + return value; + + return ir_build_unwrap_maybe(irb, node, value, true); +} + static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, AstNode *node, LValPurpose lval) { assert(node->type == NodeTypePrefixOpExpr); @@ -1531,7 +1585,7 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, AstNode *node, LValP case PrefixOpUnwrapError: return ir_gen_prefix_op_id(irb, node, IrUnOpUnwrapError); case PrefixOpUnwrapMaybe: - return ir_gen_prefix_op_id(irb, node, IrUnOpUnwrapMaybe); + return ir_gen_prefix_op_unwrap_maybe(irb, node); } zig_unreachable(); } @@ -1883,6 +1937,69 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, AstNode *node) { return ir_build_asm(irb, node, input_list, output_types, return_count, is_volatile); } +static IrInstruction *ir_gen_if_var_expr(IrBuilder *irb, AstNode *node) { + assert(node->type == NodeTypeIfVarExpr); + + AstNodeVariableDeclaration *var_decl = &node->data.if_var_expr.var_decl; + AstNode *expr_node = var_decl->expr; + AstNode *then_node = node->data.if_var_expr.then_block; + AstNode *else_node = node->data.if_var_expr.else_node; + bool var_is_ptr = node->data.if_var_expr.var_is_ptr; + + IrInstruction *expr_value = ir_gen_node_extra(irb, expr_node, node->block_context, LValPurposeAddressOf); + if (expr_value == irb->codegen->invalid_instruction) + return expr_value; + + IrInstruction *is_nonnull_value = ir_build_test_null(irb, node, expr_value); + + IrBasicBlock *then_block = ir_build_basic_block(irb, "MaybeThen"); + IrBasicBlock *else_block = ir_build_basic_block(irb, "MaybeElse"); + IrBasicBlock *endif_block = ir_build_basic_block(irb, "MaybeEndIf"); + + bool is_inline = (node->block_context->fn_entry == nullptr); + ir_build_cond_br(irb, node, is_nonnull_value, then_block, else_block, is_inline); + + ir_set_cursor_at_end(irb, then_block); + IrInstruction *var_type = nullptr; + if (var_decl->type) + var_type = ir_gen_node(irb, var_decl->type, node->block_context); + BlockContext *child_scope = new_block_context(node, node->block_context); + bool is_shadowable = false; + bool is_const = var_decl->is_const; + VariableTableEntry *var = ir_add_local_var(irb, node, child_scope, + var_decl->symbol, is_const, is_const, is_shadowable, is_inline); + IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, node, expr_value, false); + IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, node, var_ptr_value); + ir_build_var_decl(irb, node, var, var_type, var_value); + IrInstruction *then_expr_result = ir_gen_node(irb, then_node, child_scope); + if (then_expr_result == irb->codegen->invalid_instruction) + return then_expr_result; + IrBasicBlock *after_then_block = irb->current_basic_block; + ir_build_br(irb, node, endif_block, is_inline); + + ir_set_cursor_at_end(irb, else_block); + IrInstruction *else_expr_result; + if (else_node) { + else_expr_result = ir_gen_node(irb, else_node, node->block_context); + if (else_expr_result == irb->codegen->invalid_instruction) + return else_expr_result; + } else { + else_expr_result = ir_build_const_void(irb, node); + } + IrBasicBlock *after_else_block = irb->current_basic_block; + ir_build_br(irb, node, endif_block, is_inline); + + ir_set_cursor_at_end(irb, endif_block); + IrInstruction **incoming_values = allocate(2); + incoming_values[0] = then_expr_result; + incoming_values[1] = else_expr_result; + IrBasicBlock **incoming_blocks = allocate(2); + incoming_blocks[0] = after_then_block; + incoming_blocks[1] = after_else_block; + + return ir_build_phi(irb, node, 2, incoming_blocks, incoming_values); +} + static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockContext *block_context, LValPurpose lval) { @@ -1932,10 +2049,11 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockCont return ir_gen_asm_expr(irb, node); case NodeTypeNullLiteral: return ir_gen_null_literal(irb, node); + case NodeTypeIfVarExpr: + return ir_gen_if_var_expr(irb, node); case NodeTypeUnwrapErrorExpr: case NodeTypeDefer: case NodeTypeSliceExpr: - case NodeTypeIfVarExpr: case NodeTypeGoto: case NodeTypeBreak: case NodeTypeContinue: @@ -4488,6 +4606,82 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira, zig_unreachable(); } +static TypeTableEntry *ir_analyze_instruction_test_null(IrAnalyze *ira, + IrInstructionTestNull *test_null_instruction) +{ + IrInstruction *value = test_null_instruction->value->other; + if (value->type_entry->id == TypeTableEntryIdInvalid) + return ira->codegen->builtin_types.entry_invalid; + + // This will be a pointer type because test null IR instruction operates on a pointer to a thing. + TypeTableEntry *ptr_type = value->type_entry; + assert(ptr_type->id == TypeTableEntryIdPointer); + + TypeTableEntry *type_entry = ptr_type->data.pointer.child_type; + if (type_entry->id != TypeTableEntryIdMaybe) { + add_node_error(ira->codegen, test_null_instruction->base.source_node, + buf_sprintf("expected nullable type, found '%s'", buf_ptr(&type_entry->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + if (value->static_value.special != ConstValSpecialRuntime) { + ConstExprValue *maybe_val = value->static_value.data.x_ptr.base_ptr; + assert(value->static_value.data.x_ptr.index == SIZE_MAX); + + if (maybe_val->special != ConstValSpecialRuntime) { + bool depends_on_compile_var = maybe_val->depends_on_compile_var; + ConstExprValue *out_val = ir_build_const_from(ira, &test_null_instruction->base, + depends_on_compile_var); + out_val->data.x_bool = (maybe_val->data.x_maybe == nullptr); + return ira->codegen->builtin_types.entry_bool; + } + } + + ir_build_test_null_from(&ira->new_irb, &test_null_instruction->base, value); + return ira->codegen->builtin_types.entry_bool; +} + +static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, + IrInstructionUnwrapMaybe *unwrap_maybe_instruction) +{ + IrInstruction *value = unwrap_maybe_instruction->value->other; + if (value->type_entry->id == TypeTableEntryIdInvalid) + return ira->codegen->builtin_types.entry_invalid; + + // This will be a pointer type because test null IR instruction operates on a pointer to a thing. + TypeTableEntry *ptr_type = value->type_entry; + assert(ptr_type->id == TypeTableEntryIdPointer); + + TypeTableEntry *type_entry = ptr_type->data.pointer.child_type; + if (type_entry->id == TypeTableEntryIdInvalid) { + return ira->codegen->builtin_types.entry_invalid; + } else if (type_entry->id != TypeTableEntryIdMaybe) { + add_node_error(ira->codegen, unwrap_maybe_instruction->base.source_node, + buf_sprintf("expected nullable type, found '%s'", buf_ptr(&type_entry->name))); + return ira->codegen->builtin_types.entry_invalid; + } + TypeTableEntry *child_type = type_entry->data.maybe.child_type; + TypeTableEntry *result_type = get_pointer_to_type(ira->codegen, child_type, false); + + if (value->static_value.special != ConstValSpecialRuntime) { + ConstExprValue *maybe_val = value->static_value.data.x_ptr.base_ptr; + assert(value->static_value.data.x_ptr.index == SIZE_MAX); + + if (maybe_val->special != ConstValSpecialRuntime) { + bool depends_on_compile_var = maybe_val->depends_on_compile_var; + ConstExprValue *out_val = ir_build_const_from(ira, &unwrap_maybe_instruction->base, + depends_on_compile_var); + out_val->data.x_ptr.base_ptr = maybe_val; + out_val->data.x_ptr.index = SIZE_MAX; + return result_type; + } + } + + ir_build_unwrap_maybe_from(&ira->new_irb, &unwrap_maybe_instruction->base, value, + unwrap_maybe_instruction->safety_check_on); + return result_type; +} + static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -4544,6 +4738,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_compile_var(ira, (IrInstructionCompileVar *)instruction); case IrInstructionIdSizeOf: return ir_analyze_instruction_size_of(ira, (IrInstructionSizeOf *)instruction); + case IrInstructionIdTestNull: + return ir_analyze_instruction_test_null(ira, (IrInstructionTestNull *)instruction); + case IrInstructionIdUnwrapMaybe: + return ir_analyze_instruction_unwrap_maybe(ira, (IrInstructionUnwrapMaybe *)instruction); case IrInstructionIdSwitchBr: case IrInstructionIdCast: case IrInstructionIdContainerInitList: @@ -4654,6 +4852,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdSliceType: case IrInstructionIdCompileVar: case IrInstructionIdSizeOf: + case IrInstructionIdTestNull: + case IrInstructionIdUnwrapMaybe: return false; case IrInstructionIdAsm: { @@ -6266,32 +6466,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) { // return result_type; //} // -//static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context, -// TypeTableEntry *expected_type, AstNode *node) -//{ -// assert(node->type == NodeTypeIfVarExpr); -// -// BlockContext *child_context = new_block_context(node, parent_context); -// -// analyze_variable_declaration_raw(g, import, child_context, node, &node->data.if_var_expr.var_decl, true, -// nullptr, node->data.if_var_expr.var_is_ptr); -// VariableTableEntry *var = node->data.if_var_expr.var_decl.variable; -// if (var->type->id == TypeTableEntryIdInvalid) { -// return g->builtin_types.entry_invalid; -// } -// AstNode *var_expr_node = node->data.if_var_expr.var_decl.expr; -// ConstExprValue *var_const_val = &get_resolved_expr(var_expr_node)->const_val; -// bool cond_is_const = var_const_val->ok; -// bool cond_bool_val = cond_is_const ? (var_const_val->data.x_maybe != nullptr) : false; -// -// -// AstNode **then_node = &node->data.if_var_expr.then_block; -// AstNode **else_node = &node->data.if_var_expr.else_node; -// -// return analyze_if(g, import, child_context, expected_type, -// node, then_node, else_node, cond_is_const, cond_bool_val); -//} -// //static TypeTableEntry *bad_method_call(CodeGen *g, AstNode *node, TypeTableEntry *container_type, // TypeTableEntry *expected_param_type, FnTableEntry *fn_table_entry) //{ @@ -7407,92 +7581,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) { // } //} // -// -//static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import, -// BlockContext *context, AstNode *source_node, -// AstNodeVariableDeclaration *variable_declaration, -// bool expr_is_maybe, AstNode *decl_node, bool var_is_ptr) -//{ -// bool is_const = variable_declaration->is_const; -// bool is_export = (variable_declaration->top_level_decl.visib_mod == VisibModExport); -// bool is_extern = variable_declaration->is_extern; -// -// TypeTableEntry *explicit_type = nullptr; -// if (variable_declaration->type != nullptr) { -// explicit_type = analyze_type_expr(g, import, context, variable_declaration->type); -// if (explicit_type->id == TypeTableEntryIdUnreachable) { -// add_node_error(g, variable_declaration->type, -// buf_sprintf("variable of type 'unreachable' not allowed")); -// explicit_type = g->builtin_types.entry_invalid; -// } -// } -// -// TypeTableEntry *implicit_type = nullptr; -// if (explicit_type && explicit_type->id == TypeTableEntryIdInvalid) { -// implicit_type = explicit_type; -// } else if (variable_declaration->expr) { -// implicit_type = analyze_expression(g, import, context, explicit_type, variable_declaration->expr); -// if (implicit_type->id == TypeTableEntryIdInvalid) { -// // ignore the poison value -// } else if (expr_is_maybe) { -// if (implicit_type->id == TypeTableEntryIdMaybe) { -// if (var_is_ptr) { -// // TODO if the expression is constant, can't get pointer to it -// implicit_type = get_pointer_to_type(g, implicit_type->data.maybe.child_type, false); -// } else { -// implicit_type = implicit_type->data.maybe.child_type; -// } -// } else { -// add_node_error(g, variable_declaration->expr, buf_sprintf("expected maybe type")); -// implicit_type = g->builtin_types.entry_invalid; -// } -// } else if (implicit_type->id == TypeTableEntryIdUnreachable) { -// add_node_error(g, source_node, -// buf_sprintf("variable initialization is unreachable")); -// implicit_type = g->builtin_types.entry_invalid; -// } else if ((!is_const || is_export) && -// (implicit_type->id == TypeTableEntryIdNumLitFloat || -// implicit_type->id == TypeTableEntryIdNumLitInt)) -// { -// add_node_error(g, source_node, buf_sprintf("unable to infer variable type")); -// implicit_type = g->builtin_types.entry_invalid; -// } else if (implicit_type->id == TypeTableEntryIdMetaType && !is_const) { -// add_node_error(g, source_node, buf_sprintf("variable of type 'type' must be constant")); -// implicit_type = g->builtin_types.entry_invalid; -// } -// if (implicit_type->id != TypeTableEntryIdInvalid && !context->fn_entry) { -// ConstExprValue *const_val = &get_resolved_expr(variable_declaration->expr)->const_val; -// if (!const_val->ok) { -// add_node_error(g, first_executing_node(variable_declaration->expr), -// buf_sprintf("global variable initializer requires constant expression")); -// } -// } -// } else if (!is_extern) { -// add_node_error(g, source_node, buf_sprintf("variables must be initialized")); -// implicit_type = g->builtin_types.entry_invalid; -// } -// -// TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type; -// assert(type != nullptr); // should have been caught by the parser -// -// VariableTableEntry *var = add_local_var(g, source_node, import, context, -// variable_declaration->symbol, type, is_const, -// expr_is_maybe ? nullptr : variable_declaration->expr); -// -// variable_declaration->variable = var; -// -// return var; -//} -// -//static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableEntry *import, -// BlockContext *context, TypeTableEntry *expected_type, AstNode *node) -//{ -// AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration; -// return analyze_variable_declaration_raw(g, import, context, node, variable_declaration, -// false, nullptr, false); -//} -// -// //static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, // TypeTableEntry *expected_type, AstNode *node) //{ @@ -8581,64 +8669,6 @@ static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *no // return nullptr; //} // -//static LLVMValueRef gen_unwrap_maybe_expr(CodeGen *g, AstNode *node) { -// assert(node->type == NodeTypeBinOpExpr); -// assert(node->data.bin_op_expr.bin_op == BinOpTypeUnwrapMaybe); -// -// AstNode *op1_node = node->data.bin_op_expr.op1; -// AstNode *op2_node = node->data.bin_op_expr.op2; -// -// LLVMValueRef maybe_struct_ref = gen_expr(g, op1_node); -// -// TypeTableEntry *maybe_type = get_expr_type(op1_node); -// assert(maybe_type->id == TypeTableEntryIdMaybe); -// TypeTableEntry *child_type = maybe_type->data.maybe.child_type; -// -// LLVMValueRef cond_value; -// if (child_type->id == TypeTableEntryIdPointer || -// child_type->id == TypeTableEntryIdFn) -// { -// cond_value = LLVMBuildICmp(g->builder, LLVMIntNE, maybe_struct_ref, -// LLVMConstNull(child_type->type_ref), ""); -// } else { -// LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 1, ""); -// cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, ""); -// } -// -// LLVMBasicBlockRef non_null_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeNonNull"); -// LLVMBasicBlockRef null_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeNull"); -// LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeEnd"); -// -// bool null_reachable = get_expr_type(op2_node)->id != TypeTableEntryIdUnreachable; -// -// LLVMBuildCondBr(g->builder, cond_value, non_null_block, null_block); -// -// LLVMPositionBuilderAtEnd(g->builder, non_null_block); -// LLVMValueRef non_null_result = gen_unwrap_maybe(g, op1_node, maybe_struct_ref); -// LLVMBuildBr(g->builder, end_block); -// LLVMBasicBlockRef post_non_null_result_block = LLVMGetInsertBlock(g->builder); -// -// LLVMPositionBuilderAtEnd(g->builder, null_block); -// LLVMValueRef null_result = gen_expr(g, op2_node); -// if (null_reachable) { -// LLVMBuildBr(g->builder, end_block); -// } -// LLVMBasicBlockRef post_null_result_block = LLVMGetInsertBlock(g->builder); -// -// LLVMPositionBuilderAtEnd(g->builder, end_block); -// if (null_reachable) { -// LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(non_null_result), ""); -// LLVMValueRef incoming_values[2] = {non_null_result, null_result}; -// LLVMBasicBlockRef incoming_blocks[2] = {post_non_null_result_block, post_null_result_block}; -// LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2); -// return phi; -// } else { -// return non_null_result; -// } -// -// return nullptr; -//} -// //static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) { // assert(node->type == NodeTypeUnwrapErrorExpr); // @@ -8914,120 +8944,6 @@ static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *no // } //} // -//static LLVMValueRef gen_if_var_then_block(CodeGen *g, AstNode *node, VariableTableEntry *variable, bool maybe_is_ptr, -// LLVMValueRef init_val, TypeTableEntry *child_type, AstNode *then_node) -//{ -// if (node->data.if_var_expr.var_is_ptr) { -// LLVMValueRef payload_ptr; -// if (maybe_is_ptr) { -// zig_panic("TODO"); -// } else { -// payload_ptr = LLVMBuildStructGEP(g->builder, init_val, 0, ""); -// } -// LLVMBuildStore(g->builder, payload_ptr, variable->value_ref); -// } else { -// LLVMValueRef payload_val; -// if (maybe_is_ptr) { -// payload_val = init_val; -// } else { -// LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, init_val, 0, ""); -// payload_val = get_handle_value(g, payload_ptr, child_type); -// } -// gen_assign_raw(g, node, BinOpTypeAssign, variable->value_ref, payload_val, -// variable->type, child_type); -// } -// gen_var_debug_decl(g, variable); -// -// return gen_expr(g, then_node); -//} -// -//static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) { -// assert(node->type == NodeTypeIfVarExpr); -// assert(node->data.if_var_expr.var_decl.expr); -// -// AstNodeVariableDeclaration *var_decl = &node->data.if_var_expr.var_decl; -// VariableTableEntry *variable = var_decl->variable; -// -// // test if value is the maybe state -// TypeTableEntry *expr_type = get_expr_type(var_decl->expr); -// TypeTableEntry *child_type = expr_type->data.maybe.child_type; -// -// LLVMValueRef init_val = gen_expr(g, var_decl->expr); -// -// -// AstNode *then_node = node->data.if_var_expr.then_block; -// AstNode *else_node = node->data.if_var_expr.else_node; -// bool maybe_is_ptr = child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn; -// -// ConstExprValue *const_val = &get_resolved_expr(var_decl->expr)->const_val; -// if (const_val->ok) { -// if (const_val->data.x_maybe) { -// return gen_if_var_then_block(g, node, variable, maybe_is_ptr, init_val, child_type, then_node); -// } else { -// return gen_expr(g, else_node); -// } -// } -// -// LLVMValueRef cond_value; -// if (maybe_is_ptr) { -// cond_value = LLVMBuildICmp(g->builder, LLVMIntNE, init_val, LLVMConstNull(child_type->type_ref), ""); -// } else { -// LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, init_val, 1, ""); -// cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, ""); -// } -// -// TypeTableEntry *then_type = get_expr_type(then_node); -// TypeTableEntry *else_type = get_expr_type(else_node); -// -// bool use_then_value = type_has_bits(then_type); -// bool use_else_value = type_has_bits(else_type); -// -// LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeThen"); -// LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeElse"); -// -// LLVMBasicBlockRef endif_block; -// bool then_endif_reachable = then_type->id != TypeTableEntryIdUnreachable; -// bool else_endif_reachable = else_type->id != TypeTableEntryIdUnreachable; -// if (then_endif_reachable || else_endif_reachable) { -// endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeEndIf"); -// } -// -// LLVMBuildCondBr(g->builder, cond_value, then_block, else_block); -// -// LLVMPositionBuilderAtEnd(g->builder, then_block); -// LLVMValueRef then_expr_result = gen_if_var_then_block(g, node, variable, maybe_is_ptr, init_val, child_type, then_node); -// -// if (then_endif_reachable) { -// LLVMBuildBr(g->builder, endif_block); -// } -// LLVMBasicBlockRef after_then_block = LLVMGetInsertBlock(g->builder); -// -// -// LLVMPositionBuilderAtEnd(g->builder, else_block); -// LLVMValueRef else_expr_result = gen_expr(g, else_node); -// if (else_endif_reachable) { -// LLVMBuildBr(g->builder, endif_block); -// } -// LLVMBasicBlockRef after_else_block = LLVMGetInsertBlock(g->builder); -// -// if (then_endif_reachable || else_endif_reachable) { -// LLVMPositionBuilderAtEnd(g->builder, endif_block); -// if (use_then_value && use_else_value) { -// LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(then_expr_result), ""); -// LLVMValueRef incoming_values[2] = {then_expr_result, else_expr_result}; -// LLVMBasicBlockRef incoming_blocks[2] = {after_then_block, after_else_block}; -// LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2); -// return phi; -// } else if (use_then_value) { -// return then_expr_result; -// } else if (use_else_value) { -// return else_expr_result; -// } -// } -// -// return nullptr; -//} -// //static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *implicit_return_type) { // assert(block_node->type == NodeTypeBlock); // @@ -9667,20 +9583,6 @@ static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *no // zig_unreachable(); //} // -//static LLVMValueRef gen_unwrap_maybe(CodeGen *g, AstNode *node, LLVMValueRef maybe_struct_ref) { -// TypeTableEntry *type_entry = get_expr_type(node); -// assert(type_entry->id == TypeTableEntryIdMaybe); -// TypeTableEntry *child_type = type_entry->data.maybe.child_type; -// if (child_type->id == TypeTableEntryIdPointer || -// child_type->id == TypeTableEntryIdFn) -// { -// return maybe_struct_ref; -// } else { -// LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 0, ""); -// return get_handle_value(g, maybe_field_ptr, child_type); -// } -//} -// //static size_t get_conditional_defer_count(BlockContext *inner_block, BlockContext *outer_block) { // size_t result = 0; // while (inner_block != outer_block) { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index c65334d0d7..fffe33588a 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -497,6 +497,20 @@ static void ir_print_size_of(IrPrint *irp, IrInstructionSizeOf *instruction) { fprintf(irp->f, ")"); } +static void ir_print_test_null(IrPrint *irp, IrInstructionTestNull *instruction) { + fprintf(irp->f, "*"); + ir_print_other_instruction(irp, instruction->value); + fprintf(irp->f, " == null"); +} + +static void ir_print_unwrap_maybe(IrPrint *irp, IrInstructionUnwrapMaybe *instruction) { + fprintf(irp->f, "&??*"); + ir_print_other_instruction(irp, instruction->value); + if (!instruction->safety_check_on) { + fprintf(irp->f, " // no safety"); + } +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -592,6 +606,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdSizeOf: ir_print_size_of(irp, (IrInstructionSizeOf *)instruction); break; + case IrInstructionIdTestNull: + ir_print_test_null(irp, (IrInstructionTestNull *)instruction); + break; + case IrInstructionIdUnwrapMaybe: + ir_print_unwrap_maybe(irp, (IrInstructionUnwrapMaybe *)instruction); + break; case IrInstructionIdSwitchBr: zig_panic("TODO print more IR instructions"); } -- cgit v1.2.3