Commit cd1bd78aa9
Changed files (6)
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
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;
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
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<IrInstruction>(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<IrInstruction>(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) {
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<typename T>
-static T *ir_build_instruction(IrGen *ir, AstNode *source_node) {
+static T *ir_create_instruction(IrExecutable *exec, AstNode *source_node) {
T *special_instruction = allocate<T>(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;
+}
+
+template<typename T>
+static T *ir_build_instruction(IrGen *irg, AstNode *source_node) {
+ T *special_instruction = ir_create_instruction<T>(irg->exec, source_node);
+ ir_instruction_append(irg->current_basic_block, &special_instruction->base);
return special_instruction;
}
-static IrInstruction *ir_build_return(IrGen *ir, AstNode *source_node, IrInstruction *return_value) {
- IrInstructionReturn *return_instruction = ir_build_instruction<IrInstructionReturn>(ir, source_node);
- return_instruction->base.type_entry = ir->codegen->builtin_types.entry_unreachable;
+static IrInstruction *ir_insert_const_type(IrAnalyze *ira, IrInstruction *before_instruction,
+ IrInstruction *after_instruction, TypeTableEntry *type_entry)
+{
+ IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(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<IrInstructionCast>(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<IrInstructionReturn>(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<IrInstructionConst>(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<IrInstructionConst>(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<IrInstructionConst>(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<IrInstructionConst>(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<IrInstructionConst>(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<IrInstructionConst>(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<IrInstructionBinOp>(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<IrInstructionLoadVar>(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<IrBasicBlock*>(1);
- ir->exec->basic_block_count = 1;
+ irg->exec->basic_block_list = allocate<IrBasicBlock*>(1);
+ irg->exec->basic_block_count = 1;
IrBasicBlock *entry_basic_block = allocate<IrBasicBlock>(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)
+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);
+
+ 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_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(instruction);
- assert(instruction != g->invalid_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(instruction->type_entry);
- assert(instruction->type_entry->id != TypeTableEntryIdInvalid);
+ assert(value->type_entry);
+ assert(value->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;
+ 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_panic("TODO implicit cast instruction");
+ zig_unreachable();
}
-static TypeTableEntry *ir_analyze_instruction_return(CodeGen *g, IrInstructionReturn *return_instruction) {
+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);
}
}
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);
}
}
}