diff --git a/src/all_types.hpp b/src/all_types.hpp index 20cb73967..8090f1072 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1420,6 +1420,7 @@ enum IrInstructionId { IrInstructionIdBuiltinCall, IrInstructionIdConst, IrInstructionIdReturn, + IrInstructionIdCast, }; struct IrInstruction { @@ -1539,5 +1540,12 @@ struct IrInstructionReturn { IrInstruction *value; }; +struct IrInstructionCast { + IrInstruction base; + + IrInstruction *value; + IrInstruction *dest_type; + bool is_implicit; +}; #endif diff --git a/src/analyze.cpp b/src/analyze.cpp index e15370312..fc583b265 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -42,10 +42,8 @@ static TypeTableEntry *resolve_expr_const_val_as_unsigned_num_lit(CodeGen *g, As TypeTableEntry *expected_type, uint64_t x, bool depends_on_compile_var); static TypeTableEntry *resolve_expr_const_val_as_bool(CodeGen *g, AstNode *node, bool value, bool depends_on_compile_var); -static AstNode *find_decl(BlockContext *context, Buf *name); static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNode *decl_node, bool pointer_only, BlockContext *block_context, bool depends_on_compile_var); -static TopLevelDecl *get_as_top_level_decl(AstNode *node); static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *source_node, AstNodeVariableDeclaration *variable_declaration, @@ -1757,7 +1755,7 @@ static void preview_error_value_decl(CodeGen *g, AstNode *node) { node->data.error_value_decl.top_level_decl.resolution = TldResolutionOk; } -static void resolve_top_level_decl(CodeGen *g, AstNode *node, bool pointer_only) { +void resolve_top_level_decl(CodeGen *g, AstNode *node, bool pointer_only) { TopLevelDecl *tld = get_as_top_level_decl(node); if (tld->resolution != TldResolutionUnresolved) { return; @@ -1978,7 +1976,7 @@ static bool num_lit_fits_in_other_type(CodeGen *g, AstNode *literal_node, TypeTa return false; } -static bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *actual_type) { +bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *actual_type) { if (expected_type == actual_type) return true; @@ -2302,7 +2300,7 @@ static TypeTableEntry *resolve_type_compatibility(CodeGen *g, ImportTableEntry * return g->builtin_types.entry_invalid; } -static TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, ImportTableEntry *import, +TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, ImportTableEntry *import, BlockContext *block_context, AstNode *parent_source_node, AstNode **child_nodes, TypeTableEntry **child_types, size_t child_count) { @@ -2358,7 +2356,7 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) { return context; } -static AstNode *find_decl(BlockContext *context, Buf *name) { +AstNode *find_decl(BlockContext *context, Buf *name) { while (context) { auto entry = context->decl_table.maybe_get(name); if (entry) { @@ -2369,7 +2367,7 @@ static AstNode *find_decl(BlockContext *context, Buf *name) { return nullptr; } -static VariableTableEntry *find_variable(CodeGen *g, BlockContext *orig_context, Buf *name) { +VariableTableEntry *find_variable(CodeGen *g, BlockContext *orig_context, Buf *name) { BlockContext *context = orig_context; while (context) { auto entry = context->var_table.maybe_get(name); @@ -3248,10 +3246,6 @@ static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNod static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node, bool pointer_only) { - if (node->data.symbol_expr.override_type_entry) { - return resolve_expr_const_val_as_type(g, node, node->data.symbol_expr.override_type_entry, false); - } - Buf *variable_name = node->data.symbol_expr.symbol; auto primitive_table_entry = g->primitive_type_table.maybe_get(variable_name); @@ -4091,11 +4085,6 @@ static TypeTableEntry *analyze_this_literal_expr(CodeGen *g, ImportTableEntry *i static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *block_context, TypeTableEntry *expected_type, AstNode *node) { - if (node->data.number_literal.overflow) { - add_node_error(g, node, buf_sprintf("number literal too large to be represented in any type")); - return g->builtin_types.entry_invalid; - } - return resolve_expr_const_val_as_bignum(g, node, expected_type, node->data.number_literal.bignum, false); } @@ -7536,7 +7525,7 @@ Expr *get_resolved_expr(AstNode *node) { zig_unreachable(); } -static TopLevelDecl *get_as_top_level_decl(AstNode *node) { +TopLevelDecl *get_as_top_level_decl(AstNode *node) { switch (node->type) { case NodeTypeVariableDeclaration: return &node->data.variable_declaration.top_level_decl; diff --git a/src/analyze.hpp b/src/analyze.hpp index ee3e5e305..9948ef9a8 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -45,4 +45,14 @@ ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, AstNode *first_executing_node(AstNode *node); +TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, ImportTableEntry *import, + BlockContext *block_context, AstNode *parent_source_node, + AstNode **child_nodes, TypeTableEntry **child_types, size_t child_count); + +bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *actual_type); +VariableTableEntry *find_variable(CodeGen *g, BlockContext *orig_context, Buf *name); +AstNode *find_decl(BlockContext *context, Buf *name); +void resolve_top_level_decl(CodeGen *g, AstNode *node, bool pointer_only); +TopLevelDecl *get_as_top_level_decl(AstNode *node); + #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index f2d7c8bb6..2cf56169a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -65,8 +65,6 @@ CodeGen *codegen_create(Buf *root_source_dir, const ZigTarget *target) { g->is_test_build = false; g->want_h_file = true; - g->invalid_instruction = allocate(1); - // the error.Ok value g->error_decls.append(nullptr); @@ -252,10 +250,6 @@ static void set_debug_source_node(CodeGen *g, AstNode *node) { ZigLLVMSetCurrentDebugLocation(g->builder, node->line + 1, node->column + 1, node->block_context->di_scope); } -static void ir_set_debug(CodeGen *g, IrInstruction *instruction) { - set_debug_source_node(g, instruction->source_node); -} - static void clear_debug_source_node(CodeGen *g) { ZigLLVMClearCurrentDebugLocation(g->builder); } @@ -375,6 +369,10 @@ static bool want_debug_safety(CodeGen *g, AstNode *node) { return want_debug_safety_recursive(g, node->block_context); } +static bool ir_want_debug_safety(CodeGen *g, IrInstruction *instruction) { + return want_debug_safety(g, instruction->source_node); +} + static void gen_debug_safety_crash(CodeGen *g) { LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, ""); LLVMBuildUnreachable(g->builder); @@ -2800,12 +2798,104 @@ static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) { } static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) { - ir_set_debug(g, &return_instruction->base); LLVMBuildRet(g->builder, return_instruction->value->llvm_value); return nullptr; } +static LLVMValueRef ir_render_load_var(CodeGen *g, IrExecutable *executable, + IrInstructionLoadVar *load_var_instruction) +{ + VariableTableEntry *var = load_var_instruction->var; + if (!type_has_bits(var->type)) + return nullptr; + + assert(var->value_ref); + return get_handle_value(g, load_var_instruction->base.source_node, var->value_ref, var->type); +} + +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; + if (op_id == IrBinOpBoolOr) { + return LLVMBuildOr(g->builder, op1, op2, ""); + } else if (op_id == IrBinOpBoolAnd) { + return LLVMBuildAnd(g->builder, op1, op2, ""); + } else { + zig_unreachable(); + } +} + +static LLVMValueRef ir_render_bin_op_add(CodeGen *g, IrExecutable *executable, + IrInstructionBinOp *bin_op_instruction) +{ + IrBinOp op_id = bin_op_instruction->op_id; + IrInstruction *op1 = bin_op_instruction->op1; + IrInstruction *op2 = bin_op_instruction->op2; + + assert(op1->type_entry == op2->type_entry); + + if (op1->type_entry->id == TypeTableEntryIdFloat) { + return LLVMBuildFAdd(g->builder, op1->llvm_value, op2->llvm_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, ""); + } 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); + } else if (op1->type_entry->data.integral.is_signed) { + return LLVMBuildNSWAdd(g->builder, op1->llvm_value, op2->llvm_value, ""); + } else { + return LLVMBuildNUWAdd(g->builder, op1->llvm_value, op2->llvm_value, ""); + } + } else { + zig_unreachable(); + } +} + +static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, + IrInstructionBinOp *bin_op_instruction) +{ + IrBinOp op_id = bin_op_instruction->op_id; + switch (op_id) { + case IrBinOpInvalid: + case IrBinOpArrayCat: + case IrBinOpArrayMult: + zig_unreachable(); + case IrBinOpBoolOr: + case IrBinOpBoolAnd: + return ir_render_bin_op_bool(g, executable, bin_op_instruction); + case IrBinOpCmpEq: + case IrBinOpCmpNotEq: + case IrBinOpCmpLessThan: + case IrBinOpCmpGreaterThan: + case IrBinOpCmpLessOrEq: + case IrBinOpCmpGreaterOrEq: + zig_panic("TODO bin op cmp"); + case IrBinOpAdd: + case IrBinOpAddWrap: + return ir_render_bin_op_add(g, executable, bin_op_instruction); + case IrBinOpBinOr: + case IrBinOpBinXor: + case IrBinOpBinAnd: + case IrBinOpBitShiftLeft: + case IrBinOpBitShiftLeftWrap: + case IrBinOpBitShiftRight: + case IrBinOpSub: + case IrBinOpSubWrap: + case IrBinOpMult: + case IrBinOpMultWrap: + case IrBinOpDiv: + case IrBinOpMod: + zig_panic("TODO render more bin ops to LLVM"); + } + 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(); @@ -2813,14 +2903,17 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return gen_const_val(g, instruction->type_entry, &instruction->static_value); case IrInstructionIdReturn: return ir_render_return(g, executable, (IrInstructionReturn *)instruction); + case IrInstructionIdLoadVar: + return ir_render_load_var(g, executable, (IrInstructionLoadVar *)instruction); + case IrInstructionIdBinOp: + return ir_render_bin_op(g, executable, (IrInstructionBinOp *)instruction); case IrInstructionIdCondBr: case IrInstructionIdSwitchBr: case IrInstructionIdPhi: - case IrInstructionIdBinOp: - case IrInstructionIdLoadVar: case IrInstructionIdStoreVar: case IrInstructionIdCall: case IrInstructionIdBuiltinCall: + case IrInstructionIdCast: zig_panic("TODO render more IR instructions to LLVM"); } zig_unreachable(); @@ -5013,6 +5106,9 @@ static void init(CodeGen *g, Buf *source_path) { define_builtin_types(g); define_builtin_fns(g); + + g->invalid_instruction = allocate(1); + g->invalid_instruction->type_entry = g->builtin_types.entry_invalid; } void codegen_parseh(CodeGen *g, Buf *src_dirname, Buf *src_basename, Buf *source_code) { diff --git a/src/ir.cpp b/src/ir.cpp index 4c432ecf4..71e776321 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9,7 +9,13 @@ struct IrGen { IrExecutable *exec; }; -static IrInstruction *ir_gen_node(IrGen *ir, AstNode *node, BlockContext *block_context); +struct IrAnalyze { + CodeGen *codegen; + IrExecutable *exec; + IrBasicBlock *current_basic_block; +}; + +static IrInstruction *ir_gen_node(IrGen *irg, AstNode *node, BlockContext *scope); static void ir_instruction_append(IrBasicBlock *basic_block, IrInstruction *instruction) { if (!basic_block->last) { @@ -25,9 +31,33 @@ static void ir_instruction_append(IrBasicBlock *basic_block, IrInstruction *inst } } -static size_t exec_next_debug_id(IrGen *ir) { - size_t result = ir->exec->next_debug_id; - ir->exec->next_debug_id += 1; +static void ir_instruction_insert(IrBasicBlock *basic_block, + IrInstruction *before_instruction, IrInstruction *after_instruction, + IrInstruction *new_instruction) +{ + assert(before_instruction || after_instruction); + assert(!before_instruction || !after_instruction); + + if (before_instruction) { + IrInstruction *displaced_instruction = before_instruction->prev; + before_instruction->prev = new_instruction; + new_instruction->prev = displaced_instruction; + new_instruction->next = before_instruction; + if (basic_block->first == before_instruction) + basic_block->first = new_instruction; + } else { + IrInstruction *displaced_instruction = after_instruction->next; + after_instruction->next = new_instruction; + new_instruction->prev = after_instruction; + new_instruction->next = displaced_instruction; + if (basic_block->last == after_instruction) + basic_block->last = new_instruction; + } +} + +static size_t exec_next_debug_id(IrExecutable *exec) { + size_t result = exec->next_debug_id; + exec->next_debug_id += 1; return result; } @@ -71,32 +101,117 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionReturn *) { return IrInstructionIdReturn; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionCast *) { + return IrInstructionIdCast; +} + template -static T *ir_build_instruction(IrGen *ir, AstNode *source_node) { +static T *ir_create_instruction(IrExecutable *exec, AstNode *source_node) { T *special_instruction = allocate(1); special_instruction->base.id = ir_instruction_id(special_instruction); special_instruction->base.source_node = source_node; - special_instruction->base.type_entry = ir->codegen->builtin_types.entry_unreachable; - special_instruction->base.debug_id = exec_next_debug_id(ir); - ir_instruction_append(ir->current_basic_block, &special_instruction->base); + special_instruction->base.debug_id = exec_next_debug_id(exec); return special_instruction; } -static IrInstruction *ir_build_return(IrGen *ir, AstNode *source_node, IrInstruction *return_value) { - IrInstructionReturn *return_instruction = ir_build_instruction(ir, source_node); - return_instruction->base.type_entry = ir->codegen->builtin_types.entry_unreachable; +template +static T *ir_build_instruction(IrGen *irg, AstNode *source_node) { + T *special_instruction = ir_create_instruction(irg->exec, source_node); + ir_instruction_append(irg->current_basic_block, &special_instruction->base); + return special_instruction; +} + +static IrInstruction *ir_insert_const_type(IrAnalyze *ira, IrInstruction *before_instruction, + IrInstruction *after_instruction, TypeTableEntry *type_entry) +{ + IrInstructionConst *const_instruction = ir_create_instruction(ira->exec, + before_instruction->source_node); + const_instruction->base.type_entry = ira->codegen->builtin_types.entry_type; + const_instruction->base.static_value.ok = true; + const_instruction->base.static_value.data.x_type = type_entry; + ir_instruction_insert(ira->current_basic_block, before_instruction, after_instruction, &const_instruction->base); + return &const_instruction->base; +} + +static IrInstruction *ir_insert_cast(IrAnalyze *ira, + IrInstruction *before_instruction, IrInstruction *after_instruction, + IrInstruction *dest_type, IrInstruction *value, bool is_implicit) +{ + IrInstructionCast *cast_instruction = ir_create_instruction(ira->exec, + before_instruction->source_node); + cast_instruction->dest_type = dest_type; + cast_instruction->value = value; + cast_instruction->is_implicit = is_implicit; + ir_instruction_insert(ira->current_basic_block, before_instruction, after_instruction, &cast_instruction->base); + return &cast_instruction->base; +} + +static IrInstruction *ir_build_return(IrGen *irg, AstNode *source_node, IrInstruction *return_value) { + IrInstructionReturn *return_instruction = ir_build_instruction(irg, source_node); + return_instruction->base.type_entry = irg->codegen->builtin_types.entry_unreachable; return_instruction->base.static_value.ok = true; return_instruction->value = return_value; return &return_instruction->base; } -static IrInstruction *ir_build_void(IrGen *ir, AstNode *source_node) { - IrInstructionConst *const_instruction = ir_build_instruction(ir, source_node); - const_instruction->base.type_entry = ir->codegen->builtin_types.entry_void; +static IrInstruction *ir_build_const_void(IrGen *irg, AstNode *source_node) { + IrInstructionConst *const_instruction = ir_build_instruction(irg, source_node); + const_instruction->base.type_entry = irg->codegen->builtin_types.entry_void; const_instruction->base.static_value.ok = true; return &const_instruction->base; } +static IrInstruction *ir_build_const_bignum(IrGen *irg, AstNode *source_node, BigNum *bignum) { + IrInstructionConst *const_instruction = ir_build_instruction(irg, source_node); + const_instruction->base.type_entry = (bignum->kind == BigNumKindInt) ? + irg->codegen->builtin_types.entry_num_lit_int : irg->codegen->builtin_types.entry_num_lit_float; + const_instruction->base.static_value.ok = true; + const_instruction->base.static_value.data.x_bignum = *bignum; + return &const_instruction->base; +} + +static IrInstruction *ir_build_const_type(IrGen *irg, AstNode *source_node, TypeTableEntry *type_entry) { + IrInstructionConst *const_instruction = ir_build_instruction(irg, source_node); + const_instruction->base.type_entry = irg->codegen->builtin_types.entry_type; + const_instruction->base.static_value.ok = true; + const_instruction->base.static_value.data.x_type = type_entry; + return &const_instruction->base; +} + +static IrInstruction *ir_build_const_fn(IrGen *irg, AstNode *source_node, FnTableEntry *fn_entry) { + IrInstructionConst *const_instruction = ir_build_instruction(irg, source_node); + const_instruction->base.type_entry = fn_entry->type_entry; + const_instruction->base.static_value.ok = true; + const_instruction->base.static_value.data.x_fn = fn_entry; + return &const_instruction->base; +} + +static IrInstruction *ir_build_const_generic_fn(IrGen *irg, AstNode *source_node, TypeTableEntry *fn_type) { + IrInstructionConst *const_instruction = ir_build_instruction(irg, source_node); + const_instruction->base.type_entry = fn_type; + const_instruction->base.static_value.ok = true; + const_instruction->base.static_value.data.x_type = fn_type; + return &const_instruction->base; +} + +static IrInstruction *ir_build_bin_op(IrGen *irg, AstNode *source_node, IrBinOp op_id, + IrInstruction *op1, IrInstruction *op2) +{ + IrInstructionBinOp *bin_op_instruction = ir_build_instruction(irg, source_node); + bin_op_instruction->op_id = op_id; + bin_op_instruction->op1 = op1; + bin_op_instruction->op2 = op2; + return &bin_op_instruction->base; +} + +static IrInstruction *ir_build_load_var(IrGen *irg, AstNode *source_node, VariableTableEntry *var) { + IrInstructionLoadVar *load_var_instruction = ir_build_instruction(irg, source_node); + load_var_instruction->base.type_entry = var->type; + load_var_instruction->var = var; + return &load_var_instruction->base; +} + + //static size_t get_conditional_defer_count(BlockContext *inner_block, BlockContext *outer_block) { // size_t result = 0; // while (inner_block != outer_block) { @@ -111,7 +226,7 @@ static IrInstruction *ir_build_void(IrGen *ir, AstNode *source_node) { // return result; //} -static void ir_gen_defers_for_block(IrGen *ir, BlockContext *inner_block, BlockContext *outer_block, +static void ir_gen_defers_for_block(IrGen *irg, BlockContext *inner_block, BlockContext *outer_block, bool gen_error_defers, bool gen_maybe_defers) { while (inner_block != outer_block) { @@ -121,15 +236,15 @@ static void ir_gen_defers_for_block(IrGen *ir, BlockContext *inner_block, BlockC (gen_maybe_defers && inner_block->node->data.defer.kind == ReturnKindMaybe))) { AstNode *defer_expr_node = inner_block->node->data.defer.expr; - ir_gen_node(ir, defer_expr_node, defer_expr_node->block_context); + ir_gen_node(irg, defer_expr_node, defer_expr_node->block_context); } inner_block = inner_block->parent; } } -//static IrInstruction *ir_gen_return(IrGen *ir, AstNode *source_node, IrInstruction *value, ReturnKnowledge rk) { +//static IrInstruction *ir_gen_return(IrGen *irg, AstNode *source_node, IrInstruction *value, ReturnKnowledge rk) { // BlockContext *defer_inner_block = source_node->block_context; -// BlockContext *defer_outer_block = ir->node->block_context; +// BlockContext *defer_outer_block = irg->node->block_context; // if (rk == ReturnKnowledgeUnknown) { // if (get_conditional_defer_count(defer_inner_block, defer_outer_block) > 0) { // // generate branching code that checks the return value and generates defers @@ -137,14 +252,14 @@ static void ir_gen_defers_for_block(IrGen *ir, BlockContext *inner_block, BlockC // zig_panic("TODO"); // } // } else if (rk != ReturnKnowledgeSkipDefers) { -// ir_gen_defers_for_block(ir, defer_inner_block, defer_outer_block, +// ir_gen_defers_for_block(irg, defer_inner_block, defer_outer_block, // rk == ReturnKnowledgeKnownError, rk == ReturnKnowledgeKnownNull); // } // -// return ir_build_return(ir, source_node, value); +// return ir_build_return(irg, source_node, value); //} -static IrInstruction *ir_gen_block(IrGen *ir, AstNode *block_node) { +static IrInstruction *ir_gen_block(IrGen *irg, AstNode *block_node) { assert(block_node->type == NodeTypeBlock); BlockContext *parent_context = block_node->block_context; @@ -154,8 +269,8 @@ static IrInstruction *ir_gen_block(IrGen *ir, AstNode *block_node) { IrInstruction *return_value = nullptr; for (size_t i = 0; i < block_node->data.block.statements.length; i += 1) { AstNode *statement_node = block_node->data.block.statements.at(i); - return_value = ir_gen_node(ir, statement_node, child_context); - if (statement_node->type == NodeTypeDefer && return_value != ir->codegen->invalid_instruction) { + return_value = ir_gen_node(irg, statement_node, child_context); + if (statement_node->type == NodeTypeDefer && return_value != irg->codegen->invalid_instruction) { // defer starts a new block context child_context = statement_node->data.defer.child_block; assert(child_context); @@ -163,20 +278,188 @@ static IrInstruction *ir_gen_block(IrGen *ir, AstNode *block_node) { } if (!return_value) - return_value = ir_build_void(ir, block_node); + return_value = ir_build_const_void(irg, block_node); - ir_gen_defers_for_block(ir, child_context, outer_block_context, false, false); + ir_gen_defers_for_block(irg, child_context, outer_block_context, false, false); return return_value; } -static IrInstruction *ir_gen_node(IrGen *ir, AstNode *node, BlockContext *block_context) { +static IrInstruction *ir_gen_bin_op_id(IrGen *irg, AstNode *node, IrBinOp op_id) { + IrInstruction *op1 = ir_gen_node(irg, node->data.bin_op_expr.op1, node->block_context); + IrInstruction *op2 = ir_gen_node(irg, node->data.bin_op_expr.op2, node->block_context); + return ir_build_bin_op(irg, node, op_id, op1, op2); +} + +static IrInstruction *ir_gen_bin_op(IrGen *irg, AstNode *node) { + assert(node->type == NodeTypeBinOpExpr); + + BinOpType bin_op_type = node->data.bin_op_expr.bin_op; + switch (bin_op_type) { + case BinOpTypeInvalid: + zig_unreachable(); + case BinOpTypeAssign: + case BinOpTypeAssignTimes: + case BinOpTypeAssignTimesWrap: + case BinOpTypeAssignDiv: + case BinOpTypeAssignMod: + case BinOpTypeAssignPlus: + case BinOpTypeAssignPlusWrap: + case BinOpTypeAssignMinus: + case BinOpTypeAssignMinusWrap: + case BinOpTypeAssignBitShiftLeft: + case BinOpTypeAssignBitShiftLeftWrap: + case BinOpTypeAssignBitShiftRight: + case BinOpTypeAssignBitAnd: + case BinOpTypeAssignBitXor: + case BinOpTypeAssignBitOr: + case BinOpTypeAssignBoolAnd: + case BinOpTypeAssignBoolOr: + zig_panic("TODO gen IR for assignment"); + case BinOpTypeBoolOr: + case BinOpTypeBoolAnd: + // note: this is not a direct mapping to IrBinOpBoolOr/And + // because of the control flow + zig_panic("TODO gen IR for bool or/and"); + case BinOpTypeCmpEq: + return ir_gen_bin_op_id(irg, node, IrBinOpCmpEq); + case BinOpTypeCmpNotEq: + return ir_gen_bin_op_id(irg, node, IrBinOpCmpNotEq); + case BinOpTypeCmpLessThan: + return ir_gen_bin_op_id(irg, node, IrBinOpCmpLessThan); + case BinOpTypeCmpGreaterThan: + return ir_gen_bin_op_id(irg, node, IrBinOpCmpGreaterThan); + case BinOpTypeCmpLessOrEq: + return ir_gen_bin_op_id(irg, node, IrBinOpCmpLessOrEq); + case BinOpTypeCmpGreaterOrEq: + return ir_gen_bin_op_id(irg, node, IrBinOpCmpGreaterOrEq); + case BinOpTypeBinOr: + return ir_gen_bin_op_id(irg, node, IrBinOpBinOr); + case BinOpTypeBinXor: + return ir_gen_bin_op_id(irg, node, IrBinOpBinXor); + case BinOpTypeBinAnd: + return ir_gen_bin_op_id(irg, node, IrBinOpBinAnd); + case BinOpTypeBitShiftLeft: + return ir_gen_bin_op_id(irg, node, IrBinOpBitShiftLeft); + case BinOpTypeBitShiftLeftWrap: + return ir_gen_bin_op_id(irg, node, IrBinOpBitShiftLeftWrap); + case BinOpTypeBitShiftRight: + return ir_gen_bin_op_id(irg, node, IrBinOpBitShiftRight); + case BinOpTypeAdd: + return ir_gen_bin_op_id(irg, node, IrBinOpAdd); + case BinOpTypeAddWrap: + return ir_gen_bin_op_id(irg, node, IrBinOpAddWrap); + case BinOpTypeSub: + return ir_gen_bin_op_id(irg, node, IrBinOpSub); + case BinOpTypeSubWrap: + return ir_gen_bin_op_id(irg, node, IrBinOpSubWrap); + case BinOpTypeMult: + return ir_gen_bin_op_id(irg, node, IrBinOpMult); + case BinOpTypeMultWrap: + return ir_gen_bin_op_id(irg, node, IrBinOpMultWrap); + case BinOpTypeDiv: + return ir_gen_bin_op_id(irg, node, IrBinOpDiv); + case BinOpTypeMod: + return ir_gen_bin_op_id(irg, node, IrBinOpMod); + case BinOpTypeArrayCat: + return ir_gen_bin_op_id(irg, node, IrBinOpArrayCat); + case BinOpTypeArrayMult: + return ir_gen_bin_op_id(irg, node, IrBinOpArrayMult); + case BinOpTypeUnwrapMaybe: + zig_panic("TODO gen IR for unwrap maybe"); + } + zig_unreachable(); +} + +static IrInstruction *ir_gen_num_lit(IrGen *irg, AstNode *node) { + assert(node->type == NodeTypeNumberLiteral); + + if (node->data.number_literal.overflow) { + add_node_error(irg->codegen, node, buf_sprintf("number literal too large to be represented in any type")); + return irg->codegen->invalid_instruction; + } + + return ir_build_const_bignum(irg, node, node->data.number_literal.bignum); +} + +static IrInstruction *ir_gen_decl_ref(IrGen *irg, AstNode *source_node, AstNode *decl_node, + bool pointer_only, BlockContext *scope) +{ + resolve_top_level_decl(irg->codegen, decl_node, pointer_only); + TopLevelDecl *tld = get_as_top_level_decl(decl_node); + if (tld->resolution == TldResolutionInvalid) + return irg->codegen->invalid_instruction; + + if (decl_node->type == NodeTypeVariableDeclaration) { + VariableTableEntry *var = decl_node->data.variable_declaration.variable; + return ir_build_load_var(irg, source_node, var); + } else if (decl_node->type == NodeTypeFnProto) { + FnTableEntry *fn_entry = decl_node->data.fn_proto.fn_table_entry; + assert(fn_entry->type_entry); + if (fn_entry->type_entry->id == TypeTableEntryIdGenericFn) { + return ir_build_const_generic_fn(irg, source_node, fn_entry->type_entry); + } else { + return ir_build_const_fn(irg, source_node, fn_entry); + } + } else if (decl_node->type == NodeTypeContainerDecl) { + if (decl_node->data.struct_decl.generic_params.length > 0) { + TypeTableEntry *type_entry = decl_node->data.struct_decl.generic_fn_type; + assert(type_entry); + return ir_build_const_generic_fn(irg, source_node, type_entry); + } else { + return ir_build_const_type(irg, source_node, decl_node->data.struct_decl.type_entry); + } + } else if (decl_node->type == NodeTypeTypeDecl) { + return ir_build_const_type(irg, source_node, decl_node->data.type_decl.child_type_entry); + } else { + zig_unreachable(); + } +} + +static IrInstruction *ir_gen_symbol(IrGen *irg, AstNode *node, bool pointer_only) { + assert(node->type == NodeTypeSymbol); + + if (node->data.symbol_expr.override_type_entry) + return ir_build_const_type(irg, node, node->data.symbol_expr.override_type_entry); + + Buf *variable_name = node->data.symbol_expr.symbol; + + auto primitive_table_entry = irg->codegen->primitive_type_table.maybe_get(variable_name); + if (primitive_table_entry) + return ir_build_const_type(irg, node, primitive_table_entry->value); + + VariableTableEntry *var = find_variable(irg->codegen, node->block_context, variable_name); + if (var) + return ir_build_load_var(irg, node, var); + + AstNode *decl_node = find_decl(node->block_context, variable_name); + if (decl_node) + return ir_gen_decl_ref(irg, node, decl_node, pointer_only, node->block_context); + + if (node->owner->any_imports_failed) { + // skip the error message since we had a failing import in this file + // if an import breaks we don't need redundant undeclared identifier errors + return irg->codegen->invalid_instruction; + } + + add_node_error(irg->codegen, node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name))); + return irg->codegen->invalid_instruction; +} + +static IrInstruction *ir_gen_node_extra(IrGen *irg, AstNode *node, BlockContext *block_context, + bool pointer_only) +{ node->block_context = block_context; switch (node->type) { case NodeTypeBlock: - return ir_gen_block(ir, node); + return ir_gen_block(irg, node); case NodeTypeBinOpExpr: + return ir_gen_bin_op(irg, node); + case NodeTypeNumberLiteral: + return ir_gen_num_lit(irg, node); + case NodeTypeSymbol: + return ir_gen_symbol(irg, node, pointer_only); case NodeTypeUnwrapErrorExpr: case NodeTypeReturnExpr: case NodeTypeDefer: @@ -191,14 +474,12 @@ static IrInstruction *ir_gen_node(IrGen *ir, AstNode *node, BlockContext *block_ case NodeTypeWhileExpr: case NodeTypeForExpr: case NodeTypeAsmExpr: - case NodeTypeSymbol: case NodeTypeGoto: case NodeTypeBreak: case NodeTypeContinue: case NodeTypeLabel: case NodeTypeContainerInitExpr: case NodeTypeSwitchExpr: - case NodeTypeNumberLiteral: case NodeTypeBoolLiteral: case NodeTypeStringLiteral: case NodeTypeCharLiteral: @@ -228,42 +509,49 @@ static IrInstruction *ir_gen_node(IrGen *ir, AstNode *node, BlockContext *block_ zig_unreachable(); } +static IrInstruction *ir_gen_node(IrGen *irg, AstNode *node, BlockContext *scope) { + bool pointer_only_no = false; + return ir_gen_node_extra(irg, node, scope, pointer_only_no); +} + static IrInstruction *ir_gen_add_return(CodeGen *g, AstNode *node, BlockContext *scope, - IrExecutable *ir_executable, bool add_return) + IrExecutable *ir_executable, bool add_return, bool pointer_only) { assert(node->owner); IrGen ir_gen = {0}; - IrGen *ir = &ir_gen; + IrGen *irg = &ir_gen; - ir->codegen = g; - ir->node = node; - ir->exec = ir_executable; + irg->codegen = g; + irg->node = node; + irg->exec = ir_executable; - ir->exec->basic_block_list = allocate(1); - ir->exec->basic_block_count = 1; + irg->exec->basic_block_list = allocate(1); + irg->exec->basic_block_count = 1; IrBasicBlock *entry_basic_block = allocate(1); - ir->current_basic_block = entry_basic_block; - ir->exec->basic_block_list[0] = entry_basic_block; + irg->current_basic_block = entry_basic_block; + irg->exec->basic_block_list[0] = entry_basic_block; - IrInstruction *result = ir_gen_node(ir, node, scope); + IrInstruction *result = ir_gen_node_extra(irg, node, scope, pointer_only); assert(result); if (result == g->invalid_instruction) return result; if (add_return) - return ir_build_return(ir, result->source_node, result); + return ir_build_return(irg, result->source_node, result); return result; } -IrInstruction *ir_gen(CodeGen *g, AstNode *node, BlockContext *scope, IrExecutable *ir_executable) { - return ir_gen_add_return(g, node, scope, ir_executable, false); +IrInstruction *ir_gen(CodeGen *codegen, AstNode *node, BlockContext *scope, IrExecutable *ir_executable) { + bool add_return_no = false; + bool pointer_only_no = false; + return ir_gen_add_return(codegen, node, scope, ir_executable, add_return_no, pointer_only_no); } -IrInstruction *ir_gen_fn(CodeGen *g, FnTableEntry *fn_entry) { +IrInstruction *ir_gen_fn(CodeGen *codegn, FnTableEntry *fn_entry) { assert(fn_entry); IrExecutable *ir_executable = &fn_entry->ir_executable; @@ -274,7 +562,8 @@ IrInstruction *ir_gen_fn(CodeGen *g, FnTableEntry *fn_entry) { BlockContext *scope = fn_def_node->data.fn_def.block_context; bool add_return_yes = true; - return ir_gen_add_return(g, body_node, scope, ir_executable, add_return_yes); + bool pointer_only_no = false; + return ir_gen_add_return(codegn, body_node, scope, ir_executable, add_return_yes, pointer_only_no); } /* @@ -322,96 +611,496 @@ static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *no // return nullptr; //} -static IrInstruction *ir_get_casted_instruction(CodeGen *g, IrInstruction *instruction, - TypeTableEntry *expected_type) -{ - assert(instruction); - assert(instruction != g->invalid_instruction); - assert(!expected_type || expected_type->id != TypeTableEntryIdInvalid); - assert(instruction->type_entry); - assert(instruction->type_entry->id != TypeTableEntryIdInvalid); - if (expected_type == nullptr) - return instruction; // anything will do - if (expected_type == instruction->type_entry) - return instruction; // match - if (instruction->type_entry->id == TypeTableEntryIdUnreachable) - return instruction; +static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruction, TypeTableEntry *other_type) { + TypeTableEntry *other_type_underlying = get_underlying_type(other_type); - zig_panic("TODO implicit cast instruction"); + if (other_type_underlying->id == TypeTableEntryIdInvalid) { + return false; + } + + ConstExprValue *const_val = &instruction->static_value; + assert(const_val->ok); + if (other_type_underlying->id == TypeTableEntryIdFloat) { + return true; + } else if (other_type_underlying->id == TypeTableEntryIdInt && + const_val->data.x_bignum.kind == BigNumKindInt) + { + if (bignum_fits_in_bits(&const_val->data.x_bignum, other_type_underlying->data.integral.bit_count, + other_type_underlying->data.integral.is_signed)) + { + return true; + } + } else if ((other_type_underlying->id == TypeTableEntryIdNumLitFloat && + const_val->data.x_bignum.kind == BigNumKindFloat) || + (other_type_underlying->id == TypeTableEntryIdNumLitInt && + const_val->data.x_bignum.kind == BigNumKindInt)) + { + return true; + } + + const char *num_lit_str = (const_val->data.x_bignum.kind == BigNumKindFloat) ? "float" : "integer"; + + add_node_error(ira->codegen, instruction->source_node, + buf_sprintf("%s value %s cannot be implicitly casted to type '%s'", + num_lit_str, + buf_ptr(bignum_to_buf(&const_val->data.x_bignum)), + buf_ptr(&other_type->name))); + return false; } -static TypeTableEntry *ir_analyze_instruction_return(CodeGen *g, IrInstructionReturn *return_instruction) { +static TypeTableEntry *ir_determine_peer_types(IrAnalyze *ira, IrInstruction *parent_instruction, + IrInstruction **instructions, size_t instruction_count) +{ + assert(instruction_count >= 1); + IrInstruction *prev_inst = instructions[0]; + if (prev_inst->type_entry->id == TypeTableEntryIdInvalid) { + return ira->codegen->builtin_types.entry_invalid; + } + for (size_t i = 1; i < instruction_count; i += 1) { + IrInstruction *cur_inst = instructions[i]; + TypeTableEntry *cur_type = cur_inst->type_entry; + TypeTableEntry *prev_type = prev_inst->type_entry; + if (cur_type->id == TypeTableEntryIdInvalid) { + return cur_type; + } else if (types_match_const_cast_only(prev_type, cur_type)) { + continue; + } else if (types_match_const_cast_only(cur_type, prev_type)) { + prev_inst = cur_inst; + continue; + } else if (prev_type->id == TypeTableEntryIdUnreachable) { + prev_inst = cur_inst; + } else if (cur_type->id == TypeTableEntryIdUnreachable) { + continue; + } else if (prev_type->id == TypeTableEntryIdInt && + cur_type->id == TypeTableEntryIdInt && + prev_type->data.integral.is_signed == cur_type->data.integral.is_signed) + { + if (cur_type->data.integral.bit_count > prev_type->data.integral.bit_count) { + prev_inst = cur_inst; + } + continue; + } else if (prev_type->id == TypeTableEntryIdFloat && + cur_type->id == TypeTableEntryIdFloat) + { + if (cur_type->data.floating.bit_count > prev_type->data.floating.bit_count) { + prev_inst = cur_inst; + } + } else if (prev_type->id == TypeTableEntryIdErrorUnion && + types_match_const_cast_only(prev_type->data.error.child_type, cur_type)) + { + continue; + } else if (cur_type->id == TypeTableEntryIdErrorUnion && + types_match_const_cast_only(cur_type->data.error.child_type, prev_type)) + { + prev_inst = cur_inst; + continue; + } else if (prev_type->id == TypeTableEntryIdNumLitInt || + prev_type->id == TypeTableEntryIdNumLitFloat) + { + if (ir_num_lit_fits_in_other_type(ira, prev_inst, cur_type)) { + prev_inst = cur_inst; + continue; + } else { + return ira->codegen->builtin_types.entry_invalid; + } + } else if (cur_type->id == TypeTableEntryIdNumLitInt || + cur_type->id == TypeTableEntryIdNumLitFloat) + { + if (ir_num_lit_fits_in_other_type(ira, cur_inst, prev_type)) { + continue; + } else { + return ira->codegen->builtin_types.entry_invalid; + } + } else { + add_node_error(ira->codegen, parent_instruction->source_node, + buf_sprintf("incompatible types: '%s' and '%s'", + buf_ptr(&prev_type->name), buf_ptr(&cur_type->name))); + + return ira->codegen->builtin_types.entry_invalid; + } + } + return prev_inst->type_entry; +} + +enum ImplicitCastMatchResult { + ImplicitCastMatchResultNo, + ImplicitCastMatchResultYes, + ImplicitCastMatchResultReportedError, +}; + +static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, TypeTableEntry *expected_type, + TypeTableEntry *actual_type, IrInstruction *value) +{ + if (types_match_const_cast_only(expected_type, actual_type)) { + return ImplicitCastMatchResultYes; + } + + // implicit conversion from non maybe type to maybe type + if (expected_type->id == TypeTableEntryIdMaybe && + ir_types_match_with_implicit_cast(ira, expected_type->data.maybe.child_type, actual_type, value)) + { + return ImplicitCastMatchResultYes; + } + + // implicit conversion from null literal to maybe type + if (expected_type->id == TypeTableEntryIdMaybe && + actual_type->id == TypeTableEntryIdNullLit) + { + return ImplicitCastMatchResultYes; + } + + // implicit conversion from error child type to error type + if (expected_type->id == TypeTableEntryIdErrorUnion && + ir_types_match_with_implicit_cast(ira, expected_type->data.error.child_type, actual_type, value)) + { + return ImplicitCastMatchResultYes; + } + + // implicit conversion from pure error to error union type + if (expected_type->id == TypeTableEntryIdErrorUnion && + actual_type->id == TypeTableEntryIdPureError) + { + return ImplicitCastMatchResultYes; + } + + // implicit widening conversion + if (expected_type->id == TypeTableEntryIdInt && + actual_type->id == TypeTableEntryIdInt && + expected_type->data.integral.is_signed == actual_type->data.integral.is_signed && + expected_type->data.integral.bit_count >= actual_type->data.integral.bit_count) + { + return ImplicitCastMatchResultYes; + } + + // small enough unsigned ints can get casted to large enough signed ints + if (expected_type->id == TypeTableEntryIdInt && expected_type->data.integral.is_signed && + actual_type->id == TypeTableEntryIdInt && !actual_type->data.integral.is_signed && + expected_type->data.integral.bit_count > actual_type->data.integral.bit_count) + { + return ImplicitCastMatchResultYes; + } + + // implicit float widening conversion + if (expected_type->id == TypeTableEntryIdFloat && + actual_type->id == TypeTableEntryIdFloat && + expected_type->data.floating.bit_count >= actual_type->data.floating.bit_count) + { + return ImplicitCastMatchResultYes; + } + + // implicit array to slice conversion + if (expected_type->id == TypeTableEntryIdStruct && + expected_type->data.structure.is_slice && + actual_type->id == TypeTableEntryIdArray && + types_match_const_cast_only( + expected_type->data.structure.fields[0].type_entry->data.pointer.child_type, + actual_type->data.array.child_type)) + { + return ImplicitCastMatchResultYes; + } + + // implicit number literal to typed number + if ((actual_type->id == TypeTableEntryIdNumLitFloat || + actual_type->id == TypeTableEntryIdNumLitInt)) + { + if (ir_num_lit_fits_in_other_type(ira, value, expected_type)) { + return ImplicitCastMatchResultYes; + } else { + return ImplicitCastMatchResultReportedError; + } + } + + return ImplicitCastMatchResultNo; +} + +static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, IrInstruction *parent_instruction, + IrInstruction **instructions, size_t instruction_count) +{ + return ir_determine_peer_types(ira, parent_instruction, instructions, instruction_count); +} + +static IrInstruction *ir_get_casted_value(IrAnalyze *ira, IrInstruction *value, + TypeTableEntry *expected_type, + IrInstruction *before_instruction, IrInstruction *after_instruction) +{ + assert(value); + assert(before_instruction || after_instruction); + assert(!before_instruction || !after_instruction); + assert(value != ira->codegen->invalid_instruction); + assert(!expected_type || expected_type->id != TypeTableEntryIdInvalid); + assert(value->type_entry); + assert(value->type_entry->id != TypeTableEntryIdInvalid); + if (expected_type == nullptr) + return value; // anything will do + if (expected_type == value->type_entry) + return value; // match + if (value->type_entry->id == TypeTableEntryIdUnreachable) + return value; + + ImplicitCastMatchResult result = ir_types_match_with_implicit_cast(ira, expected_type, value->type_entry, value); + switch (result) { + case ImplicitCastMatchResultNo: + add_node_error(ira->codegen, first_executing_node(value->source_node), + buf_sprintf("expected type '%s', got '%s'", + buf_ptr(&expected_type->name), + buf_ptr(&value->type_entry->name))); + return ira->codegen->invalid_instruction; + + case ImplicitCastMatchResultYes: + { + IrInstruction *dest_type = ir_insert_const_type(ira, before_instruction, + after_instruction, expected_type); + bool is_implicit = true; + IrInstruction *cast_instruction = ir_insert_cast(ira, nullptr, dest_type, + dest_type, value, is_implicit); + return cast_instruction; + } + case ImplicitCastMatchResultReportedError: + return ira->codegen->invalid_instruction; + } + + zig_unreachable(); +} + +static TypeTableEntry *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructionReturn *return_instruction) { AstNode *source_node = return_instruction->base.source_node; BlockContext *scope = source_node->block_context; if (!scope->fn_entry) { - add_node_error(g, source_node, buf_sprintf("return expression outside function definition")); - return g->builtin_types.entry_invalid; + add_node_error(ira->codegen, source_node, buf_sprintf("return expression outside function definition")); + return ira->codegen->builtin_types.entry_invalid; } TypeTableEntry *expected_return_type = scope->fn_entry->type_entry->data.fn.fn_type_id.return_type; if (expected_return_type->id == TypeTableEntryIdVoid && !return_instruction->value) { - return g->builtin_types.entry_unreachable; + return ira->codegen->builtin_types.entry_unreachable; } - return_instruction->value = ir_get_casted_instruction(g, return_instruction->value, expected_return_type); - if (return_instruction->value == g->invalid_instruction) { - return g->builtin_types.entry_invalid; + return_instruction->value = ir_get_casted_value(ira, + return_instruction->value, expected_return_type, &return_instruction->base, nullptr); + if (return_instruction->value == ira->codegen->invalid_instruction) { + return ira->codegen->builtin_types.entry_invalid; } - return g->builtin_types.entry_unreachable; + return ira->codegen->builtin_types.entry_unreachable; } -static TypeTableEntry *ir_analyze_instruction_const(CodeGen *g, IrInstructionConst *const_instruction) { +static TypeTableEntry *ir_analyze_instruction_const(IrAnalyze *ira, IrInstructionConst *const_instruction) { return const_instruction->base.type_entry; } -static TypeTableEntry *ir_analyze_instruction_nocast(CodeGen *g, IrInstruction *instruction) { +static TypeTableEntry *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { + IrInstruction *op1 = bin_op_instruction->op1; + IrInstruction *op2 = bin_op_instruction->op2; + + IrInstruction *casted_op1 = ir_get_casted_value(ira, op1, ira->codegen->builtin_types.entry_bool, + &bin_op_instruction->base, nullptr); + if (casted_op1 == ira->codegen->invalid_instruction) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *casted_op2 = ir_get_casted_value(ira, op2, ira->codegen->builtin_types.entry_bool, + &bin_op_instruction->base, nullptr); + if (casted_op2 == ira->codegen->invalid_instruction) + return ira->codegen->builtin_types.entry_invalid; + + return ira->codegen->builtin_types.entry_bool; +} + +static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { + IrInstruction *op1 = bin_op_instruction->op1; + IrInstruction *op2 = bin_op_instruction->op2; + IrInstruction *instructions[] = {op1, op2}; + TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, &bin_op_instruction->base, instructions, 2); + if (resolved_type->id == TypeTableEntryIdInvalid) + return resolved_type; + IrBinOp op_id = bin_op_instruction->op_id; + + bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); + AstNode *source_node = bin_op_instruction->base.source_node; + switch (resolved_type->id) { + case TypeTableEntryIdInvalid: + return ira->codegen->builtin_types.entry_invalid; + + case TypeTableEntryIdNumLitFloat: + case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdInt: + case TypeTableEntryIdFloat: + break; + + case TypeTableEntryIdBool: + case TypeTableEntryIdMetaType: + case TypeTableEntryIdVoid: + case TypeTableEntryIdPointer: + case TypeTableEntryIdPureError: + case TypeTableEntryIdFn: + case TypeTableEntryIdTypeDecl: + case TypeTableEntryIdNamespace: + case TypeTableEntryIdBlock: + case TypeTableEntryIdGenericFn: + if (!is_equality_cmp) { + add_node_error(ira->codegen, source_node, + buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + break; + + case TypeTableEntryIdEnum: + if (!is_equality_cmp || resolved_type->data.enumeration.gen_field_count != 0) { + add_node_error(ira->codegen, source_node, + buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + break; + + case TypeTableEntryIdUnreachable: + case TypeTableEntryIdArray: + case TypeTableEntryIdStruct: + case TypeTableEntryIdUndefLit: + case TypeTableEntryIdNullLit: + case TypeTableEntryIdMaybe: + case TypeTableEntryIdErrorUnion: + case TypeTableEntryIdUnion: + add_node_error(ira->codegen, source_node, + buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); + return ira->codegen->builtin_types.entry_invalid; + + case TypeTableEntryIdVar: + zig_unreachable(); + } + + return ira->codegen->builtin_types.entry_bool; +} + +static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { + IrInstruction *op1 = bin_op_instruction->op1; + IrInstruction *op2 = bin_op_instruction->op2; + IrInstruction *instructions[] = {op1, op2}; + TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, &bin_op_instruction->base, instructions, 2); + if (resolved_type->id == TypeTableEntryIdInvalid) + return resolved_type; + IrBinOp op_id = bin_op_instruction->op_id; + + if (resolved_type->id == TypeTableEntryIdInt || + resolved_type->id == TypeTableEntryIdNumLitInt) + { + // int + } else if ((resolved_type->id == TypeTableEntryIdFloat || + resolved_type->id == TypeTableEntryIdNumLitFloat) && + (op_id == IrBinOpAdd || + op_id == IrBinOpSub || + op_id == IrBinOpMult || + op_id == IrBinOpDiv || + op_id == IrBinOpMod)) + { + // float + } else { + AstNode *source_node = bin_op_instruction->base.source_node; + add_node_error(ira->codegen, source_node, buf_sprintf("invalid operands to binary expression: '%s' and '%s'", + buf_ptr(&op1->type_entry->name), + buf_ptr(&op2->type_entry->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + return resolved_type; +} + + +static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { + IrBinOp op_id = bin_op_instruction->op_id; + switch (op_id) { + case IrBinOpInvalid: + zig_unreachable(); + case IrBinOpBoolOr: + case IrBinOpBoolAnd: + return ir_analyze_bin_op_bool(ira, bin_op_instruction); + case IrBinOpCmpEq: + case IrBinOpCmpNotEq: + case IrBinOpCmpLessThan: + case IrBinOpCmpGreaterThan: + case IrBinOpCmpLessOrEq: + case IrBinOpCmpGreaterOrEq: + return ir_analyze_bin_op_cmp(ira, bin_op_instruction); + case IrBinOpBinOr: + case IrBinOpBinXor: + case IrBinOpBinAnd: + case IrBinOpBitShiftLeft: + case IrBinOpBitShiftLeftWrap: + case IrBinOpBitShiftRight: + case IrBinOpAdd: + case IrBinOpAddWrap: + case IrBinOpSub: + case IrBinOpSubWrap: + case IrBinOpMult: + case IrBinOpMultWrap: + case IrBinOpDiv: + case IrBinOpMod: + return ir_analyze_bin_op_math(ira, bin_op_instruction); + case IrBinOpArrayCat: + case IrBinOpArrayMult: + zig_panic("TODO analyze more binary operations"); + } + zig_unreachable(); +} + +static TypeTableEntry *ir_analyze_instruction_load_var(IrAnalyze *ira, IrInstructionLoadVar *load_var_instruction) { + return load_var_instruction->var->type; +} + +static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: zig_unreachable(); case IrInstructionIdReturn: - return ir_analyze_instruction_return(g, (IrInstructionReturn *)instruction); + return ir_analyze_instruction_return(ira, (IrInstructionReturn *)instruction); case IrInstructionIdConst: - return ir_analyze_instruction_const(g, (IrInstructionConst *)instruction); + return ir_analyze_instruction_const(ira, (IrInstructionConst *)instruction); + case IrInstructionIdBinOp: + return ir_analyze_instruction_bin_op(ira, (IrInstructionBinOp *)instruction); + case IrInstructionIdLoadVar: + return ir_analyze_instruction_load_var(ira, (IrInstructionLoadVar *)instruction); case IrInstructionIdCondBr: case IrInstructionIdSwitchBr: case IrInstructionIdPhi: - case IrInstructionIdBinOp: - case IrInstructionIdLoadVar: case IrInstructionIdStoreVar: case IrInstructionIdCall: case IrInstructionIdBuiltinCall: + case IrInstructionIdCast: zig_panic("TODO analyze more instructions"); } zig_unreachable(); } -static TypeTableEntry *ir_analyze_instruction(CodeGen *g, IrInstruction *instruction, +static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction, TypeTableEntry *expected_type) { - TypeTableEntry *instruction_type = ir_analyze_instruction_nocast(g, instruction); + TypeTableEntry *instruction_type = ir_analyze_instruction_nocast(ira, instruction); instruction->type_entry = instruction_type; - IrInstruction *casted_instruction = ir_get_casted_instruction(g, instruction, expected_type); + IrInstruction *casted_instruction = ir_get_casted_value(ira, instruction, expected_type, + nullptr, instruction); return casted_instruction->type_entry; } -TypeTableEntry *ir_analyze(CodeGen *g, IrExecutable *executable, TypeTableEntry *expected_type) { - TypeTableEntry *return_type = g->builtin_types.entry_void; +TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *executable, TypeTableEntry *expected_type) { + IrAnalyze ir_analyze_data = {}; + IrAnalyze *ira = &ir_analyze_data; + ira->codegen = codegen; + ira->exec = executable; + + TypeTableEntry *return_type = ira->codegen->builtin_types.entry_void; for (size_t i = 0; i < executable->basic_block_count; i += 1) { - IrBasicBlock *current_block = executable->basic_block_list[i]; + ira->current_basic_block = executable->basic_block_list[i]; - for (IrInstruction *instruction = current_block->first; instruction != nullptr; + for (IrInstruction *instruction = ira->current_basic_block->first; instruction != nullptr; instruction = instruction->next) { if (return_type->id == TypeTableEntryIdUnreachable) { - add_node_error(g, first_executing_node(instruction->source_node), + add_node_error(ira->codegen, first_executing_node(instruction->source_node), buf_sprintf("unreachable code")); break; } - bool is_last = (instruction == current_block->last); + bool is_last = (instruction == ira->current_basic_block->last); TypeTableEntry *passed_expected_type = is_last ? expected_type : nullptr; - return_type = ir_analyze_instruction(g, instruction, passed_expected_type); + return_type = ir_analyze_instruction(ira, instruction, passed_expected_type); } } diff --git a/src/ir_print.cpp b/src/ir_print.cpp index e3033f2db..852ea7b9d 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -25,12 +25,24 @@ static void ir_print_return(IrPrint *irp, IrInstructionReturn *return_instructio static void ir_print_const(IrPrint *irp, IrInstructionConst *const_instruction) { ir_print_prefix(irp, &const_instruction->base); - switch (const_instruction->base.type_entry->id) { + TypeTableEntry *type_entry = const_instruction->base.type_entry; + fprintf(irp->f, "%s ", buf_ptr(&type_entry->name)); + switch (type_entry->id) { case TypeTableEntryIdInvalid: zig_unreachable(); case TypeTableEntryIdVoid: - fprintf(irp->f, "void\n"); + fprintf(irp->f, "%s\n", "void"); break; + case TypeTableEntryIdNumLitFloat: + fprintf(irp->f, "%f\n", const_instruction->base.static_value.data.x_bignum.data.x_float); + break; + case TypeTableEntryIdNumLitInt: + { + BigNum *bignum = &const_instruction->base.static_value.data.x_bignum; + const char *negative_str = bignum->is_negative ? "-" : ""; + fprintf(irp->f, "%s%llu\n", negative_str, bignum->data.x_uint); + break; + } case TypeTableEntryIdVar: case TypeTableEntryIdMetaType: case TypeTableEntryIdBool: @@ -40,8 +52,6 @@ static void ir_print_const(IrPrint *irp, IrInstructionConst *const_instruction) case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: @@ -58,6 +68,104 @@ static void ir_print_const(IrPrint *irp, IrInstructionConst *const_instruction) } } + +static const char *ir_bin_op_id_str(IrBinOp op_id) { + switch (op_id) { + case IrBinOpInvalid: + zig_unreachable(); + case IrBinOpBoolOr: + return "BoolOr"; + case IrBinOpBoolAnd: + return "BoolAnd"; + case IrBinOpCmpEq: + return "=="; + case IrBinOpCmpNotEq: + return "!="; + case IrBinOpCmpLessThan: + return "<"; + case IrBinOpCmpGreaterThan: + return ">"; + case IrBinOpCmpLessOrEq: + return "<="; + case IrBinOpCmpGreaterOrEq: + return ">="; + case IrBinOpBinOr: + return "|"; + case IrBinOpBinXor: + return "^"; + case IrBinOpBinAnd: + return "&"; + case IrBinOpBitShiftLeft: + return "<<"; + case IrBinOpBitShiftLeftWrap: + return "<<%"; + case IrBinOpBitShiftRight: + return ">>"; + case IrBinOpAdd: + return "+"; + case IrBinOpAddWrap: + return "+%"; + case IrBinOpSub: + return "-"; + case IrBinOpSubWrap: + return "-%"; + case IrBinOpMult: + return "*"; + case IrBinOpMultWrap: + return "*%"; + case IrBinOpDiv: + return "/"; + case IrBinOpMod: + return "%"; + case IrBinOpArrayCat: + return "++"; + case IrBinOpArrayMult: + return "**"; + } + zig_unreachable(); +} + +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); +} + +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)); +} + +static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { + switch (instruction->id) { + case IrInstructionIdInvalid: + zig_unreachable(); + case IrInstructionIdReturn: + ir_print_return(irp, (IrInstructionReturn *)instruction); + break; + case IrInstructionIdConst: + ir_print_const(irp, (IrInstructionConst *)instruction); + break; + case IrInstructionIdBinOp: + ir_print_bin_op(irp, (IrInstructionBinOp *)instruction); + break; + case IrInstructionIdLoadVar: + ir_print_load_var(irp, (IrInstructionLoadVar *)instruction); + break; + case IrInstructionIdCondBr: + case IrInstructionIdSwitchBr: + case IrInstructionIdPhi: + case IrInstructionIdStoreVar: + case IrInstructionIdCall: + case IrInstructionIdBuiltinCall: + case IrInstructionIdCast: + zig_panic("TODO print more IR instructions"); + } +} + void ir_print(FILE *f, IrExecutable *executable, int indent_size) { IrPrint ir_print = {}; IrPrint *irp = &ir_print; @@ -70,25 +178,7 @@ void ir_print(FILE *f, IrExecutable *executable, int indent_size) { for (IrInstruction *instruction = current_block->first; instruction != nullptr; instruction = instruction->next) { - switch (instruction->id) { - case IrInstructionIdInvalid: - zig_unreachable(); - case IrInstructionIdReturn: - ir_print_return(irp, (IrInstructionReturn *)instruction); - break; - case IrInstructionIdConst: - ir_print_const(irp, (IrInstructionConst *)instruction); - break; - case IrInstructionIdCondBr: - case IrInstructionIdSwitchBr: - case IrInstructionIdPhi: - case IrInstructionIdBinOp: - case IrInstructionIdLoadVar: - case IrInstructionIdStoreVar: - case IrInstructionIdCall: - case IrInstructionIdBuiltinCall: - zig_panic("TODO print more IR instructions"); - } + ir_print_instruction(irp, instruction); } } }