From ac6d1674e3384bacd6893191feaf814a23d24b08 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 16 Oct 2016 02:19:01 -0400 Subject: [PATCH] IR working for if statements --- src/all_types.hpp | 26 ++ src/analyze.cpp | 190 +------------ src/codegen.cpp | 397 ++++++++++++++------------ src/ir.cpp | 711 ++++++++++++++++++++++++++++++++++++++++++---- src/ir.hpp | 2 + src/ir_print.cpp | 160 +++++++---- src/parser.cpp | 20 +- 7 files changed, 1018 insertions(+), 488 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 05341f1e2..5d1f5a0b9 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1411,6 +1411,8 @@ struct IrBasicBlock { IrBasicBlock *other; const char *name_hint; size_t debug_id; + size_t ref_count; + LLVMBasicBlockRef llvm_block; }; enum IrInstructionId { @@ -1428,6 +1430,9 @@ enum IrInstructionId { IrInstructionIdConst, IrInstructionIdReturn, IrInstructionIdCast, + IrInstructionIdContainerInitList, + IrInstructionIdContainerInitFields, + IrInstructionIdUnreachable, }; struct IrInstruction { @@ -1586,4 +1591,25 @@ struct IrInstructionCast { LLVMValueRef tmp_ptr; }; +struct IrInstructionContainerInitList { + IrInstruction base; + + IrInstruction *container_type; + size_t item_count; + IrInstruction **items; +}; + +struct IrInstructionContainerInitFields { + IrInstruction base; + + IrInstruction *container_type; + size_t field_count; + Buf **field_names; + IrInstruction **field_values; +}; + +struct IrInstructionUnreachable { + IrInstruction base; +}; + #endif diff --git a/src/analyze.cpp b/src/analyze.cpp index 451b259e2..f8c3f4c35 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -29,7 +29,6 @@ static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry * BlockContext *context, AstNode *node, Buf *err_name); static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node); -static TypeTableEntry *resolve_expr_const_val_as_void(CodeGen *g, AstNode *node); static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, bool depends_on_compile_var); static TypeTableEntry *resolve_expr_const_val_as_generic_fn(CodeGen *g, AstNode *node, @@ -2386,187 +2385,6 @@ static TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf * return nullptr; } -static const char *err_container_init_syntax_name(ContainerInitKind kind) { - switch (kind) { - case ContainerInitKindStruct: - return "struct"; - case ContainerInitKindArray: - return "array"; - } - zig_unreachable(); -} - -static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry *import, - BlockContext *context, AstNode *node) -{ - assert(node->type == NodeTypeContainerInitExpr); - - AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr; - - ContainerInitKind kind = container_init_expr->kind; - - if (container_init_expr->type->type == NodeTypeFieldAccessExpr) { - container_init_expr->type->data.field_access_expr.container_init_expr_node = node; - } - - TypeTableEntry *container_meta_type = analyze_expression(g, import, context, nullptr, - container_init_expr->type); - - if (container_meta_type->id == TypeTableEntryIdInvalid) { - return g->builtin_types.entry_invalid; - } - - if (node->data.container_init_expr.enum_type) { - get_resolved_expr(node)->const_val = get_resolved_expr(container_init_expr->type)->const_val; - return node->data.container_init_expr.enum_type; - } - - TypeTableEntry *container_type = resolve_type(g, container_init_expr->type); - - if (container_type->id == TypeTableEntryIdInvalid) { - return container_type; - } else if (container_type->id == TypeTableEntryIdStruct && - !container_type->data.structure.is_slice && - (kind == ContainerInitKindStruct || (kind == ContainerInitKindArray && - container_init_expr->entries.length == 0))) - { - StructValExprCodeGen *codegen = &container_init_expr->resolved_struct_val_expr; - codegen->type_entry = container_type; - codegen->source_node = node; - - - size_t expr_field_count = container_init_expr->entries.length; - size_t actual_field_count = container_type->data.structure.src_field_count; - - AstNode *non_const_expr_culprit = nullptr; - - size_t *field_use_counts = allocate(actual_field_count); - ConstExprValue *const_val = &get_resolved_expr(node)->const_val; - const_val->ok = true; - const_val->data.x_struct.fields = allocate(actual_field_count); - for (size_t i = 0; i < expr_field_count; i += 1) { - AstNode *val_field_node = container_init_expr->entries.at(i); - assert(val_field_node->type == NodeTypeStructValueField); - - val_field_node->block_context = context; - - TypeStructField *type_field = find_struct_type_field(container_type, - val_field_node->data.struct_val_field.name); - - if (!type_field) { - add_node_error(g, val_field_node, - buf_sprintf("no member named '%s' in '%s'", - buf_ptr(val_field_node->data.struct_val_field.name), buf_ptr(&container_type->name))); - continue; - } - - if (type_field->type_entry->id == TypeTableEntryIdInvalid) { - return g->builtin_types.entry_invalid; - } - - size_t field_index = type_field->src_index; - field_use_counts[field_index] += 1; - if (field_use_counts[field_index] > 1) { - add_node_error(g, val_field_node, buf_sprintf("duplicate field")); - continue; - } - - val_field_node->data.struct_val_field.type_struct_field = type_field; - - analyze_expression(g, import, context, type_field->type_entry, - val_field_node->data.struct_val_field.expr); - - if (const_val->ok) { - ConstExprValue *field_val = - &get_resolved_expr(val_field_node->data.struct_val_field.expr)->const_val; - if (field_val->ok) { - const_val->data.x_struct.fields[field_index] = field_val; - const_val->depends_on_compile_var = const_val->depends_on_compile_var || field_val->depends_on_compile_var; - } else { - const_val->ok = false; - non_const_expr_culprit = val_field_node->data.struct_val_field.expr; - } - } - } - if (!const_val->ok) { - assert(non_const_expr_culprit); - if (context->fn_entry) { - context->fn_entry->struct_val_expr_alloca_list.append(codegen); - } else { - add_node_error(g, non_const_expr_culprit, buf_sprintf("unable to evaluate constant expression")); - } - } - - for (size_t i = 0; i < actual_field_count; i += 1) { - if (field_use_counts[i] == 0) { - add_node_error(g, node, - buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i].name))); - } - } - return container_type; - } else if (container_type->id == TypeTableEntryIdStruct && - container_type->data.structure.is_slice && - kind == ContainerInitKindArray) - { - size_t elem_count = container_init_expr->entries.length; - - TypeTableEntry *pointer_type = container_type->data.structure.fields[0].type_entry; - assert(pointer_type->id == TypeTableEntryIdPointer); - TypeTableEntry *child_type = pointer_type->data.pointer.child_type; - - ConstExprValue *const_val = &get_resolved_expr(node)->const_val; - const_val->ok = true; - const_val->data.x_array.fields = allocate(elem_count); - - for (size_t i = 0; i < elem_count; i += 1) { - AstNode **elem_node = &container_init_expr->entries.at(i); - analyze_expression(g, import, context, child_type, *elem_node); - - if (const_val->ok) { - ConstExprValue *elem_const_val = &get_resolved_expr(*elem_node)->const_val; - if (elem_const_val->ok) { - const_val->data.x_array.fields[i] = elem_const_val; - const_val->depends_on_compile_var = const_val->depends_on_compile_var || - elem_const_val->depends_on_compile_var; - } else { - const_val->ok = false; - } - } - } - - TypeTableEntry *fixed_size_array_type = get_array_type(g, child_type, elem_count); - - StructValExprCodeGen *codegen = &container_init_expr->resolved_struct_val_expr; - codegen->type_entry = fixed_size_array_type; - codegen->source_node = node; - if (!const_val->ok) { - if (!context->fn_entry) { - add_node_error(g, node, - buf_sprintf("unable to evaluate constant expression")); - } else { - context->fn_entry->struct_val_expr_alloca_list.append(codegen); - } - } - - return fixed_size_array_type; - } else if (container_type->id == TypeTableEntryIdArray) { - zig_panic("TODO array container init"); - return container_type; - } else if (container_type->id == TypeTableEntryIdVoid) { - if (container_init_expr->entries.length != 0) { - add_node_error(g, node, buf_sprintf("void expression expects no arguments")); - return g->builtin_types.entry_invalid; - } else { - return resolve_expr_const_val_as_void(g, node); - } - } else { - add_node_error(g, node, - buf_sprintf("type '%s' does not support %s initialization syntax", - buf_ptr(&container_type->name), err_container_init_syntax_name(kind))); - return g->builtin_types.entry_invalid; - } -} - static bool is_container(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: @@ -2928,12 +2746,6 @@ static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *i return return_type; } -static TypeTableEntry *resolve_expr_const_val_as_void(CodeGen *g, AstNode *node) { - Expr *expr = get_resolved_expr(node); - expr->const_val.ok = true; - return g->builtin_types.entry_void; -} - static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, TypeTableEntry *type, bool depends_on_compile_var) { @@ -5331,7 +5143,7 @@ static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEn return_type = analyze_field_access_expr(g, import, context, expected_type, node); break; case NodeTypeContainerInitExpr: - return_type = analyze_container_init_expr(g, import, context, node); + zig_panic("analyze container init moved to ir.cpp"); break; case NodeTypeNumberLiteral: return_type = analyze_number_literal_expr(g, import, context, expected_type, node); diff --git a/src/codegen.cpp b/src/codegen.cpp index ebf2d76b8..59ba859ba 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -12,6 +12,7 @@ #include "errmsg.hpp" #include "error.hpp" #include "hash_map.hpp" +#include "ir.hpp" #include "link.hpp" #include "os.hpp" #include "parseh.hpp" @@ -513,18 +514,6 @@ static LLVMValueRef gen_truncate(CodeGen *g, AstNode *node) { return LLVMBuildTrunc(g->builder, src_val, dest_type->type_ref, ""); } -static LLVMValueRef gen_unreachable(CodeGen *g, AstNode *node) { - assert(node->type == NodeTypeFnCallExpr); - - if (want_debug_safety(g, node) || g->is_test_build) { - gen_debug_safety_crash(g); - } else { - LLVMBuildUnreachable(g->builder); - } - - return nullptr; -} - static LLVMValueRef gen_shl_with_overflow(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); @@ -711,7 +700,7 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { case BuiltinFnIdTruncate: return gen_truncate(g, node); case BuiltinFnIdUnreachable: - return gen_unreachable(g, node); + zig_panic("moved to ir render"); case BuiltinFnIdSetFnTest: case BuiltinFnIdSetFnVisible: case BuiltinFnIdSetFnStaticEval: @@ -1334,154 +1323,6 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, TypeTableEntry *type_entry, return result; } -static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { - assert(node->type == NodeTypePrefixOpExpr); - assert(node->data.prefix_op_expr.primary_expr); - - AstNode *expr_node = node->data.prefix_op_expr.primary_expr; - TypeTableEntry *expr_type = get_expr_type(expr_node); - - PrefixOp op = node->data.prefix_op_expr.prefix_op; - - switch (op) { - case PrefixOpInvalid: - zig_unreachable(); - case PrefixOpNegation: - case PrefixOpNegationWrap: - { - LLVMValueRef expr = gen_expr(g, expr_node); - if (expr_type->id == TypeTableEntryIdFloat) { - return LLVMBuildFNeg(g->builder, expr, ""); - } else if (expr_type->id == TypeTableEntryIdInt) { - if (op == PrefixOpNegationWrap) { - return LLVMBuildNeg(g->builder, expr, ""); - } else if (want_debug_safety(g, expr_node)) { - LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(expr)); - return gen_overflow_op(g, expr_type, AddSubMulSub, zero, expr); - } else if (expr_type->data.integral.is_signed) { - return LLVMBuildNSWNeg(g->builder, expr, ""); - } else { - return LLVMBuildNUWNeg(g->builder, expr, ""); - } - } else { - zig_unreachable(); - } - } - case PrefixOpBoolNot: - { - LLVMValueRef expr = gen_expr(g, expr_node); - LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(expr)); - return LLVMBuildICmp(g->builder, LLVMIntEQ, expr, zero, ""); - } - case PrefixOpBinNot: - { - LLVMValueRef expr = gen_expr(g, expr_node); - return LLVMBuildNot(g->builder, expr, ""); - } - case PrefixOpAddressOf: - case PrefixOpConstAddressOf: - { - TypeTableEntry *lvalue_type; - return gen_lvalue(g, node, expr_node, &lvalue_type); - } - - case PrefixOpDereference: - { - LLVMValueRef expr = gen_expr(g, expr_node); - assert(expr_type->id == TypeTableEntryIdPointer); - if (!type_has_bits(expr_type)) { - return nullptr; - } else { - TypeTableEntry *child_type = expr_type->data.pointer.child_type; - return get_handle_value(g, expr, child_type); - } - } - case PrefixOpMaybe: - { - zig_panic("TODO codegen PrefixOpMaybe"); - } - case PrefixOpError: - { - zig_panic("TODO codegen PrefixOpError"); - } - case PrefixOpUnwrapError: - { - LLVMValueRef expr_val = gen_expr(g, expr_node); - TypeTableEntry *expr_type = get_expr_type(expr_node); - assert(expr_type->id == TypeTableEntryIdErrorUnion); - TypeTableEntry *child_type = expr_type->data.error.child_type; - - if (want_debug_safety(g, node)) { - LLVMValueRef err_val; - if (type_has_bits(child_type)) { - LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 0, ""); - err_val = LLVMBuildLoad(g->builder, err_val_ptr, ""); - } else { - err_val = expr_val; - } - LLVMValueRef zero = LLVMConstNull(g->err_tag_type->type_ref); - LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, ""); - LLVMBasicBlockRef err_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrError"); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrOk"); - LLVMBuildCondBr(g->builder, cond_val, ok_block, err_block); - - LLVMPositionBuilderAtEnd(g->builder, err_block); - gen_debug_safety_crash(g); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - } - - if (type_has_bits(child_type)) { - LLVMValueRef child_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 1, ""); - return get_handle_value(g, child_val_ptr, child_type); - } else { - return nullptr; - } - } - case PrefixOpUnwrapMaybe: - { - LLVMValueRef expr_val = gen_expr(g, expr_node); - - TypeTableEntry *expr_type = get_expr_type(expr_node); - assert(expr_type->id == TypeTableEntryIdMaybe); - TypeTableEntry *child_type = expr_type->data.maybe.child_type; - - if (want_debug_safety(g, node)) { - LLVMValueRef cond_val; - if (child_type->id == TypeTableEntryIdPointer || - child_type->id == TypeTableEntryIdFn) - { - cond_val = LLVMBuildICmp(g->builder, LLVMIntNE, expr_val, - LLVMConstNull(child_type->type_ref), ""); - } else { - LLVMValueRef maybe_null_ptr = LLVMBuildStructGEP(g->builder, expr_val, 1, ""); - cond_val = LLVMBuildLoad(g->builder, maybe_null_ptr, ""); - } - - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapMaybeOk"); - LLVMBasicBlockRef null_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapMaybeNull"); - LLVMBuildCondBr(g->builder, cond_val, ok_block, null_block); - - LLVMPositionBuilderAtEnd(g->builder, null_block); - gen_debug_safety_crash(g); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - } - - - if (child_type->id == TypeTableEntryIdPointer || - child_type->id == TypeTableEntryIdFn) - { - return expr_val; - } else { - LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, expr_val, 0, ""); - return get_handle_value(g, maybe_field_ptr, child_type); - } - } - } - zig_unreachable(); -} - static LLVMValueRef gen_div(CodeGen *g, AstNode *source_node, LLVMValueRef val1, LLVMValueRef val2, TypeTableEntry *type_entry, bool exact) { @@ -2468,8 +2309,20 @@ static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) { return nullptr; } +static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) { + if (!type_has_bits(instruction->type_entry)) + return nullptr; + if (!instruction->llvm_value) { + assert(instruction->static_value.ok); + assert(instruction->type_entry); + instruction->llvm_value = gen_const_val(g, instruction->type_entry, &instruction->static_value); + assert(instruction->llvm_value); + } + return instruction->llvm_value; +} + static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) { - LLVMBuildRet(g->builder, return_instruction->value->llvm_value); + LLVMBuildRet(g->builder, ir_llvm_value(g, return_instruction->value)); return nullptr; } @@ -2488,8 +2341,8 @@ static LLVMValueRef ir_render_bin_op_bool(CodeGen *g, IrExecutable *executable, IrInstructionBinOp *bin_op_instruction) { IrBinOp op_id = bin_op_instruction->op_id; - LLVMValueRef op1 = bin_op_instruction->op1->llvm_value; - LLVMValueRef op2 = bin_op_instruction->op2->llvm_value; + LLVMValueRef op1 = ir_llvm_value(g, bin_op_instruction->op1); + LLVMValueRef op2 = ir_llvm_value(g, bin_op_instruction->op2); if (op_id == IrBinOpBoolOr) { return LLVMBuildOr(g->builder, op1, op2, ""); } else if (op_id == IrBinOpBoolAnd) { @@ -2508,18 +2361,21 @@ static LLVMValueRef ir_render_bin_op_add(CodeGen *g, IrExecutable *executable, assert(op1->type_entry == op2->type_entry); + LLVMValueRef op1_value = ir_llvm_value(g, op1); + LLVMValueRef op2_value = ir_llvm_value(g, op2); + if (op1->type_entry->id == TypeTableEntryIdFloat) { - return LLVMBuildFAdd(g->builder, op1->llvm_value, op2->llvm_value, ""); + return LLVMBuildFAdd(g->builder, op1_value, op2_value, ""); } else if (op1->type_entry->id == TypeTableEntryIdInt) { bool is_wrapping = (op_id == IrBinOpAddWrap); if (is_wrapping) { - return LLVMBuildAdd(g->builder, op1->llvm_value, op2->llvm_value, ""); + return LLVMBuildAdd(g->builder, op1_value, op2_value, ""); } else if (ir_want_debug_safety(g, &bin_op_instruction->base)) { - return gen_overflow_op(g, op1->type_entry, AddSubMulAdd, op1->llvm_value, op2->llvm_value); + return gen_overflow_op(g, op1->type_entry, AddSubMulAdd, op1_value, op2_value); } else if (op1->type_entry->data.integral.is_signed) { - return LLVMBuildNSWAdd(g->builder, op1->llvm_value, op2->llvm_value, ""); + return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, ""); } else { - return LLVMBuildNUWAdd(g->builder, op1->llvm_value, op2->llvm_value, ""); + return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, ""); } } else { zig_unreachable(); @@ -2570,7 +2426,7 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, { TypeTableEntry *actual_type = cast_instruction->value->type_entry; TypeTableEntry *wanted_type = cast_instruction->base.type_entry; - LLVMValueRef expr_val = cast_instruction->value->llvm_value; + LLVMValueRef expr_val = ir_llvm_value(g, cast_instruction->value); assert(expr_val); switch (cast_instruction->cast_op) { @@ -2792,13 +2648,176 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, zig_unreachable(); } +static LLVMValueRef ir_render_unreachable(CodeGen *g, IrExecutable *executable, + IrInstructionUnreachable *unreachable_instruction) +{ + if (ir_want_debug_safety(g, &unreachable_instruction->base) || g->is_test_build) { + gen_debug_safety_crash(g); + } else { + LLVMBuildUnreachable(g->builder); + } + return nullptr; +} + +static LLVMValueRef ir_render_cond_br(CodeGen *g, IrExecutable *executable, + IrInstructionCondBr *cond_br_instruction) +{ + LLVMBuildCondBr(g->builder, + ir_llvm_value(g, cond_br_instruction->condition), + cond_br_instruction->then_block->llvm_block, + cond_br_instruction->else_block->llvm_block); + return nullptr; +} + +static LLVMValueRef ir_render_br(CodeGen *g, IrExecutable *executable, IrInstructionBr *br_instruction) { + LLVMBuildBr(g->builder, br_instruction->dest_block->llvm_block); + return nullptr; +} + +static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInstructionUnOp *un_op_instruction) { + IrUnOp op_id = un_op_instruction->op_id; + LLVMValueRef expr = ir_llvm_value(g, un_op_instruction->value); + TypeTableEntry *expr_type = un_op_instruction->value->type_entry; + + switch (op_id) { + case IrUnOpInvalid: + zig_unreachable(); + case IrUnOpNegation: + case IrUnOpNegationWrap: + { + if (expr_type->id == TypeTableEntryIdFloat) { + return LLVMBuildFNeg(g->builder, expr, ""); + } else if (expr_type->id == TypeTableEntryIdInt) { + if (op_id == IrUnOpNegationWrap) { + return LLVMBuildNeg(g->builder, expr, ""); + } else if (ir_want_debug_safety(g, &un_op_instruction->base)) { + LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(expr)); + return gen_overflow_op(g, expr_type, AddSubMulSub, zero, expr); + } else if (expr_type->data.integral.is_signed) { + return LLVMBuildNSWNeg(g->builder, expr, ""); + } else { + return LLVMBuildNUWNeg(g->builder, expr, ""); + } + } else { + zig_unreachable(); + } + } + case IrUnOpBoolNot: + { + LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(expr)); + return LLVMBuildICmp(g->builder, LLVMIntEQ, expr, zero, ""); + } + case IrUnOpBinNot: + return LLVMBuildNot(g->builder, expr, ""); + case IrUnOpAddressOf: + case IrUnOpConstAddressOf: + zig_panic("TODO address of codegen"); + //{ + // TypeTableEntry *lvalue_type; + // return gen_lvalue(g, node, expr_node, &lvalue_type); + //} + case IrUnOpDereference: + { + assert(expr_type->id == TypeTableEntryIdPointer); + if (!type_has_bits(expr_type)) { + return nullptr; + } else { + TypeTableEntry *child_type = expr_type->data.pointer.child_type; + return get_handle_value(g, expr, child_type); + } + } + case IrUnOpError: + { + zig_panic("TODO codegen PrefixOpError"); + } + case IrUnOpMaybe: + { + zig_panic("TODO codegen PrefixOpMaybe"); + } + case IrUnOpUnwrapError: + { + assert(expr_type->id == TypeTableEntryIdErrorUnion); + TypeTableEntry *child_type = expr_type->data.error.child_type; + + if (ir_want_debug_safety(g, &un_op_instruction->base)) { + LLVMValueRef err_val; + if (type_has_bits(child_type)) { + LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, expr, 0, ""); + err_val = LLVMBuildLoad(g->builder, err_val_ptr, ""); + } else { + err_val = expr; + } + LLVMValueRef zero = LLVMConstNull(g->err_tag_type->type_ref); + LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, ""); + LLVMBasicBlockRef err_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrError"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrOk"); + LLVMBuildCondBr(g->builder, cond_val, ok_block, err_block); + + LLVMPositionBuilderAtEnd(g->builder, err_block); + gen_debug_safety_crash(g); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + + if (type_has_bits(child_type)) { + LLVMValueRef child_val_ptr = LLVMBuildStructGEP(g->builder, expr, 1, ""); + return get_handle_value(g, child_val_ptr, child_type); + } else { + return nullptr; + } + } + case IrUnOpUnwrapMaybe: + { + assert(expr_type->id == TypeTableEntryIdMaybe); + TypeTableEntry *child_type = expr_type->data.maybe.child_type; + + if (ir_want_debug_safety(g, &un_op_instruction->base)) { + LLVMValueRef cond_val; + if (child_type->id == TypeTableEntryIdPointer || + child_type->id == TypeTableEntryIdFn) + { + cond_val = LLVMBuildICmp(g->builder, LLVMIntNE, expr, + LLVMConstNull(child_type->type_ref), ""); + } else { + LLVMValueRef maybe_null_ptr = LLVMBuildStructGEP(g->builder, expr, 1, ""); + cond_val = LLVMBuildLoad(g->builder, maybe_null_ptr, ""); + } + + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapMaybeOk"); + LLVMBasicBlockRef null_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapMaybeNull"); + LLVMBuildCondBr(g->builder, cond_val, ok_block, null_block); + + LLVMPositionBuilderAtEnd(g->builder, null_block); + gen_debug_safety_crash(g); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + + + if (child_type->id == TypeTableEntryIdPointer || + child_type->id == TypeTableEntryIdFn) + { + return expr; + } else { + LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, expr, 0, ""); + return get_handle_value(g, maybe_field_ptr, child_type); + } + } + case IrUnOpErrorReturn: + case IrUnOpMaybeReturn: + zig_panic("TODO codegen more un ops"); + } + + zig_unreachable(); +} + static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, IrInstruction *instruction) { set_debug_source_node(g, instruction->source_node); + switch (instruction->id) { case IrInstructionIdInvalid: - zig_unreachable(); case IrInstructionIdConst: - return gen_const_val(g, instruction->type_entry, &instruction->static_value); + zig_unreachable(); case IrInstructionIdReturn: return ir_render_return(g, executable, (IrInstructionReturn *)instruction); case IrInstructionIdLoadVar: @@ -2807,14 +2826,21 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_bin_op(g, executable, (IrInstructionBinOp *)instruction); case IrInstructionIdCast: return ir_render_cast(g, executable, (IrInstructionCast *)instruction); + case IrInstructionIdUnreachable: + return ir_render_unreachable(g, executable, (IrInstructionUnreachable *)instruction); case IrInstructionIdCondBr: + return ir_render_cond_br(g, executable, (IrInstructionCondBr *)instruction); case IrInstructionIdBr: + return ir_render_br(g, executable, (IrInstructionBr *)instruction); + case IrInstructionIdUnOp: + return ir_render_un_op(g, executable, (IrInstructionUnOp *)instruction); case IrInstructionIdSwitchBr: case IrInstructionIdPhi: case IrInstructionIdStoreVar: case IrInstructionIdCall: case IrInstructionIdBuiltinCall: - case IrInstructionIdUnOp: + case IrInstructionIdContainerInitList: + case IrInstructionIdContainerInitFields: zig_panic("TODO render more IR instructions to LLVM"); } zig_unreachable(); @@ -2826,8 +2852,14 @@ static void ir_render(CodeGen *g, FnTableEntry *fn_entry) { assert(executable->basic_block_list.length > 0); for (size_t block_i = 0; block_i < executable->basic_block_list.length; block_i += 1) { IrBasicBlock *current_block = executable->basic_block_list.at(block_i); + if (current_block->ref_count == 0) + continue; + assert(current_block->llvm_block); + LLVMPositionBuilderAtEnd(g->builder, current_block->llvm_block); for (size_t instr_i = 0; instr_i < current_block->instruction_list.length; instr_i += 1) { IrInstruction *instruction = current_block->instruction_list.at(instr_i); + if (instruction->ref_count == 0 && !ir_has_side_effects(instruction)) + continue; instruction->llvm_value = ir_render_instruction(g, executable, instruction); } } @@ -3622,7 +3654,7 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { case NodeTypeVariableDeclaration: return gen_var_decl_expr(g, node); case NodeTypePrefixOpExpr: - return gen_prefix_op_expr(g, node); + zig_panic("moved to ir render"); case NodeTypeFnCallExpr: return gen_fn_call_expr(g, node); case NodeTypeArrayAccessExpr: @@ -4018,14 +4050,15 @@ static void generate_error_name_table(CodeGen *g) { LLVMSetUnnamedAddr(g->err_name_table, true); } -static void build_label_blocks(CodeGen *g, FnTableEntry *fn) { - LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn->fn_value, "entry"); - for (size_t i = 0; i < fn->all_labels.length; i += 1) { - LabelTableEntry *label = fn->all_labels.at(i); - Buf *name = label->decl_node->data.label.name; - label->basic_block = LLVMAppendBasicBlock(fn->fn_value, buf_ptr(name)); +static void build_all_basic_blocks(CodeGen *g, FnTableEntry *fn) { + IrExecutable *executable = &fn->analyzed_executable; + assert(executable->basic_block_list.length > 0); + for (size_t block_i = 0; block_i < executable->basic_block_list.length; block_i += 1) { + IrBasicBlock *bb = executable->basic_block_list.at(block_i); + bb->llvm_block = LLVMAppendBasicBlock(fn->fn_value, bb->name_hint); } - LLVMPositionBuilderAtEnd(g->builder, entry_block); + IrBasicBlock *entry_bb = executable->basic_block_list.at(0); + LLVMPositionBuilderAtEnd(g->builder, entry_bb->llvm_block); } static void gen_global_var(CodeGen *g, VariableTableEntry *var, LLVMValueRef init_val, @@ -4252,7 +4285,7 @@ static void do_code_gen(CodeGen *g) { assert(proto_node->type == NodeTypeFnProto); AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; - build_label_blocks(g, fn_table_entry); + build_all_basic_blocks(g, fn_table_entry); // Set up debug info for blocks diff --git a/src/ir.cpp b/src/ir.cpp index 35f2a6c62..cd54324d4 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -40,6 +40,29 @@ static size_t exec_next_debug_id(IrExecutable *exec) { return result; } +static void ir_link_new_instruction(IrInstruction *new_instruction, IrInstruction *old_instruction) { + new_instruction->other = old_instruction; + old_instruction->other = new_instruction; +} + +static void ir_link_new_bb(IrBasicBlock *new_bb, IrBasicBlock *old_bb) { + new_bb->other = old_bb; + old_bb->other = new_bb; +} + +static void ir_ref_bb(IrBasicBlock *bb) { + bb->ref_count += 1; +} + +static void ir_unref_bb(IrBasicBlock *bb) { + bb->ref_count -= 1; + assert(bb->ref_count != SIZE_MAX); +} + +static void ir_ref_instruction(IrInstruction *instruction) { + instruction->ref_count += 1; +} + static IrBasicBlock *ir_build_basic_block(IrBuilder *irb, const char *name_hint) { IrBasicBlock *result = allocate(1); result->name_hint = name_hint; @@ -48,10 +71,20 @@ static IrBasicBlock *ir_build_basic_block(IrBuilder *irb, const char *name_hint) return result; } +static IrBasicBlock *ir_build_bb_from(IrBuilder *irb, IrBasicBlock *other_bb) { + IrBasicBlock *new_bb = ir_build_basic_block(irb, other_bb->name_hint); + ir_link_new_bb(new_bb, other_bb); + return new_bb; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionCondBr *) { return IrInstructionIdCondBr; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionBr *) { + return IrInstructionIdBr; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionSwitchBr *) { return IrInstructionIdSwitchBr; } @@ -96,8 +129,16 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCast *) { return IrInstructionIdCast; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionBr *) { - return IrInstructionIdBr; +static constexpr IrInstructionId ir_instruction_id(IrInstructionContainerInitList *) { + return IrInstructionIdContainerInitList; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionContainerInitFields *) { + return IrInstructionIdContainerInitFields; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionUnreachable *) { + return IrInstructionIdUnreachable; } template @@ -123,6 +164,10 @@ static IrInstruction *ir_build_cast(IrBuilder *irb, AstNode *source_node, IrInst cast_instruction->dest_type = dest_type; cast_instruction->value = value; cast_instruction->cast_op = cast_op; + + ir_ref_instruction(dest_type); + ir_ref_instruction(value); + return &cast_instruction->base; } @@ -135,19 +180,44 @@ static IrInstruction *ir_build_cond_br(IrBuilder *irb, AstNode *source_node, IrI cond_br_instruction->condition = condition; cond_br_instruction->then_block = then_block; cond_br_instruction->else_block = else_block; + + ir_ref_instruction(condition); + ir_ref_bb(then_block); + ir_ref_bb(else_block); + return &cond_br_instruction->base; } +static IrInstruction *ir_build_cond_br_from(IrBuilder *irb, IrInstruction *old_instruction, + IrInstruction *condition, IrBasicBlock *then_block, IrBasicBlock *else_block) +{ + IrInstruction *new_instruction = ir_build_cond_br(irb, old_instruction->source_node, + condition, then_block, else_block); + ir_link_new_instruction(new_instruction, old_instruction); + return new_instruction; +} + static IrInstruction *ir_build_return(IrBuilder *irb, AstNode *source_node, IrInstruction *return_value) { IrInstructionReturn *return_instruction = ir_build_instruction(irb, source_node); return_instruction->base.type_entry = irb->codegen->builtin_types.entry_unreachable; return_instruction->base.static_value.ok = true; return_instruction->value = return_value; + + ir_ref_instruction(return_value); + return &return_instruction->base; } -static IrInstruction *ir_build_const(IrBuilder *irb, AstNode *source_node, TypeTableEntry *type_entry) { - IrInstructionConst *const_instruction = ir_build_instruction(irb, source_node); +static IrInstruction *ir_build_return_from(IrBuilder *irb, IrInstruction *old_instruction, + IrInstruction *return_value) +{ + IrInstruction *new_instruction = ir_build_return(irb, old_instruction->source_node, return_value); + ir_link_new_instruction(new_instruction, old_instruction); + return new_instruction; +} + +static IrInstruction *ir_create_const(IrBuilder *irb, AstNode *source_node, TypeTableEntry *type_entry) { + IrInstructionConst *const_instruction = ir_create_instruction(irb->exec, source_node); const_instruction->base.type_entry = type_entry; const_instruction->base.static_value.ok = true; return &const_instruction->base; @@ -206,9 +276,21 @@ static IrInstruction *ir_build_bin_op(IrBuilder *irb, AstNode *source_node, IrBi bin_op_instruction->op_id = op_id; bin_op_instruction->op1 = op1; bin_op_instruction->op2 = op2; + + ir_ref_instruction(op1); + ir_ref_instruction(op2); + return &bin_op_instruction->base; } +static IrInstruction *ir_build_bin_op_from(IrBuilder *irb, IrInstruction *old_instruction, IrBinOp op_id, + IrInstruction *op1, IrInstruction *op2) +{ + IrInstruction *new_instruction = ir_build_bin_op(irb, old_instruction->source_node, op_id, op1, op2); + ir_link_new_instruction(new_instruction, old_instruction); + return new_instruction; +} + static IrInstruction *ir_build_load_var(IrBuilder *irb, AstNode *source_node, VariableTableEntry *var) { IrInstructionLoadVar *load_var_instruction = ir_build_instruction(irb, source_node); load_var_instruction->base.type_entry = var->type; @@ -216,6 +298,15 @@ static IrInstruction *ir_build_load_var(IrBuilder *irb, AstNode *source_node, Va return &load_var_instruction->base; } +static IrInstruction *ir_build_load_var_from(IrBuilder *irb, IrInstruction *old_instruction, + VariableTableEntry *var) +{ + IrInstruction *new_instruction = ir_build_load_var(irb, old_instruction->source_node, var); + ir_link_new_instruction(new_instruction, old_instruction); + return new_instruction; + +} + static IrInstruction *ir_build_call(IrBuilder *irb, AstNode *source_node, IrInstruction *fn, size_t arg_count, IrInstruction **args) { @@ -223,6 +314,12 @@ static IrInstruction *ir_build_call(IrBuilder *irb, AstNode *source_node, call_instruction->fn = fn; call_instruction->arg_count = arg_count; call_instruction->args = args; + + ir_ref_instruction(fn); + for (size_t i = 0; i < arg_count; i += 1) { + ir_ref_instruction(args[i]); + } + return &call_instruction->base; } @@ -232,6 +329,11 @@ static IrInstruction *ir_build_builtin_call(IrBuilder *irb, AstNode *source_node IrInstructionBuiltinCall *call_instruction = ir_build_instruction(irb, source_node); call_instruction->fn = fn; call_instruction->args = args; + + for (size_t i = 0; i < fn->param_count; i += 1) { + ir_ref_instruction(args[i]); + } + return &call_instruction->base; } @@ -242,22 +344,105 @@ static IrInstruction *ir_build_phi(IrBuilder *irb, AstNode *source_node, phi_instruction->incoming_count = incoming_count; phi_instruction->incoming_blocks = incoming_blocks; phi_instruction->incoming_values = incoming_values; + + for (size_t i = 0; i < incoming_count; i += 1) { + ir_ref_instruction(incoming_values[i]); + } + return &phi_instruction->base; } +static IrInstruction *ir_build_phi_from(IrBuilder *irb, IrInstruction *old_instruction, + size_t incoming_count, IrBasicBlock **incoming_blocks, IrInstruction **incoming_values) +{ + IrInstruction *new_instruction = ir_build_phi(irb, old_instruction->source_node, + incoming_count, incoming_blocks, incoming_values); + ir_link_new_instruction(new_instruction, old_instruction); + return new_instruction; +} + static IrInstruction *ir_build_br(IrBuilder *irb, AstNode *source_node, IrBasicBlock *dest_block) { IrInstructionBr *br_instruction = ir_build_instruction(irb, source_node); br_instruction->dest_block = dest_block; + + ir_ref_bb(dest_block); + return &br_instruction->base; } +static IrInstruction *ir_build_br_from(IrBuilder *irb, IrInstruction *old_instruction, IrBasicBlock *dest_block) { + IrInstruction *new_instruction = ir_build_br(irb, old_instruction->source_node, dest_block); + ir_link_new_instruction(new_instruction, old_instruction); + return new_instruction; +} + static IrInstruction *ir_build_un_op(IrBuilder *irb, AstNode *source_node, IrUnOp op_id, IrInstruction *value) { IrInstructionUnOp *br_instruction = ir_build_instruction(irb, source_node); br_instruction->op_id = op_id; br_instruction->value = value; + + ir_ref_instruction(value); + return &br_instruction->base; } +static IrInstruction *ir_build_un_op_from(IrBuilder *irb, IrInstruction *old_instruction, + IrUnOp op_id, IrInstruction *value) +{ + IrInstruction *new_instruction = ir_build_un_op(irb, old_instruction->source_node, op_id, value); + ir_link_new_instruction(new_instruction, old_instruction); + return new_instruction; +} + +static IrInstruction *ir_build_container_init_list(IrBuilder *irb, AstNode *source_node, + IrInstruction *container_type, size_t item_count, IrInstruction **items) +{ + IrInstructionContainerInitList *container_init_list_instruction = + ir_build_instruction(irb, source_node); + container_init_list_instruction->container_type = container_type; + container_init_list_instruction->item_count = item_count; + container_init_list_instruction->items = items; + + ir_ref_instruction(container_type); + for (size_t i = 0; i < item_count; i += 1) { + ir_ref_instruction(items[i]); + } + + return &container_init_list_instruction->base; +} + +static IrInstruction *ir_build_container_init_fields(IrBuilder *irb, AstNode *source_node, + IrInstruction *container_type, size_t field_count, Buf **field_names, IrInstruction **field_values) +{ + IrInstructionContainerInitFields *container_init_fields_instruction = + ir_build_instruction(irb, source_node); + container_init_fields_instruction->container_type = container_type; + container_init_fields_instruction->field_count = field_count; + container_init_fields_instruction->field_names = field_names; + container_init_fields_instruction->field_values = field_values; + + ir_ref_instruction(container_type); + for (size_t i = 0; i < field_count; i += 1) { + ir_ref_instruction(field_values[i]); + } + + return &container_init_fields_instruction->base; +} + +static IrInstruction *ir_build_unreachable(IrBuilder *irb, AstNode *source_node) { + IrInstructionUnreachable *unreachable_instruction = + ir_build_instruction(irb, source_node); + unreachable_instruction->base.static_value.ok = true; + unreachable_instruction->base.type_entry = irb->codegen->builtin_types.entry_unreachable; + return &unreachable_instruction->base; +} + +static IrInstruction *ir_build_unreachable_from(IrBuilder *irb, IrInstruction *old_instruction) { + IrInstruction *new_instruction = ir_build_unreachable(irb, old_instruction->source_node); + ir_link_new_instruction(new_instruction, old_instruction); + return new_instruction; +} + //static size_t get_conditional_defer_count(BlockContext *inner_block, BlockContext *outer_block) { // size_t result = 0; @@ -524,6 +709,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, AstNode *node) { builtin_fn->ref_count += 1; + if (builtin_fn->id == BuiltinFnIdUnreachable) { + return ir_build_unreachable(irb, node); + } + IrInstruction **args = allocate(actual_param_count); for (size_t i = 0; i < actual_param_count; i += 1) { AstNode *arg_node = node->data.fn_call_expr.params.at(i); @@ -649,6 +838,51 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, AstNode *node) { zig_unreachable(); } +static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, AstNode *node) { + assert(node->type == NodeTypeContainerInitExpr); + + AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr; + ContainerInitKind kind = container_init_expr->kind; + + IrInstruction *container_type = ir_gen_node(irb, container_init_expr->type, node->block_context); + if (container_type == irb->codegen->invalid_instruction) + return container_type; + + if (kind == ContainerInitKindStruct) { + size_t field_count = container_init_expr->entries.length; + IrInstruction **values = allocate(field_count); + Buf **names = allocate(field_count); + for (size_t i = 0; i < field_count; i += 1) { + AstNode *entry_node = container_init_expr->entries.at(i); + assert(entry_node->type == NodeTypeStructValueField); + + Buf *name = entry_node->data.struct_val_field.name; + AstNode *expr_node = entry_node->data.struct_val_field.expr; + IrInstruction *expr_value = ir_gen_node(irb, expr_node, node->block_context); + if (expr_value == irb->codegen->invalid_instruction) + return expr_value; + + names[i] = name; + values[i] = expr_value; + } + return ir_build_container_init_fields(irb, node, container_type, field_count, names, values); + } else if (kind == ContainerInitKindArray) { + size_t item_count = container_init_expr->entries.length; + IrInstruction **values = allocate(item_count); + for (size_t i = 0; i < item_count; i += 1) { + AstNode *expr_node = container_init_expr->entries.at(i); + IrInstruction *expr_value = ir_gen_node(irb, expr_node, node->block_context); + if (expr_value == irb->codegen->invalid_instruction) + return expr_value; + + values[i] = expr_value; + } + return ir_build_container_init_list(irb, node, container_type, item_count, values); + } else { + zig_unreachable(); + } +} + static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockContext *block_context, bool pointer_only) { @@ -670,6 +904,8 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockCont return ir_gen_if_bool_expr(irb, node); case NodeTypePrefixOpExpr: return ir_gen_prefix_op_expr(irb, node); + case NodeTypeContainerInitExpr: + return ir_gen_container_init_expr(irb, node); case NodeTypeUnwrapErrorExpr: case NodeTypeReturnExpr: case NodeTypeDefer: @@ -685,7 +921,6 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockCont case NodeTypeBreak: case NodeTypeContinue: case NodeTypeLabel: - case NodeTypeContainerInitExpr: case NodeTypeSwitchExpr: case NodeTypeBoolLiteral: case NodeTypeStringLiteral: @@ -733,6 +968,8 @@ static IrInstruction *ir_gen_add_return(CodeGen *g, AstNode *node, BlockContext irb->exec = ir_executable; irb->current_basic_block = ir_build_basic_block(irb, "Entry"); + // Entry block gets a reference because we enter it to begin. + ir_ref_bb(irb->current_basic_block); IrInstruction *result = ir_gen_node_extra(irb, node, scope, pointer_only); assert(result); @@ -767,11 +1004,6 @@ IrInstruction *ir_gen_fn(CodeGen *codegn, FnTableEntry *fn_entry) { return ir_gen_add_return(codegn, body_node, scope, ir_executable, add_return_yes, pointer_only_no); } -static void ir_link_new(IrInstruction *new_instruction, IrInstruction *old_instruction) { - new_instruction->other = old_instruction; - old_instruction->other = new_instruction; -} - /* static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *node) { assert(node->type == NodeTypeGoto); @@ -1033,7 +1265,7 @@ static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_inst TypeTableEntry *wanted_type = dest_type->static_value.data.x_type; if (value->static_value.ok) { - IrInstruction *result = ir_build_const(&ira->new_irb, source_instr->source_node, wanted_type); + IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->source_node, wanted_type); eval_const_expr_implicit_cast(cast_op, &value->static_value, value->type_entry, &result->static_value, wanted_type); return result; @@ -1058,6 +1290,12 @@ static bool is_u8(TypeTableEntry *type) { !type->data.integral.is_signed && type->data.integral.bit_count == 8; } +static IrBasicBlock *ir_get_new_bb(IrAnalyze *ira, IrBasicBlock *old_bb) { + if (old_bb->other) + return old_bb->other; + return ir_build_bb_from(&ira->new_irb, old_bb); +} + static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *dest_type, IrInstruction *value) { @@ -1365,10 +1603,7 @@ static TypeTableEntry *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructi return ira->codegen->builtin_types.entry_invalid; } - ir_link_new(ir_build_return(&ira->new_irb, return_instruction->base.source_node, value), - &return_instruction->base); - - return ira->codegen->builtin_types.entry_unreachable; + return ir_build_return_from(&ira->new_irb, &return_instruction->base, value)->type_entry; } static TypeTableEntry *ir_analyze_instruction_const(IrAnalyze *ira, IrInstructionConst *const_instruction) { @@ -1411,8 +1646,7 @@ static TypeTableEntry *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstructionBinOp return bool_type; } - ir_link_new(ir_build_bin_op(&ira->new_irb, bin_op_instruction->base.source_node, - bin_op_instruction->op_id, op1->other, op2->other), &bin_op_instruction->base); + ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, bin_op_instruction->op_id, op1->other, op2->other); return bool_type; } @@ -1481,8 +1715,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp zig_panic("TODO interpret bin_op_cmp"); - ir_link_new(ir_build_bin_op(&ira->new_irb, bin_op_instruction->base.source_node, - op_id, op1->other, op2->other), &bin_op_instruction->base); + ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, op_id, op1->other, op2->other); return ira->codegen->builtin_types.entry_bool; } @@ -1639,8 +1872,7 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp } - ir_link_new(ir_build_bin_op(&ira->new_irb, bin_op_instruction->base.source_node, - op_id, op1->other, op2->other), &bin_op_instruction->base); + ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, op_id, op1->other, op2->other); return resolved_type; } @@ -1684,8 +1916,7 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi } static TypeTableEntry *ir_analyze_instruction_load_var(IrAnalyze *ira, IrInstructionLoadVar *load_var_instruction) { - ir_link_new(ir_build_load_var(&ira->new_irb, load_var_instruction->base.source_node, - load_var_instruction->var), &load_var_instruction->base); + ir_build_load_var_from(&ira->new_irb, &load_var_instruction->base, load_var_instruction->var); return load_var_instruction->var->type; } @@ -1709,15 +1940,14 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction if (cast_instruction == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; - ir_link_new(cast_instruction, &call_instruction->base); + ir_link_new_instruction(cast_instruction, &call_instruction->base); return cast_instruction->type_entry; } else { zig_panic("TODO analyze more fn call types"); } } else { - //ir_link_new(ir_build_call(&ira->new_irb, call_instruction->base.source_node, - // call_instruction->fn, call_instruction->arg_count, call_instruction->args), - // &call_instruction->base); + //ir_build_call_from(&ira->new_irb, &call_instruction->base, + // call_instruction->fn, call_instruction->arg_count, call_instruction->args); zig_panic("TODO analyze fn call"); } @@ -1726,7 +1956,7 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction static TypeTableEntry *ir_analyze_unary_bool_not(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) { TypeTableEntry *bool_type = ira->codegen->builtin_types.entry_bool; - IrInstruction *casted_value = ir_get_casted_value(ira, un_op_instruction->value, bool_type); + IrInstruction *casted_value = ir_get_casted_value(ira, un_op_instruction->value->other, bool_type); if (casted_value == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; @@ -1739,9 +1969,7 @@ static TypeTableEntry *ir_analyze_unary_bool_not(IrAnalyze *ira, IrInstructionUn return bool_type; } - IrInstruction *new_instruction = ir_build_un_op(&ira->new_irb, un_op_instruction->base.source_node, - IrUnOpBoolNot, casted_value); - ir_link_new(new_instruction, &un_op_instruction->base); + ir_build_un_op_from(&ira->new_irb, &un_op_instruction->base, IrUnOpBoolNot, casted_value); return bool_type; } @@ -2924,8 +3152,6 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio // return analyze_compile_err(g, import, context, node); // case BuiltinFnIdIntType: // return analyze_int_type(g, import, context, node); -// case BuiltinFnIdUnreachable: -// return g->builtin_types.entry_unreachable; // case BuiltinFnIdSetFnTest: // return analyze_set_fn_test(g, import, context, node); // case BuiltinFnIdSetFnNoInline: @@ -2940,6 +3166,300 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio // zig_unreachable(); //} +//static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry *import, +// BlockContext *context, AstNode *node) +//{ +// assert(node->type == NodeTypeContainerInitExpr); +// +// AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr; +// +// ContainerInitKind kind = container_init_expr->kind; +// +// if (container_init_expr->type->type == NodeTypeFieldAccessExpr) { +// container_init_expr->type->data.field_access_expr.container_init_expr_node = node; +// } +// +// TypeTableEntry *container_meta_type = analyze_expression(g, import, context, nullptr, +// container_init_expr->type); +// +// if (container_meta_type->id == TypeTableEntryIdInvalid) { +// return g->builtin_types.entry_invalid; +// } +// +// if (node->data.container_init_expr.enum_type) { +// get_resolved_expr(node)->const_val = get_resolved_expr(container_init_expr->type)->const_val; +// return node->data.container_init_expr.enum_type; +// } +// +// TypeTableEntry *container_type = resolve_type(g, container_init_expr->type); +// +// if (container_type->id == TypeTableEntryIdInvalid) { +// return container_type; +// } else if (container_type->id == TypeTableEntryIdStruct && +// !container_type->data.structure.is_slice && +// (kind == ContainerInitKindStruct || (kind == ContainerInitKindArray && +// container_init_expr->entries.length == 0))) +// { +// StructValExprCodeGen *codegen = &container_init_expr->resolved_struct_val_expr; +// codegen->type_entry = container_type; +// codegen->source_node = node; +// +// +// size_t expr_field_count = container_init_expr->entries.length; +// size_t actual_field_count = container_type->data.structure.src_field_count; +// +// AstNode *non_const_expr_culprit = nullptr; +// +// size_t *field_use_counts = allocate(actual_field_count); +// ConstExprValue *const_val = &get_resolved_expr(node)->const_val; +// const_val->ok = true; +// const_val->data.x_struct.fields = allocate(actual_field_count); +// for (size_t i = 0; i < expr_field_count; i += 1) { +// AstNode *val_field_node = container_init_expr->entries.at(i); +// assert(val_field_node->type == NodeTypeStructValueField); +// +// val_field_node->block_context = context; +// +// TypeStructField *type_field = find_struct_type_field(container_type, +// val_field_node->data.struct_val_field.name); +// +// if (!type_field) { +// add_node_error(g, val_field_node, +// buf_sprintf("no member named '%s' in '%s'", +// buf_ptr(val_field_node->data.struct_val_field.name), buf_ptr(&container_type->name))); +// continue; +// } +// +// if (type_field->type_entry->id == TypeTableEntryIdInvalid) { +// return g->builtin_types.entry_invalid; +// } +// +// size_t field_index = type_field->src_index; +// field_use_counts[field_index] += 1; +// if (field_use_counts[field_index] > 1) { +// add_node_error(g, val_field_node, buf_sprintf("duplicate field")); +// continue; +// } +// +// val_field_node->data.struct_val_field.type_struct_field = type_field; +// +// analyze_expression(g, import, context, type_field->type_entry, +// val_field_node->data.struct_val_field.expr); +// +// if (const_val->ok) { +// ConstExprValue *field_val = +// &get_resolved_expr(val_field_node->data.struct_val_field.expr)->const_val; +// if (field_val->ok) { +// const_val->data.x_struct.fields[field_index] = field_val; +// const_val->depends_on_compile_var = const_val->depends_on_compile_var || field_val->depends_on_compile_var; +// } else { +// const_val->ok = false; +// non_const_expr_culprit = val_field_node->data.struct_val_field.expr; +// } +// } +// } +// if (!const_val->ok) { +// assert(non_const_expr_culprit); +// if (context->fn_entry) { +// context->fn_entry->struct_val_expr_alloca_list.append(codegen); +// } else { +// add_node_error(g, non_const_expr_culprit, buf_sprintf("unable to evaluate constant expression")); +// } +// } +// +// for (size_t i = 0; i < actual_field_count; i += 1) { +// if (field_use_counts[i] == 0) { +// add_node_error(g, node, +// buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i].name))); +// } +// } +// return container_type; +// } else if (container_type->id == TypeTableEntryIdStruct && +// container_type->data.structure.is_slice && +// kind == ContainerInitKindArray) +// { +// size_t elem_count = container_init_expr->entries.length; +// +// TypeTableEntry *pointer_type = container_type->data.structure.fields[0].type_entry; +// assert(pointer_type->id == TypeTableEntryIdPointer); +// TypeTableEntry *child_type = pointer_type->data.pointer.child_type; +// +// ConstExprValue *const_val = &get_resolved_expr(node)->const_val; +// const_val->ok = true; +// const_val->data.x_array.fields = allocate(elem_count); +// +// for (size_t i = 0; i < elem_count; i += 1) { +// AstNode **elem_node = &container_init_expr->entries.at(i); +// analyze_expression(g, import, context, child_type, *elem_node); +// +// if (const_val->ok) { +// ConstExprValue *elem_const_val = &get_resolved_expr(*elem_node)->const_val; +// if (elem_const_val->ok) { +// const_val->data.x_array.fields[i] = elem_const_val; +// const_val->depends_on_compile_var = const_val->depends_on_compile_var || +// elem_const_val->depends_on_compile_var; +// } else { +// const_val->ok = false; +// } +// } +// } +// +// TypeTableEntry *fixed_size_array_type = get_array_type(g, child_type, elem_count); +// +// StructValExprCodeGen *codegen = &container_init_expr->resolved_struct_val_expr; +// codegen->type_entry = fixed_size_array_type; +// codegen->source_node = node; +// if (!const_val->ok) { +// if (!context->fn_entry) { +// add_node_error(g, node, +// buf_sprintf("unable to evaluate constant expression")); +// } else { +// context->fn_entry->struct_val_expr_alloca_list.append(codegen); +// } +// } +// +// return fixed_size_array_type; +// } else if (container_type->id == TypeTableEntryIdArray) { +// zig_panic("TODO array container init"); +// return container_type; +// } else if (container_type->id == TypeTableEntryIdVoid) { +// if (container_init_expr->entries.length != 0) { +// add_node_error(g, node, buf_sprintf("void expression expects no arguments")); +// return g->builtin_types.entry_invalid; +// } else { +// return resolve_expr_const_val_as_void(g, node); +// } +// } else { +// add_node_error(g, node, +// buf_sprintf("type '%s' does not support %s initialization syntax", +// buf_ptr(&container_type->name), err_container_init_syntax_name(kind))); +// return g->builtin_types.entry_invalid; +// } +//} + +static TypeTableEntry *ir_analyze_instruction_br(IrAnalyze *ira, IrInstructionBr *br_instruction) { + IrBasicBlock *old_dest_block = br_instruction->dest_block; + IrBasicBlock *new_bb = ir_get_new_bb(ira, old_dest_block); + ir_build_br_from(&ira->new_irb, &br_instruction->base, new_bb); + return ira->codegen->builtin_types.entry_unreachable; +} + +static TypeTableEntry *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructionCondBr *cond_br_instruction) { + TypeTableEntry *bool_type = ira->codegen->builtin_types.entry_bool; + IrInstruction *condition = ir_get_casted_value(ira, cond_br_instruction->condition->other, bool_type); + if (condition == ira->codegen->invalid_instruction) + return ira->codegen->builtin_types.entry_invalid; + + if (condition->static_value.ok) { + IrBasicBlock *old_dest_block; + IrBasicBlock *old_ignored_block; + if (condition->static_value.data.x_bool) { + old_dest_block = cond_br_instruction->then_block; + old_ignored_block = cond_br_instruction->else_block; + } else { + old_dest_block = cond_br_instruction->else_block; + old_ignored_block = cond_br_instruction->then_block; + } + ir_unref_bb(old_ignored_block); + IrBasicBlock *new_bb = ir_get_new_bb(ira, old_dest_block); + ir_build_br_from(&ira->new_irb, &cond_br_instruction->base, new_bb); + return ira->codegen->builtin_types.entry_unreachable; + } + + IrBasicBlock *new_then_block = ir_get_new_bb(ira, cond_br_instruction->then_block); + IrBasicBlock *new_else_block = ir_get_new_bb(ira, cond_br_instruction->else_block); + ir_build_cond_br_from(&ira->new_irb, &cond_br_instruction->base, condition, new_then_block, new_else_block); + return ira->codegen->builtin_types.entry_unreachable; +} + +static TypeTableEntry *ir_analyze_instruction_builtin_call(IrAnalyze *ira, + IrInstructionBuiltinCall *builtin_call_instruction) +{ + switch (builtin_call_instruction->fn->id) { + case BuiltinFnIdInvalid: + case BuiltinFnIdUnreachable: + zig_unreachable(); + case BuiltinFnIdMemcpy: + case BuiltinFnIdMemset: + case BuiltinFnIdSizeof: + case BuiltinFnIdAlignof: + case BuiltinFnIdMaxValue: + case BuiltinFnIdMinValue: + case BuiltinFnIdMemberCount: + case BuiltinFnIdTypeof: + case BuiltinFnIdAddWithOverflow: + case BuiltinFnIdSubWithOverflow: + case BuiltinFnIdMulWithOverflow: + case BuiltinFnIdShlWithOverflow: + case BuiltinFnIdCInclude: + case BuiltinFnIdCDefine: + case BuiltinFnIdCUndef: + case BuiltinFnIdCompileVar: + case BuiltinFnIdCompileErr: + case BuiltinFnIdConstEval: + case BuiltinFnIdCtz: + case BuiltinFnIdClz: + case BuiltinFnIdImport: + case BuiltinFnIdCImport: + case BuiltinFnIdErrName: + case BuiltinFnIdBreakpoint: + case BuiltinFnIdReturnAddress: + case BuiltinFnIdFrameAddress: + case BuiltinFnIdEmbedFile: + case BuiltinFnIdCmpExchange: + case BuiltinFnIdFence: + case BuiltinFnIdDivExact: + case BuiltinFnIdTruncate: + case BuiltinFnIdIntType: + case BuiltinFnIdSetFnTest: + case BuiltinFnIdSetFnVisible: + case BuiltinFnIdSetFnStaticEval: + case BuiltinFnIdSetFnNoInline: + case BuiltinFnIdSetDebugSafety: + zig_panic("TODO analyze more builtin functions"); + } + zig_unreachable(); +} + +static TypeTableEntry *ir_analyze_instruction_unreachable(IrAnalyze *ira, + IrInstructionUnreachable *unreachable_instruction) +{ + return ir_build_unreachable_from(&ira->new_irb, &unreachable_instruction->base)->type_entry; +} + +static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPhi *phi_instruction) { + ZigList new_incoming_blocks = {0}; + ZigList new_incoming_values = {0}; + + for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) { + IrBasicBlock *predecessor = phi_instruction->incoming_blocks[i]; + if (predecessor->ref_count == 0) + continue; + + assert(predecessor->other); + new_incoming_blocks.append(predecessor->other); + + IrInstruction *old_value = phi_instruction->incoming_values[i]; + assert(old_value); + new_incoming_values.append(old_value->other); + } + assert(new_incoming_blocks.length != 0); + + if (new_incoming_blocks.length == 1) { + IrInstruction *first_value = new_incoming_values.at(0); + phi_instruction->base.other = first_value; + return first_value->type_entry; + } + + TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, &phi_instruction->base, + new_incoming_values.items, new_incoming_values.length); + if (resolved_type->id == TypeTableEntryIdInvalid) + return resolved_type; + + ir_build_phi_from(&ira->new_irb, &phi_instruction->base, new_incoming_blocks.length, + new_incoming_blocks.items, new_incoming_values.items); + return resolved_type; +} static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { @@ -2957,13 +3477,21 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_load_var(ira, (IrInstructionLoadVar *)instruction); case IrInstructionIdCall: return ir_analyze_instruction_call(ira, (IrInstructionCall *)instruction); - case IrInstructionIdCondBr: case IrInstructionIdBr: - case IrInstructionIdSwitchBr: - case IrInstructionIdPhi: - case IrInstructionIdStoreVar: + return ir_analyze_instruction_br(ira, (IrInstructionBr *)instruction); + case IrInstructionIdCondBr: + return ir_analyze_instruction_cond_br(ira, (IrInstructionCondBr *)instruction); case IrInstructionIdBuiltinCall: + return ir_analyze_instruction_builtin_call(ira, (IrInstructionBuiltinCall *)instruction); + case IrInstructionIdUnreachable: + return ir_analyze_instruction_unreachable(ira, (IrInstructionUnreachable *)instruction); + case IrInstructionIdPhi: + return ir_analyze_instruction_phi(ira, (IrInstructionPhi *)instruction); + case IrInstructionIdSwitchBr: + case IrInstructionIdStoreVar: case IrInstructionIdCast: + case IrInstructionIdContainerInitList: + case IrInstructionIdContainerInitFields: zig_panic("TODO analyze more instructions"); } zig_unreachable(); @@ -3000,25 +3528,106 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl ira->exec_context.var_slot_list = allocate(ira->exec_context.var_slot_count); TypeTableEntry *return_type = ira->codegen->builtin_types.entry_void; + for (size_t bb_i = 0; bb_i < ira->old_irb.exec->basic_block_list.length; bb_i += 1) { + ira->old_irb.current_basic_block = ira->old_irb.exec->basic_block_list.at(bb_i); + if (ira->old_irb.current_basic_block->ref_count == 0) + continue; - ira->new_irb.current_basic_block = ir_build_basic_block(&ira->new_irb, "Entry"); - ira->old_irb.current_basic_block = ira->old_irb.exec->basic_block_list.at(0); + ira->new_irb.current_basic_block = ir_get_new_bb(ira, ira->old_irb.current_basic_block); - ira->new_irb.current_basic_block->other = ira->old_irb.current_basic_block; - ira->old_irb.current_basic_block->other = ira->new_irb.current_basic_block; + return_type = ira->codegen->builtin_types.entry_void; - - for (size_t i = 0; i < ira->old_irb.current_basic_block->instruction_list.length; i += 1) { - IrInstruction *instruction = ira->old_irb.current_basic_block->instruction_list.at(i); - if (return_type->id == TypeTableEntryIdUnreachable) { - add_node_error(ira->codegen, first_executing_node(instruction->source_node), - buf_sprintf("unreachable code")); - break; + for (size_t instr_i = 0; instr_i < ira->old_irb.current_basic_block->instruction_list.length; instr_i += 1) { + IrInstruction *instruction = ira->old_irb.current_basic_block->instruction_list.at(instr_i); + if (return_type->id == TypeTableEntryIdUnreachable) { + // TODO + //add_node_error(ira->codegen, first_executing_node(instruction->source_node), + // buf_sprintf("unreachable code")); + break; + } + bool is_last = (instr_i == ira->old_irb.current_basic_block->instruction_list.length - 1); + TypeTableEntry *passed_expected_type = is_last ? expected_type : nullptr; + return_type = ir_analyze_instruction(ira, instruction, passed_expected_type); } - bool is_last = (i == ira->old_irb.current_basic_block->instruction_list.length - 1); - TypeTableEntry *passed_expected_type = is_last ? expected_type : nullptr; - return_type = ir_analyze_instruction(ira, instruction, passed_expected_type); } + // Give entry block a ref + ir_ref_bb(ira->new_irb.exec->basic_block_list.at(0)); + return return_type; } + +static bool ir_builtin_call_has_side_effects(IrInstructionBuiltinCall *call_instruction) { + switch (call_instruction->fn->id) { + case BuiltinFnIdInvalid: + zig_unreachable(); + case BuiltinFnIdMemcpy: + case BuiltinFnIdMemset: + case BuiltinFnIdAddWithOverflow: + case BuiltinFnIdSubWithOverflow: + case BuiltinFnIdMulWithOverflow: + case BuiltinFnIdShlWithOverflow: + case BuiltinFnIdCInclude: + case BuiltinFnIdCDefine: + case BuiltinFnIdCUndef: + case BuiltinFnIdImport: + case BuiltinFnIdCImport: + case BuiltinFnIdBreakpoint: + case BuiltinFnIdEmbedFile: + case BuiltinFnIdCmpExchange: + case BuiltinFnIdFence: + case BuiltinFnIdUnreachable: + case BuiltinFnIdSetFnTest: + case BuiltinFnIdSetFnVisible: + case BuiltinFnIdSetFnStaticEval: + case BuiltinFnIdSetFnNoInline: + case BuiltinFnIdSetDebugSafety: + return true; + case BuiltinFnIdSizeof: + case BuiltinFnIdAlignof: + case BuiltinFnIdMaxValue: + case BuiltinFnIdMinValue: + case BuiltinFnIdMemberCount: + case BuiltinFnIdTypeof: + case BuiltinFnIdCompileVar: + case BuiltinFnIdCompileErr: + case BuiltinFnIdConstEval: + case BuiltinFnIdCtz: + case BuiltinFnIdClz: + case BuiltinFnIdErrName: + case BuiltinFnIdReturnAddress: + case BuiltinFnIdFrameAddress: + case BuiltinFnIdDivExact: + case BuiltinFnIdTruncate: + case BuiltinFnIdIntType: + return false; + } + zig_unreachable(); +} + +bool ir_has_side_effects(IrInstruction *instruction) { + switch (instruction->id) { + case IrInstructionIdInvalid: + zig_unreachable(); + case IrInstructionIdBr: + case IrInstructionIdCondBr: + case IrInstructionIdSwitchBr: + case IrInstructionIdStoreVar: + case IrInstructionIdCall: + case IrInstructionIdReturn: + case IrInstructionIdUnreachable: + return true; + case IrInstructionIdPhi: + case IrInstructionIdUnOp: + case IrInstructionIdBinOp: + case IrInstructionIdLoadVar: + case IrInstructionIdConst: + case IrInstructionIdCast: + case IrInstructionIdContainerInitList: + case IrInstructionIdContainerInitFields: + return false; + case IrInstructionIdBuiltinCall: + return ir_builtin_call_has_side_effects((IrInstructionBuiltinCall *)instruction); + } + zig_unreachable(); +} diff --git a/src/ir.hpp b/src/ir.hpp index 101c8b60a..af1dfe96e 100644 --- a/src/ir.hpp +++ b/src/ir.hpp @@ -16,4 +16,6 @@ IrInstruction *ir_gen_fn(CodeGen *g, FnTableEntry *fn_entry); TypeTableEntry *ir_analyze(CodeGen *g, IrExecutable *old_executable, IrExecutable *new_executable, TypeTableEntry *expected_type); +bool ir_has_side_effects(IrInstruction *instruction); + #endif diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 22e7fe6d1..fb6f4d16b 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1,3 +1,4 @@ +#include "ir.hpp" #include "ir_print.hpp" struct IrPrint { @@ -14,49 +15,46 @@ static void ir_print_indent(IrPrint *irp) { static void ir_print_prefix(IrPrint *irp, IrInstruction *instruction) { ir_print_indent(irp); - fprintf(irp->f, "#%-3zu| ", instruction->debug_id); + const char *type_name = instruction->type_entry ? buf_ptr(&instruction->type_entry->name) : "(unknown)"; + const char *ref_count = ir_has_side_effects(instruction) ? + "-" : buf_ptr(buf_sprintf("%zu", instruction->ref_count)); + fprintf(irp->f, "#%-3zu| %-12s| %-2s| ", instruction->debug_id, type_name, ref_count); } -static void ir_print_return(IrPrint *irp, IrInstructionReturn *return_instruction) { - ir_print_prefix(irp, &return_instruction->base); - assert(return_instruction->value); - fprintf(irp->f, "return #%zu\n", return_instruction->value->debug_id); -} - -static void ir_print_const(IrPrint *irp, IrInstructionConst *const_instruction) { - ir_print_prefix(irp, &const_instruction->base); - TypeTableEntry *type_entry = const_instruction->base.type_entry; - fprintf(irp->f, "%s ", buf_ptr(&type_entry->name)); +static void ir_print_const_instruction(IrPrint *irp, IrInstruction *instruction) { + TypeTableEntry *type_entry = instruction->type_entry; switch (type_entry->id) { case TypeTableEntryIdInvalid: zig_unreachable(); case TypeTableEntryIdVoid: - fprintf(irp->f, "%s\n", "void"); + fprintf(irp->f, "{}"); break; case TypeTableEntryIdNumLitFloat: - fprintf(irp->f, "%f\n", const_instruction->base.static_value.data.x_bignum.data.x_float); + fprintf(irp->f, "%f", instruction->static_value.data.x_bignum.data.x_float); break; case TypeTableEntryIdNumLitInt: { - BigNum *bignum = &const_instruction->base.static_value.data.x_bignum; + BigNum *bignum = &instruction->static_value.data.x_bignum; const char *negative_str = bignum->is_negative ? "-" : ""; - fprintf(irp->f, "%s%llu\n", negative_str, bignum->data.x_uint); + fprintf(irp->f, "%s%llu", negative_str, bignum->data.x_uint); break; } case TypeTableEntryIdMetaType: - fprintf(irp->f, "%s\n", buf_ptr(&const_instruction->base.static_value.data.x_type->name)); + fprintf(irp->f, "%s", buf_ptr(&instruction->static_value.data.x_type->name)); break; case TypeTableEntryIdInt: { - BigNum *bignum = &const_instruction->base.static_value.data.x_bignum; + BigNum *bignum = &instruction->static_value.data.x_bignum; assert(bignum->kind == BigNumKindInt); const char *negative_str = bignum->is_negative ? "-" : ""; - fprintf(irp->f, "%s%llu\n", negative_str, bignum->data.x_uint); + fprintf(irp->f, "%s%llu", negative_str, bignum->data.x_uint); } break; + case TypeTableEntryIdUnreachable: + fprintf(irp->f, "@unreachable()"); + break; case TypeTableEntryIdVar: case TypeTableEntryIdBool: - case TypeTableEntryIdUnreachable: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: @@ -77,6 +75,27 @@ static void ir_print_const(IrPrint *irp, IrInstructionConst *const_instruction) } } +static void ir_print_other_instruction(IrPrint *irp, IrInstruction *instruction) { + if (instruction->static_value.ok) { + ir_print_const_instruction(irp, instruction); + } else { + fprintf(irp->f, "#%zu", instruction->debug_id); + } +} + +static void ir_print_other_block(IrPrint *irp, IrBasicBlock *bb) { + fprintf(irp->f, "$%s_%zu", bb->name_hint, bb->debug_id); +} + +static void ir_print_return(IrPrint *irp, IrInstructionReturn *return_instruction) { + assert(return_instruction->value); + fprintf(irp->f, "return "); + ir_print_other_instruction(irp, return_instruction->value); +} + +static void ir_print_const(IrPrint *irp, IrInstructionConst *const_instruction) { + ir_print_const_instruction(irp, &const_instruction->base); +} static const char *ir_bin_op_id_str(IrBinOp op_id) { switch (op_id) { @@ -169,87 +188,108 @@ static const char *ir_un_op_id_str(IrUnOp op_id) { } static void ir_print_un_op(IrPrint *irp, IrInstructionUnOp *un_op_instruction) { - ir_print_prefix(irp, &un_op_instruction->base); - fprintf(irp->f, "%s #%zu\n", - ir_un_op_id_str(un_op_instruction->op_id), - un_op_instruction->value->debug_id); + fprintf(irp->f, "%s ", ir_un_op_id_str(un_op_instruction->op_id)); + ir_print_other_instruction(irp, un_op_instruction->value); } static void ir_print_bin_op(IrPrint *irp, IrInstructionBinOp *bin_op_instruction) { - ir_print_prefix(irp, &bin_op_instruction->base); - fprintf(irp->f, "#%zu %s #%zu\n", - bin_op_instruction->op1->debug_id, - ir_bin_op_id_str(bin_op_instruction->op_id), - bin_op_instruction->op2->debug_id); + ir_print_other_instruction(irp, bin_op_instruction->op1); + fprintf(irp->f, " %s ", ir_bin_op_id_str(bin_op_instruction->op_id)); + ir_print_other_instruction(irp, bin_op_instruction->op2); } static void ir_print_load_var(IrPrint *irp, IrInstructionLoadVar *load_var_instruction) { - ir_print_prefix(irp, &load_var_instruction->base); - fprintf(irp->f, "%s\n", - buf_ptr(&load_var_instruction->var->name)); + fprintf(irp->f, "%s", buf_ptr(&load_var_instruction->var->name)); } static void ir_print_cast(IrPrint *irp, IrInstructionCast *cast_instruction) { - ir_print_prefix(irp, &cast_instruction->base); - fprintf(irp->f, "cast #%zu to #%zu\n", - cast_instruction->value->debug_id, - cast_instruction->dest_type->debug_id); + fprintf(irp->f, "cast "); + ir_print_other_instruction(irp, cast_instruction->value); + fprintf(irp->f, " to "); + ir_print_other_instruction(irp, cast_instruction->dest_type); } static void ir_print_call(IrPrint *irp, IrInstructionCall *call_instruction) { - ir_print_prefix(irp, &call_instruction->base); - fprintf(irp->f, "#%zu(", call_instruction->fn->debug_id); + ir_print_other_instruction(irp, call_instruction->fn); + fprintf(irp->f, "("); for (size_t i = 0; i < call_instruction->arg_count; i += 1) { IrInstruction *arg = call_instruction->args[i]; if (i != 0) fprintf(irp->f, ", "); - fprintf(irp->f, "#%zu", arg->debug_id); + ir_print_other_instruction(irp, arg); } - fprintf(irp->f, ")\n"); + fprintf(irp->f, ")"); } static void ir_print_builtin_call(IrPrint *irp, IrInstructionBuiltinCall *call_instruction) { - ir_print_prefix(irp, &call_instruction->base); fprintf(irp->f, "@%s(", buf_ptr(&call_instruction->fn->name)); for (size_t i = 0; i < call_instruction->fn->param_count; i += 1) { IrInstruction *arg = call_instruction->args[i]; if (i != 0) fprintf(irp->f, ", "); - fprintf(irp->f, "#%zu", arg->debug_id); + ir_print_other_instruction(irp, arg); } - fprintf(irp->f, ")\n"); + fprintf(irp->f, ")"); } static void ir_print_cond_br(IrPrint *irp, IrInstructionCondBr *cond_br_instruction) { - ir_print_prefix(irp, &cond_br_instruction->base); - fprintf(irp->f, "if #%zu then $%s_%zu else $%s_%zu\n", - cond_br_instruction->condition->debug_id, - cond_br_instruction->then_block->name_hint, cond_br_instruction->then_block->debug_id, - cond_br_instruction->else_block->name_hint, cond_br_instruction->else_block->debug_id); + fprintf(irp->f, "if ("); + ir_print_other_instruction(irp, cond_br_instruction->condition); + fprintf(irp->f, ") "); + ir_print_other_block(irp, cond_br_instruction->then_block); + fprintf(irp->f, " else "); + ir_print_other_block(irp, cond_br_instruction->else_block); } static void ir_print_br(IrPrint *irp, IrInstructionBr *br_instruction) { - ir_print_prefix(irp, &br_instruction->base); - fprintf(irp->f, "goto $%s_%zu\n", - br_instruction->dest_block->name_hint, br_instruction->dest_block->debug_id); + fprintf(irp->f, "goto "); + ir_print_other_block(irp, br_instruction->dest_block); } static void ir_print_phi(IrPrint *irp, IrInstructionPhi *phi_instruction) { - ir_print_prefix(irp, &phi_instruction->base); for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) { IrBasicBlock *incoming_block = phi_instruction->incoming_blocks[i]; IrInstruction *incoming_value = phi_instruction->incoming_values[i]; if (i != 0) fprintf(irp->f, " "); - fprintf(irp->f, "$%s_%zu:#%zu", - incoming_block->name_hint, incoming_block->debug_id, - incoming_value->debug_id); + ir_print_other_block(irp, incoming_block); + fprintf(irp->f, ":"); + ir_print_other_instruction(irp, incoming_value); } - fprintf(irp->f, "\n"); +} + +static void ir_print_container_init_list(IrPrint *irp, IrInstructionContainerInitList *instruction) { + ir_print_other_instruction(irp, instruction->container_type); + fprintf(irp->f, "{"); + for (size_t i = 0; i < instruction->item_count; i += 1) { + IrInstruction *item = instruction->items[i]; + if (i != 0) + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, item); + } + fprintf(irp->f, "}"); +} + +static void ir_print_container_init_fields(IrPrint *irp, IrInstructionContainerInitFields *instruction) { + ir_print_other_instruction(irp, instruction->container_type); + fprintf(irp->f, "{"); + for (size_t i = 0; i < instruction->field_count; i += 1) { + Buf *name = instruction->field_names[i]; + IrInstruction *field_value = instruction->field_values[i]; + const char *comma = (i == 0) ? "" : ", "; + fprintf(irp->f, "%s.%s = ", comma, buf_ptr(name)); + ir_print_other_instruction(irp, field_value); + } + fprintf(irp->f, "}"); +} + +static void ir_print_unreachable(IrPrint *irp, IrInstructionUnreachable *instruction) { + fprintf(irp->f, "unreachable"); } static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { + ir_print_prefix(irp, instruction); switch (instruction->id) { case IrInstructionIdInvalid: zig_unreachable(); @@ -286,10 +326,20 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdPhi: ir_print_phi(irp, (IrInstructionPhi *)instruction); break; + case IrInstructionIdContainerInitList: + ir_print_container_init_list(irp, (IrInstructionContainerInitList *)instruction); + break; + case IrInstructionIdContainerInitFields: + ir_print_container_init_fields(irp, (IrInstructionContainerInitFields *)instruction); + break; + case IrInstructionIdUnreachable: + ir_print_unreachable(irp, (IrInstructionUnreachable *)instruction); + break; case IrInstructionIdSwitchBr: case IrInstructionIdStoreVar: zig_panic("TODO print more IR instructions"); } + fprintf(irp->f, "\n"); } void ir_print(FILE *f, IrExecutable *executable, int indent_size) { diff --git a/src/parser.cpp b/src/parser.cpp index cbd96e20e..82ca4d0ee 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1912,14 +1912,11 @@ static AstNode *ast_parse_label(ParseContext *pc, size_t *token_index, bool mand return node; } -//static AstNode *ast_create_void_expr(ParseContext *pc, Token *token) { -// AstNode *node = ast_create_node(pc, NodeTypeContainerInitExpr, token); -// node->data.container_init_expr.type = ast_create_node(pc, NodeTypeSymbol, token); -// node->data.container_init_expr.kind = ContainerInitKindArray; -// node->data.container_init_expr.type->data.symbol_expr.symbol = pc->void_buf; -// normalize_parent_ptrs(node); -// return node; -//} +static AstNode *ast_create_void_expr(ParseContext *pc, Token *token) { + AstNode *node = ast_create_node(pc, NodeTypeBlock, token); + normalize_parent_ptrs(node); + return node; +} /* Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace) @@ -1961,12 +1958,13 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand semicolon_expected = !statement_node; if (!statement_node) { statement_node = ast_parse_non_block_expr(pc, token_index, false); + if (!statement_node) { + statement_node = ast_create_void_expr(pc, last_token); + } } } } - if (statement_node) { - node->data.block.statements.append(statement_node); - } + node->data.block.statements.append(statement_node); last_token = &pc->tokens->at(*token_index); if (last_token->id == TokenIdRBrace) {