/* * Copyright (c) 2016 Andrew Kelley * * This file is part of zig, which is MIT licensed. * See http://opensource.org/licenses/MIT */ #include "analyze.hpp" #include "ast_render.hpp" #include "error.hpp" #include "eval.hpp" #include "ir.hpp" #include "ir_print.hpp" #include "os.hpp" #include "parseh.hpp" struct IrExecContext { ConstExprValue *mem_slot_list; size_t mem_slot_count; }; struct LoopStackItem { IrBasicBlock *break_block; IrBasicBlock *continue_block; bool is_inline; }; struct IrBuilder { CodeGen *codegen; IrExecutable *exec; IrBasicBlock *current_basic_block; ZigList loop_stack; }; struct IrAnalyze { CodeGen *codegen; IrBuilder old_irb; IrBuilder new_irb; IrExecContext exec_context; ZigList old_bb_queue; size_t block_queue_index; size_t instruction_index; TypeTableEntry *explicit_return_type; ZigList implicit_return_type_list; IrBasicBlock *const_predecessor_bb; }; static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope); static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LValPurpose lval); static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction); ConstExprValue *const_ptr_pointee(ConstExprValue *const_val) { assert(const_val->special == ConstValSpecialStatic); ConstExprValue *base_ptr = const_val->data.x_ptr.base_ptr; size_t index = const_val->data.x_ptr.index; if (index == SIZE_MAX) { return base_ptr; } else { assert(index < base_ptr->data.x_array.size); return &base_ptr->data.x_array.elements[index]; } } static bool ir_should_inline(IrBuilder *irb) { return irb->exec->is_inline; } static void ir_instruction_append(IrBasicBlock *basic_block, IrInstruction *instruction) { assert(basic_block); assert(instruction); basic_block->instruction_list.append(instruction); } static size_t exec_next_debug_id(IrExecutable *exec) { size_t result = exec->next_debug_id; exec->next_debug_id += 1; return result; } static size_t exec_next_mem_slot(IrExecutable *exec) { size_t result = exec->mem_slot_count; exec->mem_slot_count += 1; return result; } static FnTableEntry *exec_fn_entry(IrExecutable *exec) { return exec->fn_entry; } static Buf *exec_c_import_buf(IrExecutable *exec) { return exec->c_import_buf; } static void ir_link_new_instruction(IrInstruction *new_instruction, IrInstruction *old_instruction) { new_instruction->other = old_instruction; old_instruction->other = new_instruction; } static void ir_link_new_bb(IrBasicBlock *new_bb, IrBasicBlock *old_bb) { new_bb->other = old_bb; old_bb->other = new_bb; } static void ir_ref_bb(IrBasicBlock *bb) { bb->ref_count += 1; } static void ir_ref_instruction(IrInstruction *instruction) { instruction->ref_count += 1; } static void ir_ref_var(VariableTableEntry *var) { var->ref_count += 1; } static IrBasicBlock *ir_create_basic_block(IrBuilder *irb, Scope *scope, const char *name_hint) { IrBasicBlock *result = allocate(1); result->scope = scope; result->name_hint = name_hint; result->debug_id = exec_next_debug_id(irb->exec); return result; } static IrBasicBlock *ir_build_basic_block(IrBuilder *irb, Scope *scope, const char *name_hint) { IrBasicBlock *result = ir_create_basic_block(irb, scope, name_hint); irb->exec->basic_block_list.append(result); return result; } static IrBasicBlock *ir_build_bb_from(IrBuilder *irb, IrBasicBlock *other_bb) { IrBasicBlock *new_bb = ir_create_basic_block(irb, other_bb->scope, other_bb->name_hint); ir_link_new_bb(new_bb, other_bb); return new_bb; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCondBr *) { return IrInstructionIdCondBr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionBr *) { return IrInstructionIdBr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionSwitchBr *) { return IrInstructionIdSwitchBr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionSwitchVar *) { return IrInstructionIdSwitchVar; } static constexpr IrInstructionId ir_instruction_id(IrInstructionSwitchTarget *) { return IrInstructionIdSwitchTarget; } static constexpr IrInstructionId ir_instruction_id(IrInstructionPhi *) { return IrInstructionIdPhi; } static constexpr IrInstructionId ir_instruction_id(IrInstructionUnOp *) { return IrInstructionIdUnOp; } static constexpr IrInstructionId ir_instruction_id(IrInstructionBinOp *) { return IrInstructionIdBinOp; } static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVar *) { return IrInstructionIdDeclVar; } static constexpr IrInstructionId ir_instruction_id(IrInstructionLoadPtr *) { return IrInstructionIdLoadPtr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionStorePtr *) { return IrInstructionIdStorePtr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionFieldPtr *) { return IrInstructionIdFieldPtr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionStructFieldPtr *) { return IrInstructionIdStructFieldPtr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumFieldPtr *) { return IrInstructionIdEnumFieldPtr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionElemPtr *) { return IrInstructionIdElemPtr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionVarPtr *) { return IrInstructionIdVarPtr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCall *) { return IrInstructionIdCall; } static constexpr IrInstructionId ir_instruction_id(IrInstructionConst *) { return IrInstructionIdConst; } static constexpr IrInstructionId ir_instruction_id(IrInstructionReturn *) { return IrInstructionIdReturn; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCast *) { return IrInstructionIdCast; } static constexpr IrInstructionId ir_instruction_id(IrInstructionContainerInitList *) { return IrInstructionIdContainerInitList; } static constexpr IrInstructionId ir_instruction_id(IrInstructionContainerInitFields *) { return IrInstructionIdContainerInitFields; } static constexpr IrInstructionId ir_instruction_id(IrInstructionUnreachable *) { return IrInstructionIdUnreachable; } static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeOf *) { return IrInstructionIdTypeOf; } static constexpr IrInstructionId ir_instruction_id(IrInstructionToPtrType *) { return IrInstructionIdToPtrType; } static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrTypeChild *) { return IrInstructionIdPtrTypeChild; } static constexpr IrInstructionId ir_instruction_id(IrInstructionSetFnTest *) { return IrInstructionIdSetFnTest; } static constexpr IrInstructionId ir_instruction_id(IrInstructionSetFnVisible *) { return IrInstructionIdSetFnVisible; } static constexpr IrInstructionId ir_instruction_id(IrInstructionSetDebugSafety *) { return IrInstructionIdSetDebugSafety; } static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayType *) { return IrInstructionIdArrayType; } static constexpr IrInstructionId ir_instruction_id(IrInstructionSliceType *) { return IrInstructionIdSliceType; } static constexpr IrInstructionId ir_instruction_id(IrInstructionAsm *) { return IrInstructionIdAsm; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCompileVar *) { return IrInstructionIdCompileVar; } static constexpr IrInstructionId ir_instruction_id(IrInstructionSizeOf *) { return IrInstructionIdSizeOf; } static constexpr IrInstructionId ir_instruction_id(IrInstructionTestNull *) { return IrInstructionIdTestNull; } static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapMaybe *) { return IrInstructionIdUnwrapMaybe; } static constexpr IrInstructionId ir_instruction_id(IrInstructionClz *) { return IrInstructionIdClz; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCtz *) { return IrInstructionIdCtz; } static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumTag *) { return IrInstructionIdEnumTag; } static constexpr IrInstructionId ir_instruction_id(IrInstructionStaticEval *) { return IrInstructionIdStaticEval; } static constexpr IrInstructionId ir_instruction_id(IrInstructionImport *) { return IrInstructionIdImport; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCImport *) { return IrInstructionIdCImport; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCInclude *) { return IrInstructionIdCInclude; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCDefine *) { return IrInstructionIdCDefine; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCUndef *) { return IrInstructionIdCUndef; } static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayLen *) { return IrInstructionIdArrayLen; } static constexpr IrInstructionId ir_instruction_id(IrInstructionRef *) { return IrInstructionIdRef; } static constexpr IrInstructionId ir_instruction_id(IrInstructionStructInit *) { return IrInstructionIdStructInit; } static constexpr IrInstructionId ir_instruction_id(IrInstructionMinValue *) { return IrInstructionIdMinValue; } static constexpr IrInstructionId ir_instruction_id(IrInstructionMaxValue *) { return IrInstructionIdMaxValue; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCompileErr *) { return IrInstructionIdCompileErr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionErrName *) { return IrInstructionIdErrName; } static constexpr IrInstructionId ir_instruction_id(IrInstructionEmbedFile *) { return IrInstructionIdEmbedFile; } static constexpr IrInstructionId ir_instruction_id(IrInstructionCmpxchg *) { return IrInstructionIdCmpxchg; } static constexpr IrInstructionId ir_instruction_id(IrInstructionFence *) { return IrInstructionIdFence; } static constexpr IrInstructionId ir_instruction_id(IrInstructionDivExact *) { return IrInstructionIdDivExact; } static constexpr IrInstructionId ir_instruction_id(IrInstructionTruncate *) { return IrInstructionIdTruncate; } static constexpr IrInstructionId ir_instruction_id(IrInstructionIntType *) { return IrInstructionIdIntType; } static constexpr IrInstructionId ir_instruction_id(IrInstructionBoolNot *) { return IrInstructionIdBoolNot; } static constexpr IrInstructionId ir_instruction_id(IrInstructionAlloca *) { return IrInstructionIdAlloca; } static constexpr IrInstructionId ir_instruction_id(IrInstructionMemset *) { return IrInstructionIdMemset; } static constexpr IrInstructionId ir_instruction_id(IrInstructionMemcpy *) { return IrInstructionIdMemcpy; } static constexpr IrInstructionId ir_instruction_id(IrInstructionSlice *) { return IrInstructionIdSlice; } static constexpr IrInstructionId ir_instruction_id(IrInstructionMemberCount *) { return IrInstructionIdMemberCount; } template static T *ir_create_instruction(IrExecutable *exec, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); special_instruction->base.id = ir_instruction_id(special_instruction); special_instruction->base.scope = scope; special_instruction->base.source_node = source_node; special_instruction->base.debug_id = exec_next_debug_id(exec); return special_instruction; } template static T *ir_build_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { assert(source_node); T *special_instruction = ir_create_instruction(irb->exec, scope, source_node); ir_instruction_append(irb->current_basic_block, &special_instruction->base); return special_instruction; } static IrInstruction *ir_build_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, TypeTableEntry *dest_type, IrInstruction *value, CastOp cast_op) { IrInstructionCast *cast_instruction = ir_build_instruction(irb, scope, source_node); cast_instruction->dest_type = dest_type; cast_instruction->value = value; cast_instruction->cast_op = cast_op; ir_ref_instruction(value); return &cast_instruction->base; } static IrInstruction *ir_build_cond_br(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *condition, IrBasicBlock *then_block, IrBasicBlock *else_block, bool is_inline) { IrInstructionCondBr *cond_br_instruction = ir_build_instruction(irb, scope, source_node); cond_br_instruction->base.type_entry = irb->codegen->builtin_types.entry_unreachable; cond_br_instruction->base.static_value.special = ConstValSpecialStatic; cond_br_instruction->condition = condition; cond_br_instruction->then_block = then_block; cond_br_instruction->else_block = else_block; cond_br_instruction->is_inline = is_inline; ir_ref_instruction(condition); ir_ref_bb(then_block); ir_ref_bb(else_block); return &cond_br_instruction->base; } static IrInstruction *ir_build_cond_br_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *condition, IrBasicBlock *then_block, IrBasicBlock *else_block, bool is_inline) { IrInstruction *new_instruction = ir_build_cond_br(irb, old_instruction->scope, old_instruction->source_node, condition, then_block, else_block, is_inline); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_return(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *return_value) { IrInstructionReturn *return_instruction = ir_build_instruction(irb, scope, source_node); return_instruction->base.type_entry = irb->codegen->builtin_types.entry_unreachable; return_instruction->base.static_value.special = ConstValSpecialStatic; return_instruction->value = return_value; ir_ref_instruction(return_value); return &return_instruction->base; } static IrInstruction *ir_build_return_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *return_value) { IrInstruction *new_instruction = ir_build_return(irb, old_instruction->scope, old_instruction->source_node, return_value); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_create_const(IrBuilder *irb, Scope *scope, AstNode *source_node, TypeTableEntry *type_entry, bool depends_on_compile_var) { assert(type_entry); IrInstructionConst *const_instruction = ir_create_instruction(irb->exec, scope, source_node); const_instruction->base.type_entry = type_entry; const_instruction->base.static_value.special = ConstValSpecialStatic; const_instruction->base.static_value.depends_on_compile_var = depends_on_compile_var; return &const_instruction->base; } static IrInstruction *ir_build_const_void(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->base.type_entry = irb->codegen->builtin_types.entry_void; const_instruction->base.static_value.special = ConstValSpecialStatic; return &const_instruction->base; } static IrInstruction *ir_build_const_undefined(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->base.static_value.special = ConstValSpecialUndef; const_instruction->base.type_entry = irb->codegen->builtin_types.entry_undef; return &const_instruction->base; } static IrInstruction *ir_build_const_bignum(IrBuilder *irb, Scope *scope, AstNode *source_node, BigNum *bignum) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->base.type_entry = (bignum->kind == BigNumKindInt) ? irb->codegen->builtin_types.entry_num_lit_int : irb->codegen->builtin_types.entry_num_lit_float; const_instruction->base.static_value.special = ConstValSpecialStatic; const_instruction->base.static_value.data.x_bignum = *bignum; return &const_instruction->base; } static IrInstruction *ir_build_const_null(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->base.type_entry = irb->codegen->builtin_types.entry_null; const_instruction->base.static_value.special = ConstValSpecialStatic; return &const_instruction->base; } static IrInstruction *ir_build_const_usize(IrBuilder *irb, Scope *scope, AstNode *source_node, uint64_t value) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->base.type_entry = irb->codegen->builtin_types.entry_usize; const_instruction->base.static_value.special = ConstValSpecialStatic; bignum_init_unsigned(&const_instruction->base.static_value.data.x_bignum, value); return &const_instruction->base; } static IrInstruction *ir_create_const_type(IrBuilder *irb, Scope *scope, AstNode *source_node, TypeTableEntry *type_entry) { IrInstructionConst *const_instruction = ir_create_instruction(irb->exec, scope, source_node); const_instruction->base.type_entry = irb->codegen->builtin_types.entry_type; const_instruction->base.static_value.special = ConstValSpecialStatic; const_instruction->base.static_value.data.x_type = type_entry; return &const_instruction->base; } static IrInstruction *ir_build_const_type(IrBuilder *irb, Scope *scope, AstNode *source_node, TypeTableEntry *type_entry) { IrInstruction *instruction = ir_create_const_type(irb, scope, source_node, type_entry); ir_instruction_append(irb->current_basic_block, instruction); return instruction; } static IrInstruction *ir_build_const_fn(IrBuilder *irb, Scope *scope, AstNode *source_node, FnTableEntry *fn_entry) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->base.type_entry = fn_entry->type_entry; const_instruction->base.static_value.special = ConstValSpecialStatic; const_instruction->base.static_value.data.x_fn = fn_entry; return &const_instruction->base; } static IrInstruction *ir_build_const_import(IrBuilder *irb, Scope *scope, AstNode *source_node, ImportTableEntry *import) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->base.type_entry = irb->codegen->builtin_types.entry_namespace; const_instruction->base.static_value.special = ConstValSpecialStatic; const_instruction->base.static_value.data.x_import = import; return &const_instruction->base; } static IrInstruction *ir_build_const_scope(IrBuilder *irb, Scope *parent_scope, AstNode *source_node, Scope *target_scope) { IrInstructionConst *const_instruction = ir_build_instruction(irb, parent_scope, source_node); const_instruction->base.type_entry = irb->codegen->builtin_types.entry_block; const_instruction->base.static_value.special = ConstValSpecialStatic; const_instruction->base.static_value.data.x_block = target_scope; return &const_instruction->base; } static IrInstruction *ir_build_const_bool(IrBuilder *irb, Scope *scope, AstNode *source_node, bool value) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->base.type_entry = irb->codegen->builtin_types.entry_bool; const_instruction->base.static_value.special = ConstValSpecialStatic; const_instruction->base.static_value.data.x_bool = value; return &const_instruction->base; } static IrInstruction *ir_build_const_bound_fn(IrBuilder *irb, Scope *scope, AstNode *source_node, FnTableEntry *fn_entry, IrInstruction *first_arg, bool depends_on_compile_var) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->base.type_entry = get_bound_fn_type(irb->codegen, fn_entry); const_instruction->base.static_value.special = ConstValSpecialStatic; const_instruction->base.static_value.depends_on_compile_var = depends_on_compile_var; const_instruction->base.static_value.data.x_bound_fn.fn = fn_entry; const_instruction->base.static_value.data.x_bound_fn.first_arg = first_arg; return &const_instruction->base; } static IrInstruction *ir_create_const_str_lit(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *str) { IrInstructionConst *const_instruction = ir_create_instruction(irb->exec, scope, source_node); TypeTableEntry *u8_type = irb->codegen->builtin_types.entry_u8; TypeTableEntry *type_entry = get_array_type(irb->codegen, u8_type, buf_len(str)); const_instruction->base.type_entry = type_entry; ConstExprValue *const_val = &const_instruction->base.static_value; init_const_str_lit(const_val, str); return &const_instruction->base; } static IrInstruction *ir_build_const_str_lit(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *str) { IrInstruction *instruction = ir_create_const_str_lit(irb, scope, source_node, str); ir_instruction_append(irb->current_basic_block, instruction); return instruction; } static IrInstruction *ir_build_const_c_str_lit(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *str) { // first we build the underlying array size_t len_with_null = buf_len(str) + 1; ConstExprValue *array_val = allocate(1); array_val->special = ConstValSpecialStatic; array_val->data.x_array.elements = allocate(len_with_null); array_val->data.x_array.size = len_with_null; for (size_t i = 0; i < buf_len(str); i += 1) { ConstExprValue *this_char = &array_val->data.x_array.elements[i]; this_char->special = ConstValSpecialStatic; bignum_init_unsigned(&this_char->data.x_bignum, buf_ptr(str)[i]); } ConstExprValue *null_char = &array_val->data.x_array.elements[len_with_null - 1]; null_char->special = ConstValSpecialStatic; bignum_init_unsigned(&null_char->data.x_bignum, 0); // then make the pointer point to it IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); TypeTableEntry *u8_type = irb->codegen->builtin_types.entry_u8; TypeTableEntry *type_entry = get_pointer_to_type(irb->codegen, u8_type, true); const_instruction->base.type_entry = type_entry; ConstExprValue *ptr_val = &const_instruction->base.static_value; ptr_val->special = ConstValSpecialStatic; ptr_val->data.x_ptr.base_ptr = array_val; ptr_val->data.x_ptr.index = 0; ptr_val->data.x_ptr.special = ConstPtrSpecialCStr; return &const_instruction->base; } static IrInstruction *ir_build_bin_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrBinOp op_id, IrInstruction *op1, IrInstruction *op2, bool safety_check_on) { IrInstructionBinOp *bin_op_instruction = ir_build_instruction(irb, scope, source_node); bin_op_instruction->op_id = op_id; bin_op_instruction->op1 = op1; bin_op_instruction->op2 = op2; bin_op_instruction->safety_check_on = safety_check_on; ir_ref_instruction(op1); ir_ref_instruction(op2); return &bin_op_instruction->base; } static IrInstruction *ir_build_bin_op_from(IrBuilder *irb, IrInstruction *old_instruction, IrBinOp op_id, IrInstruction *op1, IrInstruction *op2, bool safety_check_on) { IrInstruction *new_instruction = ir_build_bin_op(irb, old_instruction->scope, old_instruction->source_node, op_id, op1, op2, safety_check_on); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_var_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, VariableTableEntry *var) { IrInstructionVarPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->var = var; ir_ref_var(var); return &instruction->base; } static IrInstruction *ir_build_var_ptr_from(IrBuilder *irb, IrInstruction *old_instruction, VariableTableEntry *var) { IrInstruction *new_instruction = ir_build_var_ptr(irb, old_instruction->scope, old_instruction->source_node, var); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_elem_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *array_ptr, IrInstruction *elem_index, bool safety_check_on) { IrInstructionElemPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->array_ptr = array_ptr; instruction->elem_index = elem_index; instruction->safety_check_on = safety_check_on; ir_ref_instruction(array_ptr); ir_ref_instruction(elem_index); return &instruction->base; } static IrInstruction *ir_build_elem_ptr_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *array_ptr, IrInstruction *elem_index, bool safety_check_on) { IrInstruction *new_instruction = ir_build_elem_ptr(irb, old_instruction->scope, old_instruction->source_node, array_ptr, elem_index, safety_check_on); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *container_ptr, Buf *field_name) { IrInstructionFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->container_ptr = container_ptr; instruction->field_name = field_name; ir_ref_instruction(container_ptr); return &instruction->base; } static IrInstruction *ir_build_struct_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *struct_ptr, TypeStructField *field) { IrInstructionStructFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->struct_ptr = struct_ptr; instruction->field = field; ir_ref_instruction(struct_ptr); return &instruction->base; } static IrInstruction *ir_build_struct_field_ptr_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *struct_ptr, TypeStructField *type_struct_field) { IrInstruction *new_instruction = ir_build_struct_field_ptr(irb, old_instruction->scope, old_instruction->source_node, struct_ptr, type_struct_field); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_enum_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *enum_ptr, TypeEnumField *field) { IrInstructionEnumFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->enum_ptr = enum_ptr; instruction->field = field; ir_ref_instruction(enum_ptr); return &instruction->base; } static IrInstruction *ir_build_enum_field_ptr_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *enum_ptr, TypeEnumField *type_enum_field) { IrInstruction *new_instruction = ir_build_enum_field_ptr(irb, old_instruction->scope, old_instruction->source_node, enum_ptr, type_enum_field); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *source_node, FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args) { IrInstructionCall *call_instruction = ir_build_instruction(irb, scope, source_node); call_instruction->fn_entry = fn_entry; call_instruction->fn_ref = fn_ref; call_instruction->arg_count = arg_count; call_instruction->args = args; if (fn_ref) ir_ref_instruction(fn_ref); for (size_t i = 0; i < arg_count; i += 1) ir_ref_instruction(args[i]); return &call_instruction->base; } static IrInstruction *ir_build_call_from(IrBuilder *irb, IrInstruction *old_instruction, FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args) { IrInstruction *new_instruction = ir_build_call(irb, old_instruction->scope, old_instruction->source_node, fn_entry, fn_ref, arg_count, args); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_phi(IrBuilder *irb, Scope *scope, AstNode *source_node, size_t incoming_count, IrBasicBlock **incoming_blocks, IrInstruction **incoming_values) { assert(incoming_count != 0); assert(incoming_count != SIZE_MAX); IrInstructionPhi *phi_instruction = ir_build_instruction(irb, scope, source_node); phi_instruction->incoming_count = incoming_count; phi_instruction->incoming_blocks = incoming_blocks; phi_instruction->incoming_values = incoming_values; for (size_t i = 0; i < incoming_count; i += 1) { ir_ref_bb(incoming_blocks[i]); ir_ref_instruction(incoming_values[i]); } return &phi_instruction->base; } static IrInstruction *ir_build_phi_from(IrBuilder *irb, IrInstruction *old_instruction, size_t incoming_count, IrBasicBlock **incoming_blocks, IrInstruction **incoming_values) { IrInstruction *new_instruction = ir_build_phi(irb, old_instruction->scope, old_instruction->source_node, incoming_count, incoming_blocks, incoming_values); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_create_br(IrBuilder *irb, Scope *scope, AstNode *source_node, IrBasicBlock *dest_block, bool is_inline) { IrInstructionBr *br_instruction = ir_create_instruction(irb->exec, scope, source_node); br_instruction->base.type_entry = irb->codegen->builtin_types.entry_unreachable; br_instruction->base.static_value.special = ConstValSpecialStatic; br_instruction->dest_block = dest_block; br_instruction->is_inline = is_inline; ir_ref_bb(dest_block); return &br_instruction->base; } static IrInstruction *ir_build_br(IrBuilder *irb, Scope *scope, AstNode *source_node, IrBasicBlock *dest_block, bool is_inline) { IrInstruction *instruction = ir_create_br(irb, scope, source_node, dest_block, is_inline); ir_instruction_append(irb->current_basic_block, instruction); return instruction; } static IrInstruction *ir_build_br_from(IrBuilder *irb, IrInstruction *old_instruction, IrBasicBlock *dest_block) { IrInstruction *new_instruction = ir_build_br(irb, old_instruction->scope, old_instruction->source_node, dest_block, false); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_un_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrUnOp op_id, IrInstruction *value) { IrInstructionUnOp *br_instruction = ir_build_instruction(irb, scope, source_node); br_instruction->op_id = op_id; br_instruction->value = value; ir_ref_instruction(value); return &br_instruction->base; } static IrInstruction *ir_build_un_op_from(IrBuilder *irb, IrInstruction *old_instruction, IrUnOp op_id, IrInstruction *value) { IrInstruction *new_instruction = ir_build_un_op(irb, old_instruction->scope, old_instruction->source_node, op_id, value); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_container_init_list(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *container_type, size_t item_count, IrInstruction **items) { IrInstructionContainerInitList *container_init_list_instruction = ir_build_instruction(irb, scope, source_node); container_init_list_instruction->container_type = container_type; container_init_list_instruction->item_count = item_count; container_init_list_instruction->items = items; ir_ref_instruction(container_type); for (size_t i = 0; i < item_count; i += 1) { ir_ref_instruction(items[i]); } return &container_init_list_instruction->base; } static IrInstruction *ir_build_container_init_list_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *container_type, size_t item_count, IrInstruction **items) { IrInstruction *new_instruction = ir_build_container_init_list(irb, old_instruction->scope, old_instruction->source_node, container_type, item_count, items); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_container_init_fields(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *container_type, size_t field_count, IrInstructionContainerInitFieldsField *fields) { IrInstructionContainerInitFields *container_init_fields_instruction = ir_build_instruction(irb, scope, source_node); container_init_fields_instruction->container_type = container_type; container_init_fields_instruction->field_count = field_count; container_init_fields_instruction->fields = fields; ir_ref_instruction(container_type); for (size_t i = 0; i < field_count; i += 1) { ir_ref_instruction(fields[i].value); } return &container_init_fields_instruction->base; } static IrInstruction *ir_build_struct_init(IrBuilder *irb, Scope *scope, AstNode *source_node, TypeTableEntry *struct_type, size_t field_count, IrInstructionStructInitField *fields) { IrInstructionStructInit *struct_init_instruction = ir_build_instruction(irb, scope, source_node); struct_init_instruction->struct_type = struct_type; struct_init_instruction->field_count = field_count; struct_init_instruction->fields = fields; for (size_t i = 0; i < field_count; i += 1) ir_ref_instruction(fields[i].value); return &struct_init_instruction->base; } static IrInstruction *ir_build_struct_init_from(IrBuilder *irb, IrInstruction *old_instruction, TypeTableEntry *struct_type, size_t field_count, IrInstructionStructInitField *fields) { IrInstruction *new_instruction = ir_build_struct_init(irb, old_instruction->scope, old_instruction->source_node, struct_type, field_count, fields); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionUnreachable *unreachable_instruction = ir_build_instruction(irb, scope, source_node); unreachable_instruction->base.static_value.special = ConstValSpecialStatic; unreachable_instruction->base.type_entry = irb->codegen->builtin_types.entry_unreachable; return &unreachable_instruction->base; } static IrInstruction *ir_build_unreachable_from(IrBuilder *irb, IrInstruction *old_instruction) { IrInstruction *new_instruction = ir_build_unreachable(irb, old_instruction->scope, old_instruction->source_node); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_store_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr, IrInstruction *value) { IrInstructionStorePtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->base.static_value.special = ConstValSpecialStatic; instruction->base.type_entry = irb->codegen->builtin_types.entry_void; instruction->ptr = ptr; instruction->value = value; ir_ref_instruction(ptr); ir_ref_instruction(value); return &instruction->base; } static IrInstruction *ir_build_store_ptr_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *ptr, IrInstruction *value) { IrInstruction *new_instruction = ir_build_store_ptr(irb, old_instruction->scope, old_instruction->source_node, ptr, value); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_var_decl(IrBuilder *irb, Scope *scope, AstNode *source_node, VariableTableEntry *var, IrInstruction *var_type, IrInstruction *init_value) { IrInstructionDeclVar *decl_var_instruction = ir_build_instruction(irb, scope, source_node); decl_var_instruction->base.static_value.special = ConstValSpecialStatic; decl_var_instruction->base.type_entry = irb->codegen->builtin_types.entry_void; decl_var_instruction->var = var; decl_var_instruction->var_type = var_type; decl_var_instruction->init_value = init_value; if (var_type) ir_ref_instruction(var_type); ir_ref_instruction(init_value); return &decl_var_instruction->base; } static IrInstruction *ir_build_var_decl_from(IrBuilder *irb, IrInstruction *old_instruction, VariableTableEntry *var, IrInstruction *var_type, IrInstruction *init_value) { IrInstruction *new_instruction = ir_build_var_decl(irb, old_instruction->scope, old_instruction->source_node, var, var_type, init_value); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_load_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr) { IrInstructionLoadPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->ptr = ptr; ir_ref_instruction(ptr); return &instruction->base; } static IrInstruction *ir_build_load_ptr_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *ptr) { IrInstruction *new_instruction = ir_build_load_ptr(irb, old_instruction->scope, old_instruction->source_node, ptr); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_typeof(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionTypeOf *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value); return &instruction->base; } static IrInstruction *ir_build_to_ptr_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionToPtrType *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value); return &instruction->base; } static IrInstruction *ir_build_ptr_type_child(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionPtrTypeChild *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value); return &instruction->base; } static IrInstruction *ir_build_set_fn_test(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *fn_value) { IrInstructionSetFnTest *instruction = ir_build_instruction(irb, scope, source_node); instruction->fn_value = fn_value; ir_ref_instruction(fn_value); return &instruction->base; } static IrInstruction *ir_build_set_fn_visible(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *fn_value, IrInstruction *is_visible) { IrInstructionSetFnVisible *instruction = ir_build_instruction(irb, scope, source_node); instruction->fn_value = fn_value; instruction->is_visible = is_visible; ir_ref_instruction(fn_value); ir_ref_instruction(is_visible); return &instruction->base; } static IrInstruction *ir_build_set_debug_safety(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *scope_value, IrInstruction *debug_safety_on) { IrInstructionSetDebugSafety *instruction = ir_build_instruction(irb, scope, source_node); instruction->scope_value = scope_value; instruction->debug_safety_on = debug_safety_on; ir_ref_instruction(scope_value); ir_ref_instruction(debug_safety_on); return &instruction->base; } static IrInstruction *ir_build_array_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *size, IrInstruction *child_type) { IrInstructionArrayType *instruction = ir_build_instruction(irb, scope, source_node); instruction->size = size; instruction->child_type = child_type; ir_ref_instruction(size); ir_ref_instruction(child_type); return &instruction->base; } static IrInstruction *ir_build_slice_type(IrBuilder *irb, Scope *scope, AstNode *source_node, bool is_const, IrInstruction *child_type) { IrInstructionSliceType *instruction = ir_build_instruction(irb, scope, source_node); instruction->is_const = is_const; instruction->child_type = child_type; ir_ref_instruction(child_type); return &instruction->base; } static IrInstruction *ir_build_asm(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction **input_list, IrInstruction **output_types, VariableTableEntry **output_vars, size_t return_count, bool has_side_effects) { IrInstructionAsm *instruction = ir_build_instruction(irb, scope, source_node); instruction->input_list = input_list; instruction->output_types = output_types; instruction->output_vars = output_vars; instruction->return_count = return_count; instruction->has_side_effects = has_side_effects; assert(source_node->type == NodeTypeAsmExpr); for (size_t i = 0; i < source_node->data.asm_expr.output_list.length; i += 1) { IrInstruction *output_type = output_types[i]; if (output_type) ir_ref_instruction(output_type); } for (size_t i = 0; i < source_node->data.asm_expr.input_list.length; i += 1) { IrInstruction *input_value = input_list[i]; ir_ref_instruction(input_value); } return &instruction->base; } static IrInstruction *ir_build_asm_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction **input_list, IrInstruction **output_types, VariableTableEntry **output_vars, size_t return_count, bool has_side_effects) { IrInstruction *new_instruction = ir_build_asm(irb, old_instruction->scope, old_instruction->source_node, input_list, output_types, output_vars, return_count, has_side_effects); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_compile_var(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name) { IrInstructionCompileVar *instruction = ir_build_instruction(irb, scope, source_node); instruction->name = name; ir_ref_instruction(name); return &instruction->base; } static IrInstruction *ir_build_size_of(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value) { IrInstructionSizeOf *instruction = ir_build_instruction(irb, scope, source_node); instruction->type_value = type_value; ir_ref_instruction(type_value); return &instruction->base; } static IrInstruction *ir_build_test_null(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionTestNull *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value); return &instruction->base; } static IrInstruction *ir_build_test_null_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) { IrInstruction *new_instruction = ir_build_test_null(irb, old_instruction->scope, old_instruction->source_node, value); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_unwrap_maybe(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value, bool safety_check_on) { IrInstructionUnwrapMaybe *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; instruction->safety_check_on = safety_check_on; ir_ref_instruction(value); return &instruction->base; } static IrInstruction *ir_build_unwrap_maybe_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value, bool safety_check_on) { IrInstruction *new_instruction = ir_build_unwrap_maybe(irb, old_instruction->scope, old_instruction->source_node, value, safety_check_on); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_clz(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionClz *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value); return &instruction->base; } static IrInstruction *ir_build_clz_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) { IrInstruction *new_instruction = ir_build_clz(irb, old_instruction->scope, old_instruction->source_node, value); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_ctz(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionCtz *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value); return &instruction->base; } static IrInstruction *ir_build_ctz_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) { IrInstruction *new_instruction = ir_build_ctz(irb, old_instruction->scope, old_instruction->source_node, value); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target_value, IrBasicBlock *else_block, size_t case_count, IrInstructionSwitchBrCase *cases, bool is_inline) { IrInstructionSwitchBr *instruction = ir_build_instruction(irb, scope, source_node); instruction->base.type_entry = irb->codegen->builtin_types.entry_unreachable; instruction->base.static_value.special = ConstValSpecialStatic; instruction->target_value = target_value; instruction->else_block = else_block; instruction->case_count = case_count; instruction->cases = cases; instruction->is_inline = is_inline; ir_ref_instruction(target_value); ir_ref_bb(else_block); for (size_t i = 0; i < case_count; i += 1) { ir_ref_instruction(cases[i].value); ir_ref_bb(cases[i].block); } return &instruction->base; } static IrInstruction *ir_build_switch_br_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *target_value, IrBasicBlock *else_block, size_t case_count, IrInstructionSwitchBrCase *cases, bool is_inline) { IrInstruction *new_instruction = ir_build_switch_br(irb, old_instruction->scope, old_instruction->source_node, target_value, else_block, case_count, cases, is_inline); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_switch_target(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target_value_ptr) { IrInstructionSwitchTarget *instruction = ir_build_instruction(irb, scope, source_node); instruction->target_value_ptr = target_value_ptr; ir_ref_instruction(target_value_ptr); return &instruction->base; } static IrInstruction *ir_build_switch_var(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target_value_ptr, IrInstruction *prong_value) { IrInstructionSwitchVar *instruction = ir_build_instruction(irb, scope, source_node); instruction->target_value_ptr = target_value_ptr; instruction->prong_value = prong_value; ir_ref_instruction(target_value_ptr); ir_ref_instruction(prong_value); return &instruction->base; } static IrInstruction *ir_build_enum_tag(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionEnumTag *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value); return &instruction->base; } static IrInstruction *ir_build_enum_tag_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) { IrInstruction *new_instruction = ir_build_enum_tag(irb, old_instruction->scope, old_instruction->source_node, value); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_static_eval(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionStaticEval *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value); return &instruction->base; } static IrInstruction *ir_build_import(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name) { IrInstructionImport *instruction = ir_build_instruction(irb, scope, source_node); instruction->name = name; ir_ref_instruction(name); return &instruction->base; } static IrInstruction *ir_build_array_len(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *array_value) { IrInstructionArrayLen *instruction = ir_build_instruction(irb, scope, source_node); instruction->array_value = array_value; ir_ref_instruction(array_value); return &instruction->base; } static IrInstruction *ir_build_ref(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionRef *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value); return &instruction->base; } static IrInstruction *ir_build_ref_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) { IrInstruction *new_instruction = ir_build_ref(irb, old_instruction->scope, old_instruction->source_node, value); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_min_value(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionMinValue *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value); return &instruction->base; } static IrInstruction *ir_build_max_value(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionMaxValue *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value); return &instruction->base; } static IrInstruction *ir_build_compile_err(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *msg) { IrInstructionCompileErr *instruction = ir_build_instruction(irb, scope, source_node); instruction->msg = msg; ir_ref_instruction(msg); return &instruction->base; } static IrInstruction *ir_build_err_name(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionErrName *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value); return &instruction->base; } static IrInstruction *ir_build_err_name_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) { IrInstruction *new_instruction = ir_build_err_name(irb, old_instruction->scope, old_instruction->source_node, value); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_c_import(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionCImport *instruction = ir_build_instruction(irb, scope, source_node); return &instruction->base; } static IrInstruction *ir_build_c_include(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name) { IrInstructionCInclude *instruction = ir_build_instruction(irb, scope, source_node); instruction->name = name; ir_ref_instruction(name); return &instruction->base; } static IrInstruction *ir_build_c_define(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name, IrInstruction *value) { IrInstructionCDefine *instruction = ir_build_instruction(irb, scope, source_node); instruction->name = name; instruction->value = value; ir_ref_instruction(name); ir_ref_instruction(value); return &instruction->base; } static IrInstruction *ir_build_c_undef(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name) { IrInstructionCUndef *instruction = ir_build_instruction(irb, scope, source_node); instruction->name = name; ir_ref_instruction(name); return &instruction->base; } static IrInstruction *ir_build_embed_file(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name) { IrInstructionEmbedFile *instruction = ir_build_instruction(irb, scope, source_node); instruction->name = name; ir_ref_instruction(name); return &instruction->base; } static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value, AtomicOrder success_order, AtomicOrder failure_order) { IrInstructionCmpxchg *instruction = ir_build_instruction(irb, scope, source_node); instruction->ptr = ptr; instruction->cmp_value = cmp_value; instruction->new_value = new_value; instruction->success_order_value = success_order_value; instruction->failure_order_value = failure_order_value; instruction->success_order = success_order; instruction->failure_order = failure_order; ir_ref_instruction(ptr); ir_ref_instruction(cmp_value); ir_ref_instruction(new_value); ir_ref_instruction(success_order_value); ir_ref_instruction(failure_order_value); return &instruction->base; } static IrInstruction *ir_build_cmpxchg_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value, AtomicOrder success_order, AtomicOrder failure_order) { IrInstruction *new_instruction = ir_build_cmpxchg(irb, old_instruction->scope, old_instruction->source_node, ptr, cmp_value, new_value, success_order_value, failure_order_value, success_order, failure_order); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_fence(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *order_value, AtomicOrder order) { IrInstructionFence *instruction = ir_build_instruction(irb, scope, source_node); instruction->order_value = order_value; instruction->order = order; ir_ref_instruction(order_value); return &instruction->base; } static IrInstruction *ir_build_fence_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *order_value, AtomicOrder order) { IrInstruction *new_instruction = ir_build_fence(irb, old_instruction->scope, old_instruction->source_node, order_value, order); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_div_exact(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *op1, IrInstruction *op2) { IrInstructionDivExact *instruction = ir_build_instruction(irb, scope, source_node); instruction->op1 = op1; instruction->op2 = op2; ir_ref_instruction(op1); ir_ref_instruction(op2); return &instruction->base; } static IrInstruction *ir_build_div_exact_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *op1, IrInstruction *op2) { IrInstruction *new_instruction = ir_build_div_exact(irb, old_instruction->scope, old_instruction->source_node, op1, op2); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_truncate(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) { IrInstructionTruncate *instruction = ir_build_instruction(irb, scope, source_node); instruction->dest_type = dest_type; instruction->target = target; ir_ref_instruction(dest_type); ir_ref_instruction(target); return &instruction->base; } static IrInstruction *ir_build_truncate_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *dest_type, IrInstruction *target) { IrInstruction *new_instruction = ir_build_truncate(irb, old_instruction->scope, old_instruction->source_node, dest_type, target); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_int_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *is_signed, IrInstruction *bit_count) { IrInstructionIntType *instruction = ir_build_instruction(irb, scope, source_node); instruction->is_signed = is_signed; instruction->bit_count = bit_count; ir_ref_instruction(is_signed); ir_ref_instruction(bit_count); return &instruction->base; } static IrInstruction *ir_build_bool_not(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionBoolNot *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value); return &instruction->base; } static IrInstruction *ir_build_bool_not_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) { IrInstruction *new_instruction = ir_build_bool_not(irb, old_instruction->scope, old_instruction->source_node, value); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_alloca(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value, IrInstruction *count) { IrInstructionAlloca *instruction = ir_build_instruction(irb, scope, source_node); instruction->type_value = type_value; instruction->count = count; ir_ref_instruction(type_value); ir_ref_instruction(count); return &instruction->base; } static IrInstruction *ir_build_alloca_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *type_value, IrInstruction *count) { IrInstruction *new_instruction = ir_build_alloca(irb, old_instruction->scope, old_instruction->source_node, type_value, count); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_memset(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_ptr, IrInstruction *byte, IrInstruction *count) { IrInstructionMemset *instruction = ir_build_instruction(irb, scope, source_node); instruction->dest_ptr = dest_ptr; instruction->byte = byte; instruction->count = count; ir_ref_instruction(dest_ptr); ir_ref_instruction(byte); ir_ref_instruction(count); return &instruction->base; } static IrInstruction *ir_build_memset_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *dest_ptr, IrInstruction *byte, IrInstruction *count) { IrInstruction *new_instruction = ir_build_memset(irb, old_instruction->scope, old_instruction->source_node, dest_ptr, byte, count); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_memcpy(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_ptr, IrInstruction *src_ptr, IrInstruction *count) { IrInstructionMemcpy *instruction = ir_build_instruction(irb, scope, source_node); instruction->dest_ptr = dest_ptr; instruction->src_ptr = src_ptr; instruction->count = count; ir_ref_instruction(dest_ptr); ir_ref_instruction(src_ptr); ir_ref_instruction(count); return &instruction->base; } static IrInstruction *ir_build_memcpy_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *dest_ptr, IrInstruction *src_ptr, IrInstruction *count) { IrInstruction *new_instruction = ir_build_memcpy(irb, old_instruction->scope, old_instruction->source_node, dest_ptr, src_ptr, count); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_slice(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr, IrInstruction *start, IrInstruction *end, bool is_const) { IrInstructionSlice *instruction = ir_build_instruction(irb, scope, source_node); instruction->ptr = ptr; instruction->start = start; instruction->end = end; instruction->is_const = is_const; ir_ref_instruction(ptr); ir_ref_instruction(start); if (end) ir_ref_instruction(end); return &instruction->base; } static IrInstruction *ir_build_slice_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *ptr, IrInstruction *start, IrInstruction *end, bool is_const) { IrInstruction *new_instruction = ir_build_slice(irb, old_instruction->scope, old_instruction->source_node, ptr, start, end, is_const); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } static IrInstruction *ir_build_member_count(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *container) { IrInstructionMemberCount *instruction = ir_build_instruction(irb, scope, source_node); instruction->container = container; ir_ref_instruction(container); return &instruction->base; } static void ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, bool gen_error_defers, bool gen_maybe_defers) { while (inner_scope != outer_scope) { assert(inner_scope); if (inner_scope->id == ScopeIdDefer) { AstNode *defer_node = inner_scope->source_node; assert(defer_node->type == NodeTypeDefer); ReturnKind defer_kind = defer_node->data.defer.kind; if (defer_kind == ReturnKindUnconditional || (gen_error_defers && defer_kind == ReturnKindError) || (gen_maybe_defers && defer_kind == ReturnKindMaybe)) { AstNode *defer_expr_node = defer_node->data.defer.expr; ir_gen_node(irb, defer_expr_node, defer_node->data.defer.parent_scope); } } inner_scope = inner_scope->parent; } } static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeReturnExpr); FnTableEntry *fn_entry = exec_fn_entry(irb->exec); if (!fn_entry) { add_node_error(irb->codegen, node, buf_sprintf("return expression outside function definition")); return irb->codegen->invalid_instruction; } AstNode *expr_node = node->data.return_expr.expr; switch (node->data.return_expr.kind) { case ReturnKindUnconditional: { IrInstruction *return_value; if (expr_node) { return_value = ir_gen_node(irb, expr_node, scope); } else { return_value = ir_build_const_void(irb, scope, node); } Scope *outer_scope = fn_entry->child_scope; ir_gen_defers_for_block(irb, scope, outer_scope, false, false); return ir_build_return(irb, scope, node, return_value); } case ReturnKindError: zig_panic("TODO gen IR for %%return"); case ReturnKindMaybe: zig_panic("TODO gen IR for ?return"); } zig_unreachable(); } static void ir_set_cursor_at_end(IrBuilder *irb, IrBasicBlock *basic_block) { assert(basic_block); irb->current_basic_block = basic_block; } static VariableTableEntry *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_scope, Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, bool is_inline) { VariableTableEntry *variable_entry = allocate(1); variable_entry->parent_scope = parent_scope; variable_entry->shadowable = is_shadowable; variable_entry->mem_slot_index = SIZE_MAX; variable_entry->is_inline = is_inline; variable_entry->src_arg_index = SIZE_MAX; if (name) { buf_init_from_buf(&variable_entry->name, name); VariableTableEntry *existing_var = find_variable(codegen, parent_scope, name); if (existing_var && !existing_var->shadowable) { ErrorMsg *msg = add_node_error(codegen, node, buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); variable_entry->type = codegen->builtin_types.entry_invalid; } else { auto primitive_table_entry = codegen->primitive_type_table.maybe_get(name); if (primitive_table_entry) { TypeTableEntry *type = primitive_table_entry->value; add_node_error(codegen, node, buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name))); variable_entry->type = codegen->builtin_types.entry_invalid; } else { Tld *tld = find_decl(parent_scope, name); if (tld && tld->id != TldIdVar) { ErrorMsg *msg = add_node_error(codegen, node, buf_sprintf("redefinition of '%s'", buf_ptr(name))); add_error_note(codegen, msg, tld->source_node, buf_sprintf("previous definition is here")); variable_entry->type = codegen->builtin_types.entry_invalid; } } } } else { assert(is_shadowable); // TODO make this name not actually be in scope. user should be able to make a variable called "_anon" // might already be solved, let's just make sure it has test coverage // maybe we put a prefix on this so the debug info doesn't clobber user debug info for same named variables buf_init_from_str(&variable_entry->name, "_anon"); } variable_entry->src_is_const = src_is_const; variable_entry->gen_is_const = gen_is_const; variable_entry->decl_node = node; variable_entry->child_scope = create_var_scope(node, parent_scope, variable_entry); return variable_entry; } // Set name to nullptr to make the variable anonymous (not visible to programmer). // After you call this function var->child_scope has the variable in scope static VariableTableEntry *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, bool is_inline) { VariableTableEntry *var = create_local_var(irb->codegen, node, scope, name, src_is_const, gen_is_const, is_shadowable, is_inline); if (is_inline || gen_is_const) var->mem_slot_index = exec_next_mem_slot(irb->exec); assert(var->child_scope); return var; } static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node) { assert(block_node->type == NodeTypeBlock); Scope *outer_block_scope = create_block_scope(block_node, parent_scope); Scope *child_scope = outer_block_scope; 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(irb, statement_node, child_scope); if (statement_node->type == NodeTypeDefer && return_value != irb->codegen->invalid_instruction) { // defer starts a new scope child_scope = statement_node->data.defer.child_scope; assert(child_scope); } else if (return_value->id == IrInstructionIdDeclVar) { // variable declarations start a new scope IrInstructionDeclVar *decl_var_instruction = (IrInstructionDeclVar *)return_value; child_scope = decl_var_instruction->var->child_scope; } } if (!return_value) return_value = ir_build_const_void(irb, child_scope, block_node); ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false, false); return return_value; } static IrInstruction *ir_gen_bin_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrBinOp op_id) { IrInstruction *op1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope); IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); return ir_build_bin_op(irb, scope, node, op_id, op1, op2, true); } static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node) { IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPurposeAssign); if (lvalue == irb->codegen->invalid_instruction) return lvalue; IrInstruction *rvalue = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); if (rvalue == irb->codegen->invalid_instruction) return rvalue; ir_build_store_ptr(irb, scope, node, lvalue, rvalue); return ir_build_const_void(irb, scope, node); } static IrInstruction *ir_gen_assign_op(IrBuilder *irb, Scope *scope, AstNode *node, IrBinOp op_id) { IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPurposeAssign); if (lvalue == irb->codegen->invalid_instruction) return lvalue; IrInstruction *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue); IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); if (op2 == irb->codegen->invalid_instruction) return op2; IrInstruction *result = ir_build_bin_op(irb, scope, node, op_id, op1, op2, true); ir_build_store_ptr(irb, scope, node, lvalue, result); return ir_build_const_void(irb, scope, node); } static IrInstruction *ir_gen_bool_or(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeBinOpExpr); bool is_inline = ir_should_inline(irb); IrInstruction *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope); if (val1 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrBasicBlock *post_val1_block = irb->current_basic_block; // block for when val1 == false IrBasicBlock *false_block = ir_build_basic_block(irb, scope, "BoolOrFalse"); // block for when val1 == true (don't even evaluate the second part) IrBasicBlock *true_block = ir_build_basic_block(irb, scope, "BoolOrTrue"); ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_inline); ir_set_cursor_at_end(irb, false_block); IrInstruction *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); if (val2 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrBasicBlock *post_val2_block = irb->current_basic_block; ir_build_br(irb, scope, node, true_block, is_inline); ir_set_cursor_at_end(irb, true_block); IrInstruction **incoming_values = allocate(2); incoming_values[0] = val1; incoming_values[1] = val2; IrBasicBlock **incoming_blocks = allocate(2); incoming_blocks[0] = post_val1_block; incoming_blocks[1] = post_val2_block; return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); } static IrInstruction *ir_gen_bool_and(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeBinOpExpr); bool is_inline = ir_should_inline(irb); IrInstruction *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope); if (val1 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrBasicBlock *post_val1_block = irb->current_basic_block; // block for when val1 == true IrBasicBlock *true_block = ir_build_basic_block(irb, scope, "BoolAndTrue"); // block for when val1 == false (don't even evaluate the second part) IrBasicBlock *false_block = ir_build_basic_block(irb, scope, "BoolAndFalse"); ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_inline); ir_set_cursor_at_end(irb, true_block); IrInstruction *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); if (val2 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrBasicBlock *post_val2_block = irb->current_basic_block; ir_build_br(irb, scope, node, false_block, is_inline); ir_set_cursor_at_end(irb, false_block); IrInstruction **incoming_values = allocate(2); incoming_values[0] = val1; incoming_values[1] = val2; IrBasicBlock **incoming_blocks = allocate(2); incoming_blocks[0] = post_val1_block; incoming_blocks[1] = post_val2_block; return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); } static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, 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: return ir_gen_assign(irb, scope, node); case BinOpTypeAssignTimes: return ir_gen_assign_op(irb, scope, node, IrBinOpMult); case BinOpTypeAssignTimesWrap: return ir_gen_assign_op(irb, scope, node, IrBinOpMultWrap); case BinOpTypeAssignDiv: return ir_gen_assign_op(irb, scope, node, IrBinOpDiv); case BinOpTypeAssignMod: return ir_gen_assign_op(irb, scope, node, IrBinOpMod); case BinOpTypeAssignPlus: return ir_gen_assign_op(irb, scope, node, IrBinOpAdd); case BinOpTypeAssignPlusWrap: return ir_gen_assign_op(irb, scope, node, IrBinOpAddWrap); case BinOpTypeAssignMinus: return ir_gen_assign_op(irb, scope, node, IrBinOpSub); case BinOpTypeAssignMinusWrap: return ir_gen_assign_op(irb, scope, node, IrBinOpSubWrap); case BinOpTypeAssignBitShiftLeft: return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeft); case BinOpTypeAssignBitShiftLeftWrap: return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeftWrap); case BinOpTypeAssignBitShiftRight: return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftRight); case BinOpTypeAssignBitAnd: return ir_gen_assign_op(irb, scope, node, IrBinOpBinAnd); case BinOpTypeAssignBitXor: return ir_gen_assign_op(irb, scope, node, IrBinOpBinXor); case BinOpTypeAssignBitOr: return ir_gen_assign_op(irb, scope, node, IrBinOpBinOr); case BinOpTypeAssignBoolAnd: return ir_gen_assign_op(irb, scope, node, IrBinOpBoolAnd); case BinOpTypeAssignBoolOr: return ir_gen_assign_op(irb, scope, node, IrBinOpBoolOr); case BinOpTypeBoolOr: return ir_gen_bool_or(irb, scope, node); case BinOpTypeBoolAnd: return ir_gen_bool_and(irb, scope, node); case BinOpTypeCmpEq: return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpEq); case BinOpTypeCmpNotEq: return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpNotEq); case BinOpTypeCmpLessThan: return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessThan); case BinOpTypeCmpGreaterThan: return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterThan); case BinOpTypeCmpLessOrEq: return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessOrEq); case BinOpTypeCmpGreaterOrEq: return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterOrEq); case BinOpTypeBinOr: return ir_gen_bin_op_id(irb, scope, node, IrBinOpBinOr); case BinOpTypeBinXor: return ir_gen_bin_op_id(irb, scope, node, IrBinOpBinXor); case BinOpTypeBinAnd: return ir_gen_bin_op_id(irb, scope, node, IrBinOpBinAnd); case BinOpTypeBitShiftLeft: return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeft); case BinOpTypeBitShiftLeftWrap: return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeftWrap); case BinOpTypeBitShiftRight: return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftRight); case BinOpTypeAdd: return ir_gen_bin_op_id(irb, scope, node, IrBinOpAdd); case BinOpTypeAddWrap: return ir_gen_bin_op_id(irb, scope, node, IrBinOpAddWrap); case BinOpTypeSub: return ir_gen_bin_op_id(irb, scope, node, IrBinOpSub); case BinOpTypeSubWrap: return ir_gen_bin_op_id(irb, scope, node, IrBinOpSubWrap); case BinOpTypeMult: return ir_gen_bin_op_id(irb, scope, node, IrBinOpMult); case BinOpTypeMultWrap: return ir_gen_bin_op_id(irb, scope, node, IrBinOpMultWrap); case BinOpTypeDiv: return ir_gen_bin_op_id(irb, scope, node, IrBinOpDiv); case BinOpTypeMod: return ir_gen_bin_op_id(irb, scope, node, IrBinOpMod); case BinOpTypeArrayCat: return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat); case BinOpTypeArrayMult: return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult); case BinOpTypeUnwrapMaybe: zig_panic("TODO gen IR for unwrap maybe binary operation"); } zig_unreachable(); } static IrInstruction *ir_gen_num_lit(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeNumberLiteral); if (node->data.number_literal.overflow) { add_node_error(irb->codegen, node, buf_sprintf("number literal too large to be represented in any type")); return irb->codegen->invalid_instruction; } return ir_build_const_bignum(irb, scope, node, node->data.number_literal.bignum); } static IrInstruction *ir_gen_null_literal(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeNullLiteral); return ir_build_const_null(irb, scope, node); } static IrInstruction *ir_gen_decl_ref(IrBuilder *irb, AstNode *source_node, Tld *tld, LValPurpose lval, Scope *scope) { resolve_top_level_decl(irb->codegen, tld, lval != LValPurposeNone); if (tld->resolution == TldResolutionInvalid) return irb->codegen->invalid_instruction; switch (tld->id) { case TldIdVar: { TldVar *tld_var = (TldVar *)tld; VariableTableEntry *var = tld_var->var; IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, source_node, var); if (lval != LValPurposeNone) return var_ptr; else return ir_build_load_ptr(irb, scope, source_node, var_ptr); } case TldIdFn: { TldFn *tld_fn = (TldFn *)tld; FnTableEntry *fn_entry = tld_fn->fn_entry; assert(fn_entry->type_entry); IrInstruction *ref_instruction = ir_build_const_fn(irb, scope, source_node, fn_entry); if (lval != LValPurposeNone) return ir_build_ref(irb, scope, source_node, ref_instruction); else return ref_instruction; } case TldIdContainer: { TldContainer *tld_container = (TldContainer *)tld; IrInstruction *ref_instruction = ir_build_const_type(irb, scope, source_node, tld_container->type_entry); if (lval != LValPurposeNone) return ir_build_ref(irb, scope, source_node, ref_instruction); else return ref_instruction; } case TldIdTypeDef: { TldTypeDef *tld_typedef = (TldTypeDef *)tld; TypeTableEntry *typedef_type = tld_typedef->type_entry; IrInstruction *ref_instruction = ir_build_const_type(irb, scope, source_node, typedef_type); if (lval != LValPurposeNone) return ir_build_ref(irb, scope, source_node, ref_instruction); else return ref_instruction; } } zig_unreachable(); } static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, LValPurpose lval) { assert(node->type == NodeTypeSymbol); Buf *variable_name = node->data.symbol_expr.symbol; auto primitive_table_entry = irb->codegen->primitive_type_table.maybe_get(variable_name); if (primitive_table_entry) { IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_table_entry->value); if (lval == LValPurposeAddressOf) { return ir_build_un_op(irb, scope, node, IrUnOpAddressOf, value); } else { return value; } } VariableTableEntry *var = find_variable(irb->codegen, scope, variable_name); if (var) { IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, node, var); if (lval != LValPurposeNone) return var_ptr; else return ir_build_load_ptr(irb, scope, node, var_ptr); } Tld *tld = find_decl(scope, variable_name); if (tld) return ir_gen_decl_ref(irb, node, tld, lval, scope); 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 irb->codegen->invalid_instruction; } add_node_error(irb->codegen, node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name))); return irb->codegen->invalid_instruction; } static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode *node, LValPurpose lval) { assert(node->type == NodeTypeArrayAccessExpr); AstNode *array_ref_node = node->data.array_access_expr.array_ref_expr; IrInstruction *array_ref_instruction = ir_gen_node_extra(irb, array_ref_node, scope, LValPurposeAddressOf); if (array_ref_instruction == irb->codegen->invalid_instruction) return array_ref_instruction; AstNode *subscript_node = node->data.array_access_expr.subscript; IrInstruction *subscript_instruction = ir_gen_node(irb, subscript_node, scope); if (subscript_instruction == irb->codegen->invalid_instruction) return subscript_instruction; IrInstruction *ptr_instruction = ir_build_elem_ptr(irb, scope, node, array_ref_instruction, subscript_instruction, true); if (lval != LValPurposeNone) return ptr_instruction; return ir_build_load_ptr(irb, scope, node, ptr_instruction); } static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode *node, LValPurpose lval) { assert(node->type == NodeTypeFieldAccessExpr); AstNode *container_ref_node = node->data.field_access_expr.struct_expr; Buf *field_name = node->data.field_access_expr.field_name; IrInstruction *container_ref_instruction = ir_gen_node_extra(irb, container_ref_node, scope, LValPurposeAddressOf); if (container_ref_instruction == irb->codegen->invalid_instruction) return container_ref_instruction; IrInstruction *ptr_instruction = ir_build_field_ptr(irb, scope, node, container_ref_instruction, field_name); if (lval != LValPurposeNone) return ptr_instruction; return ir_build_load_ptr(irb, scope, node, ptr_instruction); } static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; Buf *name = fn_ref_expr->data.symbol_expr.symbol; auto entry = irb->codegen->builtin_fn_table.maybe_get(name); if (!entry) { add_node_error(irb->codegen, node, buf_sprintf("invalid builtin function: '%s'", buf_ptr(name))); return irb->codegen->invalid_instruction; } BuiltinFnEntry *builtin_fn = entry->value; size_t actual_param_count = node->data.fn_call_expr.params.length; if (builtin_fn->param_count != actual_param_count) { add_node_error(irb->codegen, node, buf_sprintf("expected %zu arguments, found %zu", builtin_fn->param_count, actual_param_count)); return irb->codegen->invalid_instruction; } builtin_fn->ref_count += 1; switch (builtin_fn->id) { case BuiltinFnIdInvalid: zig_unreachable(); case BuiltinFnIdUnreachable: return ir_build_unreachable(irb, scope, node); case BuiltinFnIdTypeof: { AstNode *arg_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg = ir_gen_node(irb, arg_node, scope); if (arg == irb->codegen->invalid_instruction) return arg; return ir_build_typeof(irb, scope, node, arg); } case BuiltinFnIdSetFnTest: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; return ir_build_set_fn_test(irb, scope, node, arg0_value); } case BuiltinFnIdSetFnVisible: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; return ir_build_set_fn_visible(irb, scope, node, arg0_value, arg1_value); } case BuiltinFnIdSetDebugSafety: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; return ir_build_set_debug_safety(irb, scope, node, arg0_value, arg1_value); } case BuiltinFnIdCompileVar: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; return ir_build_compile_var(irb, scope, node, arg0_value); } case BuiltinFnIdSizeof: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; return ir_build_size_of(irb, scope, node, arg0_value); } case BuiltinFnIdCtz: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; return ir_build_ctz(irb, scope, node, arg0_value); } case BuiltinFnIdClz: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; return ir_build_clz(irb, scope, node, arg0_value); } case BuiltinFnIdStaticEval: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; return ir_build_static_eval(irb, scope, node, arg0_value); } case BuiltinFnIdImport: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; if (exec_fn_entry(irb->exec)) { add_node_error(irb->codegen, node, buf_sprintf("import valid only at global scope")); return irb->codegen->invalid_instruction; } return ir_build_import(irb, scope, node, arg0_value); } case BuiltinFnIdCImport: { if (exec_fn_entry(irb->exec)) { add_node_error(irb->codegen, node, buf_sprintf("C import valid only at global scope")); return irb->codegen->invalid_instruction; } return ir_build_c_import(irb, scope, node); } case BuiltinFnIdCInclude: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; if (!exec_c_import_buf(irb->exec)) { add_node_error(irb->codegen, node, buf_sprintf("C include valid only inside C import block")); return irb->codegen->invalid_instruction; } return ir_build_c_include(irb, scope, node, arg0_value); } case BuiltinFnIdCDefine: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; if (!exec_c_import_buf(irb->exec)) { add_node_error(irb->codegen, node, buf_sprintf("C define valid only inside C import block")); return irb->codegen->invalid_instruction; } return ir_build_c_define(irb, scope, node, arg0_value, arg1_value); } case BuiltinFnIdCUndef: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; if (!exec_c_import_buf(irb->exec)) { add_node_error(irb->codegen, node, buf_sprintf("C undef valid only inside C import block")); return irb->codegen->invalid_instruction; } return ir_build_c_undef(irb, scope, node, arg0_value); } case BuiltinFnIdMaxValue: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; return ir_build_max_value(irb, scope, node, arg0_value); } case BuiltinFnIdMinValue: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; return ir_build_min_value(irb, scope, node, arg0_value); } case BuiltinFnIdCompileErr: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; return ir_build_compile_err(irb, scope, node, arg0_value); } case BuiltinFnIdErrName: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; return ir_build_err_name(irb, scope, node, arg0_value); } case BuiltinFnIdEmbedFile: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; return ir_build_embed_file(irb, scope, node, arg0_value); } case BuiltinFnIdCmpExchange: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; AstNode *arg3_node = node->data.fn_call_expr.params.at(3); IrInstruction *arg3_value = ir_gen_node(irb, arg3_node, scope); if (arg3_value == irb->codegen->invalid_instruction) return arg3_value; AstNode *arg4_node = node->data.fn_call_expr.params.at(4); IrInstruction *arg4_value = ir_gen_node(irb, arg4_node, scope); if (arg4_value == irb->codegen->invalid_instruction) return arg4_value; return ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value, arg2_value, arg3_value, arg4_value, AtomicOrderUnordered, AtomicOrderUnordered); } case BuiltinFnIdFence: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; return ir_build_fence(irb, scope, node, arg0_value, AtomicOrderUnordered); } case BuiltinFnIdDivExact: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; return ir_build_div_exact(irb, scope, node, arg0_value, arg1_value); } case BuiltinFnIdTruncate: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; return ir_build_truncate(irb, scope, node, arg0_value, arg1_value); } case BuiltinFnIdIntType: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; return ir_build_int_type(irb, scope, node, arg0_value, arg1_value); } case BuiltinFnIdAlloca: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; return ir_build_alloca(irb, scope, node, arg0_value, arg1_value); } case BuiltinFnIdMemcpy: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; return ir_build_memcpy(irb, scope, node, arg0_value, arg1_value, arg2_value); } case BuiltinFnIdMemset: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; return ir_build_memset(irb, scope, node, arg0_value, arg1_value, arg2_value); } case BuiltinFnIdMemberCount: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; return ir_build_member_count(irb, scope, node, arg0_value); } case BuiltinFnIdAlignof: case BuiltinFnIdAddWithOverflow: case BuiltinFnIdSubWithOverflow: case BuiltinFnIdMulWithOverflow: case BuiltinFnIdShlWithOverflow: case BuiltinFnIdBreakpoint: case BuiltinFnIdReturnAddress: case BuiltinFnIdFrameAddress: zig_panic("TODO IR gen more builtin functions"); } zig_unreachable(); } static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); if (node->data.fn_call_expr.is_builtin) return ir_gen_builtin_fn_call(irb, scope, node); AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr; IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope); if (fn_ref == irb->codegen->invalid_instruction) return fn_ref; size_t arg_count = node->data.fn_call_expr.params.length; IrInstruction **args = allocate(arg_count); for (size_t i = 0; i < arg_count; i += 1) { AstNode *arg_node = node->data.fn_call_expr.params.at(i); args[i] = ir_gen_node(irb, arg_node, scope); } return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args); } static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeIfBoolExpr); IrInstruction *condition = ir_gen_node(irb, node->data.if_bool_expr.condition, scope); if (condition == irb->codegen->invalid_instruction) return condition; AstNode *then_node = node->data.if_bool_expr.then_block; AstNode *else_node = node->data.if_bool_expr.else_node; IrBasicBlock *then_block = ir_build_basic_block(irb, scope, "Then"); IrBasicBlock *else_block = ir_build_basic_block(irb, scope, "Else"); IrBasicBlock *endif_block = ir_build_basic_block(irb, scope, "EndIf"); bool is_inline = ir_should_inline(irb) || node->data.if_bool_expr.is_inline; ir_build_cond_br(irb, scope, condition->source_node, condition, then_block, else_block, is_inline); ir_set_cursor_at_end(irb, then_block); IrInstruction *then_expr_result = ir_gen_node(irb, then_node, scope); if (then_expr_result == irb->codegen->invalid_instruction) return then_expr_result; IrBasicBlock *after_then_block = irb->current_basic_block; ir_build_br(irb, scope, node, endif_block, is_inline); ir_set_cursor_at_end(irb, else_block); IrInstruction *else_expr_result; if (else_node) { else_expr_result = ir_gen_node(irb, else_node, scope); if (else_expr_result == irb->codegen->invalid_instruction) return else_expr_result; } else { else_expr_result = ir_build_const_void(irb, scope, node); } IrBasicBlock *after_else_block = irb->current_basic_block; ir_build_br(irb, scope, node, endif_block, is_inline); ir_set_cursor_at_end(irb, endif_block); IrInstruction **incoming_values = allocate(2); incoming_values[0] = then_expr_result; incoming_values[1] = else_expr_result; IrBasicBlock **incoming_blocks = allocate(2); incoming_blocks[0] = after_then_block; incoming_blocks[1] = after_else_block; return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); } static IrInstruction *ir_gen_prefix_op_id_lval(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id, LValPurpose lval) { assert(node->type == NodeTypePrefixOpExpr); AstNode *expr_node = node->data.prefix_op_expr.primary_expr; IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval); if (value == irb->codegen->invalid_instruction) return value; if (lval == LValPurposeAddressOf && (op_id == IrUnOpAddressOf || op_id == IrUnOpConstAddressOf)) { return value; } return ir_build_un_op(irb, scope, node, op_id, value); } static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id) { return ir_gen_prefix_op_id_lval(irb, scope, node, op_id, LValPurposeNone); } static IrInstruction *ir_gen_prefix_op_unwrap_maybe(IrBuilder *irb, Scope *scope, AstNode *node, LValPurpose lval) { AstNode *expr = node->data.prefix_op_expr.primary_expr; IrInstruction *value = ir_gen_node_extra(irb, expr, scope, LValPurposeAddressOf); if (value == irb->codegen->invalid_instruction) return value; IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, value, true); if (lval == LValPurposeNone) return ir_build_load_ptr(irb, scope, node, unwrapped_ptr); else return unwrapped_ptr; } static IrInstruction *ir_gen_bool_not(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypePrefixOpExpr); AstNode *expr_node = node->data.prefix_op_expr.primary_expr; IrInstruction *value = ir_gen_node(irb, expr_node, scope); if (value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; return ir_build_bool_not(irb, scope, node, value); } static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LValPurpose lval) { if (lval == LValPurposeNone) return value; if (value == irb->codegen->invalid_instruction) return value; // We needed a pointer to a value, but we got a value. So we create // an instruction which just makes a const pointer of it. return ir_build_ref(irb, scope, value->source_node, value); } static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNode *node, LValPurpose lval) { assert(node->type == NodeTypePrefixOpExpr); PrefixOp prefix_op = node->data.prefix_op_expr.prefix_op; switch (prefix_op) { case PrefixOpInvalid: zig_unreachable(); case PrefixOpBoolNot: return ir_lval_wrap(irb, scope, ir_gen_bool_not(irb, scope, node), lval); case PrefixOpBinNot: return ir_gen_prefix_op_id(irb, scope, node, IrUnOpBinNot); case PrefixOpNegation: return ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegation); case PrefixOpNegationWrap: return ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap); case PrefixOpAddressOf: return ir_gen_prefix_op_id_lval(irb, scope, node, IrUnOpAddressOf, LValPurposeAddressOf); case PrefixOpConstAddressOf: return ir_gen_prefix_op_id_lval(irb, scope, node, IrUnOpConstAddressOf, LValPurposeAddressOf); case PrefixOpDereference: return ir_gen_prefix_op_id_lval(irb, scope, node, IrUnOpDereference, lval); case PrefixOpMaybe: return ir_gen_prefix_op_id(irb, scope, node, IrUnOpMaybe); case PrefixOpError: return ir_gen_prefix_op_id(irb, scope, node, IrUnOpError); case PrefixOpUnwrapError: return ir_gen_prefix_op_id(irb, scope, node, IrUnOpUnwrapError); case PrefixOpUnwrapMaybe: return ir_gen_prefix_op_unwrap_maybe(irb, scope, node, lval); } zig_unreachable(); } static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeContainerInitExpr); AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr; ContainerInitKind kind = container_init_expr->kind; IrInstruction *container_type = ir_gen_node(irb, container_init_expr->type, scope); if (container_type == irb->codegen->invalid_instruction) return container_type; if (kind == ContainerInitKindStruct) { size_t field_count = container_init_expr->entries.length; IrInstructionContainerInitFieldsField *fields = allocate(field_count); for (size_t i = 0; i < field_count; i += 1) { AstNode *entry_node = container_init_expr->entries.at(i); assert(entry_node->type == NodeTypeStructValueField); Buf *name = entry_node->data.struct_val_field.name; AstNode *expr_node = entry_node->data.struct_val_field.expr; IrInstruction *expr_value = ir_gen_node(irb, expr_node, scope); if (expr_value == irb->codegen->invalid_instruction) return expr_value; fields[i].name = name; fields[i].value = expr_value; fields[i].source_node = entry_node; } return ir_build_container_init_fields(irb, scope, node, container_type, field_count, fields); } else if (kind == ContainerInitKindArray) { size_t item_count = container_init_expr->entries.length; IrInstruction **values = allocate(item_count); for (size_t i = 0; i < item_count; i += 1) { AstNode *expr_node = container_init_expr->entries.at(i); IrInstruction *expr_value = ir_gen_node(irb, expr_node, scope); if (expr_value == irb->codegen->invalid_instruction) return expr_value; values[i] = expr_value; } return ir_build_container_init_list(irb, scope, node, container_type, item_count, values); } else { zig_unreachable(); } } static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeVariableDeclaration); AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration; IrInstruction *type_instruction; if (variable_declaration->type != nullptr) { type_instruction = ir_gen_node(irb, variable_declaration->type, scope); if (type_instruction == irb->codegen->invalid_instruction) return type_instruction; } else { type_instruction = nullptr; } IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope); if (init_value == irb->codegen->invalid_instruction) return init_value; bool is_shadowable = false; bool is_const = variable_declaration->is_const; bool is_extern = variable_declaration->is_extern; bool is_inline = ir_should_inline(irb) || variable_declaration->is_inline; VariableTableEntry *var = ir_create_var(irb, node, scope, variable_declaration->symbol, is_const, is_const, is_shadowable, is_inline); // we detect IrInstructionIdDeclVar in gen_block to make sure the next node // is inside var->child_scope if (!is_extern && !variable_declaration->expr) { var->type = irb->codegen->builtin_types.entry_invalid; add_node_error(irb->codegen, node, buf_sprintf("variables must be initialized")); return irb->codegen->invalid_instruction; } return ir_build_var_decl(irb, scope, node, var, type_instruction, init_value); } static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeWhileExpr); AstNode *continue_expr_node = node->data.while_expr.continue_expr; IrBasicBlock *cond_block = ir_build_basic_block(irb, scope, "WhileCond"); IrBasicBlock *body_block = ir_build_basic_block(irb, scope, "WhileBody"); IrBasicBlock *continue_block = continue_expr_node ? ir_build_basic_block(irb, scope, "WhileContinue") : cond_block; IrBasicBlock *end_block = ir_build_basic_block(irb, scope, "WhileEnd"); bool is_inline = ir_should_inline(irb) || node->data.while_expr.is_inline; ir_build_br(irb, scope, node, cond_block, is_inline); if (continue_expr_node) { ir_set_cursor_at_end(irb, continue_block); ir_gen_node(irb, continue_expr_node, scope); ir_build_br(irb, scope, node, cond_block, is_inline); } ir_set_cursor_at_end(irb, cond_block); IrInstruction *cond_val = ir_gen_node(irb, node->data.while_expr.condition, scope); ir_build_cond_br(irb, scope, node->data.while_expr.condition, cond_val, body_block, end_block, is_inline); ir_set_cursor_at_end(irb, body_block); LoopStackItem *loop_stack_item = irb->loop_stack.add_one(); loop_stack_item->break_block = end_block; loop_stack_item->continue_block = continue_block; loop_stack_item->is_inline = is_inline; ir_gen_node(irb, node->data.while_expr.body, scope); irb->loop_stack.pop(); ir_build_br(irb, scope, node, continue_block, is_inline); ir_set_cursor_at_end(irb, end_block); return ir_build_const_void(irb, scope, node); } static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeForExpr); AstNode *array_node = node->data.for_expr.array_expr; AstNode *elem_node = node->data.for_expr.elem_node; AstNode *index_node = node->data.for_expr.index_node; AstNode *body_node = node->data.for_expr.body; if (!elem_node) { add_node_error(irb->codegen, node, buf_sprintf("for loop expression missing element parameter")); return irb->codegen->invalid_instruction; } assert(elem_node->type == NodeTypeSymbol); IrInstruction *array_val_ptr = ir_gen_node_extra(irb, array_node, parent_scope, LValPurposeAddressOf); if (array_val_ptr == irb->codegen->invalid_instruction) return array_val_ptr; IrInstruction *array_val = ir_build_load_ptr(irb, parent_scope, array_node, array_val_ptr); IrInstruction *array_type = ir_build_typeof(irb, parent_scope, array_node, array_val); IrInstruction *pointer_type = ir_build_to_ptr_type(irb, parent_scope, array_node, array_type); IrInstruction *elem_var_type; if (node->data.for_expr.elem_is_ptr) { elem_var_type = pointer_type; } else { elem_var_type = ir_build_ptr_type_child(irb, parent_scope, elem_node, pointer_type); } bool is_inline = ir_should_inline(irb) || node->data.for_expr.is_inline; Scope *child_scope = create_loop_scope(node, parent_scope); // TODO make it an error to write to element variable or i variable. Buf *elem_var_name = elem_node->data.symbol_expr.symbol; VariableTableEntry *elem_var = ir_create_var(irb, elem_node, child_scope, elem_var_name, true, false, false, is_inline); child_scope = elem_var->child_scope; IrInstruction *undefined_value = ir_build_const_undefined(irb, child_scope, elem_node); ir_build_var_decl(irb, child_scope, elem_node, elem_var, elem_var_type, undefined_value); IrInstruction *elem_var_ptr = ir_build_var_ptr(irb, child_scope, node, elem_var); AstNode *index_var_source_node; VariableTableEntry *index_var; if (index_node) { index_var_source_node = index_node; Buf *index_var_name = index_node->data.symbol_expr.symbol; index_var = ir_create_var(irb, index_node, child_scope, index_var_name, true, false, false, is_inline); } else { index_var_source_node = node; index_var = ir_create_var(irb, node, child_scope, nullptr, true, false, true, is_inline); } child_scope = index_var->child_scope; IrInstruction *usize = ir_build_const_type(irb, child_scope, node, irb->codegen->builtin_types.entry_usize); IrInstruction *zero = ir_build_const_usize(irb, child_scope, node, 0); IrInstruction *one = ir_build_const_usize(irb, child_scope, node, 1); ir_build_var_decl(irb, child_scope, index_var_source_node, index_var, usize, zero); IrInstruction *index_ptr = ir_build_var_ptr(irb, child_scope, node, index_var); IrBasicBlock *cond_block = ir_build_basic_block(irb, child_scope, "ForCond"); IrBasicBlock *body_block = ir_build_basic_block(irb, child_scope, "ForBody"); IrBasicBlock *end_block = ir_build_basic_block(irb, child_scope, "ForEnd"); IrBasicBlock *continue_block = ir_build_basic_block(irb, child_scope, "ForContinue"); IrInstruction *len_val = ir_build_array_len(irb, child_scope, node, array_val); ir_build_br(irb, child_scope, node, cond_block, is_inline); ir_set_cursor_at_end(irb, cond_block); IrInstruction *index_val = ir_build_load_ptr(irb, child_scope, node, index_ptr); IrInstruction *cond = ir_build_bin_op(irb, child_scope, node, IrBinOpCmpLessThan, index_val, len_val, false); ir_build_cond_br(irb, child_scope, node, cond, body_block, end_block, is_inline); ir_set_cursor_at_end(irb, body_block); IrInstruction *elem_ptr = ir_build_elem_ptr(irb, child_scope, node, array_val_ptr, index_val, false); IrInstruction *elem_val; if (node->data.for_expr.elem_is_ptr) { elem_val = elem_ptr; } else { elem_val = ir_build_load_ptr(irb, child_scope, node, elem_ptr); } ir_build_store_ptr(irb, child_scope, node, elem_var_ptr, elem_val); LoopStackItem *loop_stack_item = irb->loop_stack.add_one(); loop_stack_item->break_block = end_block; loop_stack_item->continue_block = continue_block; loop_stack_item->is_inline = is_inline; ir_gen_node(irb, body_node, child_scope); irb->loop_stack.pop(); ir_build_br(irb, child_scope, node, continue_block, is_inline); ir_set_cursor_at_end(irb, continue_block); IrInstruction *new_index_val = ir_build_bin_op(irb, child_scope, node, IrBinOpAdd, index_val, one, false); ir_build_store_ptr(irb, child_scope, node, index_ptr, new_index_val); ir_build_br(irb, child_scope, node, cond_block, is_inline); ir_set_cursor_at_end(irb, end_block); return ir_build_const_void(irb, child_scope, node); } static IrInstruction *ir_gen_this_literal(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeThisLiteral); if (!scope->parent) return ir_build_const_import(irb, scope, node, node->owner); FnTableEntry *fn_entry = exec_fn_entry(irb->exec); if (fn_entry && scope->parent && scope->parent->parent && !scope_fn_entry(scope->parent->parent)) { return ir_build_const_fn(irb, scope, node, fn_entry); } if (scope->id == ScopeIdDecls) { ScopeDecls *decls_scope = (ScopeDecls *)scope; TypeTableEntry *container_type = decls_scope->container_type; assert(container_type); return ir_build_const_type(irb, scope, node, container_type); } if (scope->id == ScopeIdBlock) return ir_build_const_scope(irb, scope, node, scope); zig_unreachable(); } static IrInstruction *ir_gen_bool_literal(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeBoolLiteral); return ir_build_const_bool(irb, scope, node, node->data.bool_literal.value); } static IrInstruction *ir_gen_string_literal(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeStringLiteral); if (node->data.string_literal.c) { return ir_build_const_c_str_lit(irb, scope, node, node->data.string_literal.buf); } else { return ir_build_const_str_lit(irb, scope, node, node->data.string_literal.buf); } } static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeArrayType); AstNode *size_node = node->data.array_type.size; AstNode *child_type_node = node->data.array_type.child_type; bool is_const = node->data.array_type.is_const; if (size_node) { if (is_const) { add_node_error(irb->codegen, node, buf_create_from_str("const qualifier invalid on array type")); return irb->codegen->invalid_instruction; } IrInstruction *size_value = ir_gen_node(irb, size_node, scope); if (size_value == irb->codegen->invalid_instruction) return size_value; IrInstruction *child_type = ir_gen_node(irb, child_type_node, scope); if (child_type == irb->codegen->invalid_instruction) return child_type; return ir_build_array_type(irb, scope, node, size_value, child_type); } else { IrInstruction *child_type = ir_gen_node(irb, child_type_node, scope); if (child_type == irb->codegen->invalid_instruction) return child_type; return ir_build_slice_type(irb, scope, node, is_const, child_type); } } static IrInstruction *ir_gen_undefined_literal(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeUndefinedLiteral); return ir_build_const_undefined(irb, scope, node); } static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeAsmExpr); IrInstruction **input_list = allocate(node->data.asm_expr.input_list.length); IrInstruction **output_types = allocate(node->data.asm_expr.output_list.length); VariableTableEntry **output_vars = allocate(node->data.asm_expr.output_list.length); size_t return_count = 0; bool is_volatile = node->data.asm_expr.is_volatile; if (!is_volatile && node->data.asm_expr.output_list.length == 0) { add_node_error(irb->codegen, node, buf_sprintf("assembly expression with no output must be marked volatile")); return irb->codegen->invalid_instruction; } for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1) { AsmOutput *asm_output = node->data.asm_expr.output_list.at(i); if (asm_output->return_type) { return_count += 1; IrInstruction *return_type = ir_gen_node(irb, asm_output->return_type, scope); if (return_type == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; if (return_count > 1) { add_node_error(irb->codegen, node, buf_sprintf("inline assembly allows up to one output value")); return irb->codegen->invalid_instruction; } output_types[i] = return_type; } else { Buf *variable_name = asm_output->variable_name; VariableTableEntry *var = find_variable(irb->codegen, scope, variable_name); if (var) { output_vars[i] = var; } else { add_node_error(irb->codegen, node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name))); return irb->codegen->invalid_instruction; } } } for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1) { AsmInput *asm_input = node->data.asm_expr.input_list.at(i); IrInstruction *input_value = ir_gen_node(irb, asm_input->expr, scope); if (input_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; input_list[i] = input_value; } return ir_build_asm(irb, scope, node, input_list, output_types, output_vars, return_count, is_volatile); } static IrInstruction *ir_gen_if_var_expr(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeIfVarExpr); AstNodeVariableDeclaration *var_decl = &node->data.if_var_expr.var_decl; AstNode *expr_node = var_decl->expr; AstNode *then_node = node->data.if_var_expr.then_block; AstNode *else_node = node->data.if_var_expr.else_node; bool var_is_ptr = node->data.if_var_expr.var_is_ptr; IrInstruction *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValPurposeAddressOf); if (expr_value == irb->codegen->invalid_instruction) return expr_value; IrInstruction *is_nonnull_value = ir_build_test_null(irb, scope, node, expr_value); IrBasicBlock *then_block = ir_build_basic_block(irb, scope, "MaybeThen"); IrBasicBlock *else_block = ir_build_basic_block(irb, scope, "MaybeElse"); IrBasicBlock *endif_block = ir_build_basic_block(irb, scope, "MaybeEndIf"); bool is_inline = ir_should_inline(irb) || node->data.if_var_expr.is_inline; ir_build_cond_br(irb, scope, node, is_nonnull_value, then_block, else_block, is_inline); ir_set_cursor_at_end(irb, then_block); IrInstruction *var_type = nullptr; if (var_decl->type) { var_type = ir_gen_node(irb, var_decl->type, scope); if (var_type == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } bool is_shadowable = false; bool is_const = var_decl->is_const; VariableTableEntry *var = ir_create_var(irb, node, scope, var_decl->symbol, is_const, is_const, is_shadowable, is_inline); IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, scope, node, expr_value, false); IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, scope, node, var_ptr_value); ir_build_var_decl(irb, scope, node, var, var_type, var_value); IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var->child_scope); if (then_expr_result == irb->codegen->invalid_instruction) return then_expr_result; IrBasicBlock *after_then_block = irb->current_basic_block; ir_build_br(irb, scope, node, endif_block, is_inline); ir_set_cursor_at_end(irb, else_block); IrInstruction *else_expr_result; if (else_node) { else_expr_result = ir_gen_node(irb, else_node, scope); if (else_expr_result == irb->codegen->invalid_instruction) return else_expr_result; } else { else_expr_result = ir_build_const_void(irb, scope, node); } IrBasicBlock *after_else_block = irb->current_basic_block; ir_build_br(irb, scope, node, endif_block, is_inline); ir_set_cursor_at_end(irb, endif_block); IrInstruction **incoming_values = allocate(2); incoming_values[0] = then_expr_result; incoming_values[1] = else_expr_result; IrBasicBlock **incoming_blocks = allocate(2); incoming_blocks[0] = after_then_block; incoming_blocks[1] = after_else_block; return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); } static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *switch_node, AstNode *prong_node, IrBasicBlock *end_block, bool is_inline, IrInstruction *target_value_ptr, IrInstruction *prong_value, ZigList *incoming_blocks, ZigList *incoming_values) { assert(switch_node->type == NodeTypeSwitchExpr); assert(prong_node->type == NodeTypeSwitchProng); AstNode *expr_node = prong_node->data.switch_prong.expr; AstNode *var_symbol_node = prong_node->data.switch_prong.var_symbol; Scope *child_scope; if (var_symbol_node) { assert(var_symbol_node->type == NodeTypeSymbol); Buf *var_name = var_symbol_node->data.symbol_expr.symbol; bool var_is_ptr = prong_node->data.switch_prong.var_is_ptr; bool is_shadowable = false; bool is_const = true; VariableTableEntry *var = ir_create_var(irb, var_symbol_node, scope, var_name, is_const, is_const, is_shadowable, is_inline); child_scope = var->child_scope; IrInstruction *var_value; if (prong_value) { IrInstruction *var_ptr_value = ir_build_switch_var(irb, scope, var_symbol_node, target_value_ptr, prong_value); var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, scope, var_symbol_node, var_ptr_value); } else { var_value = var_is_ptr ? target_value_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, target_value_ptr); } IrInstruction *var_type = nullptr; // infer the type ir_build_var_decl(irb, scope, var_symbol_node, var, var_type, var_value); } else { child_scope = scope; } IrInstruction *expr_result = ir_gen_node(irb, expr_node, child_scope); if (expr_result == irb->codegen->invalid_instruction) return false; ir_build_br(irb, scope, switch_node, end_block, is_inline); incoming_blocks->append(irb->current_basic_block); incoming_values->append(expr_result); return true; } static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeSwitchExpr); AstNode *target_node = node->data.switch_expr.expr; IrInstruction *target_value_ptr = ir_gen_node_extra(irb, target_node, scope, LValPurposeAddressOf); if (target_value_ptr == irb->codegen->invalid_instruction) return target_value_ptr; IrInstruction *target_value = ir_build_switch_target(irb, scope, node, target_value_ptr); IrBasicBlock *else_block = ir_build_basic_block(irb, scope, "SwitchElse"); IrBasicBlock *end_block = ir_build_basic_block(irb, scope, "SwitchEnd"); size_t prong_count = node->data.switch_expr.prongs.length; ZigList cases = {0}; bool is_inline = ir_should_inline(irb) || node->data.switch_expr.is_inline; ZigList incoming_values = {0}; ZigList incoming_blocks = {0}; AstNode *else_prong = nullptr; for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) { AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); size_t prong_item_count = prong_node->data.switch_prong.items.length; if (prong_item_count == 0) { if (else_prong) { ErrorMsg *msg = add_node_error(irb->codegen, prong_node, buf_sprintf("multiple else prongs in switch expression")); add_error_note(irb->codegen, msg, else_prong, buf_sprintf("previous else prong is here")); return irb->codegen->invalid_instruction; } else_prong = prong_node; IrBasicBlock *prev_block = irb->current_basic_block; ir_set_cursor_at_end(irb, else_block); if (!ir_gen_switch_prong_expr(irb, scope, node, prong_node, end_block, is_inline, target_value_ptr, nullptr, &incoming_blocks, &incoming_values)) { return irb->codegen->invalid_instruction; } ir_set_cursor_at_end(irb, prev_block); } else { if (prong_node->data.switch_prong.any_items_are_range) { IrInstruction *ok_bit = nullptr; AstNode *last_item_node = nullptr; for (size_t item_i = 0; item_i < prong_item_count; item_i += 1) { AstNode *item_node = prong_node->data.switch_prong.items.at(item_i); last_item_node = item_node; if (item_node->type == NodeTypeSwitchRange) { AstNode *start_node = item_node->data.switch_range.start; AstNode *end_node = item_node->data.switch_range.end; IrInstruction *start_value = ir_gen_node(irb, start_node, scope); if (start_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *end_value = ir_gen_node(irb, end_node, scope); if (end_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *start_value_const = ir_build_static_eval(irb, scope, start_node, start_value); IrInstruction *end_value_const = ir_build_static_eval(irb, scope, start_node, end_value); IrInstruction *lower_range_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpGreaterOrEq, target_value, start_value_const, false); IrInstruction *upper_range_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpLessOrEq, target_value, end_value_const, false); IrInstruction *both_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolAnd, lower_range_ok, upper_range_ok, false); if (ok_bit) { ok_bit = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolOr, both_ok, ok_bit, false); } else { ok_bit = both_ok; } } else { IrInstruction *item_value = ir_gen_node(irb, item_node, scope); if (item_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *cmp_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpEq, item_value, target_value, false); if (ok_bit) { ok_bit = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolOr, cmp_ok, ok_bit, false); } else { ok_bit = cmp_ok; } } } IrBasicBlock *range_block_yes = ir_build_basic_block(irb, scope, "SwitchRangeYes"); IrBasicBlock *range_block_no = ir_build_basic_block(irb, scope, "SwitchRangeNo"); assert(ok_bit); assert(last_item_node); ir_build_cond_br(irb, scope, last_item_node, ok_bit, range_block_yes, range_block_no, is_inline); ir_set_cursor_at_end(irb, range_block_yes); if (!ir_gen_switch_prong_expr(irb, scope, node, prong_node, end_block, is_inline, target_value_ptr, nullptr, &incoming_blocks, &incoming_values)) { return irb->codegen->invalid_instruction; } ir_set_cursor_at_end(irb, range_block_no); } else { IrBasicBlock *prong_block = ir_build_basic_block(irb, scope, "SwitchProng"); IrInstruction *last_item_value = nullptr; for (size_t item_i = 0; item_i < prong_item_count; item_i += 1) { AstNode *item_node = prong_node->data.switch_prong.items.at(item_i); assert(item_node->type != NodeTypeSwitchRange); IrInstruction *item_value = ir_gen_node(irb, item_node, scope); if (item_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstructionSwitchBrCase *this_case = cases.add_one(); this_case->value = item_value; this_case->block = prong_block; last_item_value = item_value; } IrInstruction *only_item_value = (prong_item_count == 1) ? last_item_value : nullptr; IrBasicBlock *prev_block = irb->current_basic_block; ir_set_cursor_at_end(irb, prong_block); if (!ir_gen_switch_prong_expr(irb, scope, node, prong_node, end_block, is_inline, target_value_ptr, only_item_value, &incoming_blocks, &incoming_values)) { return irb->codegen->invalid_instruction; } ir_set_cursor_at_end(irb, prev_block); } } } if (cases.length == 0) { ir_build_br(irb, scope, node, else_block, is_inline); } else { ir_build_switch_br(irb, scope, node, target_value, else_block, cases.length, cases.items, is_inline); } if (!else_prong) { ir_set_cursor_at_end(irb, else_block); ir_build_unreachable(irb, scope, node); } ir_set_cursor_at_end(irb, end_block); assert(incoming_blocks.length == incoming_values.length); return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } static LabelTableEntry *find_label(IrExecutable *exec, Scope *scope, Buf *name) { while (scope) { if (scope->id == ScopeIdBlock) { ScopeBlock *block_scope = (ScopeBlock *)scope; auto entry = block_scope->label_table.maybe_get(name); if (entry) return entry->value; } scope = scope->parent; } return nullptr; } static ScopeBlock *find_block_scope(IrExecutable *exec, Scope *scope) { while (scope) { if (scope->id == ScopeIdBlock) return (ScopeBlock *)scope; scope = scope->parent; } return nullptr; } static IrInstruction *ir_gen_label(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeLabel); Buf *label_name = node->data.label.name; IrBasicBlock *label_block = ir_build_basic_block(irb, scope, buf_ptr(label_name)); LabelTableEntry *label = allocate(1); label->decl_node = node; label->bb = label_block; irb->exec->all_labels.append(label); LabelTableEntry *existing_label = find_label(irb->exec, scope, label_name); if (existing_label) { ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("duplicate label name '%s'", buf_ptr(label_name))); add_error_note(irb->codegen, msg, existing_label->decl_node, buf_sprintf("other label here")); return irb->codegen->invalid_instruction; } else { ScopeBlock *scope_block = find_block_scope(irb->exec, scope); scope_block->label_table.put(label_name, label); } bool is_inline = ir_should_inline(irb); ir_build_br(irb, scope, node, label_block, is_inline); ir_set_cursor_at_end(irb, label_block); return ir_build_const_void(irb, scope, node); } static IrInstruction *ir_gen_goto(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeGoto); // make a placeholder unreachable statement and a note to come back and // replace the instruction with a branch instruction IrGotoItem *goto_item = irb->exec->goto_list.add_one(); goto_item->bb = irb->current_basic_block; goto_item->instruction_index = irb->current_basic_block->instruction_list.length; goto_item->source_node = node; goto_item->scope = scope; // we don't know if we need to generate defer expressions yet // we do that later when we find out which label we're jumping to. return ir_build_unreachable(irb, scope, node); } static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeBreak); if (irb->loop_stack.length == 0) { add_node_error(irb->codegen, node, buf_sprintf("'break' expression outside loop")); return irb->codegen->invalid_instruction; } bool is_inline = ir_should_inline(irb) || node->data.break_expr.is_inline; LoopStackItem *loop_stack_item = &irb->loop_stack.last(); IrBasicBlock *dest_block = loop_stack_item->break_block; ir_gen_defers_for_block(irb, scope, dest_block->scope, false, false); return ir_build_br(irb, scope, node, dest_block, is_inline); } static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeContinue); if (irb->loop_stack.length == 0) { add_node_error(irb->codegen, node, buf_sprintf("'continue' expression outside loop")); return irb->codegen->invalid_instruction; } bool is_inline = ir_should_inline(irb) || node->data.continue_expr.is_inline; LoopStackItem *loop_stack_item = &irb->loop_stack.last(); IrBasicBlock *dest_block = loop_stack_item->continue_block; ir_gen_defers_for_block(irb, scope, dest_block->scope, false, false); return ir_build_br(irb, scope, node, dest_block, is_inline); } static IrInstruction *ir_gen_type_literal(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeTypeLiteral); return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_type); } static IrInstruction *ir_gen_error_type(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeErrorType); return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_pure_error); } static IrInstruction *ir_gen_defer(IrBuilder *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeDefer); ScopeDefer *defer_scope = create_defer_scope(node, parent_scope); node->data.defer.child_scope = &defer_scope->base; node->data.defer.parent_scope = parent_scope; return ir_build_const_void(irb, parent_scope, node); } static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeSliceExpr); AstNodeSliceExpr *slice_expr = &node->data.slice_expr; AstNode *array_node = slice_expr->array_ref_expr; AstNode *start_node = slice_expr->start; AstNode *end_node = slice_expr->end; IrInstruction *ptr_value = ir_gen_node(irb, array_node, scope); if (ptr_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *start_value = ir_gen_node(irb, start_node, scope); if (ptr_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *end_value; if (end_node) { end_value = ir_gen_node(irb, end_node, scope); if (end_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } else { end_value = nullptr; } return ir_build_slice(irb, scope, node, ptr_value, start_value, end_value, slice_expr->is_const); } static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope, LValPurpose lval) { assert(scope); switch (node->type) { case NodeTypeStructValueField: case NodeTypeRoot: case NodeTypeParamDecl: case NodeTypeUse: case NodeTypeSwitchProng: case NodeTypeSwitchRange: zig_unreachable(); case NodeTypeBlock: return ir_lval_wrap(irb, scope, ir_gen_block(irb, scope, node), lval); case NodeTypeBinOpExpr: return ir_lval_wrap(irb, scope, ir_gen_bin_op(irb, scope, node), lval); case NodeTypeNumberLiteral: return ir_lval_wrap(irb, scope, ir_gen_num_lit(irb, scope, node), lval); case NodeTypeSymbol: return ir_gen_symbol(irb, scope, node, lval); case NodeTypeFnCallExpr: return ir_lval_wrap(irb, scope, ir_gen_fn_call(irb, scope, node), lval); case NodeTypeIfBoolExpr: return ir_lval_wrap(irb, scope, ir_gen_if_bool_expr(irb, scope, node), lval); case NodeTypePrefixOpExpr: return ir_gen_prefix_op_expr(irb, scope, node, lval); case NodeTypeContainerInitExpr: return ir_lval_wrap(irb, scope, ir_gen_container_init_expr(irb, scope, node), lval); case NodeTypeVariableDeclaration: return ir_lval_wrap(irb, scope, ir_gen_var_decl(irb, scope, node), lval); case NodeTypeWhileExpr: return ir_lval_wrap(irb, scope, ir_gen_while_expr(irb, scope, node), lval); case NodeTypeForExpr: return ir_lval_wrap(irb, scope, ir_gen_for_expr(irb, scope, node), lval); case NodeTypeArrayAccessExpr: return ir_gen_array_access(irb, scope, node, lval); case NodeTypeReturnExpr: return ir_lval_wrap(irb, scope, ir_gen_return(irb, scope, node), lval); case NodeTypeFieldAccessExpr: return ir_gen_field_access(irb, scope, node, lval); case NodeTypeThisLiteral: return ir_lval_wrap(irb, scope, ir_gen_this_literal(irb, scope, node), lval); case NodeTypeBoolLiteral: return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval); case NodeTypeArrayType: return ir_lval_wrap(irb, scope, ir_gen_array_type(irb, scope, node), lval); case NodeTypeStringLiteral: return ir_lval_wrap(irb, scope, ir_gen_string_literal(irb, scope, node), lval); case NodeTypeUndefinedLiteral: return ir_lval_wrap(irb, scope, ir_gen_undefined_literal(irb, scope, node), lval); case NodeTypeAsmExpr: return ir_lval_wrap(irb, scope, ir_gen_asm_expr(irb, scope, node), lval); case NodeTypeNullLiteral: return ir_lval_wrap(irb, scope, ir_gen_null_literal(irb, scope, node), lval); case NodeTypeIfVarExpr: return ir_lval_wrap(irb, scope, ir_gen_if_var_expr(irb, scope, node), lval); case NodeTypeSwitchExpr: return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node), lval); case NodeTypeLabel: return ir_lval_wrap(irb, scope, ir_gen_label(irb, scope, node), lval); case NodeTypeGoto: return ir_lval_wrap(irb, scope, ir_gen_goto(irb, scope, node), lval); case NodeTypeTypeLiteral: return ir_lval_wrap(irb, scope, ir_gen_type_literal(irb, scope, node), lval); case NodeTypeErrorType: return ir_lval_wrap(irb, scope, ir_gen_error_type(irb, scope, node), lval); case NodeTypeBreak: return ir_lval_wrap(irb, scope, ir_gen_break(irb, scope, node), lval); case NodeTypeContinue: return ir_lval_wrap(irb, scope, ir_gen_continue(irb, scope, node), lval); case NodeTypeDefer: return ir_lval_wrap(irb, scope, ir_gen_defer(irb, scope, node), lval); case NodeTypeSliceExpr: return ir_lval_wrap(irb, scope, ir_gen_slice(irb, scope, node), lval); case NodeTypeUnwrapErrorExpr: case NodeTypeCharLiteral: case NodeTypeZeroesLiteral: case NodeTypeVarLiteral: case NodeTypeFnProto: case NodeTypeFnDef: case NodeTypeFnDecl: case NodeTypeContainerDecl: case NodeTypeStructField: case NodeTypeErrorValueDecl: case NodeTypeTypeDecl: zig_panic("TODO more IR gen for node types"); } zig_unreachable(); } static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LValPurpose lval) { IrInstruction *result = ir_gen_node_raw(irb, node, scope, lval); irb->exec->invalid = irb->exec->invalid || (result == irb->codegen->invalid_instruction); return result; } static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope) { return ir_gen_node_extra(irb, node, scope, LValPurposeNone); } static bool ir_goto_pass2(IrBuilder *irb) { for (size_t i = 0; i < irb->exec->goto_list.length; i += 1) { IrGotoItem *goto_item = &irb->exec->goto_list.at(i); AstNode *source_node = goto_item->source_node; // Since a goto will always end a basic block, we move the "current instruction" // index back to over the placeholder unreachable instruction and begin overwriting irb->current_basic_block = goto_item->bb; irb->current_basic_block->instruction_list.resize(goto_item->instruction_index); Buf *label_name = source_node->data.goto_expr.name; LabelTableEntry *label = find_label(irb->exec, goto_item->scope, label_name); if (!label) { add_node_error(irb->codegen, source_node, buf_sprintf("no label in scope named '%s'", buf_ptr(label_name))); return false; } label->used = true; bool is_inline = ir_should_inline(irb) || source_node->data.goto_expr.is_inline; ir_gen_defers_for_block(irb, goto_item->scope, label->bb->scope, false, false); ir_build_br(irb, goto_item->scope, source_node, label->bb, is_inline); } for (size_t i = 0; i < irb->exec->all_labels.length; i += 1) { LabelTableEntry *label = irb->exec->all_labels.at(i); if (!label->used) { add_node_error(irb->codegen, label->decl_node, buf_sprintf("label '%s' defined but not used", buf_ptr(label->decl_node->data.label.name))); return false; } } return true; } IrInstruction *ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_executable) { assert(node->owner); IrBuilder ir_builder = {0}; IrBuilder *irb = &ir_builder; irb->codegen = codegen; irb->exec = ir_executable; irb->current_basic_block = ir_build_basic_block(irb, scope, "Entry"); // Entry block gets a reference because we enter it to begin. ir_ref_bb(irb->current_basic_block); IrInstruction *result = ir_gen_node_extra(irb, node, scope, LValPurposeNone); assert(result); if (irb->exec->invalid) return codegen->invalid_instruction; IrInstruction *return_instruction = ir_build_return(irb, scope, result->source_node, result); assert(return_instruction); if (!ir_goto_pass2(irb)) { irb->exec->invalid = true; return codegen->invalid_instruction; } return return_instruction; } IrInstruction *ir_gen_fn(CodeGen *codegen, FnTableEntry *fn_entry) { assert(fn_entry); IrExecutable *ir_executable = &fn_entry->ir_executable; AstNode *fn_def_node = fn_entry->fn_def_node; assert(fn_def_node->type == NodeTypeFnDef); AstNode *body_node = fn_def_node->data.fn_def.body; assert(fn_entry->child_scope); return ir_gen(codegen, body_node, fn_entry->child_scope, ir_executable); } static ErrorMsg *ir_add_error(IrAnalyze *ira, IrInstruction *source_instruction, Buf *msg) { ira->new_irb.exec->invalid = true; return add_node_error(ira->codegen, source_instruction->source_node, msg); } static IrInstruction *ir_exec_const_result(IrExecutable *exec) { if (exec->basic_block_list.length != 1) return nullptr; IrBasicBlock *bb = exec->basic_block_list.at(0); if (bb->instruction_list.length != 1) return nullptr; IrInstruction *only_inst = bb->instruction_list.at(0); if (only_inst->id != IrInstructionIdReturn) return nullptr; IrInstructionReturn *ret_inst = (IrInstructionReturn *)only_inst; IrInstruction *value = ret_inst->value; assert(value->static_value.special != ConstValSpecialRuntime); return value; } static bool ir_emit_global_runtime_side_effect(IrAnalyze *ira, IrInstruction *source_instruction) { if (ir_should_inline(&ira->new_irb)) { ir_add_error(ira, source_instruction, buf_sprintf("unable to evaluate constant expression")); return false; } return true; } 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->special != ConstValSpecialRuntime); 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"; ir_add_error(ira, instruction, 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, AstNode *source_node, 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; } bool any_are_pure_error = (prev_inst->type_entry->id == TypeTableEntryIdPureError); 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 (prev_type->id == TypeTableEntryIdPureError) { prev_inst = cur_inst; continue; } else if (cur_type->id == TypeTableEntryIdPureError) { any_are_pure_error = true; continue; } 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, 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; } } if (any_are_pure_error && prev_inst->type_entry->id != TypeTableEntryIdPureError) { if (prev_inst->type_entry->id == TypeTableEntryIdNumLitInt || prev_inst->type_entry->id == TypeTableEntryIdNumLitFloat) { add_node_error(ira->codegen, source_node, buf_sprintf("unable to make error union out of number literal")); return ira->codegen->builtin_types.entry_invalid; } else { return get_error_type(ira->codegen, prev_inst->type_entry); } } else { 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; } } // implicit undefined literal to anything if (actual_type->id == TypeTableEntryIdUndefLit) { return ImplicitCastMatchResultYes; } return ImplicitCastMatchResultNo; } static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, IrInstruction **instructions, size_t instruction_count) { return ir_determine_peer_types(ira, source_node, instructions, instruction_count); } static void ir_add_alloca(IrAnalyze *ira, IrInstruction *instruction, TypeTableEntry *type_entry) { if (type_has_bits(type_entry) && handle_is_ptr(type_entry)) { FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec); assert(fn_entry); fn_entry->alloca_list.append(instruction); } } static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type, CastOp cast_op, bool need_alloca) { if (value->static_value.special != ConstValSpecialRuntime) { IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, false); eval_const_expr_implicit_cast(cast_op, &value->static_value, value->type_entry, &result->static_value, wanted_type); return result; } else { IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, cast_op); result->type_entry = wanted_type; if (need_alloca) { FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec); if (fn_entry) fn_entry->alloca_list.append(result); } return result; } } static bool is_slice(TypeTableEntry *type) { return type->id == TypeTableEntryIdStruct && type->data.structure.is_slice; } static bool is_container(TypeTableEntry *type) { return type->id == TypeTableEntryIdStruct || type->id == TypeTableEntryIdEnum || type->id == TypeTableEntryIdUnion; } static bool is_u8(TypeTableEntry *type) { return type->id == TypeTableEntryIdInt && !type->data.integral.is_signed && type->data.integral.bit_count == 8; } static IrBasicBlock *ir_get_new_bb(IrAnalyze *ira, IrBasicBlock *old_bb) { if (old_bb->other) return old_bb->other; IrBasicBlock *new_bb = ir_build_bb_from(&ira->new_irb, old_bb); // We are about to enqueue old_bb for analysis. Before we do so, check old_bb // for phi instructions. Any incoming blocks in the phi instructions need to be // queued first. for (size_t instr_i = 0; instr_i < old_bb->instruction_list.length; instr_i += 1) { IrInstruction *instruction = old_bb->instruction_list.at(instr_i); if (instruction->id != IrInstructionIdPhi) break; IrInstructionPhi *phi_instruction = (IrInstructionPhi *)instruction; for (size_t incoming_i = 0; incoming_i < phi_instruction->incoming_count; incoming_i += 1) { IrBasicBlock *predecessor = phi_instruction->incoming_blocks[incoming_i]; ir_get_new_bb(ira, predecessor); } } ira->old_bb_queue.append(old_bb); return new_bb; } static void ir_start_bb(IrAnalyze *ira, IrBasicBlock *old_bb, IrBasicBlock *const_predecessor_bb) { ira->instruction_index = 0; ira->old_irb.current_basic_block = old_bb; ira->const_predecessor_bb = const_predecessor_bb; if (old_bb->other) ira->new_irb.exec->basic_block_list.append(old_bb->other); } static void ir_finish_bb(IrAnalyze *ira) { ira->block_queue_index += 1; if (ira->block_queue_index < ira->old_bb_queue.length) { IrBasicBlock *old_bb = ira->old_bb_queue.at(ira->block_queue_index); ira->new_irb.current_basic_block = ir_get_new_bb(ira, old_bb); ir_start_bb(ira, old_bb, nullptr); } } static TypeTableEntry *ir_unreach_error(IrAnalyze *ira) { ira->block_queue_index = SIZE_MAX; ira->new_irb.exec->invalid = true; return ira->codegen->builtin_types.entry_unreachable; } static bool ir_emit_backward_branch(IrAnalyze *ira, IrInstruction *source_instruction) { size_t *bbc = ira->new_irb.exec->backward_branch_count; size_t quota = ira->new_irb.exec->backward_branch_quota; // If we're already over quota, we've already given an error message for this. if (*bbc > quota) return false; *bbc += 1; if (*bbc > quota) { ir_add_error(ira, source_instruction, buf_sprintf("evaluation exceeded %zu backwards branches", quota)); return false; } return true; } static TypeTableEntry *ir_inline_bb(IrAnalyze *ira, IrInstruction *source_instruction, IrBasicBlock *old_bb) { if (old_bb->debug_id <= ira->old_irb.current_basic_block->debug_id) { if (!ir_emit_backward_branch(ira, source_instruction)) return ir_unreach_error(ira); } ir_start_bb(ira, old_bb, ira->old_irb.current_basic_block); return ira->codegen->builtin_types.entry_unreachable; } static TypeTableEntry *ir_finish_anal(IrAnalyze *ira, TypeTableEntry *result_type) { if (result_type->id == TypeTableEntryIdUnreachable) ir_finish_bb(ira); return result_type; } static ConstExprValue *ir_build_const_from(IrAnalyze *ira, IrInstruction *old_instruction, bool depends_on_compile_var) { IrInstruction *new_instruction; if (old_instruction->id == IrInstructionIdVarPtr) { IrInstructionVarPtr *old_var_ptr_instruction = (IrInstructionVarPtr *)old_instruction; IrInstructionVarPtr *var_ptr_instruction = ir_create_instruction(ira->new_irb.exec, old_instruction->scope, old_instruction->source_node); var_ptr_instruction->var = old_var_ptr_instruction->var; new_instruction = &var_ptr_instruction->base; } else if (old_instruction->id == IrInstructionIdFieldPtr) { IrInstructionFieldPtr *field_ptr_instruction = ir_create_instruction(ira->new_irb.exec, old_instruction->scope, old_instruction->source_node); new_instruction = &field_ptr_instruction->base; } else if (old_instruction->id == IrInstructionIdElemPtr) { IrInstructionElemPtr *elem_ptr_instruction = ir_create_instruction(ira->new_irb.exec, old_instruction->scope, old_instruction->source_node); new_instruction = &elem_ptr_instruction->base; } else { IrInstructionConst *const_instruction = ir_create_instruction(ira->new_irb.exec, old_instruction->scope, old_instruction->source_node); new_instruction = &const_instruction->base; } ir_link_new_instruction(new_instruction, old_instruction); ConstExprValue *const_val = &new_instruction->static_value; const_val->special = ConstValSpecialStatic; const_val->depends_on_compile_var = depends_on_compile_var; return const_val; } static TypeTableEntry *ir_analyze_void(IrAnalyze *ira, IrInstruction *instruction) { ir_build_const_from(ira, instruction, false); return ira->codegen->builtin_types.entry_void; } static TypeTableEntry *ir_analyze_const_ptr(IrAnalyze *ira, IrInstruction *instruction, ConstExprValue *pointee, TypeTableEntry *pointee_type, bool depends_on_compile_var, ConstPtrSpecial special) { TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, pointee_type, true); ConstExprValue *const_val = ir_build_const_from(ira, instruction, depends_on_compile_var || pointee->depends_on_compile_var); const_val->data.x_ptr.base_ptr = pointee; const_val->data.x_ptr.index = SIZE_MAX; const_val->data.x_ptr.special = special; return ptr_type; } static TypeTableEntry *ir_analyze_const_usize(IrAnalyze *ira, IrInstruction *instruction, uint64_t value, bool depends_on_compile_var) { ConstExprValue *const_val = ir_build_const_from(ira, instruction, depends_on_compile_var); bignum_init_unsigned(&const_val->data.x_bignum, value); return ira->codegen->builtin_types.entry_usize; } static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value) { if (value->static_value.special != ConstValSpecialStatic) { ir_add_error(ira, value, buf_sprintf("unable to evaluate constant expression")); return nullptr; } return &value->static_value; } IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, TypeTableEntry *expected_type, size_t *backward_branch_count, size_t backward_branch_quota, FnTableEntry *fn_entry, Buf *c_import_buf) { IrExecutable ir_executable = {0}; ir_executable.is_inline = true; ir_executable.fn_entry = fn_entry; ir_executable.c_import_buf = c_import_buf; ir_gen(codegen, node, scope, &ir_executable); if (ir_executable.invalid) return codegen->invalid_instruction; if (codegen->verbose) { fprintf(stderr, "\nSource: "); ast_render(stderr, node, 4); fprintf(stderr, "\n{ // (IR)\n"); ir_print(stderr, &ir_executable, 4); fprintf(stderr, "}\n"); } IrExecutable analyzed_executable = {0}; analyzed_executable.is_inline = true; analyzed_executable.fn_entry = fn_entry; analyzed_executable.c_import_buf = c_import_buf; analyzed_executable.backward_branch_count = backward_branch_count; analyzed_executable.backward_branch_quota = backward_branch_quota; TypeTableEntry *result_type = ir_analyze(codegen, &ir_executable, &analyzed_executable, expected_type, node); if (result_type->id == TypeTableEntryIdInvalid) return codegen->invalid_instruction; if (codegen->verbose) { fprintf(stderr, "{ // (analyzed)\n"); ir_print(stderr, &analyzed_executable, 4); fprintf(stderr, "}\n"); } IrInstruction *result = ir_exec_const_result(&analyzed_executable); if (!result) { add_node_error(codegen, node, buf_sprintf("unable to evaluate constant expression")); return codegen->invalid_instruction; } return result; } static TypeTableEntry *ir_resolve_type_lval(IrAnalyze *ira, IrInstruction *type_value, LValPurpose lval) { if (lval != LValPurposeNone) zig_panic("TODO"); if (type_value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; if (type_value->type_entry->id != TypeTableEntryIdMetaType) { add_node_error(ira->codegen, type_value->source_node, buf_sprintf("expected type 'type', found '%s'", buf_ptr(&type_value->type_entry->name))); return ira->codegen->builtin_types.entry_invalid; } ConstExprValue *const_val = ir_resolve_const(ira, type_value); if (!const_val) return ira->codegen->builtin_types.entry_invalid; return const_val->data.x_type; } static TypeTableEntry *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value) { return ir_resolve_type_lval(ira, type_value, LValPurposeNone); } static FnTableEntry *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { if (fn_value == ira->codegen->invalid_instruction) return nullptr; if (fn_value->type_entry->id == TypeTableEntryIdInvalid) return nullptr; if (fn_value->type_entry->id != TypeTableEntryIdFn) { add_node_error(ira->codegen, fn_value->source_node, buf_sprintf("expected function type, found '%s'", buf_ptr(&fn_value->type_entry->name))); return nullptr; } ConstExprValue *const_val = ir_resolve_const(ira, fn_value); if (!const_val) return nullptr; return const_val->data.x_fn; } static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, TypeTableEntry *wanted_type, IrInstruction *value) { TypeTableEntry *actual_type = value->type_entry; TypeTableEntry *wanted_type_canon = get_underlying_type(wanted_type); TypeTableEntry *actual_type_canon = get_underlying_type(actual_type); TypeTableEntry *isize_type = ira->codegen->builtin_types.entry_isize; TypeTableEntry *usize_type = ira->codegen->builtin_types.entry_usize; if (wanted_type_canon->id == TypeTableEntryIdInvalid || actual_type_canon->id == TypeTableEntryIdInvalid) { return ira->codegen->invalid_instruction; } // explicit match or non-const to const if (types_match_const_cast_only(wanted_type, actual_type)) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false); } // explicit cast from bool to int if (wanted_type_canon->id == TypeTableEntryIdInt && actual_type_canon->id == TypeTableEntryIdBool) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpBoolToInt, false); } // explicit cast from pointer to isize or usize if ((wanted_type_canon == isize_type || wanted_type_canon == usize_type) && type_is_codegen_pointer(actual_type_canon)) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpPtrToInt, false); } // explicit cast from isize or usize to pointer if (wanted_type_canon->id == TypeTableEntryIdPointer && (actual_type_canon == isize_type || actual_type_canon == usize_type)) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpIntToPtr, false); } // explicit widening or shortening cast if ((wanted_type_canon->id == TypeTableEntryIdInt && actual_type_canon->id == TypeTableEntryIdInt) || (wanted_type_canon->id == TypeTableEntryIdFloat && actual_type_canon->id == TypeTableEntryIdFloat)) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpWidenOrShorten, false); } // explicit cast from int to float if (wanted_type_canon->id == TypeTableEntryIdFloat && actual_type_canon->id == TypeTableEntryIdInt) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpIntToFloat, false); } // explicit cast from float to int if (wanted_type_canon->id == TypeTableEntryIdInt && actual_type_canon->id == TypeTableEntryIdFloat) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpFloatToInt, false); } // explicit cast from array to slice if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdArray && types_match_const_cast_only( wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type, actual_type->data.array.child_type)) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpToUnknownSizeArray, true); } // explicit cast from []T to []u8 or []u8 to []T if (is_slice(wanted_type) && is_slice(actual_type) && (is_u8(wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type) || is_u8(actual_type->data.structure.fields[0].type_entry->data.pointer.child_type)) && (wanted_type->data.structure.fields[0].type_entry->data.pointer.is_const || !actual_type->data.structure.fields[0].type_entry->data.pointer.is_const)) { if (!ir_emit_global_runtime_side_effect(ira, source_instr)) return ira->codegen->invalid_instruction; return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpResizeSlice, true); } // explicit cast from [N]u8 to []T if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdArray && is_u8(actual_type->data.array.child_type)) { if (!ir_emit_global_runtime_side_effect(ira, source_instr)) return ira->codegen->invalid_instruction; uint64_t child_type_size = type_size(ira->codegen, wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type); if (actual_type->data.array.len % child_type_size == 0) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpBytesToSlice, true); } else { add_node_error(ira->codegen, source_instr->source_node, buf_sprintf("unable to convert %s to %s: size mismatch", buf_ptr(&actual_type->name), buf_ptr(&wanted_type->name))); return ira->codegen->invalid_instruction; } } // explicit cast from pointer to another pointer if ((actual_type->id == TypeTableEntryIdPointer || actual_type->id == TypeTableEntryIdFn) && (wanted_type->id == TypeTableEntryIdPointer || wanted_type->id == TypeTableEntryIdFn)) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpPointerReinterpret, false); } // explicit cast from maybe pointer to another maybe pointer if (actual_type->id == TypeTableEntryIdMaybe && (actual_type->data.maybe.child_type->id == TypeTableEntryIdPointer || actual_type->data.maybe.child_type->id == TypeTableEntryIdFn) && wanted_type->id == TypeTableEntryIdMaybe && (wanted_type->data.maybe.child_type->id == TypeTableEntryIdPointer || wanted_type->data.maybe.child_type->id == TypeTableEntryIdFn)) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpPointerReinterpret, false); } // explicit cast from child type of maybe type to maybe type if (wanted_type->id == TypeTableEntryIdMaybe) { if (types_match_const_cast_only(wanted_type->data.maybe.child_type, actual_type)) { IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpMaybeWrap, true); cast_instruction->return_knowledge = ReturnKnowledgeKnownNonNull; return cast_instruction; } else if (actual_type->id == TypeTableEntryIdNumLitInt || actual_type->id == TypeTableEntryIdNumLitFloat) { if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.maybe.child_type)) { IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpMaybeWrap, true); cast_instruction->return_knowledge = ReturnKnowledgeKnownNonNull; return cast_instruction; } else { return ira->codegen->invalid_instruction; } } } // explicit cast from null literal to maybe type if (wanted_type->id == TypeTableEntryIdMaybe && actual_type->id == TypeTableEntryIdNullLit) { IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNullToMaybe, true); cast_instruction->return_knowledge = ReturnKnowledgeKnownNull; return cast_instruction; } // explicit cast from child type of error type to error type if (wanted_type->id == TypeTableEntryIdErrorUnion) { if (types_match_const_cast_only(wanted_type->data.error.child_type, actual_type)) { IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpErrorWrap, true); cast_instruction->return_knowledge = ReturnKnowledgeKnownNonError; return cast_instruction; } else if (actual_type->id == TypeTableEntryIdNumLitInt || actual_type->id == TypeTableEntryIdNumLitFloat) { if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error.child_type)) { IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpErrorWrap, true); cast_instruction->return_knowledge = ReturnKnowledgeKnownNonError; return cast_instruction; } else { return ira->codegen->invalid_instruction; } } } // explicit cast from pure error to error union type if (wanted_type->id == TypeTableEntryIdErrorUnion && actual_type->id == TypeTableEntryIdPureError) { IrInstruction *cast_instruction = ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpPureErrorWrap, false); cast_instruction->return_knowledge = ReturnKnowledgeKnownError; return cast_instruction; } // explicit cast from number literal to another type if (actual_type->id == TypeTableEntryIdNumLitFloat || actual_type->id == TypeTableEntryIdNumLitInt) { if (ir_num_lit_fits_in_other_type(ira, value, wanted_type_canon)) { CastOp op; if ((actual_type->id == TypeTableEntryIdNumLitFloat && wanted_type_canon->id == TypeTableEntryIdFloat) || (actual_type->id == TypeTableEntryIdNumLitInt && wanted_type_canon->id == TypeTableEntryIdInt)) { op = CastOpNoop; } else if (wanted_type_canon->id == TypeTableEntryIdInt) { op = CastOpFloatToInt; } else if (wanted_type_canon->id == TypeTableEntryIdFloat) { op = CastOpIntToFloat; } else { zig_unreachable(); } return ir_resolve_cast(ira, source_instr, value, wanted_type, op, false); } else { return ira->codegen->invalid_instruction; } } // explicit cast from %void to integer type which can fit it bool actual_type_is_void_err = actual_type->id == TypeTableEntryIdErrorUnion && !type_has_bits(actual_type->data.error.child_type); bool actual_type_is_pure_err = actual_type->id == TypeTableEntryIdPureError; if ((actual_type_is_void_err || actual_type_is_pure_err) && wanted_type->id == TypeTableEntryIdInt) { BigNum bn; bignum_init_unsigned(&bn, ira->codegen->error_decls.length); if (bignum_fits_in_bits(&bn, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpErrToInt, false); } else { add_node_error(ira->codegen, source_instr->source_node, buf_sprintf("too many error values to fit in '%s'", buf_ptr(&wanted_type->name))); return ira->codegen->invalid_instruction; } } // explicit cast from integer to enum type with no payload if (actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdEnum && wanted_type->data.enumeration.gen_field_count == 0) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpIntToEnum, false); } // explicit cast from enum type with no payload to integer if (wanted_type->id == TypeTableEntryIdInt && actual_type->id == TypeTableEntryIdEnum && actual_type->data.enumeration.gen_field_count == 0) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpEnumToInt, false); } // explicit cast from undefined to anything if (actual_type->id == TypeTableEntryIdUndefLit) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false); } add_node_error(ira->codegen, source_instr->source_node, buf_sprintf("invalid cast from type '%s' to '%s'", buf_ptr(&actual_type->name), buf_ptr(&wanted_type->name))); return ira->codegen->invalid_instruction; } static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, TypeTableEntry *expected_type) { assert(value); 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', found '%s'", buf_ptr(&expected_type->name), buf_ptr(&value->type_entry->name))); return ira->codegen->invalid_instruction; case ImplicitCastMatchResultYes: return ir_analyze_cast(ira, value, expected_type, value); case ImplicitCastMatchResultReportedError: return ira->codegen->invalid_instruction; } zig_unreachable(); } static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr) { TypeTableEntry *type_entry = ptr->type_entry; if (type_entry->id == TypeTableEntryIdInvalid) { return ira->codegen->invalid_instruction; } else if (type_entry->id == TypeTableEntryIdPointer) { TypeTableEntry *child_type = type_entry->data.pointer.child_type; if (ptr->static_value.special != ConstValSpecialRuntime) { ConstExprValue *pointee = const_ptr_pointee(&ptr->static_value); if (pointee->special != ConstValSpecialRuntime) { IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope, source_instruction->source_node, child_type, pointee->depends_on_compile_var); result->static_value = *pointee; return result; } } IrInstruction *load_ptr_instruction = ir_build_load_ptr(&ira->new_irb, source_instruction->scope, source_instruction->source_node, ptr); load_ptr_instruction->type_entry = child_type; return load_ptr_instruction; } else { add_node_error(ira->codegen, source_instruction->source_node, buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_instruction; } } static TypeTableEntry *ir_analyze_ref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *value) { if (value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; bool is_inline = ir_should_inline(&ira->new_irb); if (is_inline || value->static_value.special != ConstValSpecialRuntime) { ConstExprValue *val = ir_resolve_const(ira, value); if (!val) return ira->codegen->builtin_types.entry_invalid; return ir_analyze_const_ptr(ira, source_instruction, val, value->type_entry, false, ConstPtrSpecialNone); } TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, value->type_entry, true); FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec); assert(fn_entry); IrInstruction *new_instruction = ir_build_ref_from(&ira->new_irb, source_instruction, value); fn_entry->alloca_list.append(new_instruction); return ptr_type; } static bool ir_resolve_usize(IrAnalyze *ira, IrInstruction *value, uint64_t *out) { if (value->type_entry->id == TypeTableEntryIdInvalid) return false; IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_usize); if (casted_value->type_entry->id == TypeTableEntryIdInvalid) return false; ConstExprValue *const_val = ir_resolve_const(ira, casted_value); if (!const_val) return false; *out = const_val->data.x_bignum.data.x_uint; return true; } static bool ir_resolve_bool(IrAnalyze *ira, IrInstruction *value, bool *out) { if (value->type_entry->id == TypeTableEntryIdInvalid) return false; IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_bool); if (casted_value->type_entry->id == TypeTableEntryIdInvalid) return false; ConstExprValue *const_val = ir_resolve_const(ira, casted_value); if (!const_val) return false; *out = const_val->data.x_bool; return true; } static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstruction *value, AtomicOrder *out) { if (value->type_entry->id == TypeTableEntryIdInvalid) return false; IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_atomic_order_enum); if (casted_value->type_entry->id == TypeTableEntryIdInvalid) return false; ConstExprValue *const_val = ir_resolve_const(ira, casted_value); if (!const_val) return false; *out = (AtomicOrder)const_val->data.x_enum.tag; return true; } static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) { if (value->type_entry->id == TypeTableEntryIdInvalid) return nullptr; TypeTableEntry *str_type = get_slice_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true); IrInstruction *casted_value = ir_implicit_cast(ira, value, str_type); if (casted_value->type_entry->id == TypeTableEntryIdInvalid) return nullptr; ConstExprValue *const_val = ir_resolve_const(ira, casted_value); if (!const_val) return nullptr; ConstExprValue *ptr_field = &const_val->data.x_struct.fields[slice_ptr_index]; ConstExprValue *len_field = &const_val->data.x_struct.fields[slice_len_index]; ConstExprValue *array_val = ptr_field->data.x_ptr.base_ptr; assert(ptr_field->data.x_ptr.index != SIZE_MAX); size_t len = len_field->data.x_bignum.data.x_uint; Buf *result = buf_alloc(); buf_resize(result, len); for (size_t i = 0; i < len; i += 1) { size_t new_index = ptr_field->data.x_ptr.index + i; ConstExprValue *char_val = &array_val->data.x_array.elements[new_index]; uint64_t big_c = char_val->data.x_bignum.data.x_uint; assert(big_c <= UINT8_MAX); uint8_t c = big_c; buf_ptr(result)[i] = c; } return result; } static TypeTableEntry *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructionReturn *return_instruction) { IrInstruction *value = return_instruction->value->other; if (value->type_entry->id == TypeTableEntryIdInvalid) return ir_unreach_error(ira); ira->implicit_return_type_list.append(value); IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->explicit_return_type); if (casted_value == ira->codegen->invalid_instruction) return ir_unreach_error(ira); ir_build_return_from(&ira->new_irb, &return_instruction->base, casted_value); return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); } static TypeTableEntry *ir_analyze_instruction_const(IrAnalyze *ira, IrInstructionConst *const_instruction) { bool depends_on_compile_var = const_instruction->base.static_value.depends_on_compile_var; ConstExprValue *out_val = ir_build_const_from(ira, &const_instruction->base, depends_on_compile_var); *out_val = const_instruction->base.static_value; return const_instruction->base.type_entry; } static TypeTableEntry *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { IrInstruction *op1 = bin_op_instruction->op1->other; if (op1->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *op2 = bin_op_instruction->op2->other; if (op2->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *bool_type = ira->codegen->builtin_types.entry_bool; IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, bool_type); if (casted_op1 == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, bool_type); if (casted_op2 == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *op1_val = &casted_op1->static_value; ConstExprValue *op2_val = &casted_op2->static_value; if (op1_val->special != ConstValSpecialRuntime && op2_val->special != ConstValSpecialRuntime) { bool depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base, depends_on_compile_var); assert(casted_op1->type_entry->id == TypeTableEntryIdBool); assert(casted_op2->type_entry->id == TypeTableEntryIdBool); if (bin_op_instruction->op_id == IrBinOpBoolOr) { out_val->data.x_bool = op1_val->data.x_bool || op2_val->data.x_bool; } else if (bin_op_instruction->op_id == IrBinOpBoolAnd) { out_val->data.x_bool = op1_val->data.x_bool && op2_val->data.x_bool; } else { zig_unreachable(); } return bool_type; } ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, bin_op_instruction->op_id, casted_op1, casted_op2, bin_op_instruction->safety_check_on); return bool_type; } static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { IrInstruction *op1 = bin_op_instruction->op1->other; IrInstruction *op2 = bin_op_instruction->op2->other; IrInstruction *instructions[] = {op1, op2}; TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, 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 TypeTableEntryIdBoundFn: 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(); } IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, resolved_type); if (casted_op1 == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, resolved_type); if (casted_op2 == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *op1_val = &casted_op1->static_value; ConstExprValue *op2_val = &casted_op2->static_value; if (op1_val->special != ConstValSpecialRuntime && op2_val->special != ConstValSpecialRuntime) { bool type_can_gt_lt_cmp = (resolved_type->id == TypeTableEntryIdNumLitFloat || resolved_type->id == TypeTableEntryIdNumLitInt || resolved_type->id == TypeTableEntryIdFloat || resolved_type->id == TypeTableEntryIdInt); bool answer; if (type_can_gt_lt_cmp) { bool (*bignum_cmp)(BigNum *, BigNum *); if (op_id == IrBinOpCmpEq) { bignum_cmp = bignum_cmp_eq; } else if (op_id == IrBinOpCmpNotEq) { bignum_cmp = bignum_cmp_neq; } else if (op_id == IrBinOpCmpLessThan) { bignum_cmp = bignum_cmp_lt; } else if (op_id == IrBinOpCmpGreaterThan) { bignum_cmp = bignum_cmp_gt; } else if (op_id == IrBinOpCmpLessOrEq) { bignum_cmp = bignum_cmp_lte; } else if (op_id == IrBinOpCmpGreaterOrEq) { bignum_cmp = bignum_cmp_gte; } else { zig_unreachable(); } answer = bignum_cmp(&op1_val->data.x_bignum, &op2_val->data.x_bignum); } else { bool are_equal = const_values_equal(op1_val, op2_val, resolved_type); if (op_id == IrBinOpCmpEq) { answer = are_equal; } else if (op_id == IrBinOpCmpNotEq) { answer = !are_equal; } else { zig_unreachable(); } } bool depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base, depends_on_compile_var); out_val->data.x_bool = answer; return ira->codegen->builtin_types.entry_bool; } ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, op_id, casted_op1, casted_op2, bin_op_instruction->safety_check_on); return ira->codegen->builtin_types.entry_bool; } static uint64_t max_unsigned_val(TypeTableEntry *type_entry) { assert(type_entry->id == TypeTableEntryIdInt); if (type_entry->data.integral.bit_count == 64) { return UINT64_MAX; } else if (type_entry->data.integral.bit_count == 32) { return UINT32_MAX; } else if (type_entry->data.integral.bit_count == 16) { return UINT16_MAX; } else if (type_entry->data.integral.bit_count == 8) { return UINT8_MAX; } else { zig_unreachable(); } } static int ir_eval_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val, ConstExprValue *out_val, bool (*bignum_fn)(BigNum *, BigNum *, BigNum *), TypeTableEntry *type, bool wrapping_op) { bool overflow = bignum_fn(&out_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum); if (overflow) { return ErrorOverflow; } if (type->id == TypeTableEntryIdInt && !bignum_fits_in_bits(&out_val->data.x_bignum, type->data.integral.bit_count, type->data.integral.is_signed)) { if (wrapping_op) { if (type->data.integral.is_signed) { out_val->data.x_bignum.data.x_uint = max_unsigned_val(type) - out_val->data.x_bignum.data.x_uint + 1; out_val->data.x_bignum.is_negative = !out_val->data.x_bignum.is_negative; } else if (out_val->data.x_bignum.is_negative) { out_val->data.x_bignum.data.x_uint = max_unsigned_val(type) - out_val->data.x_bignum.data.x_uint + 1; out_val->data.x_bignum.is_negative = false; } else { bignum_truncate(&out_val->data.x_bignum, type->data.integral.bit_count); } } else { return ErrorOverflow; } } out_val->special = ConstValSpecialStatic; out_val->depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; return 0; } static int ir_eval_math_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, IrBinOp op_id, ConstExprValue *op2_val, TypeTableEntry *op2_type, ConstExprValue *out_val) { switch (op_id) { case IrBinOpInvalid: case IrBinOpBoolOr: case IrBinOpBoolAnd: case IrBinOpCmpEq: case IrBinOpCmpNotEq: case IrBinOpCmpLessThan: case IrBinOpCmpGreaterThan: case IrBinOpCmpLessOrEq: case IrBinOpCmpGreaterOrEq: case IrBinOpArrayCat: case IrBinOpArrayMult: zig_unreachable(); case IrBinOpBinOr: return ir_eval_bignum(op1_val, op2_val, out_val, bignum_or, op1_type, false); case IrBinOpBinXor: return ir_eval_bignum(op1_val, op2_val, out_val, bignum_xor, op1_type, false); case IrBinOpBinAnd: return ir_eval_bignum(op1_val, op2_val, out_val, bignum_and, op1_type, false); case IrBinOpBitShiftLeft: return ir_eval_bignum(op1_val, op2_val, out_val, bignum_shl, op1_type, false); case IrBinOpBitShiftLeftWrap: return ir_eval_bignum(op1_val, op2_val, out_val, bignum_shl, op1_type, true); case IrBinOpBitShiftRight: return ir_eval_bignum(op1_val, op2_val, out_val, bignum_shr, op1_type, false); case IrBinOpAdd: return ir_eval_bignum(op1_val, op2_val, out_val, bignum_add, op1_type, false); case IrBinOpAddWrap: return ir_eval_bignum(op1_val, op2_val, out_val, bignum_add, op1_type, true); case IrBinOpSub: return ir_eval_bignum(op1_val, op2_val, out_val, bignum_sub, op1_type, false); case IrBinOpSubWrap: return ir_eval_bignum(op1_val, op2_val, out_val, bignum_sub, op1_type, true); case IrBinOpMult: return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mul, op1_type, false); case IrBinOpMultWrap: return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mul, op1_type, true); case IrBinOpDiv: return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div, op1_type, false); case IrBinOpMod: return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mod, op1_type, false); } zig_unreachable(); } static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { IrInstruction *op1 = bin_op_instruction->op1->other; IrInstruction *op2 = bin_op_instruction->op2->other; IrInstruction *instructions[] = {op1, op2}; TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, 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; } IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, resolved_type); if (casted_op1 == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, resolved_type); if (casted_op2 == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; if (casted_op1->static_value.special != ConstValSpecialRuntime && casted_op2->static_value.special != ConstValSpecialRuntime) { ConstExprValue *op1_val = &casted_op1->static_value; ConstExprValue *op2_val = &casted_op2->static_value; ConstExprValue *out_val = &bin_op_instruction->base.static_value; bin_op_instruction->base.other = &bin_op_instruction->base; int err; if ((err = ir_eval_math_op(op1_val, resolved_type, op_id, op2_val, resolved_type, out_val))) { if (err == ErrorDivByZero) { add_node_error(ira->codegen, bin_op_instruction->base.source_node, buf_sprintf("division by zero is undefined")); return ira->codegen->builtin_types.entry_invalid; } else if (err == ErrorOverflow) { add_node_error(ira->codegen, bin_op_instruction->base.source_node, buf_sprintf("value cannot be represented in any integer type")); return ira->codegen->builtin_types.entry_invalid; } return ira->codegen->builtin_types.entry_invalid; } ir_num_lit_fits_in_other_type(ira, &bin_op_instruction->base, resolved_type); return resolved_type; } ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, op_id, casted_op1, casted_op2, bin_op_instruction->safety_check_on); return resolved_type; } static TypeTableEntry *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *instruction) { IrInstruction *op1 = instruction->op1->other; TypeTableEntry *op1_canon_type = get_underlying_type(op1->type_entry); if (op1_canon_type->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *op2 = instruction->op2->other; TypeTableEntry *op2_canon_type = get_underlying_type(op2->type_entry); if (op2_canon_type->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *op1_val = ir_resolve_const(ira, op1); if (!op1_val) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *op2_val = ir_resolve_const(ira, op2); if (!op2_val) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *op1_array_val; size_t op1_array_index; size_t op1_array_end; TypeTableEntry *child_type; if (op1_canon_type->id == TypeTableEntryIdArray) { child_type = op1_canon_type->data.array.child_type; op1_array_val = op1_val; op1_array_index = 0; op1_array_end = op1_val->data.x_array.size; } else if (op1_canon_type->id == TypeTableEntryIdPointer && op1_canon_type->data.pointer.child_type == ira->codegen->builtin_types.entry_u8 && op1_val->data.x_ptr.special == ConstPtrSpecialCStr) { child_type = op1_canon_type->data.pointer.child_type; op1_array_val = op1_val->data.x_ptr.base_ptr; op1_array_index = op1_val->data.x_ptr.index; op1_array_end = op1_array_val->data.x_array.size - 1; } else { ir_add_error(ira, op1, buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op1->type_entry->name))); // TODO if meta_type is type decl, add note pointing to type decl declaration return ira->codegen->builtin_types.entry_invalid; } ConstExprValue *op2_array_val; size_t op2_array_index; size_t op2_array_end; if (op2_canon_type->id == TypeTableEntryIdArray) { if (op2_canon_type->data.array.child_type != child_type) { ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", buf_ptr(&child_type->name), buf_ptr(&op2->type_entry->name))); return ira->codegen->builtin_types.entry_invalid; } op2_array_val = op2_val; op2_array_index = 0; op2_array_end = op2_array_val->data.x_array.size; } else if (op2_canon_type->id == TypeTableEntryIdPointer && op2_canon_type->data.pointer.child_type == ira->codegen->builtin_types.entry_u8 && op2_val->data.x_ptr.special == ConstPtrSpecialCStr) { if (child_type != ira->codegen->builtin_types.entry_u8) { ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", buf_ptr(&child_type->name), buf_ptr(&op2->type_entry->name))); return ira->codegen->builtin_types.entry_invalid; } op2_array_val = op2_val->data.x_ptr.base_ptr; op2_array_index = op2_val->data.x_ptr.index; op2_array_end = op2_array_val->data.x_array.size - 1; } else { ir_add_error(ira, op2, buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op1->type_entry->name))); // TODO if meta_type is type decl, add note pointing to type decl declaration return ira->codegen->builtin_types.entry_invalid; } bool depends_on_compile_var = op1->static_value.depends_on_compile_var || op2->static_value.depends_on_compile_var; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var); TypeTableEntry *result_type; ConstExprValue *out_array_val; size_t new_len = (op1_array_end - op1_array_index) + (op2_array_end - op2_array_index); if (op1_canon_type->id == TypeTableEntryIdArray || op2_canon_type->id == TypeTableEntryIdArray) { result_type = get_array_type(ira->codegen, child_type, new_len); out_array_val = out_val; } else { result_type = get_pointer_to_type(ira->codegen, child_type, true); out_array_val = allocate(1); out_array_val->special = ConstValSpecialStatic; out_val->data.x_ptr.base_ptr = out_array_val; out_val->data.x_ptr.index = 0; out_val->data.x_ptr.special = ConstPtrSpecialCStr; new_len += 1; // null byte } out_array_val->data.x_array.elements = allocate(new_len); out_array_val->data.x_array.size = new_len; size_t next_index = 0; for (size_t i = op1_array_index; i < op1_array_end; i += 1, next_index += 1) { out_array_val->data.x_array.elements[next_index] = op1_array_val->data.x_array.elements[i]; } for (size_t i = op2_array_index; i < op2_array_end; i += 1, next_index += 1) { out_array_val->data.x_array.elements[next_index] = op2_array_val->data.x_array.elements[i]; } if (next_index < new_len) { ConstExprValue *null_byte = &out_array_val->data.x_array.elements[next_index]; null_byte->special = ConstValSpecialStatic; bignum_init_unsigned(&null_byte->data.x_bignum, 0); next_index += 1; } assert(next_index == new_len); return result_type; } static TypeTableEntry *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp *instruction) { IrInstruction *op1 = instruction->op1->other; if (op1->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *op2 = instruction->op2->other; if (op2->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *array_val = ir_resolve_const(ira, op1); if (!array_val) return ira->codegen->builtin_types.entry_invalid; uint64_t mult_amt; if (!ir_resolve_usize(ira, op2, &mult_amt)) return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *array_canon_type = get_underlying_type(op1->type_entry); if (array_canon_type->id != TypeTableEntryIdArray) { ir_add_error(ira, op1, buf_sprintf("expected array type, found '%s'", buf_ptr(&op1->type_entry->name))); // TODO if meta_type is type decl, add note pointing to type decl declaration return ira->codegen->builtin_types.entry_invalid; } uint64_t old_array_len = array_canon_type->data.array.len; BigNum array_len; bignum_init_unsigned(&array_len, old_array_len); if (bignum_multiply_by_scalar(&array_len, mult_amt)) { ir_add_error(ira, &instruction->base, buf_sprintf("operation results in overflow")); return ira->codegen->builtin_types.entry_invalid; } bool depends_on_compile_var = op1->static_value.depends_on_compile_var || op2->static_value.depends_on_compile_var; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var); uint64_t new_array_len = array_len.data.x_uint; out_val->data.x_array.size = new_array_len; out_val->data.x_array.elements = allocate(new_array_len); uint64_t i = 0; for (uint64_t x = 0; x < mult_amt; x += 1) { for (uint64_t y = 0; y < old_array_len; y += 1) { out_val->data.x_array.elements[i] = array_val->data.x_array.elements[y]; i += 1; } } assert(i == new_array_len); TypeTableEntry *child_type = array_canon_type->data.array.child_type; return get_array_type(ira->codegen, child_type, new_array_len); } 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: return ir_analyze_array_cat(ira, bin_op_instruction); case IrBinOpArrayMult: return ir_analyze_array_mult(ira, bin_op_instruction); } zig_unreachable(); } static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDeclVar *decl_var_instruction) { VariableTableEntry *var = decl_var_instruction->var; IrInstruction *init_value = decl_var_instruction->init_value->other; if (init_value->type_entry->id == TypeTableEntryIdInvalid) { var->type = ira->codegen->builtin_types.entry_invalid; return var->type; } AstNodeVariableDeclaration *variable_declaration = &var->decl_node->data.variable_declaration; bool is_export = (variable_declaration->visib_mod == VisibModExport); bool is_extern = variable_declaration->is_extern; var->ref_count = 0; TypeTableEntry *explicit_type = nullptr; IrInstruction *var_type = nullptr; if (decl_var_instruction->var_type != nullptr) { var_type = decl_var_instruction->var_type->other; TypeTableEntry *proposed_type = ir_resolve_type(ira, var_type); explicit_type = validate_var_type(ira->codegen, var_type->source_node, proposed_type); if (explicit_type->id == TypeTableEntryIdInvalid) return explicit_type; } AstNode *source_node = decl_var_instruction->base.source_node; IrInstruction *casted_init_value = ir_implicit_cast(ira, init_value, explicit_type); TypeTableEntry *result_type = get_underlying_type(casted_init_value->type_entry); switch (result_type->id) { case TypeTableEntryIdTypeDecl: zig_unreachable(); case TypeTableEntryIdInvalid: result_type = ira->codegen->builtin_types.entry_invalid; break; case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: if (is_export || is_extern || casted_init_value->static_value.special == ConstValSpecialRuntime) { add_node_error(ira->codegen, source_node, buf_sprintf("unable to infer variable type")); result_type = ira->codegen->builtin_types.entry_invalid; } break; case TypeTableEntryIdUnreachable: case TypeTableEntryIdVar: case TypeTableEntryIdBlock: case TypeTableEntryIdNullLit: add_node_error(ira->codegen, source_node, buf_sprintf("variable of type '%s' not allowed", buf_ptr(&result_type->name))); result_type = ira->codegen->builtin_types.entry_invalid; break; case TypeTableEntryIdMetaType: case TypeTableEntryIdNamespace: if (casted_init_value->static_value.special == ConstValSpecialRuntime) { add_node_error(ira->codegen, source_node, buf_sprintf("variable of type '%s' must be constant", buf_ptr(&result_type->name))); result_type = ira->codegen->builtin_types.entry_invalid; } break; case TypeTableEntryIdUndefLit: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdBoundFn: // OK break; } var->type = result_type; assert(var->type); if (casted_init_value->static_value.special == ConstValSpecialStatic) { if (var->mem_slot_index != SIZE_MAX) { assert(var->mem_slot_index < ira->exec_context.mem_slot_count); ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index]; *mem_slot = casted_init_value->static_value; } } else if (var->is_inline) { ir_add_error(ira, &decl_var_instruction->base, buf_sprintf("cannot store runtime value in compile time variable")); var->type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->builtin_types.entry_invalid; } ir_build_var_decl_from(&ira->new_irb, &decl_var_instruction->base, var, var_type, casted_init_value); FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec); if (fn_entry) fn_entry->variable_list.append(var); return ira->codegen->builtin_types.entry_void; } static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node, IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i) { AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(*next_proto_i); assert(param_decl_node->type == NodeTypeParamDecl); AstNode *param_type_node = param_decl_node->data.param_decl.type; TypeTableEntry *param_type = analyze_type_expr(ira->codegen, *exec_scope, param_type_node); if (param_type->id == TypeTableEntryIdInvalid) return false; IrInstruction *casted_arg = ir_implicit_cast(ira, arg, param_type); if (casted_arg->type_entry->id == TypeTableEntryIdInvalid) return false; ConstExprValue *first_arg_val = ir_resolve_const(ira, casted_arg); if (!first_arg_val) return false; Buf *param_name = param_decl_node->data.param_decl.name; VariableTableEntry *var = add_variable(ira->codegen, param_decl_node, *exec_scope, param_name, param_type, true, first_arg_val); *exec_scope = var->child_scope; *next_proto_i += 1; return true; } static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_node, IrInstruction *arg, Scope **child_scope, size_t *next_proto_i, GenericFnTypeId *generic_id, FnTypeId *fn_type_id, IrInstruction **casted_args, FnTableEntry *impl_fn) { AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(*next_proto_i); assert(param_decl_node->type == NodeTypeParamDecl); AstNode *param_type_node = param_decl_node->data.param_decl.type; TypeTableEntry *param_type = analyze_type_expr(ira->codegen, *child_scope, param_type_node); if (param_type->id == TypeTableEntryIdInvalid) return false; bool is_var_type = (param_type->id == TypeTableEntryIdVar); IrInstruction *casted_arg; if (is_var_type) { casted_arg = arg; } else { casted_arg = ir_implicit_cast(ira, arg, param_type); if (casted_arg->type_entry->id == TypeTableEntryIdInvalid) return false; } bool inline_arg = param_decl_node->data.param_decl.is_inline; if (inline_arg || is_var_type) { ConstExprValue *arg_val = ir_resolve_const(ira, casted_arg); if (!arg_val) return false; Buf *param_name = param_decl_node->data.param_decl.name; VariableTableEntry *var = add_variable(ira->codegen, param_decl_node, *child_scope, param_name, param_type, true, arg_val); *child_scope = var->child_scope; // This generic function instance could be called with anything, so when this variable is read it // needs to know that it depends on compile time variable data. var->value->depends_on_compile_var = true; GenericParamValue *generic_param = &generic_id->params[generic_id->param_count]; generic_param->type = casted_arg->type_entry; generic_param->value = arg_val; generic_id->param_count += 1; } else { casted_args[fn_type_id->param_count] = casted_arg; FnTypeParamInfo *param_info = &fn_type_id->param_info[fn_type_id->param_count]; param_info->type = param_type; param_info->is_noalias = param_decl_node->data.param_decl.is_noalias; impl_fn->param_source_nodes[fn_type_id->param_count] = param_decl_node; fn_type_id->param_count += 1; } *next_proto_i += 1; return true; } static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction, FnTableEntry *fn_entry, TypeTableEntry *fn_type, IrInstruction *fn_ref, IrInstruction *first_arg_ptr, bool inline_fn_call) { FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; size_t first_arg_1_or_0 = first_arg_ptr ? 1 : 0; size_t src_param_count = fn_type_id->param_count; size_t call_param_count = call_instruction->arg_count + first_arg_1_or_0; AstNode *source_node = call_instruction->base.source_node; AstNode *fn_proto_node = fn_entry ? fn_entry->proto_node : nullptr;; if (fn_type_id->is_var_args) { if (call_param_count < src_param_count) { ErrorMsg *msg = add_node_error(ira->codegen, source_node, buf_sprintf("expected at least %zu arguments, found %zu", src_param_count, call_param_count)); if (fn_proto_node) { add_error_note(ira->codegen, msg, fn_proto_node, buf_sprintf("declared here")); } return ira->codegen->builtin_types.entry_invalid; } } else if (src_param_count != call_param_count) { ErrorMsg *msg = add_node_error(ira->codegen, source_node, buf_sprintf("expected %zu arguments, found %zu", src_param_count, call_param_count)); if (fn_proto_node) { add_error_note(ira->codegen, msg, fn_proto_node, buf_sprintf("declared here")); } return ira->codegen->builtin_types.entry_invalid; } if (inline_fn_call) { // No special handling is needed for compile time evaluation of generic functions. if (!fn_entry) { ir_add_error(ira, fn_ref, buf_sprintf("unable to evaluate constant expression")); return ira->codegen->builtin_types.entry_invalid; } if (!ir_emit_backward_branch(ira, &call_instruction->base)) return ira->codegen->builtin_types.entry_invalid; // Fork a scope of the function with known values for the parameters. Scope *exec_scope = &fn_entry->fndef_scope->base; size_t next_proto_i = 0; if (first_arg_ptr) { IrInstruction *first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr); if (first_arg->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; if (!ir_analyze_fn_call_inline_arg(ira, fn_proto_node, first_arg, &exec_scope, &next_proto_i)) return ira->codegen->builtin_types.entry_invalid; } for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) { IrInstruction *old_arg = call_instruction->args[call_i]->other; if (old_arg->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; if (!ir_analyze_fn_call_inline_arg(ira, fn_proto_node, old_arg, &exec_scope, &next_proto_i)) return ira->codegen->builtin_types.entry_invalid; } AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type; TypeTableEntry *return_type = analyze_type_expr(ira->codegen, exec_scope, return_type_node); if (return_type->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; // Analyze the fn body block like any other constant expression. AstNode *body_node = fn_entry->fn_def_node->data.fn_def.body; IrInstruction *result = ir_eval_const_value(ira->codegen, exec_scope, body_node, return_type, ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, fn_entry, nullptr); if (result->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &call_instruction->base, result->static_value.depends_on_compile_var); *out_val = result->static_value; return ir_finish_anal(ira, return_type); } if (fn_type->data.fn.is_generic) { assert(fn_entry); IrInstruction **casted_args = allocate(call_param_count); // Fork a scope of the function with known values for the parameters. Scope *parent_scope = fn_entry->fndef_scope->base.parent; FnTableEntry *impl_fn = create_fn(fn_proto_node); impl_fn->param_source_nodes = allocate(call_param_count); buf_init_from_buf(&impl_fn->symbol_name, &fn_entry->symbol_name); impl_fn->fndef_scope = create_fndef_scope(impl_fn->fn_def_node, parent_scope, impl_fn); impl_fn->child_scope = &impl_fn->fndef_scope->base; FnTypeId fn_type_id = {0}; init_fn_type_id(&fn_type_id, fn_proto_node); fn_type_id.param_count = 0; // TODO maybe GenericFnTypeId can be replaced with using the child_scope directly // as the key in generic_table GenericFnTypeId *generic_id = allocate(1); generic_id->fn_entry = fn_entry; generic_id->param_count = 0; generic_id->params = allocate(src_param_count); size_t next_proto_i = 0; if (first_arg_ptr) { IrInstruction *first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr); if (first_arg->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, first_arg, &impl_fn->child_scope, &next_proto_i, generic_id, &fn_type_id, casted_args, impl_fn)) { return ira->codegen->builtin_types.entry_invalid; } } for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) { IrInstruction *arg = call_instruction->args[call_i]->other; if (arg->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, arg, &impl_fn->child_scope, &next_proto_i, generic_id, &fn_type_id, casted_args, impl_fn)) { return ira->codegen->builtin_types.entry_invalid; } } auto existing_entry = ira->codegen->generic_table.put_unique(generic_id, impl_fn); if (existing_entry) { // throw away all our work and use the existing function impl_fn = existing_entry->value; } else { // finish instantiating the function AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type; TypeTableEntry *return_type = analyze_type_expr(ira->codegen, impl_fn->child_scope, return_type_node); if (return_type->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; fn_type_id.return_type = return_type; impl_fn->type_entry = get_fn_type(ira->codegen, &fn_type_id); if (impl_fn->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; ira->codegen->fn_protos.append(impl_fn); ira->codegen->fn_defs.append(impl_fn); } size_t impl_param_count = impl_fn->type_entry->data.fn.fn_type_id.param_count; IrInstruction *new_call_instruction = ir_build_call_from(&ira->new_irb, &call_instruction->base, impl_fn, nullptr, impl_param_count, casted_args); TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type; ir_add_alloca(ira, new_call_instruction, return_type); return ir_finish_anal(ira, return_type); } IrInstruction **casted_args = allocate(call_param_count); size_t next_arg_index = 0; if (first_arg_ptr) { IrInstruction *first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr); if (first_arg->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *param_type = fn_type_id->param_info[next_arg_index].type; if (param_type->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_arg = ir_implicit_cast(ira, first_arg, param_type); if (casted_arg->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; casted_args[next_arg_index] = casted_arg; next_arg_index += 1; } for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) { IrInstruction *old_arg = call_instruction->args[call_i]->other; if (old_arg->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_arg; if (next_arg_index < src_param_count) { TypeTableEntry *param_type = fn_type_id->param_info[next_arg_index].type; if (param_type->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; casted_arg = ir_implicit_cast(ira, old_arg, param_type); if (casted_arg->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; } else { casted_arg = old_arg; } casted_args[next_arg_index] = casted_arg; next_arg_index += 1; } assert(next_arg_index == call_param_count); TypeTableEntry *return_type = fn_type_id->return_type; if (return_type->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *new_call_instruction = ir_build_call_from(&ira->new_irb, &call_instruction->base, fn_entry, fn_ref, call_param_count, casted_args); ir_add_alloca(ira, new_call_instruction, return_type); return ir_finish_anal(ira, return_type); } static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionCall *call_instruction) { IrInstruction *fn_ref = call_instruction->fn_ref->other; if (fn_ref->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; bool is_inline = call_instruction->is_inline || ir_should_inline(&ira->new_irb); if (is_inline || fn_ref->static_value.special != ConstValSpecialRuntime) { if (fn_ref->type_entry->id == TypeTableEntryIdMetaType) { TypeTableEntry *dest_type = ir_resolve_type(ira, fn_ref); if (dest_type->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; size_t actual_param_count = call_instruction->arg_count; if (actual_param_count != 1) { add_node_error(ira->codegen, call_instruction->base.source_node, buf_sprintf("cast expression expects exactly one parameter")); return ira->codegen->builtin_types.entry_invalid; } IrInstruction *arg = call_instruction->args[0]->other; IrInstruction *cast_instruction = ir_analyze_cast(ira, &call_instruction->base, dest_type, arg); if (cast_instruction->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; ir_link_new_instruction(cast_instruction, &call_instruction->base); return ir_finish_anal(ira, cast_instruction->type_entry); } else if (fn_ref->type_entry->id == TypeTableEntryIdFn) { FnTableEntry *fn_table_entry = ir_resolve_fn(ira, fn_ref); return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry, fn_ref, nullptr, is_inline); } else if (fn_ref->type_entry->id == TypeTableEntryIdBoundFn) { assert(fn_ref->static_value.special == ConstValSpecialStatic); FnTableEntry *fn_table_entry = fn_ref->static_value.data.x_bound_fn.fn; IrInstruction *first_arg_ptr = fn_ref->static_value.data.x_bound_fn.first_arg; return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry, nullptr, first_arg_ptr, is_inline); } else { add_node_error(ira->codegen, fn_ref->source_node, buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->type_entry->name))); return ira->codegen->builtin_types.entry_invalid; } } if (fn_ref->type_entry->id == TypeTableEntryIdFn) { return ir_analyze_fn_call(ira, call_instruction, nullptr, fn_ref->type_entry, fn_ref, nullptr, false); } else { add_node_error(ira->codegen, fn_ref->source_node, buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->type_entry->name))); return ira->codegen->builtin_types.entry_invalid; } } static TypeTableEntry *ir_analyze_unary_prefix_op_err(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) { assert(un_op_instruction->op_id == IrUnOpError); IrInstruction *value = un_op_instruction->value->other; TypeTableEntry *type_entry = value->type_entry; if (type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *meta_type = ir_resolve_type(ira, value); TypeTableEntry *underlying_meta_type = get_underlying_type(meta_type); switch (underlying_meta_type->id) { case TypeTableEntryIdTypeDecl: zig_unreachable(); case TypeTableEntryIdInvalid: return ira->codegen->builtin_types.entry_invalid; case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdBoundFn: { ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base, value->static_value.depends_on_compile_var); TypeTableEntry *result_type = get_error_type(ira->codegen, meta_type); out_val->data.x_type = result_type; return ira->codegen->builtin_types.entry_type; } case TypeTableEntryIdMetaType: case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdUnreachable: case TypeTableEntryIdVar: add_node_error(ira->codegen, un_op_instruction->base.source_node, buf_sprintf("unable to wrap type '%s' in error type", buf_ptr(&meta_type->name))); // TODO if meta_type is type decl, add note pointing to type decl declaration return ira->codegen->builtin_types.entry_invalid; } zig_unreachable(); } static TypeTableEntry *ir_analyze_unary_address_of(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction, bool is_const) { IrInstruction *value = un_op_instruction->value->other; if (value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *target_type = value->type_entry; TypeTableEntry *canon_target_type = get_underlying_type(target_type); switch (canon_target_type->id) { case TypeTableEntryIdTypeDecl: // impossible because we look at the canonicalized type zig_unreachable(); case TypeTableEntryIdInvalid: return ira->codegen->builtin_types.entry_invalid; case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdUnreachable: case TypeTableEntryIdVar: case TypeTableEntryIdBoundFn: add_node_error(ira->codegen, un_op_instruction->base.source_node, buf_sprintf("unable to get address of type '%s'", buf_ptr(&target_type->name))); // TODO if type decl, add note pointing to type decl declaration return ira->codegen->builtin_types.entry_invalid; case TypeTableEntryIdMetaType: { ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base, value->static_value.depends_on_compile_var); assert(value->static_value.special != ConstValSpecialRuntime); TypeTableEntry *child_type = value->static_value.data.x_type; out_val->data.x_type = get_pointer_to_type(ira->codegen, child_type, is_const); return ira->codegen->builtin_types.entry_type; } case TypeTableEntryIdPointer: { // this instruction is a noop - we solved this in IR gen by passing // LValPurposeAddressOf which caused the loadptr to not do the load. ir_link_new_instruction(value, &un_op_instruction->base); return ir_finish_anal(ira, target_type); } case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: zig_unreachable(); } zig_unreachable(); } static TypeTableEntry *ir_analyze_dereference(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) { IrInstruction *value = un_op_instruction->value->other; TypeTableEntry *ptr_type = value->type_entry; TypeTableEntry *child_type; if (ptr_type->id == TypeTableEntryIdInvalid) { return ira->codegen->builtin_types.entry_invalid; } else if (ptr_type->id == TypeTableEntryIdPointer) { child_type = ptr_type->data.pointer.child_type; } else { add_node_error(ira->codegen, un_op_instruction->base.source_node, buf_sprintf("attempt to dereference non-pointer type '%s'", buf_ptr(&ptr_type->name))); return ira->codegen->builtin_types.entry_invalid; } // this dereference is always an rvalue because in the IR gen we identify lvalue and emit // one of the ptr instructions if (value->static_value.special != ConstValSpecialRuntime) { ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base, false); ConstExprValue *pointee = const_ptr_pointee(&value->static_value); *out_val = *pointee; return child_type; } ir_build_un_op_from(&ira->new_irb, &un_op_instruction->base, IrUnOpDereference, value); return child_type; } static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) { IrInstruction *value = un_op_instruction->value->other; TypeTableEntry *type_entry = ir_resolve_type(ira, value); TypeTableEntry *canon_type = get_underlying_type(type_entry); switch (canon_type->id) { case TypeTableEntryIdInvalid: return ira->codegen->builtin_types.entry_invalid; case TypeTableEntryIdVar: case TypeTableEntryIdTypeDecl: zig_unreachable(); case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: { ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base, value->static_value.depends_on_compile_var); out_val->data.x_type = get_maybe_type(ira->codegen, type_entry); return ira->codegen->builtin_types.entry_type; } case TypeTableEntryIdUnreachable: add_node_error(ira->codegen, un_op_instruction->base.source_node, buf_sprintf("type '%s' not nullable", buf_ptr(&type_entry->name))); // TODO if it's a type decl, put an error note here pointing to the decl return ira->codegen->builtin_types.entry_invalid; } zig_unreachable(); } static TypeTableEntry *ir_analyze_unwrap_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) { IrInstruction *value = un_op_instruction->value->other; TypeTableEntry *type_entry = value->type_entry; if (type_entry->id == TypeTableEntryIdInvalid) { return type_entry; } else if (type_entry->id == TypeTableEntryIdMaybe) { if (value->static_value.special != ConstValSpecialRuntime) { bool depends_on_compile_var = value->static_value.depends_on_compile_var; ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base, depends_on_compile_var); ConstExprValue *child_val = value->static_value.data.x_maybe; if (!child_val) { ir_add_error(ira, &un_op_instruction->base, buf_sprintf("unable to unwrap null")); return ira->codegen->builtin_types.entry_invalid; } *out_val = *child_val; return type_entry->data.maybe.child_type; } ir_build_un_op_from(&ira->new_irb, &un_op_instruction->base, IrUnOpUnwrapMaybe, value); return type_entry->data.maybe.child_type; } else { add_node_error(ira->codegen, un_op_instruction->base.source_node, buf_sprintf("expected maybe type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->builtin_types.entry_invalid; } } static TypeTableEntry *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) { IrInstruction *value = un_op_instruction->value->other; TypeTableEntry *expr_type = value->type_entry; if (expr_type->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; bool is_wrap_op = (un_op_instruction->op_id == IrUnOpNegationWrap); if ((expr_type->id == TypeTableEntryIdInt && expr_type->data.integral.is_signed) || expr_type->id == TypeTableEntryIdNumLitInt || ((expr_type->id == TypeTableEntryIdFloat || expr_type->id == TypeTableEntryIdNumLitFloat) && !is_wrap_op)) { ConstExprValue *target_const_val = &value->static_value; if (target_const_val->special != ConstValSpecialRuntime) { bool depends_on_compile_var = value->static_value.depends_on_compile_var; ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base, depends_on_compile_var); bignum_negate(&out_val->data.x_bignum, &target_const_val->data.x_bignum); if (expr_type->id == TypeTableEntryIdFloat || expr_type->id == TypeTableEntryIdNumLitFloat || expr_type->id == TypeTableEntryIdNumLitInt) { return expr_type; } bool overflow = !bignum_fits_in_bits(&out_val->data.x_bignum, expr_type->data.integral.bit_count, true); if (is_wrap_op) { if (overflow) out_val->data.x_bignum.is_negative = true; } else if (overflow) { ir_add_error(ira, &un_op_instruction->base, buf_sprintf("negation caused overflow")); return ira->codegen->builtin_types.entry_invalid; } return expr_type; } } const char *fmt = is_wrap_op ? "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'"; ir_add_error(ira, &un_op_instruction->base, buf_sprintf(fmt, buf_ptr(&expr_type->name))); return ira->codegen->builtin_types.entry_invalid; } static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) { IrUnOp op_id = un_op_instruction->op_id; switch (op_id) { case IrUnOpInvalid: zig_unreachable(); case IrUnOpBinNot: zig_panic("TODO analyze PrefixOpBinNot"); //{ // TypeTableEntry *expr_type = analyze_expression(g, import, context, expected_type, // *expr_node); // if (expr_type->id == TypeTableEntryIdInvalid) { // return expr_type; // } else if (expr_type->id == TypeTableEntryIdInt) { // return expr_type; // } else { // add_node_error(g, node, buf_sprintf("unable to perform binary not operation on type '%s'", // buf_ptr(&expr_type->name))); // return g->builtin_types.entry_invalid; // } // // TODO const expr eval //} case IrUnOpNegation: case IrUnOpNegationWrap: return ir_analyze_negation(ira, un_op_instruction); case IrUnOpAddressOf: case IrUnOpConstAddressOf: return ir_analyze_unary_address_of(ira, un_op_instruction, op_id == IrUnOpConstAddressOf); case IrUnOpDereference: return ir_analyze_dereference(ira, un_op_instruction); case IrUnOpMaybe: return ir_analyze_maybe(ira, un_op_instruction); case IrUnOpError: return ir_analyze_unary_prefix_op_err(ira, un_op_instruction); case IrUnOpUnwrapError: zig_panic("TODO analyze PrefixOpUnwrapError"); //{ // TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node); // if (type_entry->id == TypeTableEntryIdInvalid) { // return type_entry; // } else if (type_entry->id == TypeTableEntryIdErrorUnion) { // return type_entry->data.error.child_type; // } else { // add_node_error(g, *expr_node, // buf_sprintf("expected error type, found '%s'", buf_ptr(&type_entry->name))); // return g->builtin_types.entry_invalid; // } //} case IrUnOpUnwrapMaybe: return ir_analyze_unwrap_maybe(ira, un_op_instruction); case IrUnOpErrorReturn: zig_panic("TODO analyze IrUnOpErrorReturn"); case IrUnOpMaybeReturn: zig_panic("TODO analyze IrUnOpMaybeReturn"); } zig_unreachable(); } static TypeTableEntry *ir_analyze_instruction_br(IrAnalyze *ira, IrInstructionBr *br_instruction) { IrBasicBlock *old_dest_block = br_instruction->dest_block; if (br_instruction->is_inline || old_dest_block->ref_count == 1) { return ir_inline_bb(ira, &br_instruction->base, old_dest_block); } IrBasicBlock *new_bb = ir_get_new_bb(ira, old_dest_block); ir_build_br_from(&ira->new_irb, &br_instruction->base, new_bb); return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); } static TypeTableEntry *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructionCondBr *cond_br_instruction) { IrInstruction *condition = cond_br_instruction->condition->other; if (condition->type_entry->id == TypeTableEntryIdInvalid) return ir_unreach_error(ira); if (cond_br_instruction->is_inline || condition->static_value.special != ConstValSpecialRuntime) { bool cond_is_true; if (!ir_resolve_bool(ira, condition, &cond_is_true)) return ir_unreach_error(ira); IrBasicBlock *old_dest_block = cond_is_true ? cond_br_instruction->then_block : cond_br_instruction->else_block; if (cond_br_instruction->is_inline || old_dest_block->ref_count == 1) return ir_inline_bb(ira, &cond_br_instruction->base, old_dest_block); } TypeTableEntry *bool_type = ira->codegen->builtin_types.entry_bool; IrInstruction *casted_condition = ir_implicit_cast(ira, condition, bool_type); if (casted_condition == ira->codegen->invalid_instruction) return ir_unreach_error(ira); IrBasicBlock *new_then_block = ir_get_new_bb(ira, cond_br_instruction->then_block); IrBasicBlock *new_else_block = ir_get_new_bb(ira, cond_br_instruction->else_block); ir_build_cond_br_from(&ira->new_irb, &cond_br_instruction->base, casted_condition, new_then_block, new_else_block, false); return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); } static TypeTableEntry *ir_analyze_instruction_unreachable(IrAnalyze *ira, IrInstructionUnreachable *unreachable_instruction) { ir_build_unreachable_from(&ira->new_irb, &unreachable_instruction->base); return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); } static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPhi *phi_instruction) { if (ira->const_predecessor_bb) { for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) { IrBasicBlock *predecessor = phi_instruction->incoming_blocks[i]; if (predecessor != ira->const_predecessor_bb) continue; IrInstruction *value = phi_instruction->incoming_values[i]->other; assert(value->type_entry); if (value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; if (value->static_value.special != ConstValSpecialRuntime) { ConstExprValue *out_val = ir_build_const_from(ira, &phi_instruction->base, value->static_value.depends_on_compile_var); *out_val = value->static_value; } else { phi_instruction->base.other = value; } return value->type_entry; } zig_unreachable(); } ZigList new_incoming_blocks = {0}; ZigList new_incoming_values = {0}; for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) { IrBasicBlock *predecessor = phi_instruction->incoming_blocks[i]; if (predecessor->ref_count == 0) continue; assert(predecessor->other); new_incoming_blocks.append(predecessor->other); IrInstruction *old_value = phi_instruction->incoming_values[i]; assert(old_value); IrInstruction *new_value = old_value->other; if (new_value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; new_incoming_values.append(new_value); } assert(new_incoming_blocks.length != 0); if (new_incoming_blocks.length == 1) { IrInstruction *first_value = new_incoming_values.at(0); phi_instruction->base.other = first_value; return first_value->type_entry; } TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.source_node, new_incoming_values.items, new_incoming_values.length); if (resolved_type->id == TypeTableEntryIdInvalid) return resolved_type; if (resolved_type->id == TypeTableEntryIdNumLitFloat || resolved_type->id == TypeTableEntryIdNumLitInt) { add_node_error(ira->codegen, phi_instruction->base.source_node, buf_sprintf("unable to infer expression type")); return ira->codegen->builtin_types.entry_invalid; } // cast all literal values to the resolved type for (size_t i = 0; i < new_incoming_values.length; i += 1) { IrInstruction *new_value = new_incoming_values.at(i); IrInstruction *casted_value = ir_implicit_cast(ira, new_value, resolved_type); new_incoming_values.items[i] = casted_value; } ir_build_phi_from(&ira->new_irb, &phi_instruction->base, new_incoming_blocks.length, new_incoming_blocks.items, new_incoming_values.items); return resolved_type; } static TypeTableEntry *ir_analyze_var_ptr(IrAnalyze *ira, IrInstruction *instruction, VariableTableEntry *var) { assert(var->type); if (var->type->id == TypeTableEntryIdInvalid) return var->type; ConstExprValue *mem_slot = nullptr; FnTableEntry *fn_entry = scope_fn_entry(var->parent_scope); if (var->src_is_const && var->value) { mem_slot = var->value; assert(mem_slot->special != ConstValSpecialRuntime); } else if (fn_entry) { // TODO once the analyze code is fully ported over to IR we won't need this SIZE_MAX thing. if (var->mem_slot_index != SIZE_MAX) mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index]; } if (mem_slot && mem_slot->special != ConstValSpecialRuntime) { ConstPtrSpecial ptr_special = var->is_inline ? ConstPtrSpecialInline : ConstPtrSpecialNone; return ir_analyze_const_ptr(ira, instruction, mem_slot, var->type, false, ptr_special); } else { ir_build_var_ptr_from(&ira->new_irb, instruction, var); return get_pointer_to_type(ira->codegen, var->type, false); } } static TypeTableEntry *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructionVarPtr *var_ptr_instruction) { VariableTableEntry *var = var_ptr_instruction->var; return ir_analyze_var_ptr(ira, &var_ptr_instruction->base, var); } static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionElemPtr *elem_ptr_instruction) { IrInstruction *array_ptr = elem_ptr_instruction->array_ptr->other; if (array_ptr->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *elem_index = elem_ptr_instruction->elem_index->other; if (elem_index->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; // This will be a pointer type because elem ptr IR instruction operates on a pointer to a thing. TypeTableEntry *ptr_type = array_ptr->type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); TypeTableEntry *array_type = ptr_type->data.pointer.child_type; TypeTableEntry *return_type; if (array_type->id == TypeTableEntryIdInvalid) { return array_type; } else if (array_type->id == TypeTableEntryIdArray) { if (array_type->data.array.len == 0) { add_node_error(ira->codegen, elem_ptr_instruction->base.source_node, buf_sprintf("index 0 outside array of size 0")); } TypeTableEntry *child_type = array_type->data.array.child_type; return_type = get_pointer_to_type(ira->codegen, child_type, false); } else if (array_type->id == TypeTableEntryIdPointer) { return_type = array_type; } else if (is_slice(array_type)) { return_type = array_type->data.structure.fields[0].type_entry; } else { add_node_error(ira->codegen, elem_ptr_instruction->base.source_node, buf_sprintf("array access of non-array type '%s'", buf_ptr(&array_type->name))); return ira->codegen->builtin_types.entry_invalid; } TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize; IrInstruction *casted_elem_index = ir_implicit_cast(ira, elem_index, usize); if (casted_elem_index == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; bool safety_check_on = elem_ptr_instruction->safety_check_on; if (casted_elem_index->static_value.special != ConstValSpecialRuntime) { uint64_t index = casted_elem_index->static_value.data.x_bignum.data.x_uint; if (array_type->id == TypeTableEntryIdArray) { uint64_t array_len = array_type->data.array.len; if (index >= array_len) { add_node_error(ira->codegen, elem_ptr_instruction->base.source_node, buf_sprintf("index %" PRIu64 " outside array of size %" PRIu64, index, array_len)); return ira->codegen->builtin_types.entry_invalid; } safety_check_on = false; } ConstExprValue *array_ptr_val; if (array_ptr->static_value.special != ConstValSpecialRuntime && (array_ptr_val = const_ptr_pointee(&array_ptr->static_value)) && array_ptr_val->special != ConstValSpecialRuntime) { bool depends_on_compile_var = array_ptr_val->depends_on_compile_var || casted_elem_index->static_value.depends_on_compile_var; ConstExprValue *out_val = ir_build_const_from(ira, &elem_ptr_instruction->base, depends_on_compile_var); if (array_type->id == TypeTableEntryIdPointer) { size_t offset = array_ptr_val->data.x_ptr.index; size_t new_index; size_t mem_size; size_t old_size; if (offset == SIZE_MAX) { new_index = SIZE_MAX; mem_size = 1; old_size = 1; } else { new_index = offset + index; mem_size = array_ptr_val->data.x_ptr.base_ptr->data.x_array.size; old_size = mem_size - offset; } if (new_index >= mem_size) { add_node_error(ira->codegen, elem_ptr_instruction->base.source_node, buf_sprintf("index %" PRIu64 " outside pointer of size %" PRIu64, index, old_size)); return ira->codegen->builtin_types.entry_invalid; } out_val->data.x_ptr.base_ptr = array_ptr_val->data.x_ptr.base_ptr; out_val->data.x_ptr.index = new_index; } else if (is_slice(array_type)) { ConstExprValue *ptr_field = &array_ptr_val->data.x_struct.fields[slice_ptr_index]; ConstExprValue *len_field = &array_ptr_val->data.x_struct.fields[slice_len_index]; uint64_t slice_len = len_field->data.x_bignum.data.x_uint; if (index >= slice_len) { add_node_error(ira->codegen, elem_ptr_instruction->base.source_node, buf_sprintf("index %" PRIu64 " outside slice of size %" PRIu64, index, slice_len)); return ira->codegen->builtin_types.entry_invalid; } out_val->data.x_ptr.base_ptr = ptr_field->data.x_ptr.base_ptr; size_t offset = ptr_field->data.x_ptr.index; if (offset == SIZE_MAX) { out_val->data.x_ptr.index = SIZE_MAX; } else { uint64_t new_index = offset + index; assert(new_index < ptr_field->data.x_ptr.base_ptr->data.x_array.size); out_val->data.x_ptr.index = new_index; } } else if (array_type->id == TypeTableEntryIdArray) { out_val->data.x_ptr.base_ptr = array_ptr_val; out_val->data.x_ptr.index = index; } else { zig_unreachable(); } return return_type; } } ir_build_elem_ptr_from(&ira->new_irb, &elem_ptr_instruction->base, array_ptr, casted_elem_index, safety_check_on); return return_type; } static TypeTableEntry *ir_analyze_container_member_access_inner(IrAnalyze *ira, TypeTableEntry *bare_struct_type, Buf *field_name, IrInstructionFieldPtr *field_ptr_instruction, IrInstruction *container_ptr, TypeTableEntry *container_type) { if (!is_slice(bare_struct_type)) { ScopeDecls *container_scope = get_container_scope(bare_struct_type); auto entry = container_scope->decl_table.maybe_get(field_name); Tld *tld = entry ? entry->value : nullptr; if (tld && tld->id == TldIdFn) { resolve_top_level_decl(ira->codegen, tld, false); if (tld->resolution == TldResolutionInvalid) return ira->codegen->builtin_types.entry_invalid; TldFn *tld_fn = (TldFn *)tld; FnTableEntry *fn_entry = tld_fn->fn_entry; bool depends_on_compile_var = container_ptr->static_value.depends_on_compile_var; IrInstruction *bound_fn_value = ir_build_const_bound_fn(&ira->new_irb, field_ptr_instruction->base.scope, field_ptr_instruction->base.source_node, fn_entry, container_ptr, depends_on_compile_var); return ir_analyze_ref(ira, &field_ptr_instruction->base, bound_fn_value); } } add_node_error(ira->codegen, field_ptr_instruction->base.source_node, buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&bare_struct_type->name))); return ira->codegen->builtin_types.entry_invalid; } static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name, IrInstructionFieldPtr *field_ptr_instruction, IrInstruction *container_ptr, TypeTableEntry *container_type) { TypeTableEntry *bare_type = container_ref_type(container_type); if (!type_is_complete(bare_type)) resolve_container_type(ira->codegen, bare_type); if (bare_type->id == TypeTableEntryIdStruct) { TypeStructField *field = find_struct_type_field(bare_type, field_name); if (field) { ir_build_struct_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field); return get_pointer_to_type(ira->codegen, field->type_entry, false); } else { return ir_analyze_container_member_access_inner(ira, bare_type, field_name, field_ptr_instruction, container_ptr, container_type); } } else if (bare_type->id == TypeTableEntryIdEnum) { TypeEnumField *field = find_enum_type_field(bare_type, field_name); if (field) { ir_build_enum_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field); return get_pointer_to_type(ira->codegen, field->type_entry, false); } else { return ir_analyze_container_member_access_inner(ira, bare_type, field_name, field_ptr_instruction, container_ptr, container_type); } } else if (bare_type->id == TypeTableEntryIdUnion) { zig_panic("TODO"); } else { zig_unreachable(); } } static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source_instruction, Tld *tld, bool depends_on_compile_var) { bool pointer_only = false; resolve_top_level_decl(ira->codegen, tld, pointer_only); if (tld->resolution == TldResolutionInvalid) return ira->codegen->builtin_types.entry_invalid; switch (tld->id) { case TldIdVar: { TldVar *tld_var = (TldVar *)tld; VariableTableEntry *var = tld_var->var; return ir_analyze_var_ptr(ira, source_instruction, var); } case TldIdFn: { TldFn *tld_fn = (TldFn *)tld; FnTableEntry *fn_entry = tld_fn->fn_entry; assert(fn_entry->type_entry); // TODO instead of allocating this every time, put it in the tld value and we can reference // the same one every time ConstExprValue *const_val = allocate(1); const_val->special = ConstValSpecialStatic; const_val->data.x_fn = fn_entry; return ir_analyze_const_ptr(ira, source_instruction, const_val, fn_entry->type_entry, depends_on_compile_var, ConstPtrSpecialNone); } case TldIdContainer: { TldContainer *tld_container = (TldContainer *)tld; assert(tld_container->type_entry); // TODO instead of allocating this every time, put it in the tld value and we can reference // the same one every time ConstExprValue *const_val = allocate(1); const_val->special = ConstValSpecialStatic; const_val->data.x_type = tld_container->type_entry; return ir_analyze_const_ptr(ira, source_instruction, const_val, tld_container->type_entry, depends_on_compile_var, ConstPtrSpecialNone); } case TldIdTypeDef: { TldTypeDef *tld_typedef = (TldTypeDef *)tld; assert(tld_typedef->type_entry); // TODO instead of allocating this every time, put it in the tld value and we can reference // the same one every time ConstExprValue *const_val = allocate(1); const_val->special = ConstValSpecialStatic; const_val->data.x_type = tld_typedef->type_entry; return ir_analyze_const_ptr(ira, source_instruction, const_val, tld_typedef->type_entry, depends_on_compile_var, ConstPtrSpecialNone); } } zig_unreachable(); } static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) { IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other; if (container_ptr->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *container_type; if (container_ptr->type_entry->id == TypeTableEntryIdPointer) { container_type = container_ptr->type_entry->data.pointer.child_type; } else if (container_ptr->type_entry->id == TypeTableEntryIdMetaType) { container_type = container_ptr->type_entry; } else { zig_unreachable(); } bool depends_on_compile_var = container_ptr->static_value.depends_on_compile_var; Buf *field_name = field_ptr_instruction->field_name; AstNode *source_node = field_ptr_instruction->base.source_node; if (container_type->id == TypeTableEntryIdInvalid) { return container_type; } else if (is_container_ref(container_type)) { assert(container_ptr->type_entry->id == TypeTableEntryIdPointer); return ir_analyze_container_field_ptr(ira, field_name, field_ptr_instruction, container_ptr, container_type); } else if (container_type->id == TypeTableEntryIdArray) { if (buf_eql_str(field_name, "len")) { ConstExprValue *len_val = allocate(1); len_val->special = ConstValSpecialStatic; bignum_init_unsigned(&len_val->data.x_bignum, container_type->data.array.len); TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize; return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, len_val, usize, false, ConstPtrSpecialNone); } else { add_node_error(ira->codegen, source_node, buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&container_type->name))); return ira->codegen->builtin_types.entry_invalid; } } else if (container_type->id == TypeTableEntryIdMetaType) { ConstExprValue *container_ptr_val = ir_resolve_const(ira, container_ptr); if (!container_ptr_val) return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *child_type; if (container_ptr->type_entry->id == TypeTableEntryIdMetaType) { TypeTableEntry *ptr_type = container_ptr_val->data.x_type; assert(ptr_type->id == TypeTableEntryIdPointer); child_type = ptr_type->data.pointer.child_type; } else if (container_ptr->type_entry->id == TypeTableEntryIdPointer) { ConstExprValue *child_val = const_ptr_pointee(container_ptr_val); child_type = child_val->data.x_type; } else { zig_unreachable(); } if (child_type->id == TypeTableEntryIdInvalid) { return ira->codegen->builtin_types.entry_invalid; } else if (is_container(child_type)) { if (child_type->id == TypeTableEntryIdEnum) { TypeEnumField *field = find_enum_type_field(child_type, field_name); if (field) { if (field->type_entry->id == TypeTableEntryIdVoid) { return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, create_const_enum_tag(field->value), child_type, depends_on_compile_var, ConstPtrSpecialNone); } else { zig_panic("TODO enum tag type"); } } } ScopeDecls *container_scope = get_container_scope(child_type); auto entry = container_scope->decl_table.maybe_get(field_name); Tld *tld = entry ? entry->value : nullptr; if (tld) { return ir_analyze_decl_ref(ira, &field_ptr_instruction->base, tld, depends_on_compile_var); } ir_add_error(ira, &field_ptr_instruction->base, buf_sprintf("container '%s' has no member called '%s'", buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->builtin_types.entry_invalid; } else if (child_type->id == TypeTableEntryIdPureError) { auto err_table_entry = ira->codegen->error_table.maybe_get(field_name); if (err_table_entry) { ConstExprValue *const_val = allocate(1); const_val->special = ConstValSpecialStatic; const_val->data.x_pure_err = err_table_entry->value; return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, const_val, child_type, depends_on_compile_var, ConstPtrSpecialNone); } ir_add_error(ira, &field_ptr_instruction->base, buf_sprintf("use of undeclared error value '%s'", buf_ptr(field_name))); return ira->codegen->builtin_types.entry_invalid; } else if (child_type->id == TypeTableEntryIdInt) { if (buf_eql_str(field_name, "bit_count")) { return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, create_const_unsigned_negative(child_type->data.integral.bit_count, false), ira->codegen->builtin_types.entry_num_lit_int, depends_on_compile_var, ConstPtrSpecialNone); } else if (buf_eql_str(field_name, "is_signed")) { return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, create_const_bool(child_type->data.integral.is_signed), ira->codegen->builtin_types.entry_bool, depends_on_compile_var, ConstPtrSpecialNone); } else { ir_add_error(ira, &field_ptr_instruction->base, buf_sprintf("type '%s' has no member called '%s'", buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->builtin_types.entry_invalid; } } else { ir_add_error(ira, &field_ptr_instruction->base, buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name))); return ira->codegen->builtin_types.entry_invalid; } } else if (container_type->id == TypeTableEntryIdNamespace) { assert(container_ptr->type_entry->id == TypeTableEntryIdPointer); ConstExprValue *container_ptr_val = ir_resolve_const(ira, container_ptr); if (!container_ptr_val) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *namespace_val = const_ptr_pointee(container_ptr_val); assert(namespace_val->special == ConstValSpecialStatic); ImportTableEntry *namespace_import = namespace_val->data.x_import; Tld *tld = find_decl(&namespace_import->decls_scope->base, field_name); if (!tld) { // we must now resolve all the use decls // TODO move this check to find_decl? for (size_t i = 0; i < namespace_import->use_decls.length; i += 1) { AstNode *use_decl_node = namespace_import->use_decls.at(i); if (use_decl_node->data.use.resolution == TldResolutionUnresolved) { preview_use_decl(ira->codegen, use_decl_node); } resolve_use_decl(ira->codegen, use_decl_node); } tld = find_decl(&namespace_import->decls_scope->base, field_name); } if (tld) { if (tld->visib_mod == VisibModPrivate && tld->import != source_node->owner) { ErrorMsg *msg = add_node_error(ira->codegen, source_node, buf_sprintf("'%s' is private", buf_ptr(field_name))); add_error_note(ira->codegen, msg, tld->source_node, buf_sprintf("declared here")); return ira->codegen->builtin_types.entry_invalid; } return ir_analyze_decl_ref(ira, &field_ptr_instruction->base, tld, depends_on_compile_var); } else { const char *import_name = namespace_import->path ? buf_ptr(namespace_import->path) : "(C import)"; add_node_error(ira->codegen, source_node, buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), import_name)); return ira->codegen->builtin_types.entry_invalid; } } else { add_node_error(ira->codegen, field_ptr_instruction->base.source_node, buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name))); return ira->codegen->builtin_types.entry_invalid; } } static TypeTableEntry *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *load_ptr_instruction) { IrInstruction *ptr = load_ptr_instruction->ptr->other; IrInstruction *result = ir_get_deref(ira, &load_ptr_instruction->base, ptr); ir_link_new_instruction(result, &load_ptr_instruction->base); assert(result->type_entry); return result->type_entry; } static TypeTableEntry *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *store_ptr_instruction) { IrInstruction *ptr = store_ptr_instruction->ptr->other; if (ptr->type_entry->id == TypeTableEntryIdInvalid) return ptr->type_entry; IrInstruction *value = store_ptr_instruction->value->other; if (value->type_entry->id == TypeTableEntryIdInvalid) return value->type_entry; TypeTableEntry *child_type = ptr->type_entry->data.pointer.child_type; IrInstruction *casted_value = ir_implicit_cast(ira, value, child_type); if (casted_value == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; if (ptr->static_value.special != ConstValSpecialRuntime) { bool is_inline = (ptr->static_value.data.x_ptr.special == ConstPtrSpecialInline); if (casted_value->static_value.special != ConstValSpecialRuntime) { ConstExprValue *dest_val = const_ptr_pointee(&ptr->static_value); if (dest_val->special != ConstValSpecialRuntime) { *dest_val = casted_value->static_value; return ir_analyze_void(ira, &store_ptr_instruction->base); } } if (is_inline) { ir_add_error(ira, &store_ptr_instruction->base, buf_sprintf("cannot store runtime value in compile time variable")); return ira->codegen->builtin_types.entry_invalid; } } if (ptr->static_value.special != ConstValSpecialRuntime) { // This memory location is transforming from known at compile time to known at runtime. // We must emit our own var ptr instruction. // TODO can we delete this code now that we have inline var? ptr->static_value.special = ConstValSpecialRuntime; IrInstruction *new_ptr_inst; if (ptr->id == IrInstructionIdVarPtr) { IrInstructionVarPtr *var_ptr_inst = (IrInstructionVarPtr *)ptr; VariableTableEntry *var = var_ptr_inst->var; new_ptr_inst = ir_build_var_ptr(&ira->new_irb, store_ptr_instruction->base.scope, store_ptr_instruction->base.source_node, var); assert(var->mem_slot_index != SIZE_MAX); ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index]; mem_slot->special = ConstValSpecialRuntime; } else if (ptr->id == IrInstructionIdFieldPtr) { zig_panic("TODO"); } else if (ptr->id == IrInstructionIdElemPtr) { zig_panic("TODO"); } else { zig_unreachable(); } new_ptr_inst->type_entry = ptr->type_entry; ir_build_store_ptr(&ira->new_irb, store_ptr_instruction->base.scope, store_ptr_instruction->base.source_node, new_ptr_inst, casted_value); return ir_analyze_void(ira, &store_ptr_instruction->base); } ir_build_store_ptr_from(&ira->new_irb, &store_ptr_instruction->base, ptr, casted_value); return ira->codegen->builtin_types.entry_void; } static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructionTypeOf *typeof_instruction) { IrInstruction *expr_value = typeof_instruction->value->other; TypeTableEntry *type_entry = expr_value->type_entry; switch (type_entry->id) { case TypeTableEntryIdInvalid: return type_entry; case TypeTableEntryIdVar: add_node_error(ira->codegen, expr_value->source_node, buf_sprintf("type '%s' not eligible for @typeOf", buf_ptr(&type_entry->name))); return ira->codegen->builtin_types.entry_invalid; case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdUnreachable: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdTypeDecl: { ConstExprValue *out_val = ir_build_const_from(ira, &typeof_instruction->base, false); // TODO depends_on_compile_var should be set based on whether the type of the expression // depends_on_compile_var. but we currently don't have a thing to tell us if the type of // something depends on a compile var out_val->data.x_type = type_entry; return ira->codegen->builtin_types.entry_type; } } zig_unreachable(); } static TypeTableEntry *ir_analyze_instruction_to_ptr_type(IrAnalyze *ira, IrInstructionToPtrType *to_ptr_type_instruction) { IrInstruction *type_value = to_ptr_type_instruction->value->other; TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); if (type_entry->id == TypeTableEntryIdInvalid) return type_entry; TypeTableEntry *ptr_type; if (type_entry->id == TypeTableEntryIdArray) { ptr_type = get_pointer_to_type(ira->codegen, type_entry->data.array.child_type, false); } else if (is_slice(type_entry)) { ptr_type = type_entry->data.structure.fields[0].type_entry; } else { add_node_error(ira->codegen, to_ptr_type_instruction->base.source_node, buf_sprintf("expected array type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->builtin_types.entry_invalid; } ConstExprValue *out_val = ir_build_const_from(ira, &to_ptr_type_instruction->base, type_value->static_value.depends_on_compile_var); out_val->data.x_type = ptr_type; return ira->codegen->builtin_types.entry_type; } static TypeTableEntry *ir_analyze_instruction_ptr_type_child(IrAnalyze *ira, IrInstructionPtrTypeChild *ptr_type_child_instruction) { IrInstruction *type_value = ptr_type_child_instruction->value->other; TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); if (type_entry->id == TypeTableEntryIdInvalid) return type_entry; if (type_entry->id != TypeTableEntryIdPointer) { add_node_error(ira->codegen, ptr_type_child_instruction->base.source_node, buf_sprintf("expected pointer type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->builtin_types.entry_invalid; } ConstExprValue *out_val = ir_build_const_from(ira, &ptr_type_child_instruction->base, type_value->static_value.depends_on_compile_var); out_val->data.x_type = type_entry->data.pointer.child_type; return ira->codegen->builtin_types.entry_type; } static TypeTableEntry *ir_analyze_instruction_set_fn_test(IrAnalyze *ira, IrInstructionSetFnTest *set_fn_test_instruction) { IrInstruction *fn_value = set_fn_test_instruction->fn_value->other; FnTableEntry *fn_entry = ir_resolve_fn(ira, fn_value); if (!fn_entry) return ira->codegen->builtin_types.entry_invalid; if (!fn_entry->is_test) { fn_entry->is_test = true; ira->codegen->test_fn_count += 1; } ir_build_const_from(ira, &set_fn_test_instruction->base, false); return ira->codegen->builtin_types.entry_void; } static TypeTableEntry *ir_analyze_instruction_set_fn_visible(IrAnalyze *ira, IrInstructionSetFnVisible *set_fn_visible_instruction) { IrInstruction *fn_value = set_fn_visible_instruction->fn_value->other; IrInstruction *is_visible_value = set_fn_visible_instruction->is_visible->other; FnTableEntry *fn_entry = ir_resolve_fn(ira, fn_value); if (!fn_entry) return ira->codegen->builtin_types.entry_invalid; bool want_export; if (!ir_resolve_bool(ira, is_visible_value, &want_export)) return ira->codegen->builtin_types.entry_invalid; AstNode *source_node = set_fn_visible_instruction->base.source_node; if (fn_entry->fn_export_set_node) { ErrorMsg *msg = add_node_error(ira->codegen, source_node, buf_sprintf("function visibility set twice")); add_error_note(ira->codegen, msg, fn_entry->fn_export_set_node, buf_sprintf("first set here")); return ira->codegen->builtin_types.entry_invalid; } fn_entry->fn_export_set_node = source_node; AstNodeFnProto *fn_proto = &fn_entry->proto_node->data.fn_proto; if (fn_proto->visib_mod != VisibModExport) { ErrorMsg *msg = add_node_error(ira->codegen, source_node, buf_sprintf("function must be marked export to set function visibility")); add_error_note(ira->codegen, msg, fn_entry->proto_node, buf_sprintf("function declared here")); return ira->codegen->builtin_types.entry_invalid; } fn_entry->internal_linkage = !want_export; ir_build_const_from(ira, &set_fn_visible_instruction->base, false); return ira->codegen->builtin_types.entry_void; } static TypeTableEntry *ir_analyze_instruction_set_debug_safety(IrAnalyze *ira, IrInstructionSetDebugSafety *set_debug_safety_instruction) { IrInstruction *target_instruction = set_debug_safety_instruction->scope_value->other; TypeTableEntry *target_type = target_instruction->type_entry; if (target_type->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *target_val = ir_resolve_const(ira, target_instruction); if (!target_val) return ira->codegen->builtin_types.entry_invalid; bool *safety_off_ptr; AstNode **safety_set_node_ptr; if (target_type->id == TypeTableEntryIdBlock) { ScopeBlock *block_scope = (ScopeBlock *)target_val->data.x_block; safety_off_ptr = &block_scope->safety_off; safety_set_node_ptr = &block_scope->safety_set_node; } else if (target_type->id == TypeTableEntryIdFn) { FnTableEntry *target_fn = target_val->data.x_fn; assert(target_fn->def_scope); safety_off_ptr = &target_fn->def_scope->safety_off; safety_set_node_ptr = &target_fn->def_scope->safety_set_node; } else if (target_type->id == TypeTableEntryIdMetaType) { ScopeDecls *decls_scope; TypeTableEntry *type_arg = target_val->data.x_type; if (type_arg->id == TypeTableEntryIdStruct) { decls_scope = type_arg->data.structure.decls_scope; } else if (type_arg->id == TypeTableEntryIdEnum) { decls_scope = type_arg->data.enumeration.decls_scope; } else if (type_arg->id == TypeTableEntryIdUnion) { decls_scope = type_arg->data.unionation.decls_scope; } else { add_node_error(ira->codegen, target_instruction->source_node, buf_sprintf("expected scope reference, found type '%s'", buf_ptr(&type_arg->name))); return ira->codegen->builtin_types.entry_invalid; } safety_off_ptr = &decls_scope->safety_off; safety_set_node_ptr = &decls_scope->safety_set_node; } else { add_node_error(ira->codegen, target_instruction->source_node, buf_sprintf("expected scope reference, found type '%s'", buf_ptr(&target_type->name))); return ira->codegen->builtin_types.entry_invalid; } IrInstruction *debug_safety_on_value = set_debug_safety_instruction->debug_safety_on->other; bool want_debug_safety; if (!ir_resolve_bool(ira, debug_safety_on_value, &want_debug_safety)) return ira->codegen->builtin_types.entry_invalid; AstNode *source_node = set_debug_safety_instruction->base.source_node; if (*safety_set_node_ptr) { ErrorMsg *msg = add_node_error(ira->codegen, source_node, buf_sprintf("function test attribute set twice")); add_error_note(ira->codegen, msg, *safety_set_node_ptr, buf_sprintf("first set here")); return ira->codegen->builtin_types.entry_invalid; } *safety_set_node_ptr = source_node; *safety_off_ptr = !want_debug_safety; ir_build_const_from(ira, &set_debug_safety_instruction->base, false); return ira->codegen->builtin_types.entry_void; } static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira, IrInstructionSliceType *slice_type_instruction) { IrInstruction *child_type = slice_type_instruction->child_type->other; if (child_type->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; bool is_const = slice_type_instruction->is_const; TypeTableEntry *resolved_child_type = ir_resolve_type(ira, child_type); TypeTableEntry *canon_child_type = get_underlying_type(resolved_child_type); switch (canon_child_type->id) { case TypeTableEntryIdTypeDecl: zig_unreachable(); case TypeTableEntryIdInvalid: return ira->codegen->builtin_types.entry_invalid; case TypeTableEntryIdVar: case TypeTableEntryIdUnreachable: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdBlock: add_node_error(ira->codegen, slice_type_instruction->base.source_node, buf_sprintf("slice of type '%s' not allowed", buf_ptr(&resolved_child_type->name))); // TODO if this is a typedecl, add error note showing the declaration of the type decl return ira->codegen->builtin_types.entry_invalid; case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: case TypeTableEntryIdBoundFn: { TypeTableEntry *result_type = get_slice_type(ira->codegen, resolved_child_type, is_const); ConstExprValue *out_val = ir_build_const_from(ira, &slice_type_instruction->base, child_type->static_value.depends_on_compile_var); out_val->data.x_type = result_type; return ira->codegen->builtin_types.entry_type; } } zig_unreachable(); } static TypeTableEntry *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAsm *asm_instruction) { assert(asm_instruction->base.source_node->type == NodeTypeAsmExpr); if (!ir_emit_global_runtime_side_effect(ira, &asm_instruction->base)) return ira->codegen->builtin_types.entry_invalid; // TODO validate the output types and variable types AstNodeAsmExpr *asm_expr = &asm_instruction->base.source_node->data.asm_expr; IrInstruction **input_list = allocate(asm_expr->input_list.length); IrInstruction **output_types = allocate(asm_expr->output_list.length); TypeTableEntry *return_type = ira->codegen->builtin_types.entry_void; for (size_t i = 0; i < asm_expr->output_list.length; i += 1) { AsmOutput *asm_output = asm_expr->output_list.at(i); if (asm_output->return_type) { output_types[i] = asm_instruction->output_types[i]->other; return_type = ir_resolve_type(ira, output_types[i]); if (return_type->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; } } for (size_t i = 0; i < asm_expr->input_list.length; i += 1) { input_list[i] = asm_instruction->input_list[i]->other; if (input_list[i]->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; } ir_build_asm_from(&ira->new_irb, &asm_instruction->base, input_list, output_types, asm_instruction->output_vars, asm_instruction->return_count, asm_instruction->has_side_effects); return return_type; } static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira, IrInstructionArrayType *array_type_instruction) { IrInstruction *size_value = array_type_instruction->size->other; uint64_t size; if (!ir_resolve_usize(ira, size_value, &size)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *child_type_value = array_type_instruction->child_type->other; TypeTableEntry *child_type = ir_resolve_type(ira, child_type_value); TypeTableEntry *canon_child_type = get_underlying_type(child_type); switch (canon_child_type->id) { case TypeTableEntryIdTypeDecl: zig_unreachable(); case TypeTableEntryIdInvalid: return ira->codegen->builtin_types.entry_invalid; case TypeTableEntryIdVar: case TypeTableEntryIdUnreachable: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdBlock: add_node_error(ira->codegen, array_type_instruction->base.source_node, buf_sprintf("array of type '%s' not allowed", buf_ptr(&child_type->name))); // TODO if this is a typedecl, add error note showing the declaration of the type decl return ira->codegen->builtin_types.entry_invalid; case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: case TypeTableEntryIdBoundFn: { TypeTableEntry *result_type = get_array_type(ira->codegen, child_type, size); bool depends_on_compile_var = child_type_value->static_value.depends_on_compile_var || size_value->static_value.depends_on_compile_var; ConstExprValue *out_val = ir_build_const_from(ira, &array_type_instruction->base, depends_on_compile_var); out_val->data.x_type = result_type; return ira->codegen->builtin_types.entry_type; } } zig_unreachable(); } static TypeTableEntry *ir_analyze_instruction_compile_var(IrAnalyze *ira, IrInstructionCompileVar *compile_var_instruction) { IrInstruction *name_value = compile_var_instruction->name->other; Buf *var_name = ir_resolve_str(ira, name_value); if (!var_name) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &compile_var_instruction->base, true); if (buf_eql_str(var_name, "is_big_endian")) { out_val->data.x_bool = ira->codegen->is_big_endian; return ira->codegen->builtin_types.entry_bool; } else if (buf_eql_str(var_name, "is_release")) { out_val->data.x_bool = ira->codegen->is_release_build; return ira->codegen->builtin_types.entry_bool; } else if (buf_eql_str(var_name, "is_test")) { out_val->data.x_bool = ira->codegen->is_test_build; return ira->codegen->builtin_types.entry_bool; } else if (buf_eql_str(var_name, "os")) { out_val->data.x_enum.tag = ira->codegen->target_os_index; return ira->codegen->builtin_types.entry_os_enum; } else if (buf_eql_str(var_name, "arch")) { out_val->data.x_enum.tag = ira->codegen->target_arch_index; return ira->codegen->builtin_types.entry_arch_enum; } else if (buf_eql_str(var_name, "environ")) { out_val->data.x_enum.tag = ira->codegen->target_environ_index; return ira->codegen->builtin_types.entry_environ_enum; } else if (buf_eql_str(var_name, "object_format")) { out_val->data.x_enum.tag = ira->codegen->target_oformat_index; return ira->codegen->builtin_types.entry_oformat_enum; } else { add_node_error(ira->codegen, name_value->source_node, buf_sprintf("unrecognized compile variable: '%s'", buf_ptr(var_name))); return ira->codegen->builtin_types.entry_invalid; } zig_unreachable(); } static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira, IrInstructionSizeOf *size_of_instruction) { IrInstruction *type_value = size_of_instruction->type_value->other; TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); TypeTableEntry *canon_type_entry = get_underlying_type(type_entry); switch (canon_type_entry->id) { case TypeTableEntryIdInvalid: return ira->codegen->builtin_types.entry_invalid; case TypeTableEntryIdTypeDecl: zig_unreachable(); case TypeTableEntryIdVar: case TypeTableEntryIdUnreachable: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdBlock: case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdBoundFn: case TypeTableEntryIdMetaType: case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: add_node_error(ira->codegen, size_of_instruction->base.source_node, buf_sprintf("no size available for type '%s'", buf_ptr(&type_entry->name))); // TODO if this is a typedecl, add error note showing the declaration of the type decl return ira->codegen->builtin_types.entry_invalid; case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: { uint64_t size_in_bytes = type_size(ira->codegen, type_entry); bool depends_on_compile_var = false; // TODO types should be able to depend on compile var ConstExprValue *out_val = ir_build_const_from(ira, &size_of_instruction->base, depends_on_compile_var); bignum_init_unsigned(&out_val->data.x_bignum, size_in_bytes); return ira->codegen->builtin_types.entry_num_lit_int; } } zig_unreachable(); } static TypeTableEntry *ir_analyze_instruction_test_null(IrAnalyze *ira, IrInstructionTestNull *test_null_instruction) { IrInstruction *value = test_null_instruction->value->other; if (value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; // This will be a pointer type because test null IR instruction operates on a pointer to a thing. TypeTableEntry *ptr_type = value->type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); TypeTableEntry *type_entry = ptr_type->data.pointer.child_type; if (type_entry->id != TypeTableEntryIdMaybe) { add_node_error(ira->codegen, test_null_instruction->base.source_node, buf_sprintf("expected nullable type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->builtin_types.entry_invalid; } if (value->static_value.special != ConstValSpecialRuntime) { ConstExprValue *maybe_val = value->static_value.data.x_ptr.base_ptr; assert(value->static_value.data.x_ptr.index == SIZE_MAX); if (maybe_val->special != ConstValSpecialRuntime) { bool depends_on_compile_var = maybe_val->depends_on_compile_var; ConstExprValue *out_val = ir_build_const_from(ira, &test_null_instruction->base, depends_on_compile_var); out_val->data.x_bool = (maybe_val->data.x_maybe == nullptr); return ira->codegen->builtin_types.entry_bool; } } ir_build_test_null_from(&ira->new_irb, &test_null_instruction->base, value); return ira->codegen->builtin_types.entry_bool; } static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, IrInstructionUnwrapMaybe *unwrap_maybe_instruction) { IrInstruction *value = unwrap_maybe_instruction->value->other; if (value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; // This will be a pointer type because test null IR instruction operates on a pointer to a thing. TypeTableEntry *ptr_type = value->type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); TypeTableEntry *type_entry = ptr_type->data.pointer.child_type; if (type_entry->id == TypeTableEntryIdInvalid) { return ira->codegen->builtin_types.entry_invalid; } else if (type_entry->id != TypeTableEntryIdMaybe) { add_node_error(ira->codegen, unwrap_maybe_instruction->base.source_node, buf_sprintf("expected nullable type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->builtin_types.entry_invalid; } TypeTableEntry *child_type = type_entry->data.maybe.child_type; TypeTableEntry *result_type = get_pointer_to_type(ira->codegen, child_type, false); if (value->static_value.special != ConstValSpecialRuntime) { ConstExprValue *maybe_val = value->static_value.data.x_ptr.base_ptr; assert(value->static_value.data.x_ptr.index == SIZE_MAX); if (maybe_val->special != ConstValSpecialRuntime) { if (!maybe_val->data.x_maybe) { ir_add_error(ira, &unwrap_maybe_instruction->base, buf_sprintf("unable to unwrap null")); return ira->codegen->builtin_types.entry_invalid; } bool depends_on_compile_var = maybe_val->depends_on_compile_var; ConstExprValue *out_val = ir_build_const_from(ira, &unwrap_maybe_instruction->base, depends_on_compile_var); out_val->data.x_ptr.base_ptr = maybe_val->data.x_maybe; out_val->data.x_ptr.index = SIZE_MAX; return result_type; } } ir_build_unwrap_maybe_from(&ira->new_irb, &unwrap_maybe_instruction->base, value, unwrap_maybe_instruction->safety_check_on); return result_type; } static TypeTableEntry *ir_analyze_instruction_ctz(IrAnalyze *ira, IrInstructionCtz *ctz_instruction) { IrInstruction *value = ctz_instruction->value->other; if (value->type_entry->id == TypeTableEntryIdInvalid) { return ira->codegen->builtin_types.entry_invalid; } else if (value->type_entry->id == TypeTableEntryIdInt) { if (value->static_value.special != ConstValSpecialRuntime) { uint32_t result = bignum_ctz(&value->static_value.data.x_bignum, value->type_entry->data.integral.bit_count); bool depends_on_compile_var = value->static_value.depends_on_compile_var; ConstExprValue *out_val = ir_build_const_from(ira, &ctz_instruction->base, depends_on_compile_var); bignum_init_unsigned(&out_val->data.x_bignum, result); return value->type_entry; } ir_build_ctz_from(&ira->new_irb, &ctz_instruction->base, value); return value->type_entry; } else { add_node_error(ira->codegen, ctz_instruction->base.source_node, buf_sprintf("expected integer type, found '%s'", buf_ptr(&value->type_entry->name))); return ira->codegen->builtin_types.entry_invalid; } } static TypeTableEntry *ir_analyze_instruction_clz(IrAnalyze *ira, IrInstructionClz *clz_instruction) { IrInstruction *value = clz_instruction->value->other; if (value->type_entry->id == TypeTableEntryIdInvalid) { return ira->codegen->builtin_types.entry_invalid; } else if (value->type_entry->id == TypeTableEntryIdInt) { if (value->static_value.special != ConstValSpecialRuntime) { uint32_t result = bignum_clz(&value->static_value.data.x_bignum, value->type_entry->data.integral.bit_count); bool depends_on_compile_var = value->static_value.depends_on_compile_var; ConstExprValue *out_val = ir_build_const_from(ira, &clz_instruction->base, depends_on_compile_var); bignum_init_unsigned(&out_val->data.x_bignum, result); return value->type_entry; } ir_build_clz_from(&ira->new_irb, &clz_instruction->base, value); return value->type_entry; } else { add_node_error(ira->codegen, clz_instruction->base.source_node, buf_sprintf("expected integer type, found '%s'", buf_ptr(&value->type_entry->name))); return ira->codegen->builtin_types.entry_invalid; } } static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira, IrInstructionSwitchBr *switch_br_instruction) { IrInstruction *target_value = switch_br_instruction->target_value->other; if (target_value->type_entry->id == TypeTableEntryIdInvalid) return ir_unreach_error(ira); size_t case_count = switch_br_instruction->case_count; bool is_inline = ir_should_inline(&ira->new_irb) || switch_br_instruction->is_inline; if (is_inline || target_value->static_value.special != ConstValSpecialRuntime) { ConstExprValue *target_val = ir_resolve_const(ira, target_value); if (!target_val) return ir_unreach_error(ira); for (size_t i = 0; i < case_count; i += 1) { IrInstructionSwitchBrCase *old_case = &switch_br_instruction->cases[i]; IrInstruction *case_value = old_case->value->other; if (case_value->type_entry->id == TypeTableEntryIdInvalid) return ir_unreach_error(ira); IrInstruction *casted_case_value = ir_implicit_cast(ira, case_value, target_value->type_entry); if (casted_case_value->type_entry->id == TypeTableEntryIdInvalid) return ir_unreach_error(ira); ConstExprValue *case_val = ir_resolve_const(ira, casted_case_value); if (!case_val) return ir_unreach_error(ira); if (const_values_equal(target_val, case_val, target_value->type_entry)) { IrBasicBlock *old_dest_block = old_case->block; if (is_inline || old_dest_block->ref_count == 1) { return ir_inline_bb(ira, &switch_br_instruction->base, old_dest_block); } else { IrBasicBlock *new_dest_block = ir_get_new_bb(ira, old_dest_block); ir_build_br_from(&ira->new_irb, &switch_br_instruction->base, new_dest_block); return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); } } } } IrInstructionSwitchBrCase *cases = allocate(case_count); for (size_t i = 0; i < case_count; i += 1) { IrInstructionSwitchBrCase *old_case = &switch_br_instruction->cases[i]; IrInstructionSwitchBrCase *new_case = &cases[i]; new_case->block = ir_get_new_bb(ira, old_case->block); new_case->value = ira->codegen->invalid_instruction; IrInstruction *old_value = old_case->value; IrInstruction *new_value = old_value->other; if (new_value->type_entry->id == TypeTableEntryIdInvalid) continue; IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, target_value->type_entry); if (casted_new_value->type_entry->id == TypeTableEntryIdInvalid) continue; if (!ir_resolve_const(ira, casted_new_value)) continue; new_case->value = casted_new_value; } IrBasicBlock *new_else_block = ir_get_new_bb(ira, switch_br_instruction->else_block); ir_build_switch_br_from(&ira->new_irb, &switch_br_instruction->base, target_value, new_else_block, case_count, cases, is_inline); return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); } static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, IrInstructionSwitchTarget *switch_target_instruction) { IrInstruction *target_value_ptr = switch_target_instruction->target_value_ptr->other; if (target_value_ptr->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; assert(target_value_ptr->type_entry->id == TypeTableEntryIdPointer); TypeTableEntry *target_type = target_value_ptr->type_entry->data.pointer.child_type; bool depends_on_compile_var = target_value_ptr->static_value.depends_on_compile_var; ConstExprValue *pointee_val = nullptr; if (target_value_ptr->static_value.special != ConstValSpecialRuntime) { pointee_val = const_ptr_pointee(&target_value_ptr->static_value); if (pointee_val->special == ConstValSpecialRuntime) pointee_val = nullptr; } TypeTableEntry *canon_target_type = get_underlying_type(target_type); switch (canon_target_type->id) { case TypeTableEntryIdInvalid: case TypeTableEntryIdVar: case TypeTableEntryIdTypeDecl: zig_unreachable(); case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdPointer: case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: case TypeTableEntryIdPureError: if (pointee_val) { ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base, depends_on_compile_var); *out_val = *pointee_val; return target_type; } ir_build_load_ptr_from(&ira->new_irb, &switch_target_instruction->base, target_value_ptr); return target_type; case TypeTableEntryIdEnum: { TypeTableEntry *tag_type = target_type->data.enumeration.tag_type; if (pointee_val) { ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base, depends_on_compile_var); bignum_init_unsigned(&out_val->data.x_bignum, pointee_val->data.x_enum.tag); return tag_type; } ir_build_enum_tag_from(&ira->new_irb, &switch_target_instruction->base, target_value_ptr); return tag_type; } case TypeTableEntryIdErrorUnion: // see https://github.com/andrewrk/zig/issues/83 zig_panic("TODO switch on error union"); case TypeTableEntryIdUnreachable: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: case TypeTableEntryIdUnion: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: add_node_error(ira->codegen, switch_target_instruction->base.source_node, buf_sprintf("invalid switch target type '%s'", buf_ptr(&target_type->name))); // TODO if this is a typedecl, add error note showing the declaration of the type decl return ira->codegen->builtin_types.entry_invalid; } zig_unreachable(); } static TypeTableEntry *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstructionSwitchVar *switch_var_instruction) { zig_panic("TODO switch var analyze"); } static TypeTableEntry *ir_analyze_instruction_enum_tag(IrAnalyze *ira, IrInstructionEnumTag *enum_tag_instruction) { zig_panic("TODO ir_analyze_instruction_enum_tag"); } static TypeTableEntry *ir_analyze_instruction_static_eval(IrAnalyze *ira, IrInstructionStaticEval *static_eval_instruction) { IrInstruction *value = static_eval_instruction->value->other; if (value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *val = ir_resolve_const(ira, value); if (!val) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &static_eval_instruction->base, val->depends_on_compile_var); *out_val = *val; return value->type_entry; } static TypeTableEntry *ir_analyze_instruction_import(IrAnalyze *ira, IrInstructionImport *import_instruction) { IrInstruction *name_value = import_instruction->name->other; Buf *import_target_str = ir_resolve_str(ira, name_value); if (!import_target_str) return ira->codegen->builtin_types.entry_invalid; bool depends_on_compile_var = name_value->static_value.depends_on_compile_var; AstNode *source_node = import_instruction->base.source_node; ImportTableEntry *import = source_node->owner; Buf *import_target_path; Buf *search_dir; assert(import->package); PackageTableEntry *target_package; auto package_entry = import->package->package_table.maybe_get(import_target_str); if (package_entry) { target_package = package_entry->value; import_target_path = &target_package->root_src_path; search_dir = &target_package->root_src_dir; } else { // try it as a filename target_package = import->package; import_target_path = import_target_str; search_dir = &import->package->root_src_dir; } Buf full_path = BUF_INIT; os_path_join(search_dir, import_target_path, &full_path); Buf *import_code = buf_alloc(); Buf *abs_full_path = buf_alloc(); int err; if ((err = os_path_real(&full_path, abs_full_path))) { if (err == ErrorFileNotFound) { add_node_error(ira->codegen, source_node, buf_sprintf("unable to find '%s'", buf_ptr(import_target_path))); return ira->codegen->builtin_types.entry_invalid; } else { ira->codegen->error_during_imports = true; add_node_error(ira->codegen, source_node, buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err))); return ira->codegen->builtin_types.entry_invalid; } } auto import_entry = ira->codegen->import_table.maybe_get(abs_full_path); if (import_entry) { ConstExprValue *out_val = ir_build_const_from(ira, &import_instruction->base, depends_on_compile_var); out_val->data.x_import = import_entry->value; return ira->codegen->builtin_types.entry_namespace; } if ((err = os_fetch_file_path(abs_full_path, import_code))) { if (err == ErrorFileNotFound) { add_node_error(ira->codegen, source_node, buf_sprintf("unable to find '%s'", buf_ptr(import_target_path))); return ira->codegen->builtin_types.entry_invalid; } else { add_node_error(ira->codegen, source_node, buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err))); return ira->codegen->builtin_types.entry_invalid; } } ImportTableEntry *target_import = add_source_file(ira->codegen, target_package, abs_full_path, search_dir, import_target_path, import_code); scan_decls(ira->codegen, target_import, target_import->decls_scope, target_import->root, nullptr); ConstExprValue *out_val = ir_build_const_from(ira, &import_instruction->base, depends_on_compile_var); out_val->data.x_import = target_import; return ira->codegen->builtin_types.entry_namespace; } static TypeTableEntry *ir_analyze_instruction_array_len(IrAnalyze *ira, IrInstructionArrayLen *array_len_instruction) { IrInstruction *array_value = array_len_instruction->array_value->other; TypeTableEntry *canon_type = get_underlying_type(array_value->type_entry); if (canon_type->id == TypeTableEntryIdInvalid) { return ira->codegen->builtin_types.entry_invalid; } else if (canon_type->id == TypeTableEntryIdArray) { bool depends_on_compile_var = array_value->static_value.depends_on_compile_var; return ir_analyze_const_usize(ira, &array_len_instruction->base, canon_type->data.array.len, depends_on_compile_var); } else if (is_slice(canon_type)) { if (array_value->static_value.special != ConstValSpecialRuntime) { ConstExprValue *len_val = &array_value->static_value.data.x_struct.fields[slice_len_index]; if (len_val->special != ConstValSpecialRuntime) { bool depends_on_compile_var = len_val->depends_on_compile_var; return ir_analyze_const_usize(ira, &array_len_instruction->base, len_val->data.x_bignum.data.x_uint, depends_on_compile_var); } } TypeStructField *field = &canon_type->data.structure.fields[slice_len_index]; IrInstruction *len_ptr = ir_build_struct_field_ptr(&ira->new_irb, array_len_instruction->base.scope, array_len_instruction->base.source_node, array_value, field); len_ptr->type_entry = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_usize, true); ir_build_load_ptr_from(&ira->new_irb, &array_len_instruction->base, len_ptr); return ira->codegen->builtin_types.entry_usize; } else { add_node_error(ira->codegen, array_len_instruction->base.source_node, buf_sprintf("type '%s' has no field 'len'", buf_ptr(&array_value->type_entry->name))); // TODO if this is a typedecl, add error note showing the declaration of the type decl return ira->codegen->builtin_types.entry_invalid; } } static TypeTableEntry *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstructionRef *ref_instruction) { IrInstruction *value = ref_instruction->value->other; return ir_analyze_ref(ira, &ref_instruction->base, value); } static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruction *instruction, TypeTableEntry *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields, bool depends_on_compile_var) { size_t actual_field_count = container_type->data.structure.src_field_count; IrInstruction *first_non_const_instruction = nullptr; AstNode **field_assign_nodes = allocate(actual_field_count); IrInstructionStructInitField *new_fields = allocate(actual_field_count); FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec); bool outside_fn = (fn_entry == nullptr); ConstExprValue const_val = {}; const_val.special = ConstValSpecialStatic; const_val.depends_on_compile_var = depends_on_compile_var; const_val.data.x_struct.fields = allocate(actual_field_count); for (size_t i = 0; i < instr_field_count; i += 1) { IrInstructionContainerInitFieldsField *field = &fields[i]; IrInstruction *field_value = field->value->other; if (field_value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; TypeStructField *type_field = find_struct_type_field(container_type, field->name); if (!type_field) { add_node_error(ira->codegen, field->source_node, buf_sprintf("no member named '%s' in '%s'", buf_ptr(field->name), buf_ptr(&container_type->name))); return ira->codegen->builtin_types.entry_invalid; } if (type_field->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; size_t field_index = type_field->src_index; AstNode *existing_assign_node = field_assign_nodes[field_index]; if (existing_assign_node) { ErrorMsg *msg = add_node_error(ira->codegen, field->source_node, buf_sprintf("duplicate field")); add_error_note(ira->codegen, msg, existing_assign_node, buf_sprintf("other field here")); continue; } field_assign_nodes[field_index] = field->source_node; new_fields[field_index].value = field_value; new_fields[field_index].type_struct_field = type_field; if (const_val.special == ConstValSpecialStatic) { if (outside_fn || field_value->static_value.special != ConstValSpecialRuntime) { ConstExprValue *field_val = ir_resolve_const(ira, field_value); if (!field_val) return ira->codegen->builtin_types.entry_invalid; const_val.data.x_struct.fields[field_index] = *field_val; const_val.depends_on_compile_var = const_val.depends_on_compile_var || field_val->depends_on_compile_var; } else { first_non_const_instruction = field_value; const_val.special = ConstValSpecialRuntime; } } } bool any_missing = false; for (size_t i = 0; i < actual_field_count; i += 1) { if (!field_assign_nodes[i]) { add_node_error(ira->codegen, instruction->source_node, buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i].name))); any_missing = true; } } if (any_missing) return ira->codegen->builtin_types.entry_invalid; if (const_val.special == ConstValSpecialStatic) { ConstExprValue *out_val = ir_build_const_from(ira, instruction, const_val.depends_on_compile_var); *out_val = const_val; return container_type; } if (outside_fn) { add_node_error(ira->codegen, first_non_const_instruction->source_node, buf_sprintf("unable to evaluate constant expression")); return ira->codegen->builtin_types.entry_invalid; } IrInstruction *new_instruction = ir_build_struct_init_from(&ira->new_irb, instruction, container_type, actual_field_count, new_fields); ir_add_alloca(ira, new_instruction, container_type); return container_type; } static TypeTableEntry *ir_analyze_instruction_container_init_list(IrAnalyze *ira, IrInstructionContainerInitList *instruction) { IrInstruction *container_type_value = instruction->container_type->other; TypeTableEntry *container_type = ir_resolve_type(ira, container_type_value); if (container_type->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; size_t elem_count = instruction->item_count; bool depends_on_compile_var = container_type_value->static_value.depends_on_compile_var; if (container_type->id == TypeTableEntryIdStruct && !is_slice(container_type) && elem_count == 0) { return ir_analyze_container_init_fields(ira, &instruction->base, container_type, 0, nullptr, depends_on_compile_var); } else if (is_slice(container_type)) { TypeTableEntry *pointer_type = container_type->data.structure.fields[slice_ptr_index].type_entry; assert(pointer_type->id == TypeTableEntryIdPointer); TypeTableEntry *child_type = pointer_type->data.pointer.child_type; ConstExprValue const_val = {}; const_val.special = ConstValSpecialStatic; const_val.depends_on_compile_var = depends_on_compile_var; const_val.data.x_array.elements = allocate(elem_count); const_val.data.x_array.size = elem_count; FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec); bool outside_fn = (fn_entry == nullptr); IrInstruction **new_items = allocate(elem_count); IrInstruction *first_non_const_instruction = nullptr; for (size_t i = 0; i < elem_count; i += 1) { IrInstruction *arg_value = instruction->items[i]->other; if (arg_value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; new_items[i] = arg_value; if (const_val.special == ConstValSpecialStatic) { if (outside_fn || arg_value->static_value.special != ConstValSpecialRuntime) { ConstExprValue *elem_val = ir_resolve_const(ira, arg_value); if (!elem_val) return ira->codegen->builtin_types.entry_invalid; const_val.data.x_array.elements[i] = *elem_val; const_val.depends_on_compile_var = const_val.depends_on_compile_var || elem_val->depends_on_compile_var; } else { first_non_const_instruction = arg_value; const_val.special = ConstValSpecialRuntime; } } } TypeTableEntry *fixed_size_array_type = get_array_type(ira->codegen, child_type, elem_count); if (const_val.special == ConstValSpecialStatic) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, const_val.depends_on_compile_var); *out_val = const_val; return fixed_size_array_type; } if (outside_fn) { add_node_error(ira->codegen, first_non_const_instruction->source_node, buf_sprintf("unable to evaluate constant expression")); return ira->codegen->builtin_types.entry_invalid; } IrInstruction *new_instruction = ir_build_container_init_list_from(&ira->new_irb, &instruction->base, container_type_value, elem_count, new_items); ir_add_alloca(ira, new_instruction, fixed_size_array_type); return fixed_size_array_type; } else if (container_type->id == TypeTableEntryIdArray) { // same as slice init but we make a compile error if the length is wrong zig_panic("TODO array container init"); } else if (container_type->id == TypeTableEntryIdVoid) { if (elem_count != 0) { add_node_error(ira->codegen, instruction->base.source_node, buf_sprintf("void expression expects no arguments")); return ira->codegen->builtin_types.entry_invalid; } return ir_analyze_void(ira, &instruction->base); } else { add_node_error(ira->codegen, instruction->base.source_node, buf_sprintf("type '%s' does not support array initialization", buf_ptr(&container_type->name))); return ira->codegen->builtin_types.entry_invalid; } } static TypeTableEntry *ir_analyze_instruction_container_init_fields(IrAnalyze *ira, IrInstructionContainerInitFields *instruction) { IrInstruction *container_type_value = instruction->container_type->other; TypeTableEntry *container_type = ir_resolve_type(ira, container_type_value); if (container_type->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; bool depends_on_compile_var = container_type_value->static_value.depends_on_compile_var; return ir_analyze_container_init_fields(ira, &instruction->base, container_type, instruction->field_count, instruction->fields, depends_on_compile_var); } static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *target_type_value, bool is_max) { TypeTableEntry *target_type = ir_resolve_type(ira, target_type_value); bool depends_on_compile_var = target_type_value->static_value.depends_on_compile_var; TypeTableEntry *canon_type = get_underlying_type(target_type); switch (canon_type->id) { case TypeTableEntryIdInvalid: return ira->codegen->builtin_types.entry_invalid; case TypeTableEntryIdInt: { ConstExprValue *out_val = ir_build_const_from(ira, source_instruction, depends_on_compile_var); eval_min_max_value(ira->codegen, canon_type, out_val, is_max); return ira->codegen->builtin_types.entry_num_lit_int; } case TypeTableEntryIdFloat: { ConstExprValue *out_val = ir_build_const_from(ira, source_instruction, depends_on_compile_var); eval_min_max_value(ira->codegen, canon_type, out_val, is_max); return ira->codegen->builtin_types.entry_num_lit_float; } case TypeTableEntryIdBool: case TypeTableEntryIdVoid: { ConstExprValue *out_val = ir_build_const_from(ira, source_instruction, depends_on_compile_var); eval_min_max_value(ira->codegen, canon_type, out_val, is_max); return target_type; } case TypeTableEntryIdVar: case TypeTableEntryIdMetaType: case TypeTableEntryIdUnreachable: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdTypeDecl: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: { const char *err_format = is_max ? "no max value available for type '%s'" : "no min value available for type '%s'"; ir_add_error(ira, source_instruction, buf_sprintf(err_format, buf_ptr(&target_type->name))); // TODO if this is a typedecl, add error note showing the declaration of the type decl return ira->codegen->builtin_types.entry_invalid; } } zig_unreachable(); } static TypeTableEntry *ir_analyze_instruction_min_value(IrAnalyze *ira, IrInstructionMinValue *instruction) { return ir_analyze_min_max(ira, &instruction->base, instruction->value->other, false); } static TypeTableEntry *ir_analyze_instruction_max_value(IrAnalyze *ira, IrInstructionMaxValue *instruction) { return ir_analyze_min_max(ira, &instruction->base, instruction->value->other, true); } static TypeTableEntry *ir_analyze_instruction_compile_err(IrAnalyze *ira, IrInstructionCompileErr *instruction) { IrInstruction *msg_value = instruction->msg->other; Buf *msg_buf = ir_resolve_str(ira, msg_value); if (!msg_buf) return ira->codegen->builtin_types.entry_invalid; ir_add_error(ira, &instruction->base, msg_buf); return ira->codegen->builtin_types.entry_invalid; } static TypeTableEntry *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstructionErrName *instruction) { IrInstruction *value = instruction->value->other; if (value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_value = ir_implicit_cast(ira, value, value->type_entry); if (casted_value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *str_type = get_slice_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true); if (casted_value->static_value.special == ConstValSpecialStatic) { ErrorTableEntry *err = casted_value->static_value.data.x_pure_err; if (!err->cached_error_name_val) { err->cached_error_name_val = allocate(1); err->cached_error_name_val->special = ConstValSpecialStatic; err->cached_error_name_val->data.x_struct.fields = allocate(2); ConstExprValue *array_val = allocate(1); init_const_str_lit(array_val, &err->name); ConstExprValue *ptr_val = &err->cached_error_name_val->data.x_struct.fields[slice_ptr_index]; ptr_val->special = ConstValSpecialStatic; ptr_val->data.x_ptr.base_ptr = array_val; ptr_val->data.x_ptr.index = 0; ConstExprValue *len_val = &err->cached_error_name_val->data.x_struct.fields[slice_len_index]; len_val->special = ConstValSpecialStatic; bignum_init_unsigned(&len_val->data.x_bignum, buf_len(&err->name)); } ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, casted_value->static_value.depends_on_compile_var); *out_val = *err->cached_error_name_val; return str_type; } ira->codegen->generate_error_name_table = true; ir_build_err_name_from(&ira->new_irb, &instruction->base, value); return str_type; } static TypeTableEntry *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstructionCImport *instruction) { AstNode *node = instruction->base.source_node; assert(node->type == NodeTypeFnCallExpr); AstNode *block_node = node->data.fn_call_expr.params.at(0); ScopeCImport *cimport_scope = create_cimport_scope(node, instruction->base.scope); // Execute the C import block like an inline function TypeTableEntry *void_type = ira->codegen->builtin_types.entry_void; IrInstruction *result = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, void_type, ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, &cimport_scope->buf); if (result->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; find_libc_include_path(ira->codegen); ImportTableEntry *child_import = allocate(1); child_import->decls_scope = create_decls_scope(child_import->root, nullptr, nullptr, child_import); child_import->c_import_node = node; ZigList errors = {0}; int err; if ((err = parse_h_buf(child_import, &errors, &cimport_scope->buf, ira->codegen, node))) { zig_panic("unable to parse h file: %s\n", err_str(err)); } if (errors.length > 0) { ErrorMsg *parent_err_msg = add_node_error(ira->codegen, node, buf_sprintf("C import failed")); for (size_t i = 0; i < errors.length; i += 1) { ErrorMsg *err_msg = errors.at(i); err_msg_add_note(parent_err_msg, err_msg); } return ira->codegen->builtin_types.entry_invalid; } if (ira->codegen->verbose) { fprintf(stderr, "\nC imports:\n"); fprintf(stderr, "-----------\n"); ir_print_decls(stderr, child_import); } // TODO to get fewer false negatives on this, we would need to track this value in // the ir executable bool depends_on_compile_var = true; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var); out_val->data.x_import = child_import; return ira->codegen->builtin_types.entry_namespace; } static TypeTableEntry *ir_analyze_instruction_c_include(IrAnalyze *ira, IrInstructionCInclude *instruction) { IrInstruction *name_value = instruction->name->other; if (name_value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; Buf *include_name = ir_resolve_str(ira, name_value); if (!include_name) return ira->codegen->builtin_types.entry_invalid; Buf *c_import_buf = exec_c_import_buf(ira->new_irb.exec); // We check for this error in pass1 assert(c_import_buf); buf_appendf(c_import_buf, "#include <%s>\n", buf_ptr(include_name)); ir_build_const_from(ira, &instruction->base, false); return ira->codegen->builtin_types.entry_void; } static TypeTableEntry *ir_analyze_instruction_c_define(IrAnalyze *ira, IrInstructionCDefine *instruction) { IrInstruction *name = instruction->name->other; if (name->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; Buf *define_name = ir_resolve_str(ira, name); if (!define_name) return ira->codegen->builtin_types.entry_invalid; IrInstruction *value = instruction->value->other; if (value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; Buf *define_value = ir_resolve_str(ira, value); if (!define_value) return ira->codegen->builtin_types.entry_invalid; Buf *c_import_buf = exec_c_import_buf(ira->new_irb.exec); // We check for this error in pass1 assert(c_import_buf); buf_appendf(c_import_buf, "#define %s %s\n", buf_ptr(define_name), buf_ptr(define_value)); ir_build_const_from(ira, &instruction->base, false); return ira->codegen->builtin_types.entry_void; } static TypeTableEntry *ir_analyze_instruction_c_undef(IrAnalyze *ira, IrInstructionCUndef *instruction) { IrInstruction *name = instruction->name->other; if (name->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; Buf *undef_name = ir_resolve_str(ira, name); if (!undef_name) return ira->codegen->builtin_types.entry_invalid; Buf *c_import_buf = exec_c_import_buf(ira->new_irb.exec); // We check for this error in pass1 assert(c_import_buf); buf_appendf(c_import_buf, "#undef %s\n", buf_ptr(undef_name)); ir_build_const_from(ira, &instruction->base, false); return ira->codegen->builtin_types.entry_void; } static TypeTableEntry *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstructionEmbedFile *instruction) { IrInstruction *name = instruction->name->other; if (name->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; Buf *rel_file_path = ir_resolve_str(ira, name); if (!rel_file_path) return ira->codegen->builtin_types.entry_invalid; ImportTableEntry *import = get_scope_import(instruction->base.scope); // figure out absolute path to resource Buf source_dir_path = BUF_INIT; os_path_dirname(import->path, &source_dir_path); Buf file_path = BUF_INIT; os_path_resolve(&source_dir_path, rel_file_path, &file_path); // load from file system into const expr Buf file_contents = BUF_INIT; int err; if ((err = os_fetch_file_path(&file_path, &file_contents))) { if (err == ErrorFileNotFound) { ir_add_error(ira, instruction->name, buf_sprintf("unable to find '%s'", buf_ptr(&file_path))); return ira->codegen->builtin_types.entry_invalid; } else { ir_add_error(ira, instruction->name, buf_sprintf("unable to open '%s': %s", buf_ptr(&file_path), err_str(err))); return ira->codegen->builtin_types.entry_invalid; } } // TODO add dependency on the file we embedded so that we know if it changes // we'll have to invalidate the cache bool depends_on_compile_var = true; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var); init_const_str_lit(out_val, &file_contents); return get_array_type(ira->codegen, ira->codegen->builtin_types.entry_u8, buf_len(&file_contents)); } static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchg *instruction) { IrInstruction *ptr = instruction->ptr->other; if (ptr->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *cmp_value = instruction->cmp_value->other; if (cmp_value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *new_value = instruction->new_value->other; if (new_value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *success_order_value = instruction->success_order_value->other; if (success_order_value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; AtomicOrder success_order; if (!ir_resolve_atomic_order(ira, success_order_value, &success_order)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *failure_order_value = instruction->failure_order_value->other; if (failure_order_value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; AtomicOrder failure_order; if (!ir_resolve_atomic_order(ira, failure_order_value, &failure_order)) return ira->codegen->builtin_types.entry_invalid; if (ptr->type_entry->id != TypeTableEntryIdPointer) { ir_add_error(ira, instruction->ptr, buf_sprintf("expected pointer argument, found '%s'", buf_ptr(&ptr->type_entry->name))); return ira->codegen->builtin_types.entry_invalid; } TypeTableEntry *child_type = ptr->type_entry->data.pointer.child_type; IrInstruction *casted_cmp_value = ir_implicit_cast(ira, cmp_value, child_type); if (casted_cmp_value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, child_type); if (casted_new_value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; if (success_order < AtomicOrderMonotonic) { ir_add_error(ira, success_order_value, buf_sprintf("success atomic ordering must be Monotonic or stricter")); return ira->codegen->builtin_types.entry_invalid; } if (failure_order < AtomicOrderMonotonic) { ir_add_error(ira, failure_order_value, buf_sprintf("failure atomic ordering must be Monotonic or stricter")); return ira->codegen->builtin_types.entry_invalid; } if (failure_order > success_order) { ir_add_error(ira, failure_order_value, buf_sprintf("failure atomic ordering must be no stricter than success")); return ira->codegen->builtin_types.entry_invalid; } if (failure_order == AtomicOrderRelease || failure_order == AtomicOrderAcqRel) { ir_add_error(ira, failure_order_value, buf_sprintf("failure atomic ordering must not be Release or AcqRel")); return ira->codegen->builtin_types.entry_invalid; } ir_build_cmpxchg_from(&ira->new_irb, &instruction->base, ptr, casted_cmp_value, casted_new_value, success_order_value, failure_order_value, success_order, failure_order); return ira->codegen->builtin_types.entry_bool; } static TypeTableEntry *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstructionFence *instruction) { IrInstruction *order_value = instruction->order_value->other; if (order_value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; AtomicOrder order; if (!ir_resolve_atomic_order(ira, order_value, &order)) return ira->codegen->builtin_types.entry_invalid; ir_build_fence_from(&ira->new_irb, &instruction->base, order_value, order); return ira->codegen->builtin_types.entry_void; } static TypeTableEntry *ir_analyze_instruction_div_exact(IrAnalyze *ira, IrInstructionDivExact *instruction) { IrInstruction *op1 = instruction->op1->other; if (op1->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *op2 = instruction->op2->other; if (op2->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *peer_instructions[] = { op1, op2 }; TypeTableEntry *result_type = ir_resolve_peer_types(ira, instruction->base.source_node, peer_instructions, 2); if (result_type->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *canon_type = get_underlying_type(result_type); if (canon_type->id != TypeTableEntryIdInt && canon_type->id != TypeTableEntryIdNumLitInt) { ir_add_error(ira, &instruction->base, buf_sprintf("expected integer type, found '%s'", buf_ptr(&result_type->name))); // TODO if meta_type is type decl, add note pointing to type decl declaration return ira->codegen->builtin_types.entry_invalid; } IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, result_type); if (casted_op1->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, result_type); if (casted_op2->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; if (casted_op1->static_value.special == ConstValSpecialStatic && casted_op2->static_value.special == ConstValSpecialStatic) { ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1); ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2); assert(op1_val); assert(op2_val); if (op1_val->data.x_bignum.data.x_uint == 0) { ir_add_error(ira, &instruction->base, buf_sprintf("division by zero")); return ira->codegen->builtin_types.entry_invalid; } BigNum remainder; if (bignum_mod(&remainder, &op1_val->data.x_bignum, &op2_val->data.x_bignum)) { ir_add_error(ira, &instruction->base, buf_sprintf("integer overflow")); return ira->codegen->builtin_types.entry_invalid; } if (remainder.data.x_uint != 0) { ir_add_error(ira, &instruction->base, buf_sprintf("exact division had a remainder")); return ira->codegen->builtin_types.entry_invalid; } bool depends_on_compile_var = casted_op1->static_value.depends_on_compile_var || casted_op2->static_value.depends_on_compile_var; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var); bignum_div(&out_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum); return result_type; } ir_build_div_exact_from(&ira->new_irb, &instruction->base, casted_op1, casted_op2); return result_type; } static TypeTableEntry *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstructionTruncate *instruction) { IrInstruction *dest_type_value = instruction->dest_type->other; TypeTableEntry *dest_type = ir_resolve_type(ira, dest_type_value); TypeTableEntry *canon_dest_type = get_underlying_type(dest_type); if (canon_dest_type->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; if (canon_dest_type->id != TypeTableEntryIdInt && canon_dest_type->id != TypeTableEntryIdNumLitInt) { ir_add_error(ira, dest_type_value, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); // TODO if meta_type is type decl, add note pointing to type decl declaration return ira->codegen->builtin_types.entry_invalid; } IrInstruction *target = instruction->target->other; TypeTableEntry *src_type = target->type_entry; TypeTableEntry *canon_src_type = get_underlying_type(src_type); if (canon_src_type->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; if (canon_src_type->id != TypeTableEntryIdInt && canon_src_type->id != TypeTableEntryIdNumLitInt) { ir_add_error(ira, target, buf_sprintf("expected integer type, found '%s'", buf_ptr(&src_type->name))); // TODO if meta_type is type decl, add note pointing to type decl declaration return ira->codegen->builtin_types.entry_invalid; } if (canon_src_type->data.integral.is_signed != canon_dest_type->data.integral.is_signed) { const char *sign_str = canon_dest_type->data.integral.is_signed ? "signed" : "unsigned"; ir_add_error(ira, target, buf_sprintf("expected %s integer type, found '%s'", sign_str, buf_ptr(&src_type->name))); // TODO if meta_type is type decl, add note pointing to type decl declaration return ira->codegen->builtin_types.entry_invalid; } else if (canon_src_type->data.integral.bit_count <= canon_dest_type->data.integral.bit_count) { ir_add_error(ira, target, buf_sprintf("type '%s' has same or fewer bits than destination type '%s'", buf_ptr(&src_type->name), buf_ptr(&dest_type->name))); // TODO if meta_type is type decl, add note pointing to type decl declaration return ira->codegen->builtin_types.entry_invalid; } if (target->static_value.special == ConstValSpecialStatic) { bool depends_on_compile_var = dest_type_value->static_value.depends_on_compile_var || target->static_value.depends_on_compile_var; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var); bignum_init_bignum(&out_val->data.x_bignum, &target->static_value.data.x_bignum); bignum_truncate(&out_val->data.x_bignum, canon_dest_type->data.integral.bit_count); return dest_type; } ir_build_truncate_from(&ira->new_irb, &instruction->base, dest_type_value, target); return dest_type; } static TypeTableEntry *ir_analyze_instruction_int_type(IrAnalyze *ira, IrInstructionIntType *instruction) { IrInstruction *is_signed_value = instruction->is_signed->other; bool is_signed; if (!ir_resolve_bool(ira, is_signed_value, &is_signed)) return ira->codegen->builtin_types.entry_invalid; IrInstruction *bit_count_value = instruction->bit_count->other; uint64_t bit_count; if (!ir_resolve_usize(ira, bit_count_value, &bit_count)) return ira->codegen->builtin_types.entry_invalid; bool depends_on_compile_var = is_signed_value->static_value.depends_on_compile_var || bit_count_value->static_value.depends_on_compile_var; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var); out_val->data.x_type = get_int_type(ira->codegen, is_signed, bit_count); return ira->codegen->builtin_types.entry_type; } static TypeTableEntry *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstructionBoolNot *instruction) { IrInstruction *value = instruction->value->other; if (value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *bool_type = ira->codegen->builtin_types.entry_bool; IrInstruction *casted_value = ir_implicit_cast(ira, value, bool_type); if (casted_value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; if (casted_value->static_value.special != ConstValSpecialRuntime) { bool depends_on_compile_var = casted_value->static_value.depends_on_compile_var; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var); out_val->data.x_bool = !casted_value->static_value.data.x_bool; return bool_type; } ir_build_bool_not_from(&ira->new_irb, &instruction->base, casted_value); return bool_type; } static TypeTableEntry *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructionAlloca *instruction) { IrInstruction *type_value = instruction->type_value->other; if (type_value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *count_value = instruction->count->other; if (count_value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *child_type = ir_resolve_type(ira, type_value); TypeTableEntry *canon_type = get_underlying_type(child_type); if (count_value->static_value.special == ConstValSpecialStatic) { // this should be the same as an array declaration uint64_t count; if (!ir_resolve_usize(ira, count_value, &count)) return ira->codegen->builtin_types.entry_invalid; zig_panic("TODO alloca with compile time known count"); } switch (canon_type->id) { case TypeTableEntryIdInvalid: case TypeTableEntryIdTypeDecl: zig_unreachable(); case TypeTableEntryIdBool: case TypeTableEntryIdVoid: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: { TypeTableEntry *slice_type = get_slice_type(ira->codegen, child_type, false); IrInstruction *new_instruction = ir_build_alloca_from(&ira->new_irb, &instruction->base, type_value, count_value); ir_add_alloca(ira, new_instruction, slice_type); return slice_type; } case TypeTableEntryIdVar: case TypeTableEntryIdMetaType: case TypeTableEntryIdUnreachable: case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: ir_add_error(ira, type_value, buf_sprintf("invalid alloca type '%s'", buf_ptr(&child_type->name))); // TODO if this is a typedecl, add error note showing the declaration of the type decl return ira->codegen->builtin_types.entry_invalid; } zig_unreachable(); } static TypeTableEntry *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructionMemset *instruction) { IrInstruction *dest_ptr = instruction->dest_ptr->other; if (dest_ptr->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *byte_value = instruction->byte->other; if (byte_value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *count_value = instruction->count->other; if (count_value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize; TypeTableEntry *u8 = ira->codegen->builtin_types.entry_u8; TypeTableEntry *u8_ptr = get_pointer_to_type(ira->codegen, u8, false); IrInstruction *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr); if (casted_dest_ptr->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_byte = ir_implicit_cast(ira, byte_value, u8); if (casted_byte->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_count = ir_implicit_cast(ira, count_value, usize); if (casted_count->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; if (casted_dest_ptr->static_value.special == ConstValSpecialStatic && casted_byte->static_value.special == ConstValSpecialStatic && casted_count->static_value.special == ConstValSpecialStatic) { ConstExprValue *dest_ptr_val = &casted_dest_ptr->static_value; ConstExprValue *dest_elements; size_t start; size_t bound_end; if (dest_ptr_val->data.x_ptr.index == SIZE_MAX) { dest_elements = dest_ptr_val->data.x_ptr.base_ptr; start = 0; bound_end = 1; } else { ConstExprValue *array_val = dest_ptr_val->data.x_ptr.base_ptr; dest_elements = array_val->data.x_array.elements; start = dest_ptr_val->data.x_ptr.index; bound_end = array_val->data.x_array.size; } size_t count = casted_count->static_value.data.x_bignum.data.x_uint; size_t end = start + count; if (end > bound_end) { ir_add_error(ira, count_value, buf_sprintf("out of bounds pointer access")); return ira->codegen->builtin_types.entry_invalid; } ConstExprValue *byte_val = &casted_byte->static_value; for (size_t i = start; i < end; i += 1) { dest_elements[i] = *byte_val; } ir_build_const_from(ira, &instruction->base, false); return ira->codegen->builtin_types.entry_void; } ir_build_memset_from(&ira->new_irb, &instruction->base, casted_dest_ptr, casted_byte, casted_count); return ira->codegen->builtin_types.entry_void; } static TypeTableEntry *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructionMemcpy *instruction) { IrInstruction *dest_ptr = instruction->dest_ptr->other; if (dest_ptr->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *src_ptr = instruction->src_ptr->other; if (src_ptr->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *count_value = instruction->count->other; if (count_value->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize; TypeTableEntry *u8 = ira->codegen->builtin_types.entry_u8; TypeTableEntry *u8_ptr_mut = get_pointer_to_type(ira->codegen, u8, false); TypeTableEntry *u8_ptr_const = get_pointer_to_type(ira->codegen, u8, true); IrInstruction *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr_mut); if (casted_dest_ptr->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_src_ptr = ir_implicit_cast(ira, src_ptr, u8_ptr_const); if (casted_src_ptr->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *casted_count = ir_implicit_cast(ira, count_value, usize); if (casted_count->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; if (casted_dest_ptr->static_value.special == ConstValSpecialStatic && casted_src_ptr->static_value.special == ConstValSpecialStatic && casted_count->static_value.special == ConstValSpecialStatic) { size_t count = casted_count->static_value.data.x_bignum.data.x_uint; ConstExprValue *dest_ptr_val = &casted_dest_ptr->static_value; ConstExprValue *dest_elements; size_t dest_start; size_t dest_end; if (dest_ptr_val->data.x_ptr.index == SIZE_MAX) { dest_elements = dest_ptr_val->data.x_ptr.base_ptr; dest_start = 0; dest_end = 1; } else { ConstExprValue *array_val = dest_ptr_val->data.x_ptr.base_ptr; dest_elements = array_val->data.x_array.elements; dest_start = dest_ptr_val->data.x_ptr.index; dest_end = array_val->data.x_array.size; } if (dest_start + count > dest_end) { ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds pointer access")); return ira->codegen->builtin_types.entry_invalid; } ConstExprValue *src_ptr_val = &casted_src_ptr->static_value; ConstExprValue *src_elements; size_t src_start; size_t src_end; if (src_ptr_val->data.x_ptr.index == SIZE_MAX) { src_elements = src_ptr_val->data.x_ptr.base_ptr; src_start = 0; src_end = 1; } else { ConstExprValue *array_val = src_ptr_val->data.x_ptr.base_ptr; src_elements = array_val->data.x_array.elements; src_start = src_ptr_val->data.x_ptr.index; src_end = array_val->data.x_array.size; } if (src_start + count > src_end) { ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds pointer access")); return ira->codegen->builtin_types.entry_invalid; } // TODO check for noalias violations - this should be generalized to work for any function for (size_t i = 0; i < count; i += 1) { dest_elements[dest_start + i] = src_elements[src_start + i]; } ir_build_const_from(ira, &instruction->base, false); return ira->codegen->builtin_types.entry_void; } ir_build_memcpy_from(&ira->new_irb, &instruction->base, casted_dest_ptr, casted_src_ptr, casted_count); return ira->codegen->builtin_types.entry_void; } static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructionSlice *instruction) { IrInstruction *ptr = instruction->ptr->other; if (ptr->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *start = instruction->start->other; if (start->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize; IrInstruction *casted_start = ir_implicit_cast(ira, start, usize); if (casted_start->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; IrInstruction *end; if (instruction->end) { end = instruction->end->other; if (end->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; end = ir_implicit_cast(ira, end, usize); if (end->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; } else { end = nullptr; } TypeTableEntry *array_type = get_underlying_type(ptr->type_entry); TypeTableEntry *return_type; if (array_type->id == TypeTableEntryIdArray) { return_type = get_slice_type(ira->codegen, array_type->data.array.child_type, instruction->is_const); } else if (array_type->id == TypeTableEntryIdPointer) { return_type = get_slice_type(ira->codegen, array_type->data.pointer.child_type, instruction->is_const); if (!end) { ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value")); return ira->codegen->builtin_types.entry_invalid; } } else if (is_slice(array_type)) { return_type = get_slice_type(ira->codegen, array_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, instruction->is_const); } else { ir_add_error(ira, &instruction->base, buf_sprintf("slice of non-array type '%s'", buf_ptr(&ptr->type_entry->name))); // TODO if this is a typedecl, add error note showing the declaration of the type decl return ira->codegen->builtin_types.entry_invalid; } if (ptr->static_value.special == ConstValSpecialStatic && casted_start->static_value.special == ConstValSpecialStatic && (!end || end->static_value.special == ConstValSpecialStatic)) { bool depends_on_compile_var = ptr->static_value.depends_on_compile_var || casted_start->static_value.depends_on_compile_var || (end ? end->static_value.depends_on_compile_var : false); ConstExprValue *base_ptr; size_t abs_offset; size_t rel_end; if (array_type->id == TypeTableEntryIdArray) { base_ptr = &ptr->static_value; abs_offset = 0; rel_end = array_type->data.array.len; } else if (array_type->id == TypeTableEntryIdPointer) { base_ptr = ptr->static_value.data.x_ptr.base_ptr; abs_offset = ptr->static_value.data.x_ptr.index; if (abs_offset == SIZE_MAX) { rel_end = 1; } else { rel_end = base_ptr->data.x_array.size - abs_offset; } } else if (is_slice(array_type)) { ConstExprValue *ptr_val = &ptr->static_value.data.x_struct.fields[slice_ptr_index]; ConstExprValue *len_val = &ptr->static_value.data.x_struct.fields[slice_len_index]; base_ptr = ptr_val->data.x_ptr.base_ptr; abs_offset = ptr_val->data.x_ptr.index; if (ptr_val->data.x_ptr.index == SIZE_MAX) { rel_end = 1; } else { rel_end = len_val->data.x_bignum.data.x_uint; } } else { zig_unreachable(); } uint64_t start_scalar = casted_start->static_value.data.x_bignum.data.x_uint; if (start_scalar > rel_end) { ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice")); return ira->codegen->builtin_types.entry_invalid; } uint64_t end_scalar; if (end) { end_scalar = end->static_value.data.x_bignum.data.x_uint; } else { end_scalar = rel_end; } if (end_scalar > rel_end) { ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice")); return ira->codegen->builtin_types.entry_invalid; } if (start_scalar > end_scalar) { ir_add_error(ira, &instruction->base, buf_sprintf("slice start is greater than end")); return ira->codegen->builtin_types.entry_invalid; } ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var); out_val->data.x_struct.fields = allocate(2); ConstExprValue *ptr_val = &out_val->data.x_struct.fields[slice_ptr_index]; ptr_val->special = ConstValSpecialStatic; ptr_val->data.x_ptr.base_ptr = base_ptr; ptr_val->data.x_ptr.index = (abs_offset != SIZE_MAX) ? (abs_offset + start_scalar) : SIZE_MAX; ConstExprValue *len_val = &out_val->data.x_struct.fields[slice_len_index]; len_val->special = ConstValSpecialStatic; bignum_init_unsigned(&len_val->data.x_bignum, rel_end); return return_type; } IrInstruction *new_instruction = ir_build_slice_from(&ira->new_irb, &instruction->base, ptr, casted_start, end, instruction->is_const); ir_add_alloca(ira, new_instruction, return_type); return return_type; } static TypeTableEntry *ir_analyze_instruction_member_count(IrAnalyze *ira, IrInstructionMemberCount *instruction) { IrInstruction *container = instruction->container->other; if (container->type_entry->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *container_type = ir_resolve_type(ira, container); TypeTableEntry *canon_type = get_underlying_type(container_type); uint64_t result; if (canon_type->id == TypeTableEntryIdInvalid) { return ira->codegen->builtin_types.entry_invalid; } else if (canon_type->id == TypeTableEntryIdEnum) { result = canon_type->data.enumeration.src_field_count; } else if (canon_type->id == TypeTableEntryIdStruct) { result = canon_type->data.structure.src_field_count; } else if (canon_type->id == TypeTableEntryIdUnion) { result = canon_type->data.unionation.src_field_count; } else { ir_add_error(ira, &instruction->base, buf_sprintf("no value count available for type '%s'", buf_ptr(&container_type->name))); return ira->codegen->builtin_types.entry_invalid; } bool depends_on_compile_var = container->static_value.depends_on_compile_var; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var); bignum_init_unsigned(&out_val->data.x_bignum, result); return ira->codegen->builtin_types.entry_num_lit_int; } static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: zig_unreachable(); case IrInstructionIdReturn: return ir_analyze_instruction_return(ira, (IrInstructionReturn *)instruction); case IrInstructionIdConst: return ir_analyze_instruction_const(ira, (IrInstructionConst *)instruction); case IrInstructionIdUnOp: return ir_analyze_instruction_un_op(ira, (IrInstructionUnOp *)instruction); case IrInstructionIdBinOp: return ir_analyze_instruction_bin_op(ira, (IrInstructionBinOp *)instruction); case IrInstructionIdDeclVar: return ir_analyze_instruction_decl_var(ira, (IrInstructionDeclVar *)instruction); case IrInstructionIdLoadPtr: return ir_analyze_instruction_load_ptr(ira, (IrInstructionLoadPtr *)instruction); case IrInstructionIdStorePtr: return ir_analyze_instruction_store_ptr(ira, (IrInstructionStorePtr *)instruction); case IrInstructionIdElemPtr: return ir_analyze_instruction_elem_ptr(ira, (IrInstructionElemPtr *)instruction); case IrInstructionIdVarPtr: return ir_analyze_instruction_var_ptr(ira, (IrInstructionVarPtr *)instruction); case IrInstructionIdFieldPtr: return ir_analyze_instruction_field_ptr(ira, (IrInstructionFieldPtr *)instruction); case IrInstructionIdCall: return ir_analyze_instruction_call(ira, (IrInstructionCall *)instruction); case IrInstructionIdBr: return ir_analyze_instruction_br(ira, (IrInstructionBr *)instruction); case IrInstructionIdCondBr: return ir_analyze_instruction_cond_br(ira, (IrInstructionCondBr *)instruction); case IrInstructionIdUnreachable: return ir_analyze_instruction_unreachable(ira, (IrInstructionUnreachable *)instruction); case IrInstructionIdPhi: return ir_analyze_instruction_phi(ira, (IrInstructionPhi *)instruction); case IrInstructionIdTypeOf: return ir_analyze_instruction_typeof(ira, (IrInstructionTypeOf *)instruction); case IrInstructionIdToPtrType: return ir_analyze_instruction_to_ptr_type(ira, (IrInstructionToPtrType *)instruction); case IrInstructionIdPtrTypeChild: return ir_analyze_instruction_ptr_type_child(ira, (IrInstructionPtrTypeChild *)instruction); case IrInstructionIdSetFnTest: return ir_analyze_instruction_set_fn_test(ira, (IrInstructionSetFnTest *)instruction); case IrInstructionIdSetFnVisible: return ir_analyze_instruction_set_fn_visible(ira, (IrInstructionSetFnVisible *)instruction); case IrInstructionIdSetDebugSafety: return ir_analyze_instruction_set_debug_safety(ira, (IrInstructionSetDebugSafety *)instruction); case IrInstructionIdSliceType: return ir_analyze_instruction_slice_type(ira, (IrInstructionSliceType *)instruction); case IrInstructionIdAsm: return ir_analyze_instruction_asm(ira, (IrInstructionAsm *)instruction); case IrInstructionIdArrayType: return ir_analyze_instruction_array_type(ira, (IrInstructionArrayType *)instruction); case IrInstructionIdCompileVar: return ir_analyze_instruction_compile_var(ira, (IrInstructionCompileVar *)instruction); case IrInstructionIdSizeOf: return ir_analyze_instruction_size_of(ira, (IrInstructionSizeOf *)instruction); case IrInstructionIdTestNull: return ir_analyze_instruction_test_null(ira, (IrInstructionTestNull *)instruction); case IrInstructionIdUnwrapMaybe: return ir_analyze_instruction_unwrap_maybe(ira, (IrInstructionUnwrapMaybe *)instruction); case IrInstructionIdClz: return ir_analyze_instruction_clz(ira, (IrInstructionClz *)instruction); case IrInstructionIdCtz: return ir_analyze_instruction_ctz(ira, (IrInstructionCtz *)instruction); case IrInstructionIdSwitchBr: return ir_analyze_instruction_switch_br(ira, (IrInstructionSwitchBr *)instruction); case IrInstructionIdSwitchTarget: return ir_analyze_instruction_switch_target(ira, (IrInstructionSwitchTarget *)instruction); case IrInstructionIdSwitchVar: return ir_analyze_instruction_switch_var(ira, (IrInstructionSwitchVar *)instruction); case IrInstructionIdEnumTag: return ir_analyze_instruction_enum_tag(ira, (IrInstructionEnumTag *)instruction); case IrInstructionIdStaticEval: return ir_analyze_instruction_static_eval(ira, (IrInstructionStaticEval *)instruction); case IrInstructionIdImport: return ir_analyze_instruction_import(ira, (IrInstructionImport *)instruction); case IrInstructionIdArrayLen: return ir_analyze_instruction_array_len(ira, (IrInstructionArrayLen *)instruction); case IrInstructionIdRef: return ir_analyze_instruction_ref(ira, (IrInstructionRef *)instruction); case IrInstructionIdContainerInitList: return ir_analyze_instruction_container_init_list(ira, (IrInstructionContainerInitList *)instruction); case IrInstructionIdContainerInitFields: return ir_analyze_instruction_container_init_fields(ira, (IrInstructionContainerInitFields *)instruction); case IrInstructionIdMinValue: return ir_analyze_instruction_min_value(ira, (IrInstructionMinValue *)instruction); case IrInstructionIdMaxValue: return ir_analyze_instruction_max_value(ira, (IrInstructionMaxValue *)instruction); case IrInstructionIdCompileErr: return ir_analyze_instruction_compile_err(ira, (IrInstructionCompileErr *)instruction); case IrInstructionIdErrName: return ir_analyze_instruction_err_name(ira, (IrInstructionErrName *)instruction); case IrInstructionIdCImport: return ir_analyze_instruction_c_import(ira, (IrInstructionCImport *)instruction); case IrInstructionIdCInclude: return ir_analyze_instruction_c_include(ira, (IrInstructionCInclude *)instruction); case IrInstructionIdCDefine: return ir_analyze_instruction_c_define(ira, (IrInstructionCDefine *)instruction); case IrInstructionIdCUndef: return ir_analyze_instruction_c_undef(ira, (IrInstructionCUndef *)instruction); case IrInstructionIdEmbedFile: return ir_analyze_instruction_embed_file(ira, (IrInstructionEmbedFile *)instruction); case IrInstructionIdCmpxchg: return ir_analyze_instruction_cmpxchg(ira, (IrInstructionCmpxchg *)instruction); case IrInstructionIdFence: return ir_analyze_instruction_fence(ira, (IrInstructionFence *)instruction); case IrInstructionIdDivExact: return ir_analyze_instruction_div_exact(ira, (IrInstructionDivExact *)instruction); case IrInstructionIdTruncate: return ir_analyze_instruction_truncate(ira, (IrInstructionTruncate *)instruction); case IrInstructionIdIntType: return ir_analyze_instruction_int_type(ira, (IrInstructionIntType *)instruction); case IrInstructionIdBoolNot: return ir_analyze_instruction_bool_not(ira, (IrInstructionBoolNot *)instruction); case IrInstructionIdAlloca: return ir_analyze_instruction_alloca(ira, (IrInstructionAlloca *)instruction); case IrInstructionIdMemset: return ir_analyze_instruction_memset(ira, (IrInstructionMemset *)instruction); case IrInstructionIdMemcpy: return ir_analyze_instruction_memcpy(ira, (IrInstructionMemcpy *)instruction); case IrInstructionIdSlice: return ir_analyze_instruction_slice(ira, (IrInstructionSlice *)instruction); case IrInstructionIdMemberCount: return ir_analyze_instruction_member_count(ira, (IrInstructionMemberCount *)instruction); case IrInstructionIdCast: case IrInstructionIdStructFieldPtr: case IrInstructionIdEnumFieldPtr: case IrInstructionIdStructInit: zig_panic("TODO analyze more instructions"); } zig_unreachable(); } static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction) { TypeTableEntry *instruction_type = ir_analyze_instruction_nocast(ira, instruction); instruction->type_entry = instruction_type; if (instruction->other) { instruction->other->type_entry = instruction_type; } else { assert(instruction_type->id == TypeTableEntryIdInvalid || instruction_type->id == TypeTableEntryIdUnreachable); instruction->other = instruction; } return instruction_type; } // This function attempts to evaluate IR code while doing type checking and other analysis. // It emits a new IrExecutable which is partially evaluated IR code. TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_exec, TypeTableEntry *expected_type, AstNode *expected_type_source_node) { assert(!old_exec->invalid); IrAnalyze ir_analyze_data = {}; IrAnalyze *ira = &ir_analyze_data; ira->codegen = codegen; ira->explicit_return_type = expected_type; ira->old_irb.codegen = codegen; ira->old_irb.exec = old_exec; ira->new_irb.codegen = codegen; ira->new_irb.exec = new_exec; ira->exec_context.mem_slot_count = ira->old_irb.exec->mem_slot_count; ira->exec_context.mem_slot_list = allocate(ira->exec_context.mem_slot_count); IrBasicBlock *old_entry_bb = ira->old_irb.exec->basic_block_list.at(0); IrBasicBlock *new_entry_bb = ir_get_new_bb(ira, old_entry_bb); ir_ref_bb(new_entry_bb); ira->new_irb.current_basic_block = new_entry_bb; ira->block_queue_index = 0; ir_start_bb(ira, old_entry_bb, nullptr); while (ira->block_queue_index < ira->old_bb_queue.length) { IrInstruction *old_instruction = ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index); if (old_instruction->ref_count == 0 && !ir_has_side_effects(old_instruction)) { ira->instruction_index += 1; continue; } TypeTableEntry *return_type = ir_analyze_instruction(ira, old_instruction); // unreachable instructions do their own control flow. if (return_type->id == TypeTableEntryIdUnreachable) continue; ira->instruction_index += 1; } if (new_exec->invalid) { return ira->codegen->builtin_types.entry_invalid; } else if (ira->implicit_return_type_list.length == 0) { return codegen->builtin_types.entry_unreachable; } else { return ir_resolve_peer_types(ira, expected_type_source_node, ira->implicit_return_type_list.items, ira->implicit_return_type_list.length); } } bool ir_has_side_effects(IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: zig_unreachable(); case IrInstructionIdBr: case IrInstructionIdCondBr: case IrInstructionIdSwitchBr: case IrInstructionIdDeclVar: case IrInstructionIdStorePtr: case IrInstructionIdCall: case IrInstructionIdReturn: case IrInstructionIdUnreachable: case IrInstructionIdSetFnTest: case IrInstructionIdSetFnVisible: case IrInstructionIdSetDebugSafety: case IrInstructionIdImport: case IrInstructionIdCompileErr: case IrInstructionIdCImport: case IrInstructionIdCInclude: case IrInstructionIdCDefine: case IrInstructionIdCUndef: case IrInstructionIdCmpxchg: case IrInstructionIdFence: case IrInstructionIdMemset: case IrInstructionIdMemcpy: return true; case IrInstructionIdPhi: case IrInstructionIdUnOp: case IrInstructionIdBinOp: case IrInstructionIdLoadPtr: case IrInstructionIdConst: case IrInstructionIdCast: case IrInstructionIdContainerInitList: case IrInstructionIdContainerInitFields: case IrInstructionIdStructInit: case IrInstructionIdFieldPtr: case IrInstructionIdElemPtr: case IrInstructionIdVarPtr: case IrInstructionIdTypeOf: case IrInstructionIdToPtrType: case IrInstructionIdPtrTypeChild: case IrInstructionIdArrayLen: case IrInstructionIdStructFieldPtr: case IrInstructionIdEnumFieldPtr: case IrInstructionIdArrayType: case IrInstructionIdSliceType: case IrInstructionIdCompileVar: case IrInstructionIdSizeOf: case IrInstructionIdTestNull: case IrInstructionIdUnwrapMaybe: case IrInstructionIdClz: case IrInstructionIdCtz: case IrInstructionIdSwitchVar: case IrInstructionIdSwitchTarget: case IrInstructionIdEnumTag: case IrInstructionIdStaticEval: case IrInstructionIdRef: case IrInstructionIdMinValue: case IrInstructionIdMaxValue: case IrInstructionIdErrName: case IrInstructionIdEmbedFile: case IrInstructionIdDivExact: case IrInstructionIdTruncate: case IrInstructionIdIntType: case IrInstructionIdBoolNot: case IrInstructionIdAlloca: case IrInstructionIdSlice: case IrInstructionIdMemberCount: return false; case IrInstructionIdAsm: { IrInstructionAsm *asm_instruction = (IrInstructionAsm *)instruction; return asm_instruction->has_side_effects; } } zig_unreachable(); } // TODO port over all this commented out code into new IR way of doing things //static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, // TypeTableEntry *expected_type, AstNode *node) //{ // // switch (builtin_fn->id) { // case BuiltinFnIdInvalid: // zig_unreachable(); // case BuiltinFnIdAddWithOverflow: // case BuiltinFnIdSubWithOverflow: // case BuiltinFnIdMulWithOverflow: // case BuiltinFnIdShlWithOverflow: // { // AstNode *type_node = node->data.fn_call_expr.params.at(0); // TypeTableEntry *int_type = analyze_type_expr(g, import, context, type_node); // if (int_type->id == TypeTableEntryIdInvalid) { // return g->builtin_types.entry_bool; // } else if (int_type->id == TypeTableEntryIdInt) { // AstNode *op1_node = node->data.fn_call_expr.params.at(1); // AstNode *op2_node = node->data.fn_call_expr.params.at(2); // AstNode *result_node = node->data.fn_call_expr.params.at(3); // // analyze_expression(g, import, context, int_type, op1_node); // analyze_expression(g, import, context, int_type, op2_node); // analyze_expression(g, import, context, get_pointer_to_type(g, int_type, false), // result_node); // } else { // add_node_error(g, type_node, // buf_sprintf("expected integer type, found '%s'", buf_ptr(&int_type->name))); // } // // // TODO constant expression evaluation // // return g->builtin_types.entry_bool; // } // case BuiltinFnIdAlignof: // { // AstNode *type_node = node->data.fn_call_expr.params.at(0); // TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node); // if (type_entry->id == TypeTableEntryIdInvalid) { // return g->builtin_types.entry_invalid; // } else if (type_entry->id == TypeTableEntryIdUnreachable) { // add_node_error(g, first_executing_node(type_node), // buf_sprintf("no align available for type '%s'", buf_ptr(&type_entry->name))); // return g->builtin_types.entry_invalid; // } else { // uint64_t align_in_bytes = LLVMABISizeOfType(g->target_data_ref, type_entry->type_ref); // return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type, // align_in_bytes, false); // } // } // case BuiltinFnIdBreakpoint: // mark_impure_fn(g, context, node); // return g->builtin_types.entry_void; // case BuiltinFnIdReturnAddress: // case BuiltinFnIdFrameAddress: // mark_impure_fn(g, context, node); // return builtin_fn->return_type; // } // zig_unreachable(); //} //static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, // TypeTableEntry *expected_type, AstNode *node) //{ // if (!node->data.return_expr.expr) { // node->data.return_expr.expr = create_ast_void_node(g, import, node); // normalize_parent_ptrs(node); // } // // TypeTableEntry *expected_return_type = get_return_type(context); // // switch (node->data.return_expr.kind) { // case ReturnKindUnconditional: // zig_panic("TODO moved to ir.cpp"); // case ReturnKindError: // { // TypeTableEntry *expected_err_type; // if (expected_type) { // expected_err_type = get_error_type(g, expected_type); // } else { // expected_err_type = nullptr; // } // TypeTableEntry *resolved_type = analyze_expression(g, import, context, expected_err_type, // node->data.return_expr.expr); // if (resolved_type->id == TypeTableEntryIdInvalid) { // return resolved_type; // } else if (resolved_type->id == TypeTableEntryIdErrorUnion) { // if (expected_return_type->id != TypeTableEntryIdErrorUnion && // expected_return_type->id != TypeTableEntryIdPureError) // { // ErrorMsg *msg = add_node_error(g, node, // buf_sprintf("%%return statement in function with return type '%s'", // buf_ptr(&expected_return_type->name))); // AstNode *return_type_node = context->fn_entry->fn_def_node->data.fn_def.fn_proto->data.fn_proto.return_type; // add_error_note(g, msg, return_type_node, buf_sprintf("function return type here")); // } // // return resolved_type->data.error.child_type; // } else { // add_node_error(g, node->data.return_expr.expr, // buf_sprintf("expected error type, found '%s'", buf_ptr(&resolved_type->name))); // return g->builtin_types.entry_invalid; // } // } // case ReturnKindMaybe: // { // TypeTableEntry *expected_maybe_type; // if (expected_type) { // expected_maybe_type = get_maybe_type(g, expected_type); // } else { // expected_maybe_type = nullptr; // } // TypeTableEntry *resolved_type = analyze_expression(g, import, context, expected_maybe_type, // node->data.return_expr.expr); // if (resolved_type->id == TypeTableEntryIdInvalid) { // return resolved_type; // } else if (resolved_type->id == TypeTableEntryIdMaybe) { // if (expected_return_type->id != TypeTableEntryIdMaybe) { // ErrorMsg *msg = add_node_error(g, node, // buf_sprintf("?return statement in function with return type '%s'", // buf_ptr(&expected_return_type->name))); // AstNode *return_type_node = context->fn_entry->fn_def_node->data.fn_def.fn_proto->data.fn_proto.return_type; // add_error_note(g, msg, return_type_node, buf_sprintf("function return type here")); // } // // return resolved_type->data.maybe.child_type; // } else { // add_node_error(g, node->data.return_expr.expr, // buf_sprintf("expected maybe type, found '%s'", buf_ptr(&resolved_type->name))); // return g->builtin_types.entry_invalid; // } // } // } // zig_unreachable(); //} //static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, // AstNode *node, LValPurpose purpose) //{ // TypeTableEntry *array_type = analyze_expression(g, import, context, nullptr, // node->data.array_access_expr.array_ref_expr); // // TypeTableEntry *return_type; // // if (array_type->id == TypeTableEntryIdInvalid) { // return_type = g->builtin_types.entry_invalid; // } else if (array_type->id == TypeTableEntryIdArray) { // if (array_type->data.array.len == 0) { // add_node_error(g, node, buf_sprintf("out of bounds array access")); // } // return_type = array_type->data.array.child_type; // } else if (array_type->id == TypeTableEntryIdPointer) { // if (array_type->data.pointer.is_const && purpose == LValPurposeAssign) { // add_node_error(g, node, buf_sprintf("cannot assign to constant")); // return g->builtin_types.entry_invalid; // } // return_type = array_type->data.pointer.child_type; // } else if (array_type->id == TypeTableEntryIdStruct && // array_type->data.structure.is_slice) // { // TypeTableEntry *pointer_type = array_type->data.structure.fields[0].type_entry; // if (pointer_type->data.pointer.is_const && purpose == LValPurposeAssign) { // add_node_error(g, node, buf_sprintf("cannot assign to constant")); // return g->builtin_types.entry_invalid; // } // return_type = pointer_type->data.pointer.child_type; // } else { // add_node_error(g, node, // buf_sprintf("array access of non-array type '%s'", buf_ptr(&array_type->name))); // return_type = g->builtin_types.entry_invalid; // } // // analyze_expression(g, import, context, g->builtin_types.entry_usize, node->data.array_access_expr.subscript); // // return return_type; //} // //static TypeTableEntry *analyze_unwrap_error_expr(CodeGen *g, ImportTableEntry *import, // BlockContext *parent_context, TypeTableEntry *expected_type, AstNode *node) //{ // AstNode *op1 = node->data.unwrap_err_expr.op1; // AstNode *op2 = node->data.unwrap_err_expr.op2; // AstNode *var_node = node->data.unwrap_err_expr.symbol; // // TypeTableEntry *lhs_type = analyze_expression(g, import, parent_context, nullptr, op1); // if (lhs_type->id == TypeTableEntryIdInvalid) { // return lhs_type; // } else if (lhs_type->id == TypeTableEntryIdErrorUnion) { // TypeTableEntry *child_type = lhs_type->data.error.child_type; // BlockContext *child_context; // if (var_node) { // child_context = new_block_context(node, parent_context); // var_node->block_context = child_context; // Buf *var_name = var_node->data.symbol_expr.symbol; // node->data.unwrap_err_expr.var = add_local_var(g, var_node, import, child_context, var_name, // g->builtin_types.entry_pure_error, true, nullptr); // } else { // child_context = parent_context; // } // // analyze_expression(g, import, child_context, child_type, op2); // return child_type; // } else { // add_node_error(g, op1, // buf_sprintf("expected error type, found '%s'", buf_ptr(&lhs_type->name))); // return g->builtin_types.entry_invalid; // } //} // //static void validate_voided_expr(CodeGen *g, AstNode *source_node, TypeTableEntry *type_entry) { // if (type_entry->id == TypeTableEntryIdMetaType) { // add_node_error(g, first_executing_node(source_node), buf_sprintf("expected expression, found type")); // } else if (type_entry->id == TypeTableEntryIdErrorUnion) { // add_node_error(g, first_executing_node(source_node), buf_sprintf("statement ignores error value")); // } //} // //static LLVMValueRef gen_shl_with_overflow(CodeGen *g, AstNode *node) { // assert(node->type == NodeTypeFnCallExpr); // // size_t fn_call_param_count = node->data.fn_call_expr.params.length; // assert(fn_call_param_count == 4); // // TypeTableEntry *int_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0)); // assert(int_type->id == TypeTableEntryIdInt); // // LLVMValueRef val1 = gen_expr(g, node->data.fn_call_expr.params.at(1)); // LLVMValueRef val2 = gen_expr(g, node->data.fn_call_expr.params.at(2)); // LLVMValueRef ptr_result = gen_expr(g, node->data.fn_call_expr.params.at(3)); // // LLVMValueRef result = LLVMBuildShl(g->builder, val1, val2, ""); // LLVMValueRef orig_val; // if (int_type->data.integral.is_signed) { // orig_val = LLVMBuildAShr(g->builder, result, val2, ""); // } else { // orig_val = LLVMBuildLShr(g->builder, result, val2, ""); // } // LLVMValueRef overflow_bit = LLVMBuildICmp(g->builder, LLVMIntNE, val1, orig_val, ""); // // LLVMBuildStore(g->builder, result, ptr_result); // // return overflow_bit; //} // //static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { // assert(node->type == NodeTypeFnCallExpr); // AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; // assert(fn_ref_expr->type == NodeTypeSymbol); // BuiltinFnEntry *builtin_fn = node->data.fn_call_expr.builtin_fn; // // switch (builtin_fn->id) { // case BuiltinFnIdInvalid: // case BuiltinFnIdTypeof: // zig_unreachable(); // case BuiltinFnIdAddWithOverflow: // case BuiltinFnIdSubWithOverflow: // case BuiltinFnIdMulWithOverflow: // { // size_t fn_call_param_count = node->data.fn_call_expr.params.length; // assert(fn_call_param_count == 4); // // TypeTableEntry *int_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0)); // AddSubMul add_sub_mul; // if (builtin_fn->id == BuiltinFnIdAddWithOverflow) { // add_sub_mul = AddSubMulAdd; // } else if (builtin_fn->id == BuiltinFnIdSubWithOverflow) { // add_sub_mul = AddSubMulSub; // } else if (builtin_fn->id == BuiltinFnIdMulWithOverflow) { // add_sub_mul = AddSubMulMul; // } else { // zig_unreachable(); // } // LLVMValueRef fn_val = get_int_overflow_fn(g, int_type, add_sub_mul); // // LLVMValueRef op1 = gen_expr(g, node->data.fn_call_expr.params.at(1)); // LLVMValueRef op2 = gen_expr(g, node->data.fn_call_expr.params.at(2)); // LLVMValueRef ptr_result = gen_expr(g, node->data.fn_call_expr.params.at(3)); // // LLVMValueRef params[] = { // op1, // op2, // }; // // LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, ""); // LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, ""); // LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, ""); // LLVMBuildStore(g->builder, result, ptr_result); // // return overflow_bit; // } // case BuiltinFnIdShlWithOverflow: // return gen_shl_with_overflow(g, node); // case BuiltinFnIdAlignof: // case BuiltinFnIdMinValue: // case BuiltinFnIdMaxValue: // // caught by constant expression eval codegen // zig_unreachable(); // case BuiltinFnIdCompileVar: // return nullptr; // case BuiltinFnIdBreakpoint: // return LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, ""); // case BuiltinFnIdFrameAddress: // case BuiltinFnIdReturnAddress: // { // LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->type_ref); // return LLVMBuildCall(g->builder, builtin_fn->fn_val, &zero, 1, ""); // } // case BuiltinFnIdCmpExchange: // return gen_cmp_exchange(g, node); // case BuiltinFnIdFence: // return gen_fence(g, node); // case BuiltinFnIdUnreachable: // zig_panic("moved to ir render"); // case BuiltinFnIdSetFnTest: // case BuiltinFnIdSetFnStaticEval: // case BuiltinFnIdSetFnNoInline: // case BuiltinFnIdSetDebugSafety: // // do nothing // return nullptr; // } // zig_unreachable(); //} // //static LLVMValueRef gen_enum_value_expr(CodeGen *g, AstNode *node, TypeTableEntry *enum_type, // AstNode *arg_node) //{ // assert(node->type == NodeTypeFieldAccessExpr); // // uint64_t value = node->data.field_access_expr.type_enum_field->value; // LLVMTypeRef tag_type_ref = enum_type->data.enumeration.tag_type->type_ref; // LLVMValueRef tag_value = LLVMConstInt(tag_type_ref, value, false); // // if (enum_type->data.enumeration.gen_field_count == 0) { // return tag_value; // } else { // TypeTableEntry *arg_node_type = nullptr; // LLVMValueRef new_union_val = gen_expr(g, arg_node); // if (arg_node) { // arg_node_type = get_expr_type(arg_node); // } else { // arg_node_type = g->builtin_types.entry_void; // } // // LLVMValueRef tmp_struct_ptr = node->data.field_access_expr.resolved_struct_val_expr.ptr; // // // populate the new tag value // LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, ""); // LLVMBuildStore(g->builder, tag_value, tag_field_ptr); // // if (arg_node_type->id != TypeTableEntryIdVoid) { // // populate the union value // TypeTableEntry *union_val_type = get_expr_type(arg_node); // LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 1, ""); // LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, // LLVMPointerType(union_val_type->type_ref, 0), ""); // // gen_assign_raw(g, arg_node, BinOpTypeAssign, bitcasted_union_field_ptr, new_union_val, // union_val_type, union_val_type); // // } // // return tmp_struct_ptr; // } //} // //static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) { // assert(node->type == NodeTypeArrayAccessExpr); // // AstNode *array_expr_node = node->data.array_access_expr.array_ref_expr; // TypeTableEntry *array_type = get_expr_type(array_expr_node); // // LLVMValueRef array_ptr = gen_array_base_ptr(g, array_expr_node); // // LLVMValueRef subscript_value = gen_expr(g, node->data.array_access_expr.subscript); // return gen_array_elem_ptr(g, node, array_ptr, array_type, subscript_value); //} // //static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) { // assert(node->type == NodeTypeUnwrapErrorExpr); // // AstNode *op1 = node->data.unwrap_err_expr.op1; // AstNode *op2 = node->data.unwrap_err_expr.op2; // VariableTableEntry *var = node->data.unwrap_err_expr.var; // // LLVMValueRef expr_val = gen_expr(g, op1); // TypeTableEntry *expr_type = get_expr_type(op1); // TypeTableEntry *op2_type = get_expr_type(op2); // assert(expr_type->id == TypeTableEntryIdErrorUnion); // TypeTableEntry *child_type = expr_type->data.error.child_type; // LLVMValueRef err_val; // if (handle_is_ptr(expr_type)) { // LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 0, ""); // err_val = LLVMBuildLoad(g->builder, err_val_ptr, ""); // } else { // err_val = expr_val; // } // LLVMValueRef zero = LLVMConstNull(g->err_tag_type->type_ref); // LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, ""); // // LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrOk"); // LLVMBasicBlockRef err_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrError"); // LLVMBasicBlockRef end_block; // bool err_reachable = op2_type->id != TypeTableEntryIdUnreachable; // bool have_end_block = err_reachable && type_has_bits(child_type); // if (have_end_block) { // end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrEnd"); // } // // LLVMBuildCondBr(g->builder, cond_val, ok_block, err_block); // // LLVMPositionBuilderAtEnd(g->builder, err_block); // if (var) { // LLVMBuildStore(g->builder, err_val, var->value_ref); // } // LLVMValueRef err_result = gen_expr(g, op2); // if (have_end_block) { // LLVMBuildBr(g->builder, end_block); // } else if (err_reachable) { // LLVMBuildBr(g->builder, ok_block); // } // // LLVMPositionBuilderAtEnd(g->builder, ok_block); // if (!type_has_bits(child_type)) { // return nullptr; // } // LLVMValueRef child_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 1, ""); // LLVMValueRef child_val = get_handle_value(g, child_val_ptr, child_type); // // if (!have_end_block) { // return child_val; // } // // LLVMBuildBr(g->builder, end_block); // // LLVMPositionBuilderAtEnd(g->builder, end_block); // LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(err_result), ""); // LLVMValueRef incoming_values[2] = {child_val, err_result}; // LLVMBasicBlockRef incoming_blocks[2] = {ok_block, err_block}; // LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2); // return phi; //} // //static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) { // assert(node->type == NodeTypeReturnExpr); // AstNode *param_node = node->data.return_expr.expr; // assert(param_node); // LLVMValueRef value = gen_expr(g, param_node); // TypeTableEntry *value_type = get_expr_type(param_node); // // switch (node->data.return_expr.kind) { // case ReturnKindUnconditional: // { // Expr *expr = get_resolved_expr(param_node); // if (expr->const_val.ok) { // if (value_type->id == TypeTableEntryIdErrorUnion) { // if (expr->const_val.data.x_err.err) { // expr->return_knowledge = ReturnKnowledgeKnownError; // } else { // expr->return_knowledge = ReturnKnowledgeKnownNonError; // } // } else if (value_type->id == TypeTableEntryIdMaybe) { // if (expr->const_val.data.x_maybe) { // expr->return_knowledge = ReturnKnowledgeKnownNonNull; // } else { // expr->return_knowledge = ReturnKnowledgeKnownNull; // } // } // } // return gen_return(g, node, value, expr->return_knowledge); // } // case ReturnKindError: // { // assert(value_type->id == TypeTableEntryIdErrorUnion); // TypeTableEntry *child_type = value_type->data.error.child_type; // // LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ErrRetReturn"); // LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ErrRetContinue"); // // LLVMValueRef err_val; // if (type_has_bits(child_type)) { // LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, value, 0, ""); // err_val = LLVMBuildLoad(g->builder, err_val_ptr, ""); // } else { // err_val = value; // } // LLVMValueRef zero = LLVMConstNull(g->err_tag_type->type_ref); // LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, ""); // LLVMBuildCondBr(g->builder, cond_val, continue_block, return_block); // // LLVMPositionBuilderAtEnd(g->builder, return_block); // TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type; // if (return_type->id == TypeTableEntryIdPureError) { // gen_return(g, node, err_val, ReturnKnowledgeKnownError); // } else if (return_type->id == TypeTableEntryIdErrorUnion) { // if (type_has_bits(return_type->data.error.child_type)) { // assert(g->cur_ret_ptr); // // LLVMValueRef tag_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, 0, ""); // LLVMBuildStore(g->builder, err_val, tag_ptr); // LLVMBuildRetVoid(g->builder); // } else { // gen_return(g, node, err_val, ReturnKnowledgeKnownError); // } // } else { // zig_unreachable(); // } // // LLVMPositionBuilderAtEnd(g->builder, continue_block); // if (type_has_bits(child_type)) { // LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, value, 1, ""); // return get_handle_value(g, val_ptr, child_type); // } else { // return nullptr; // } // } // case ReturnKindMaybe: // { // assert(value_type->id == TypeTableEntryIdMaybe); // TypeTableEntry *child_type = value_type->data.maybe.child_type; // // LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeRetReturn"); // LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeRetContinue"); // // LLVMValueRef maybe_val_ptr = LLVMBuildStructGEP(g->builder, value, 1, ""); // LLVMValueRef is_non_null = LLVMBuildLoad(g->builder, maybe_val_ptr, ""); // // LLVMValueRef zero = LLVMConstNull(LLVMInt1Type()); // LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntNE, is_non_null, zero, ""); // LLVMBuildCondBr(g->builder, cond_val, continue_block, return_block); // // LLVMPositionBuilderAtEnd(g->builder, return_block); // TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type; // assert(return_type->id == TypeTableEntryIdMaybe); // if (handle_is_ptr(return_type)) { // assert(g->cur_ret_ptr); // // LLVMValueRef maybe_bit_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, 1, ""); // LLVMBuildStore(g->builder, zero, maybe_bit_ptr); // LLVMBuildRetVoid(g->builder); // } else { // LLVMValueRef ret_zero_value = LLVMConstNull(return_type->type_ref); // gen_return(g, node, ret_zero_value, ReturnKnowledgeKnownNull); // } // // LLVMPositionBuilderAtEnd(g->builder, continue_block); // if (type_has_bits(child_type)) { // LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, value, 0, ""); // return get_handle_value(g, val_ptr, child_type); // } else { // return nullptr; // } // } // } // zig_unreachable(); //}