diff options
| author | Andrew Kelley <superjoe30@gmail.com> | 2016-10-09 02:20:01 -0400 |
|---|---|---|
| committer | Andrew Kelley <superjoe30@gmail.com> | 2016-10-09 02:20:01 -0400 |
| commit | 77ae3442ef2dee1659ab5778f43c00026fd21dd9 (patch) | |
| tree | 3a9a3eae379ec2412608bc433d03df2a590e815b /src | |
| parent | 07fe60ded1727d528d3ae36b892aa7c84262ed96 (diff) | |
| download | zig-77ae3442ef2dee1659ab5778f43c00026fd21dd9.tar.gz zig-77ae3442ef2dee1659ab5778f43c00026fd21dd9.zip | |
explicit casting works with IR
Diffstat (limited to 'src')
| -rw-r--r-- | src/all_types.hpp | 7 | ||||
| -rw-r--r-- | src/analyze.cpp | 301 | ||||
| -rw-r--r-- | src/analyze.hpp | 4 | ||||
| -rw-r--r-- | src/codegen.cpp | 614 | ||||
| -rw-r--r-- | src/eval.cpp | 1 | ||||
| -rw-r--r-- | src/ir.cpp | 545 | ||||
| -rw-r--r-- | src/ir_print.cpp | 33 |
7 files changed, 835 insertions, 670 deletions
diff --git a/src/all_types.hpp b/src/all_types.hpp index 1b5e89b337..4a98fde9bc 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -29,6 +29,7 @@ struct TypeStructField; struct CodeGen; struct ConstExprValue; struct IrInstruction; +struct IrInstructionCast; struct IrBasicBlock; struct IrExecutable { @@ -1121,7 +1122,7 @@ struct FnTableEntry { AstNode *fn_test_set_node; AstNode *fn_static_eval_set_node; - ZigList<AstNode *> cast_alloca_list; + ZigList<IrInstructionCast *> cast_alloca_list; ZigList<StructValExprCodeGen *> struct_val_expr_alloca_list; ZigList<VariableTableEntry *> variable_list; ZigList<AstNode *> goto_list; @@ -1435,6 +1436,7 @@ struct IrInstruction { // if ref_count is zero, instruction can be omitted in codegen size_t ref_count; IrInstruction *other; + ReturnKnowledge return_knowledge; }; struct IrInstructionCondBr { @@ -1547,7 +1549,8 @@ struct IrInstructionCast { IrInstruction *value; IrInstruction *dest_type; - bool is_implicit; + CastOp cast_op; + LLVMValueRef tmp_ptr; }; #endif diff --git a/src/analyze.cpp b/src/analyze.cpp index 0c7e1c1292..0bb212f8d1 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -25,8 +25,6 @@ static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableE BlockContext *context, TypeTableEntry *expected_type, AstNode *node); static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *struct_type); static TypeTableEntry *unwrapped_node_type(AstNode *node); -static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, - AstNode *node); static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node, Buf *err_name); static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, @@ -115,7 +113,7 @@ AstNode *first_executing_node(AstNode *node) { zig_unreachable(); } -static void mark_impure_fn(CodeGen *g, BlockContext *context, AstNode *node) { +void mark_impure_fn(CodeGen *g, BlockContext *context, AstNode *node) { if (!context->fn_entry) return; if (!context->fn_entry->is_pure) return; @@ -261,11 +259,6 @@ uint64_t type_size(CodeGen *g, TypeTableEntry *type_entry) { } } -static bool is_u8(TypeTableEntry *type) { - return type->id == TypeTableEntryIdInt && - !type->data.integral.is_signed && type->data.integral.bit_count == 8; -} - static bool is_slice(TypeTableEntry *type) { return type->id == TypeTableEntryIdStruct && type->data.structure.is_slice; } @@ -4466,30 +4459,7 @@ static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *impor } } -static TypeTableEntry *resolve_cast(CodeGen *g, BlockContext *context, AstNode *node, - AstNode *expr_node, TypeTableEntry *wanted_type, CastOp op, bool need_alloca) -{ - node->data.fn_call_expr.cast_op = op; - - ConstExprValue *other_val = &get_resolved_expr(expr_node)->const_val; - TypeTableEntry *other_type = get_resolved_expr(expr_node)->type_entry; - ConstExprValue *const_val = &get_resolved_expr(node)->const_val; - if (other_val->ok) { - eval_const_expr_implicit_cast(node->data.fn_call_expr.cast_op, other_val, other_type, - const_val, wanted_type); - } - - if (need_alloca) { - if (context->fn_entry) { - context->fn_entry->cast_alloca_list.append(node); - } else { - assert(get_resolved_expr(node)->const_val.ok); - } - } - return wanted_type; -} - -static bool type_is_codegen_pointer(TypeTableEntry *type) { +bool type_is_codegen_pointer(TypeTableEntry *type) { if (type->id == TypeTableEntryIdPointer) return true; if (type->id == TypeTableEntryIdFn) return true; if (type->id == TypeTableEntryIdMaybe) { @@ -4499,256 +4469,6 @@ static bool type_is_codegen_pointer(TypeTableEntry *type) { return false; } -static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, - AstNode *node) -{ - assert(node->type == NodeTypeFnCallExpr); - - AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; - size_t actual_param_count = node->data.fn_call_expr.params.length; - - if (actual_param_count != 1) { - add_node_error(g, fn_ref_expr, buf_sprintf("cast expression expects exactly one parameter")); - return g->builtin_types.entry_invalid; - } - - AstNode *expr_node = node->data.fn_call_expr.params.at(0); - TypeTableEntry *wanted_type = resolve_type(g, fn_ref_expr); - TypeTableEntry *actual_type = analyze_expression(g, import, context, nullptr, expr_node); - TypeTableEntry *wanted_type_canon = get_underlying_type(wanted_type); - TypeTableEntry *actual_type_canon = get_underlying_type(actual_type); - - if (wanted_type_canon->id == TypeTableEntryIdInvalid || - actual_type_canon->id == TypeTableEntryIdInvalid) - { - return g->builtin_types.entry_invalid; - } - - // explicit match or non-const to const - if (types_match_const_cast_only(wanted_type, actual_type)) { - return resolve_cast(g, context, node, expr_node, wanted_type, CastOpNoop, false); - } - - // explicit cast from bool to int - if (wanted_type_canon->id == TypeTableEntryIdInt && - actual_type_canon->id == TypeTableEntryIdBool) - { - return resolve_cast(g, context, node, expr_node, wanted_type, CastOpBoolToInt, false); - } - - // explicit cast from pointer to isize or usize - if ((wanted_type_canon == g->builtin_types.entry_isize || wanted_type_canon == g->builtin_types.entry_usize) && - type_is_codegen_pointer(actual_type_canon)) - { - return resolve_cast(g, context, node, expr_node, wanted_type, CastOpPtrToInt, false); - } - - - // explicit cast from isize or usize to pointer - if (wanted_type_canon->id == TypeTableEntryIdPointer && - (actual_type_canon == g->builtin_types.entry_isize || actual_type_canon == g->builtin_types.entry_usize)) - { - return resolve_cast(g, context, node, expr_node, 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 resolve_cast(g, context, node, expr_node, wanted_type, CastOpWidenOrShorten, false); - } - - // explicit cast from int to float - if (wanted_type_canon->id == TypeTableEntryIdFloat && - actual_type_canon->id == TypeTableEntryIdInt) - { - return resolve_cast(g, context, node, expr_node, wanted_type, CastOpIntToFloat, false); - } - - // explicit cast from float to int - if (wanted_type_canon->id == TypeTableEntryIdInt && - actual_type_canon->id == TypeTableEntryIdFloat) - { - return resolve_cast(g, context, node, expr_node, 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 resolve_cast(g, context, node, expr_node, 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)) - { - mark_impure_fn(g, context, node); - return resolve_cast(g, context, node, expr_node, 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)) - { - mark_impure_fn(g, context, node); - uint64_t child_type_size = type_size(g, - wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type); - if (actual_type->data.array.len % child_type_size == 0) { - return resolve_cast(g, context, node, expr_node, wanted_type, CastOpBytesToSlice, true); - } else { - add_node_error(g, node, - buf_sprintf("unable to convert %s to %s: size mismatch", - buf_ptr(&actual_type->name), buf_ptr(&wanted_type->name))); - return g->builtin_types.entry_invalid; - } - } - - // 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 resolve_cast(g, context, node, expr_node, 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 resolve_cast(g, context, node, expr_node, 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)) { - get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownNonNull; - return resolve_cast(g, context, node, expr_node, wanted_type, CastOpMaybeWrap, true); - } else if (actual_type->id == TypeTableEntryIdNumLitInt || - actual_type->id == TypeTableEntryIdNumLitFloat) - { - if (num_lit_fits_in_other_type(g, expr_node, wanted_type->data.maybe.child_type)) { - get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownNonNull; - return resolve_cast(g, context, node, expr_node, wanted_type, CastOpMaybeWrap, true); - } else { - return g->builtin_types.entry_invalid; - } - } - } - - // explicit cast from null literal to maybe type - if (wanted_type->id == TypeTableEntryIdMaybe && - actual_type->id == TypeTableEntryIdNullLit) - { - get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownNull; - return resolve_cast(g, context, node, expr_node, wanted_type, CastOpNullToMaybe, true); - } - - // 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)) { - get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownNonError; - return resolve_cast(g, context, node, expr_node, wanted_type, CastOpErrorWrap, true); - } else if (actual_type->id == TypeTableEntryIdNumLitInt || - actual_type->id == TypeTableEntryIdNumLitFloat) - { - if (num_lit_fits_in_other_type(g, expr_node, wanted_type->data.error.child_type)) { - get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownNonError; - return resolve_cast(g, context, node, expr_node, wanted_type, CastOpErrorWrap, true); - } else { - return g->builtin_types.entry_invalid; - } - } - } - - // explicit cast from pure error to error union type - if (wanted_type->id == TypeTableEntryIdErrorUnion && - actual_type->id == TypeTableEntryIdPureError) - { - get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownError; - return resolve_cast(g, context, node, expr_node, wanted_type, CastOpPureErrorWrap, false); - } - - // explicit cast from number literal to another type - if (actual_type->id == TypeTableEntryIdNumLitFloat || - actual_type->id == TypeTableEntryIdNumLitInt) - { - if (num_lit_fits_in_other_type(g, expr_node, 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 resolve_cast(g, context, node, expr_node, wanted_type, op, false); - } else { - return g->builtin_types.entry_invalid; - } - } - - // 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, g->error_decls.length); - if (bignum_fits_in_bits(&bn, wanted_type->data.integral.bit_count, - wanted_type->data.integral.is_signed)) - { - return resolve_cast(g, context, node, expr_node, wanted_type, CastOpErrToInt, false); - } else { - add_node_error(g, node, - buf_sprintf("too many error values to fit in '%s'", buf_ptr(&wanted_type->name))); - return g->builtin_types.entry_invalid; - } - } - - // 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 resolve_cast(g, context, node, expr_node, 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 resolve_cast(g, context, node, expr_node, wanted_type, CastOpEnumToInt, false); - } - - add_node_error(g, node, - buf_sprintf("invalid cast from type '%s' to '%s'", - buf_ptr(&actual_type->name), - buf_ptr(&wanted_type->name))); - return g->builtin_types.entry_invalid; -} - static TypeTableEntry *analyze_import(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { @@ -5866,13 +5586,14 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, } } - if (handle_is_ptr(return_type)) { - if (context->fn_entry) { - context->fn_entry->cast_alloca_list.append(node); - } else if (!result_val->ok) { - add_node_error(g, node, buf_sprintf("unable to evaluate constant expression")); - } - } + // TODO + //if (handle_is_ptr(return_type)) { + // if (context->fn_entry) { + // context->fn_entry->cast_alloca_list.append(node); + // } else if (!result_val->ok) { + // add_node_error(g, node, buf_sprintf("unable to evaluate constant expression")); + // } + //} return return_type; } @@ -6110,7 +5831,7 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import if (const_val->ok) { if (invoke_type_entry->id == TypeTableEntryIdMetaType) { - return analyze_cast_expr(g, import, context, node); + zig_unreachable(); } else if (invoke_type_entry->id == TypeTableEntryIdFn) { AstNode *struct_node; if (fn_ref_expr->type == NodeTypeFieldAccessExpr && diff --git a/src/analyze.hpp b/src/analyze.hpp index 9948ef9a8b..48a442f973 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -49,10 +49,14 @@ TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, ImportTableEntry *im BlockContext *block_context, AstNode *parent_source_node, AstNode **child_nodes, TypeTableEntry **child_types, size_t child_count); + + bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *actual_type); VariableTableEntry *find_variable(CodeGen *g, BlockContext *orig_context, Buf *name); AstNode *find_decl(BlockContext *context, Buf *name); void resolve_top_level_decl(CodeGen *g, AstNode *node, bool pointer_only); TopLevelDecl *get_as_top_level_decl(AstNode *node); +void mark_impure_fn(CodeGen *g, BlockContext *context, AstNode *node); +bool type_is_codegen_pointer(TypeTableEntry *type); #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index fdf41415ae..144b7bb14c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -344,11 +344,10 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, Bui return *fn; } -static LLVMValueRef get_handle_value(CodeGen *g, AstNode *source_node, LLVMValueRef ptr, TypeTableEntry *type) { +static LLVMValueRef get_handle_value(CodeGen *g, LLVMValueRef ptr, TypeTableEntry *type) { if (handle_is_ptr(type)) { return ptr; } else { - set_debug_source_node(g, source_node); return LLVMBuildLoad(g->builder, ptr, ""); } } @@ -378,7 +377,7 @@ static void gen_debug_safety_crash(CodeGen *g) { LLVMBuildUnreachable(g->builder); } -static void add_bounds_check(CodeGen *g, AstNode *source_node, LLVMValueRef target_val, +static void add_bounds_check(CodeGen *g, LLVMValueRef target_val, LLVMIntPredicate lower_pred, LLVMValueRef lower_value, LLVMIntPredicate upper_pred, LLVMValueRef upper_value) { @@ -391,8 +390,6 @@ static void add_bounds_check(CodeGen *g, AstNode *source_node, LLVMValueRef targ upper_value = nullptr; } - set_debug_source_node(g, source_node); - LLVMBasicBlockRef bounds_check_fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoundsCheckFail"); LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoundsCheckOk"); LLVMBasicBlockRef lower_ok_block = upper_value ? @@ -425,12 +422,11 @@ static LLVMValueRef gen_err_name(CodeGen *g, AstNode *node) { AstNode *err_val_node = node->data.fn_call_expr.params.at(0); LLVMValueRef err_val = gen_expr(g, err_val_node); - set_debug_source_node(g, node); if (want_debug_safety(g, node)) { LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(err_val)); LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(err_val), g->error_decls.length, false); - add_bounds_check(g, node, err_val, LLVMIntNE, zero, LLVMIntULT, end_val); + add_bounds_check(g, err_val, LLVMIntNE, zero, LLVMIntULT, end_val); } LLVMValueRef indices[] = { @@ -514,15 +510,12 @@ static LLVMValueRef gen_truncate(CodeGen *g, AstNode *node) { LLVMValueRef src_val = gen_expr(g, src_node); - set_debug_source_node(g, node); return LLVMBuildTrunc(g->builder, src_val, dest_type->type_ref, ""); } static LLVMValueRef gen_unreachable(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); - set_debug_source_node(g, node); - if (want_debug_safety(g, node) || g->is_test_build) { gen_debug_safety_crash(g); } else { @@ -545,7 +538,6 @@ static LLVMValueRef gen_shl_with_overflow(CodeGen *g, AstNode *node) { 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)); - set_debug_source_node(g, node); LLVMValueRef result = LLVMBuildShl(g->builder, val1, val2, ""); LLVMValueRef orig_val; if (int_type->data.integral.is_signed) { @@ -590,7 +582,6 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { operand, LLVMConstNull(LLVMInt1Type()), }; - set_debug_source_node(g, node); return LLVMBuildCall(g->builder, fn_val, params, 2, ""); } case BuiltinFnIdAddWithOverflow: @@ -622,7 +613,6 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { op2, }; - set_debug_source_node(g, node); 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, ""); @@ -646,7 +636,6 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); - set_debug_source_node(g, node); LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, ""); LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr, ptr_u8, ""); @@ -677,7 +666,6 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); - set_debug_source_node(g, node); LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, ""); uint64_t align_in_bytes = get_memcpy_align(g, dest_type->data.pointer.child_type); @@ -707,13 +695,11 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { case BuiltinFnIdErrName: return gen_err_name(g, node); case BuiltinFnIdBreakpoint: - set_debug_source_node(g, node); return LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, ""); case BuiltinFnIdFrameAddress: case BuiltinFnIdReturnAddress: { LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->type_ref); - set_debug_source_node(g, node); return LLVMBuildCall(g->builder, builtin_fn->fn_val, &zero, 1, ""); } case BuiltinFnIdCmpExchange: @@ -760,7 +746,6 @@ static LLVMValueRef gen_enum_value_expr(CodeGen *g, AstNode *node, TypeTableEntr LLVMValueRef tmp_struct_ptr = node->data.field_access_expr.resolved_struct_val_expr.ptr; // populate the new tag value - set_debug_source_node(g, node); LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, ""); LLVMBuildStore(g->builder, tag_value, tag_field_ptr); @@ -804,7 +789,6 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeT !wanted_type->data.integral.is_signed && actual_type->data.integral.is_signed && want_debug_safety(g, source_node)) { - set_debug_source_node(g, source_node); LLVMValueRef zero = LLVMConstNull(actual_type->type_ref); LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntSGE, expr_val, zero, ""); @@ -822,14 +806,11 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeT return expr_val; } else if (actual_bits < wanted_bits) { if (actual_type->id == TypeTableEntryIdFloat) { - set_debug_source_node(g, source_node); return LLVMBuildFPExt(g->builder, expr_val, wanted_type->type_ref, ""); } else if (actual_type->id == TypeTableEntryIdInt) { if (actual_type->data.integral.is_signed) { - set_debug_source_node(g, source_node); return LLVMBuildSExt(g->builder, expr_val, wanted_type->type_ref, ""); } else { - set_debug_source_node(g, source_node); return LLVMBuildZExt(g->builder, expr_val, wanted_type->type_ref, ""); } } else { @@ -837,10 +818,8 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeT } } else if (actual_bits > wanted_bits) { if (actual_type->id == TypeTableEntryIdFloat) { - set_debug_source_node(g, source_node); return LLVMBuildFPTrunc(g->builder, expr_val, wanted_type->type_ref, ""); } else if (actual_type->id == TypeTableEntryIdInt) { - set_debug_source_node(g, source_node); LLVMValueRef trunc_val = LLVMBuildTrunc(g->builder, expr_val, wanted_type->type_ref, ""); if (!want_debug_safety(g, source_node)) { return trunc_val; @@ -869,256 +848,11 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeT } } -static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { - assert(node->type == NodeTypeFnCallExpr); - - AstNode *expr_node = node->data.fn_call_expr.params.at(0); - - LLVMValueRef expr_val = gen_expr(g, expr_node); - - TypeTableEntry *actual_type = get_expr_type(expr_node); - TypeTableEntry *wanted_type = get_expr_type(node); - - AstNodeFnCallExpr *cast_expr = &node->data.fn_call_expr; - - switch (cast_expr->cast_op) { - case CastOpNoCast: - zig_unreachable(); - case CastOpNoop: - return expr_val; - case CastOpErrToInt: - assert(actual_type->id == TypeTableEntryIdErrorUnion); - if (!type_has_bits(actual_type->data.error.child_type)) { - return gen_widen_or_shorten(g, node, g->err_tag_type, wanted_type, expr_val); - } else { - zig_panic("TODO"); - } - case CastOpMaybeWrap: - { - assert(cast_expr->tmp_ptr); - assert(wanted_type->id == TypeTableEntryIdMaybe); - assert(actual_type); - - TypeTableEntry *child_type = wanted_type->data.maybe.child_type; - - if (child_type->id == TypeTableEntryIdPointer || - child_type->id == TypeTableEntryIdFn) - { - return expr_val; - } else { - set_debug_source_node(g, node); - LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 0, ""); - gen_assign_raw(g, node, BinOpTypeAssign, - val_ptr, expr_val, child_type, actual_type); - - set_debug_source_node(g, node); - LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 1, ""); - LLVMBuildStore(g->builder, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr); - } - - return cast_expr->tmp_ptr; - } - case CastOpNullToMaybe: - // handled by constant expression evaluator - zig_unreachable(); - case CastOpErrorWrap: - { - assert(wanted_type->id == TypeTableEntryIdErrorUnion); - TypeTableEntry *child_type = wanted_type->data.error.child_type; - LLVMValueRef ok_err_val = LLVMConstNull(g->err_tag_type->type_ref); - - if (!type_has_bits(child_type)) { - return ok_err_val; - } else { - assert(cast_expr->tmp_ptr); - assert(wanted_type->id == TypeTableEntryIdErrorUnion); - assert(actual_type); - - set_debug_source_node(g, node); - LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 0, ""); - LLVMBuildStore(g->builder, ok_err_val, err_tag_ptr); - - LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 1, ""); - gen_assign_raw(g, node, BinOpTypeAssign, - payload_ptr, expr_val, child_type, actual_type); - - return cast_expr->tmp_ptr; - } - } - case CastOpPureErrorWrap: - assert(wanted_type->id == TypeTableEntryIdErrorUnion); - - if (!type_has_bits(wanted_type->data.error.child_type)) { - return expr_val; - } else { - zig_panic("TODO"); - } - case CastOpPtrToInt: - set_debug_source_node(g, node); - return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, ""); - case CastOpIntToPtr: - set_debug_source_node(g, node); - return LLVMBuildIntToPtr(g->builder, expr_val, wanted_type->type_ref, ""); - case CastOpPointerReinterpret: - set_debug_source_node(g, node); - return LLVMBuildBitCast(g->builder, expr_val, wanted_type->type_ref, ""); - case CastOpWidenOrShorten: - return gen_widen_or_shorten(g, node, actual_type, wanted_type, expr_val); - case CastOpToUnknownSizeArray: - { - assert(cast_expr->tmp_ptr); - assert(wanted_type->id == TypeTableEntryIdStruct); - assert(wanted_type->data.structure.is_slice); - - TypeTableEntry *pointer_type = wanted_type->data.structure.fields[0].type_entry; - - set_debug_source_node(g, node); - - size_t ptr_index = wanted_type->data.structure.fields[0].gen_index; - if (ptr_index != SIZE_MAX) { - LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, ptr_index, ""); - LLVMValueRef expr_bitcast = LLVMBuildBitCast(g->builder, expr_val, pointer_type->type_ref, ""); - LLVMBuildStore(g->builder, expr_bitcast, ptr_ptr); - } - - size_t len_index = wanted_type->data.structure.fields[1].gen_index; - LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, len_index, ""); - LLVMValueRef len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, - actual_type->data.array.len, false); - LLVMBuildStore(g->builder, len_val, len_ptr); - - return cast_expr->tmp_ptr; - } - case CastOpResizeSlice: - { - assert(cast_expr->tmp_ptr); - assert(wanted_type->id == TypeTableEntryIdStruct); - assert(wanted_type->data.structure.is_slice); - assert(actual_type->id == TypeTableEntryIdStruct); - assert(actual_type->data.structure.is_slice); - - TypeTableEntry *actual_pointer_type = actual_type->data.structure.fields[0].type_entry; - TypeTableEntry *actual_child_type = actual_pointer_type->data.pointer.child_type; - TypeTableEntry *wanted_pointer_type = wanted_type->data.structure.fields[0].type_entry; - TypeTableEntry *wanted_child_type = wanted_pointer_type->data.pointer.child_type; - - set_debug_source_node(g, node); - - size_t actual_ptr_index = actual_type->data.structure.fields[0].gen_index; - size_t actual_len_index = actual_type->data.structure.fields[1].gen_index; - size_t wanted_ptr_index = wanted_type->data.structure.fields[0].gen_index; - size_t wanted_len_index = wanted_type->data.structure.fields[1].gen_index; - - LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, expr_val, actual_ptr_index, ""); - LLVMValueRef src_ptr = LLVMBuildLoad(g->builder, src_ptr_ptr, ""); - LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr, - wanted_type->data.structure.fields[0].type_entry->type_ref, ""); - LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, - wanted_ptr_index, ""); - LLVMBuildStore(g->builder, src_ptr_casted, dest_ptr_ptr); - - LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, expr_val, actual_len_index, ""); - LLVMValueRef src_len = LLVMBuildLoad(g->builder, src_len_ptr, ""); - uint64_t src_size = type_size(g, actual_child_type); - uint64_t dest_size = type_size(g, wanted_child_type); - - LLVMValueRef new_len; - if (dest_size == 1) { - LLVMValueRef src_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, src_size, false); - new_len = LLVMBuildMul(g->builder, src_len, src_size_val, ""); - } else if (src_size == 1) { - LLVMValueRef dest_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, dest_size, false); - if (want_debug_safety(g, node)) { - LLVMValueRef remainder_val = LLVMBuildURem(g->builder, src_len, dest_size_val, ""); - LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_usize->type_ref); - LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SliceWidenOk"); - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SliceWidenFail"); - LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_debug_safety_crash(g); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - } - new_len = ZigLLVMBuildExactUDiv(g->builder, src_len, dest_size_val, ""); - } else { - zig_unreachable(); - } - - LLVMValueRef dest_len_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, - wanted_len_index, ""); - LLVMBuildStore(g->builder, new_len, dest_len_ptr); - - - return cast_expr->tmp_ptr; - } - case CastOpBytesToSlice: - { - assert(cast_expr->tmp_ptr); - assert(wanted_type->id == TypeTableEntryIdStruct); - assert(wanted_type->data.structure.is_slice); - assert(actual_type->id == TypeTableEntryIdArray); - - TypeTableEntry *wanted_pointer_type = wanted_type->data.structure.fields[0].type_entry; - TypeTableEntry *wanted_child_type = wanted_pointer_type->data.pointer.child_type; - - set_debug_source_node(g, node); - - size_t wanted_ptr_index = wanted_type->data.structure.fields[0].gen_index; - LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, wanted_ptr_index, ""); - LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, expr_val, wanted_pointer_type->type_ref, ""); - LLVMBuildStore(g->builder, src_ptr_casted, dest_ptr_ptr); - - size_t wanted_len_index = wanted_type->data.structure.fields[1].gen_index; - LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, wanted_len_index, ""); - LLVMValueRef len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, - actual_type->data.array.len / type_size(g, wanted_child_type), false); - LLVMBuildStore(g->builder, len_val, len_ptr); - - return cast_expr->tmp_ptr; - } - case CastOpIntToFloat: - assert(actual_type->id == TypeTableEntryIdInt); - if (actual_type->data.integral.is_signed) { - set_debug_source_node(g, node); - return LLVMBuildSIToFP(g->builder, expr_val, wanted_type->type_ref, ""); - } else { - set_debug_source_node(g, node); - return LLVMBuildUIToFP(g->builder, expr_val, wanted_type->type_ref, ""); - } - case CastOpFloatToInt: - assert(wanted_type->id == TypeTableEntryIdInt); - if (wanted_type->data.integral.is_signed) { - set_debug_source_node(g, node); - return LLVMBuildFPToSI(g->builder, expr_val, wanted_type->type_ref, ""); - } else { - set_debug_source_node(g, node); - return LLVMBuildFPToUI(g->builder, expr_val, wanted_type->type_ref, ""); - } - - case CastOpBoolToInt: - assert(wanted_type->id == TypeTableEntryIdInt); - assert(actual_type->id == TypeTableEntryIdBool); - set_debug_source_node(g, node); - return LLVMBuildZExt(g->builder, expr_val, wanted_type->type_ref, ""); - - case CastOpIntToEnum: - return gen_widen_or_shorten(g, node, actual_type, wanted_type->data.enumeration.tag_type, expr_val); - case CastOpEnumToInt: - return gen_widen_or_shorten(g, node, actual_type->data.enumeration.tag_type, wanted_type, expr_val); - } - zig_unreachable(); -} - - static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); if (node->data.fn_call_expr.is_builtin) { return gen_builtin_fn_call_expr(g, node); - } else if (node->data.fn_call_expr.cast_op != CastOpNoCast) { - return gen_cast_expr(g, node); } FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry; @@ -1186,7 +920,6 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { } } - set_debug_source_node(g, node); LLVMValueRef result = ZigLLVMBuildCall(g->builder, fn_val, gen_param_values, gen_param_index, fn_type->data.fn.calling_convention, ""); @@ -1209,7 +942,6 @@ static LLVMValueRef gen_array_base_ptr(CodeGen *g, AstNode *node) { array_ptr = gen_field_access_expr(g, node, true); if (type_entry->id == TypeTableEntryIdPointer) { // we have a double pointer so we must dereference it once - set_debug_source_node(g, node); array_ptr = LLVMBuildLoad(g->builder, array_ptr, ""); } } else { @@ -1234,20 +966,18 @@ static LLVMValueRef gen_array_elem_ptr(CodeGen *g, AstNode *source_node, LLVMVal if (want_debug_safety(g, source_node)) { LLVMValueRef end = LLVMConstInt(g->builtin_types.entry_usize->type_ref, array_type->data.array.len, false); - add_bounds_check(g, source_node, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, end); + add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, end); } LLVMValueRef indices[] = { LLVMConstNull(g->builtin_types.entry_usize->type_ref), subscript_value }; - set_debug_source_node(g, source_node); return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); } else if (array_type->id == TypeTableEntryIdPointer) { assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); LLVMValueRef indices[] = { subscript_value }; - set_debug_source_node(g, source_node); return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 1, ""); } else if (array_type->id == TypeTableEntryIdStruct) { assert(array_type->data.structure.is_slice); @@ -1255,15 +985,13 @@ static LLVMValueRef gen_array_elem_ptr(CodeGen *g, AstNode *source_node, LLVMVal assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); if (want_debug_safety(g, source_node)) { - set_debug_source_node(g, source_node); size_t len_index = array_type->data.structure.fields[1].gen_index; assert(len_index != SIZE_MAX); LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, len_index, ""); LLVMValueRef len = LLVMBuildLoad(g->builder, len_ptr, ""); - add_bounds_check(g, source_node, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, len); + add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, len); } - set_debug_source_node(g, source_node); size_t ptr_index = array_type->data.structure.fields[0].gen_index; assert(ptr_index != SIZE_MAX); LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, ptr_index, ""); @@ -1302,7 +1030,6 @@ static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **ou assert(var); if (var->type->id == TypeTableEntryIdPointer) { - set_debug_source_node(g, node); struct_ptr = LLVMBuildLoad(g->builder, var->value_ref, ""); } else { struct_ptr = var->value_ref; @@ -1312,7 +1039,6 @@ static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **ou TypeTableEntry *field_type = get_expr_type(struct_expr_node); if (field_type->id == TypeTableEntryIdPointer) { // we have a double pointer so we must dereference it once - set_debug_source_node(g, node); struct_ptr = LLVMBuildLoad(g->builder, struct_ptr, ""); } } else { @@ -1325,7 +1051,6 @@ static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **ou size_t gen_field_index = node->data.field_access_expr.type_struct_field->gen_index; assert(gen_field_index != SIZE_MAX); - set_debug_source_node(g, node); return LLVMBuildStructGEP(g->builder, struct_ptr, gen_field_index, ""); } @@ -1348,15 +1073,14 @@ static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) { } if (want_debug_safety(g, node)) { - add_bounds_check(g, node, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); + add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); if (node->data.slice_expr.end) { LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->type_ref, array_type->data.array.len, false); - add_bounds_check(g, node, end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end); + add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end); } } - set_debug_source_node(g, node); LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, ""); LLVMValueRef indices[] = { LLVMConstNull(g->builtin_types.entry_usize->type_ref), @@ -1375,10 +1099,9 @@ static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) { LLVMValueRef end_val = gen_expr(g, node->data.slice_expr.end); if (want_debug_safety(g, node)) { - add_bounds_check(g, node, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); + add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); } - set_debug_source_node(g, node); LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, ""); LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, ""); LLVMBuildStore(g->builder, slice_start_ptr, ptr_field_ptr); @@ -1400,7 +1123,6 @@ static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) { LLVMValueRef prev_end = nullptr; if (!node->data.slice_expr.end || want_debug_safety(g, node)) { - set_debug_source_node(g, node); LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, len_index, ""); prev_end = LLVMBuildLoad(g->builder, src_len_ptr, ""); } @@ -1415,13 +1137,12 @@ static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) { if (want_debug_safety(g, node)) { assert(prev_end); - add_bounds_check(g, node, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); + add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); if (node->data.slice_expr.end) { - add_bounds_check(g, node, end_val, LLVMIntEQ, nullptr, LLVMIntULE, prev_end); + add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, prev_end); } } - set_debug_source_node(g, node); LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, ptr_index, ""); LLVMValueRef src_ptr = LLVMBuildLoad(g->builder, src_ptr_ptr, ""); LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, ptr_index, ""); @@ -1461,7 +1182,6 @@ static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node, bool is_lva if (is_lvalue || !ptr || handle_is_ptr(child_type)) { return ptr; } else { - set_debug_source_node(g, node); return LLVMBuildLoad(g->builder, ptr, ""); } } @@ -1471,7 +1191,7 @@ static LLVMValueRef gen_variable(CodeGen *g, AstNode *source_node, VariableTable return nullptr; } else { assert(variable->value_ref); - return get_handle_value(g, source_node, variable->value_ref, variable->type); + return get_handle_value(g, variable->value_ref, variable->type); } } @@ -1494,7 +1214,6 @@ static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node, bool is_lva if (is_lvalue || handle_is_ptr(type_entry)) { return ptr; } else { - set_debug_source_node(g, node); return LLVMBuildLoad(g->builder, ptr, ""); } } else if (struct_type->id == TypeTableEntryIdMetaType) { @@ -1631,7 +1350,6 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { case PrefixOpNegationWrap: { LLVMValueRef expr = gen_expr(g, expr_node); - set_debug_source_node(g, node); if (expr_type->id == TypeTableEntryIdFloat) { return LLVMBuildFNeg(g->builder, expr, ""); } else if (expr_type->id == TypeTableEntryIdInt) { @@ -1653,13 +1371,11 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { { LLVMValueRef expr = gen_expr(g, expr_node); LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(expr)); - set_debug_source_node(g, node); return LLVMBuildICmp(g->builder, LLVMIntEQ, expr, zero, ""); } case PrefixOpBinNot: { LLVMValueRef expr = gen_expr(g, expr_node); - set_debug_source_node(g, node); return LLVMBuildNot(g->builder, expr, ""); } case PrefixOpAddressOf: @@ -1677,7 +1393,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { return nullptr; } else { TypeTableEntry *child_type = expr_type->data.pointer.child_type; - return get_handle_value(g, node, expr, child_type); + return get_handle_value(g, expr, child_type); } } case PrefixOpMaybe: @@ -1698,13 +1414,11 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { if (want_debug_safety(g, node)) { LLVMValueRef err_val; if (type_has_bits(child_type)) { - set_debug_source_node(g, node); LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 0, ""); err_val = LLVMBuildLoad(g->builder, err_val_ptr, ""); } else { err_val = expr_val; } - set_debug_source_node(g, node); LLVMValueRef zero = LLVMConstNull(g->err_tag_type->type_ref); LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, ""); LLVMBasicBlockRef err_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrError"); @@ -1719,7 +1433,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { if (type_has_bits(child_type)) { LLVMValueRef child_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 1, ""); - return get_handle_value(g, expr_node, child_val_ptr, child_type); + return get_handle_value(g, child_val_ptr, child_type); } else { return nullptr; } @@ -1733,7 +1447,6 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { TypeTableEntry *child_type = expr_type->data.maybe.child_type; if (want_debug_safety(g, node)) { - set_debug_source_node(g, node); LLVMValueRef cond_val; if (child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn) @@ -1761,9 +1474,8 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { { return expr_val; } else { - set_debug_source_node(g, node); LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, expr_val, 0, ""); - return get_handle_value(g, node, maybe_field_ptr, child_type); + return get_handle_value(g, maybe_field_ptr, child_type); } } } @@ -1773,7 +1485,6 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { static LLVMValueRef gen_div(CodeGen *g, AstNode *source_node, LLVMValueRef val1, LLVMValueRef val2, TypeTableEntry *type_entry, bool exact) { - set_debug_source_node(g, source_node); if (want_debug_safety(g, source_node)) { LLVMValueRef zero = LLVMConstNull(type_entry->type_ref); @@ -1846,22 +1557,18 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node, switch (bin_op) { case BinOpTypeBinOr: case BinOpTypeAssignBitOr: - set_debug_source_node(g, source_node); return LLVMBuildOr(g->builder, val1, val2, ""); case BinOpTypeBinXor: case BinOpTypeAssignBitXor: - set_debug_source_node(g, source_node); return LLVMBuildXor(g->builder, val1, val2, ""); case BinOpTypeBinAnd: case BinOpTypeAssignBitAnd: - set_debug_source_node(g, source_node); return LLVMBuildAnd(g->builder, val1, val2, ""); case BinOpTypeBitShiftLeft: case BinOpTypeBitShiftLeftWrap: case BinOpTypeAssignBitShiftLeft: case BinOpTypeAssignBitShiftLeftWrap: { - set_debug_source_node(g, source_node); assert(op1_type->id == TypeTableEntryIdInt); bool is_wrapping = (bin_op == BinOpTypeBitShiftLeftWrap) || (bin_op == BinOpTypeAssignBitShiftLeftWrap); @@ -1880,7 +1587,6 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node, assert(op1_type->id == TypeTableEntryIdInt); assert(op2_type->id == TypeTableEntryIdInt); - set_debug_source_node(g, source_node); if (op1_type->data.integral.is_signed) { return LLVMBuildAShr(g->builder, val1, val2, ""); } else { @@ -1890,7 +1596,6 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node, case BinOpTypeAddWrap: case BinOpTypeAssignPlus: case BinOpTypeAssignPlusWrap: - set_debug_source_node(g, source_node); if (op1_type->id == TypeTableEntryIdFloat) { return LLVMBuildFAdd(g->builder, val1, val2, ""); } else if (op1_type->id == TypeTableEntryIdInt) { @@ -1911,7 +1616,6 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node, case BinOpTypeSubWrap: case BinOpTypeAssignMinus: case BinOpTypeAssignMinusWrap: - set_debug_source_node(g, source_node); if (op1_type->id == TypeTableEntryIdFloat) { return LLVMBuildFSub(g->builder, val1, val2, ""); } else if (op1_type->id == TypeTableEntryIdInt) { @@ -1932,7 +1636,6 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node, case BinOpTypeMultWrap: case BinOpTypeAssignTimes: case BinOpTypeAssignTimesWrap: - set_debug_source_node(g, source_node); if (op1_type->id == TypeTableEntryIdFloat) { return LLVMBuildFMul(g->builder, val1, val2, ""); } else if (op1_type->id == TypeTableEntryIdInt) { @@ -1954,7 +1657,6 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node, return gen_div(g, source_node, val1, val2, op1_type, false); case BinOpTypeMod: case BinOpTypeAssignMod: - set_debug_source_node(g, source_node); if (op1_type->id == TypeTableEntryIdFloat) { return LLVMBuildFRem(g->builder, val1, val2, ""); } else { @@ -2044,7 +1746,6 @@ static LLVMValueRef gen_cmp_expr(CodeGen *g, AstNode *node) { TypeTableEntry *op2_type = get_expr_type(node->data.bin_op_expr.op2); assert(op1_type == op2_type); - set_debug_source_node(g, node); if (op1_type->id == TypeTableEntryIdFloat) { LLVMRealPredicate pred = cmp_op_to_real_predicate(node->data.bin_op_expr.bin_op); return LLVMBuildFCmp(g->builder, pred, val1, val2, ""); @@ -2081,18 +1782,15 @@ static LLVMValueRef gen_bool_and_expr(CodeGen *g, AstNode *node) { // block for when val1 == false (don't even evaluate the second part) LLVMBasicBlockRef false_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolAndFalse"); - set_debug_source_node(g, node); LLVMBuildCondBr(g->builder, val1, true_block, false_block); LLVMPositionBuilderAtEnd(g->builder, true_block); LLVMValueRef val2 = gen_expr(g, node->data.bin_op_expr.op2); LLVMBasicBlockRef post_val2_block = LLVMGetInsertBlock(g->builder); - set_debug_source_node(g, node); LLVMBuildBr(g->builder, false_block); LLVMPositionBuilderAtEnd(g->builder, false_block); - set_debug_source_node(g, node); LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMInt1Type(), ""); LLVMValueRef incoming_values[2] = {val1, val2}; LLVMBasicBlockRef incoming_blocks[2] = {post_val1_block, post_val2_block}; @@ -2112,7 +1810,6 @@ static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) { // block for when val1 == true (don't even evaluate the second part) LLVMBasicBlockRef true_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolOrTrue"); - set_debug_source_node(g, expr_node); LLVMBuildCondBr(g->builder, val1, true_block, false_block); LLVMPositionBuilderAtEnd(g->builder, false_block); @@ -2120,11 +1817,9 @@ static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) { LLVMBasicBlockRef post_val2_block = LLVMGetInsertBlock(g->builder); - set_debug_source_node(g, expr_node); LLVMBuildBr(g->builder, true_block); LLVMPositionBuilderAtEnd(g->builder, true_block); - set_debug_source_node(g, expr_node); LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMInt1Type(), ""); LLVMValueRef incoming_values[2] = {val1, val2}; LLVMBasicBlockRef incoming_blocks[2] = {post_val1_block, post_val2_block}; @@ -2133,14 +1828,13 @@ static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) { return phi; } -static LLVMValueRef gen_struct_memcpy(CodeGen *g, AstNode *source_node, LLVMValueRef src, LLVMValueRef dest, +static LLVMValueRef gen_struct_memcpy(CodeGen *g, LLVMValueRef src, LLVMValueRef dest, TypeTableEntry *type_entry) { assert(handle_is_ptr(type_entry)); LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); - set_debug_source_node(g, source_node); LLVMValueRef src_ptr = LLVMBuildBitCast(g->builder, src, ptr_u8, ""); LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, dest, ptr_u8, ""); @@ -2172,18 +1866,16 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType b assert(op1_type == op2_type); assert(bin_op == BinOpTypeAssign); - return gen_struct_memcpy(g, source_node, value, target_ref, op1_type); + return gen_struct_memcpy(g, value, target_ref, op1_type); } if (bin_op != BinOpTypeAssign) { assert(source_node->type == NodeTypeBinOpExpr); - set_debug_source_node(g, source_node->data.bin_op_expr.op1); LLVMValueRef left_value = LLVMBuildLoad(g->builder, target_ref, ""); value = gen_arithmetic_bin_op(g, source_node, left_value, value, op1_type, op2_type, bin_op); } - set_debug_source_node(g, source_node); LLVMBuildStore(g->builder, value, target_ref); return nullptr; } @@ -2214,9 +1906,8 @@ static LLVMValueRef gen_unwrap_maybe(CodeGen *g, AstNode *node, LLVMValueRef may { return maybe_struct_ref; } else { - set_debug_source_node(g, node); LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 0, ""); - return get_handle_value(g, node, maybe_field_ptr, child_type); + return get_handle_value(g, maybe_field_ptr, child_type); } } @@ -2240,7 +1931,6 @@ static LLVMValueRef gen_unwrap_maybe_expr(CodeGen *g, AstNode *node) { cond_value = LLVMBuildICmp(g->builder, LLVMIntNE, maybe_struct_ref, LLVMConstNull(child_type->type_ref), ""); } else { - set_debug_source_node(g, node); LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 1, ""); cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, ""); } @@ -2255,21 +1945,18 @@ static LLVMValueRef gen_unwrap_maybe_expr(CodeGen *g, AstNode *node) { LLVMPositionBuilderAtEnd(g->builder, non_null_block); LLVMValueRef non_null_result = gen_unwrap_maybe(g, op1_node, maybe_struct_ref); - set_debug_source_node(g, node); LLVMBuildBr(g->builder, end_block); LLVMBasicBlockRef post_non_null_result_block = LLVMGetInsertBlock(g->builder); LLVMPositionBuilderAtEnd(g->builder, null_block); LLVMValueRef null_result = gen_expr(g, op2_node); if (null_reachable) { - set_debug_source_node(g, node); LLVMBuildBr(g->builder, end_block); } LLVMBasicBlockRef post_null_result_block = LLVMGetInsertBlock(g->builder); LLVMPositionBuilderAtEnd(g->builder, end_block); if (null_reachable) { - set_debug_source_node(g, node); LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(non_null_result), ""); LLVMValueRef incoming_values[2] = {non_null_result, null_result}; LLVMBasicBlockRef incoming_blocks[2] = {post_non_null_result_block, post_null_result_block}; @@ -2351,7 +2038,6 @@ static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) { assert(expr_type->id == TypeTableEntryIdErrorUnion); TypeTableEntry *child_type = expr_type->data.error.child_type; LLVMValueRef err_val; - set_debug_source_node(g, node); if (handle_is_ptr(expr_type)) { LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 0, ""); err_val = LLVMBuildLoad(g->builder, err_val_ptr, ""); @@ -2377,7 +2063,6 @@ static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) { LLVMBuildStore(g->builder, err_val, var->value_ref); } LLVMValueRef err_result = gen_expr(g, op2); - set_debug_source_node(g, node); if (have_end_block) { LLVMBuildBr(g->builder, end_block); } else if (err_reachable) { @@ -2389,7 +2074,7 @@ static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) { return nullptr; } LLVMValueRef child_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 1, ""); - LLVMValueRef child_val = get_handle_value(g, node, child_val_ptr, child_type); + LLVMValueRef child_val = get_handle_value(g, child_val_ptr, child_type); if (!have_end_block) { return child_val; @@ -2452,17 +2137,14 @@ static LLVMValueRef gen_return(CodeGen *g, AstNode *source_node, LLVMValueRef va bool is_extern = g->cur_fn->type_entry->data.fn.fn_type_id.is_extern; if (handle_is_ptr(return_type)) { if (is_extern) { - set_debug_source_node(g, source_node); LLVMValueRef by_val_value = LLVMBuildLoad(g->builder, value, ""); LLVMBuildRet(g->builder, by_val_value); } else { assert(g->cur_ret_ptr); gen_assign_raw(g, source_node, BinOpTypeAssign, g->cur_ret_ptr, value, return_type, return_type); - set_debug_source_node(g, source_node); LLVMBuildRetVoid(g->builder); } } else { - set_debug_source_node(g, source_node); LLVMBuildRet(g->builder, value); } return nullptr; @@ -2504,7 +2186,6 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) { LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ErrRetReturn"); LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ErrRetContinue"); - set_debug_source_node(g, node); LLVMValueRef err_val; if (type_has_bits(child_type)) { LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, value, 0, ""); @@ -2524,7 +2205,6 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) { if (type_has_bits(return_type->data.error.child_type)) { assert(g->cur_ret_ptr); - set_debug_source_node(g, node); LLVMValueRef tag_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, 0, ""); LLVMBuildStore(g->builder, err_val, tag_ptr); LLVMBuildRetVoid(g->builder); @@ -2537,9 +2217,8 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) { LLVMPositionBuilderAtEnd(g->builder, continue_block); if (type_has_bits(child_type)) { - set_debug_source_node(g, node); LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, value, 1, ""); - return get_handle_value(g, node, val_ptr, child_type); + return get_handle_value(g, val_ptr, child_type); } else { return nullptr; } @@ -2552,7 +2231,6 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) { LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeRetReturn"); LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeRetContinue"); - set_debug_source_node(g, node); LLVMValueRef maybe_val_ptr = LLVMBuildStructGEP(g->builder, value, 1, ""); LLVMValueRef is_non_null = LLVMBuildLoad(g->builder, maybe_val_ptr, ""); @@ -2566,7 +2244,6 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) { if (handle_is_ptr(return_type)) { assert(g->cur_ret_ptr); - set_debug_source_node(g, node); LLVMValueRef maybe_bit_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, 1, ""); LLVMBuildStore(g->builder, zero, maybe_bit_ptr); LLVMBuildRetVoid(g->builder); @@ -2577,9 +2254,8 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) { LLVMPositionBuilderAtEnd(g->builder, continue_block); if (type_has_bits(child_type)) { - set_debug_source_node(g, node); LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, value, 0, ""); - return get_handle_value(g, node, val_ptr, child_type); + return get_handle_value(g, val_ptr, child_type); } else { return nullptr; } @@ -2610,7 +2286,6 @@ static LLVMValueRef gen_if_bool_expr_raw(CodeGen *g, AstNode *source_node, LLVMV endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf"); } - set_debug_source_node(g, source_node); LLVMBuildCondBr(g->builder, cond_value, then_block, else_block); LLVMPositionBuilderAtEnd(g->builder, then_block); @@ -2632,7 +2307,6 @@ static LLVMValueRef gen_if_bool_expr_raw(CodeGen *g, AstNode *source_node, LLVMV if (then_endif_reachable || else_endif_reachable) { LLVMPositionBuilderAtEnd(g->builder, endif_block); if (use_then_value && use_else_value) { - set_debug_source_node(g, source_node); LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(then_expr_result), ""); LLVMValueRef incoming_values[2] = {then_expr_result, else_expr_result}; LLVMBasicBlockRef incoming_blocks[2] = {after_then_block, after_else_block}; @@ -2697,7 +2371,7 @@ static LLVMValueRef gen_if_var_then_block(CodeGen *g, AstNode *node, VariableTab payload_val = init_val; } else { LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, init_val, 0, ""); - payload_val = get_handle_value(g, node, payload_ptr, child_type); + payload_val = get_handle_value(g, payload_ptr, child_type); } gen_assign_raw(g, node, BinOpTypeAssign, variable->value_ref, payload_val, variable->type, child_type); @@ -2736,10 +2410,8 @@ static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) { LLVMValueRef cond_value; if (maybe_is_ptr) { - set_debug_source_node(g, node); cond_value = LLVMBuildICmp(g->builder, LLVMIntNE, init_val, LLVMConstNull(child_type->type_ref), ""); } else { - set_debug_source_node(g, node); LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, init_val, 1, ""); cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, ""); } @@ -2760,7 +2432,6 @@ static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) { endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeEndIf"); } - set_debug_source_node(g, node); LLVMBuildCondBr(g->builder, cond_value, then_block, else_block); LLVMPositionBuilderAtEnd(g->builder, then_block); @@ -2810,7 +2481,7 @@ static LLVMValueRef ir_render_load_var(CodeGen *g, IrExecutable *executable, return nullptr; assert(var->value_ref); - return get_handle_value(g, load_var_instruction->base.source_node, var->value_ref, var->type); + return get_handle_value(g, var->value_ref, var->type); } static LLVMValueRef ir_render_bin_op_bool(CodeGen *g, IrExecutable *executable, @@ -2894,6 +2565,233 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, zig_unreachable(); } +static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, + IrInstructionCast *cast_instruction) +{ + TypeTableEntry *actual_type = cast_instruction->value->type_entry; + TypeTableEntry *wanted_type = cast_instruction->base.type_entry; + LLVMValueRef expr_val = cast_instruction->value->llvm_value; + assert(expr_val); + + switch (cast_instruction->cast_op) { + case CastOpNoCast: + zig_unreachable(); + case CastOpNoop: + return expr_val; + case CastOpErrToInt: + assert(actual_type->id == TypeTableEntryIdErrorUnion); + if (!type_has_bits(actual_type->data.error.child_type)) { + return gen_widen_or_shorten(g, cast_instruction->base.source_node, + g->err_tag_type, wanted_type, expr_val); + } else { + zig_panic("TODO"); + } + case CastOpMaybeWrap: + { + assert(cast_instruction->tmp_ptr); + assert(wanted_type->id == TypeTableEntryIdMaybe); + assert(actual_type); + + TypeTableEntry *child_type = wanted_type->data.maybe.child_type; + + if (child_type->id == TypeTableEntryIdPointer || + child_type->id == TypeTableEntryIdFn) + { + return expr_val; + } else { + LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, 0, ""); + gen_assign_raw(g, cast_instruction->base.source_node, BinOpTypeAssign, + val_ptr, expr_val, child_type, actual_type); + + LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, 1, ""); + LLVMBuildStore(g->builder, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr); + } + + return cast_instruction->tmp_ptr; + } + case CastOpNullToMaybe: + // handled by constant expression evaluator + zig_unreachable(); + case CastOpErrorWrap: + { + assert(wanted_type->id == TypeTableEntryIdErrorUnion); + TypeTableEntry *child_type = wanted_type->data.error.child_type; + LLVMValueRef ok_err_val = LLVMConstNull(g->err_tag_type->type_ref); + + if (!type_has_bits(child_type)) { + return ok_err_val; + } else { + assert(cast_instruction->tmp_ptr); + assert(wanted_type->id == TypeTableEntryIdErrorUnion); + assert(actual_type); + + LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, 0, ""); + LLVMBuildStore(g->builder, ok_err_val, err_tag_ptr); + + LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, 1, ""); + gen_assign_raw(g, cast_instruction->base.source_node, BinOpTypeAssign, + payload_ptr, expr_val, child_type, actual_type); + + return cast_instruction->tmp_ptr; + } + } + case CastOpPureErrorWrap: + assert(wanted_type->id == TypeTableEntryIdErrorUnion); + + if (!type_has_bits(wanted_type->data.error.child_type)) { + return expr_val; + } else { + zig_panic("TODO"); + } + case CastOpPtrToInt: + return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, ""); + case CastOpIntToPtr: + return LLVMBuildIntToPtr(g->builder, expr_val, wanted_type->type_ref, ""); + case CastOpPointerReinterpret: + return LLVMBuildBitCast(g->builder, expr_val, wanted_type->type_ref, ""); + case CastOpWidenOrShorten: + return gen_widen_or_shorten(g, cast_instruction->base.source_node, actual_type, wanted_type, expr_val); + case CastOpToUnknownSizeArray: + { + assert(cast_instruction->tmp_ptr); + assert(wanted_type->id == TypeTableEntryIdStruct); + assert(wanted_type->data.structure.is_slice); + + TypeTableEntry *pointer_type = wanted_type->data.structure.fields[0].type_entry; + + + size_t ptr_index = wanted_type->data.structure.fields[0].gen_index; + if (ptr_index != SIZE_MAX) { + LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, ptr_index, ""); + LLVMValueRef expr_bitcast = LLVMBuildBitCast(g->builder, expr_val, pointer_type->type_ref, ""); + LLVMBuildStore(g->builder, expr_bitcast, ptr_ptr); + } + + size_t len_index = wanted_type->data.structure.fields[1].gen_index; + LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, len_index, ""); + LLVMValueRef len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, + actual_type->data.array.len, false); + LLVMBuildStore(g->builder, len_val, len_ptr); + + return cast_instruction->tmp_ptr; + } + case CastOpResizeSlice: + { + assert(cast_instruction->tmp_ptr); + assert(wanted_type->id == TypeTableEntryIdStruct); + assert(wanted_type->data.structure.is_slice); + assert(actual_type->id == TypeTableEntryIdStruct); + assert(actual_type->data.structure.is_slice); + + TypeTableEntry *actual_pointer_type = actual_type->data.structure.fields[0].type_entry; + TypeTableEntry *actual_child_type = actual_pointer_type->data.pointer.child_type; + TypeTableEntry *wanted_pointer_type = wanted_type->data.structure.fields[0].type_entry; + TypeTableEntry *wanted_child_type = wanted_pointer_type->data.pointer.child_type; + + + size_t actual_ptr_index = actual_type->data.structure.fields[0].gen_index; + size_t actual_len_index = actual_type->data.structure.fields[1].gen_index; + size_t wanted_ptr_index = wanted_type->data.structure.fields[0].gen_index; + size_t wanted_len_index = wanted_type->data.structure.fields[1].gen_index; + + LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, expr_val, actual_ptr_index, ""); + LLVMValueRef src_ptr = LLVMBuildLoad(g->builder, src_ptr_ptr, ""); + LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr, + wanted_type->data.structure.fields[0].type_entry->type_ref, ""); + LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, + wanted_ptr_index, ""); + LLVMBuildStore(g->builder, src_ptr_casted, dest_ptr_ptr); + + LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, expr_val, actual_len_index, ""); + LLVMValueRef src_len = LLVMBuildLoad(g->builder, src_len_ptr, ""); + uint64_t src_size = type_size(g, actual_child_type); + uint64_t dest_size = type_size(g, wanted_child_type); + + LLVMValueRef new_len; + if (dest_size == 1) { + LLVMValueRef src_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, src_size, false); + new_len = LLVMBuildMul(g->builder, src_len, src_size_val, ""); + } else if (src_size == 1) { + LLVMValueRef dest_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, dest_size, false); + if (ir_want_debug_safety(g, &cast_instruction->base)) { + LLVMValueRef remainder_val = LLVMBuildURem(g->builder, src_len, dest_size_val, ""); + LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_usize->type_ref); + LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SliceWidenOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SliceWidenFail"); + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_debug_safety_crash(g); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + new_len = ZigLLVMBuildExactUDiv(g->builder, src_len, dest_size_val, ""); + } else { + zig_unreachable(); + } + + LLVMValueRef dest_len_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, + wanted_len_index, ""); + LLVMBuildStore(g->builder, new_len, dest_len_ptr); + + + return cast_instruction->tmp_ptr; + } + case CastOpBytesToSlice: + { + assert(cast_instruction->tmp_ptr); + assert(wanted_type->id == TypeTableEntryIdStruct); + assert(wanted_type->data.structure.is_slice); + assert(actual_type->id == TypeTableEntryIdArray); + + TypeTableEntry *wanted_pointer_type = wanted_type->data.structure.fields[0].type_entry; + TypeTableEntry *wanted_child_type = wanted_pointer_type->data.pointer.child_type; + + + size_t wanted_ptr_index = wanted_type->data.structure.fields[0].gen_index; + LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, wanted_ptr_index, ""); + LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, expr_val, wanted_pointer_type->type_ref, ""); + LLVMBuildStore(g->builder, src_ptr_casted, dest_ptr_ptr); + + size_t wanted_len_index = wanted_type->data.structure.fields[1].gen_index; + LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, wanted_len_index, ""); + LLVMValueRef len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, + actual_type->data.array.len / type_size(g, wanted_child_type), false); + LLVMBuildStore(g->builder, len_val, len_ptr); + + return cast_instruction->tmp_ptr; + } + case CastOpIntToFloat: + assert(actual_type->id == TypeTableEntryIdInt); + if (actual_type->data.integral.is_signed) { + return LLVMBuildSIToFP(g->builder, expr_val, wanted_type->type_ref, ""); + } else { + return LLVMBuildUIToFP(g->builder, expr_val, wanted_type->type_ref, ""); + } + case CastOpFloatToInt: + assert(wanted_type->id == TypeTableEntryIdInt); + if (wanted_type->data.integral.is_signed) { + return LLVMBuildFPToSI(g->builder, expr_val, wanted_type->type_ref, ""); + } else { + return LLVMBuildFPToUI(g->builder, expr_val, wanted_type->type_ref, ""); + } + + case CastOpBoolToInt: + assert(wanted_type->id == TypeTableEntryIdInt); + assert(actual_type->id == TypeTableEntryIdBool); + return LLVMBuildZExt(g->builder, expr_val, wanted_type->type_ref, ""); + + case CastOpIntToEnum: + return gen_widen_or_shorten(g, cast_instruction->base.source_node, + actual_type, wanted_type->data.enumeration.tag_type, expr_val); + case CastOpEnumToInt: + return gen_widen_or_shorten(g, cast_instruction->base.source_node, + actual_type->data.enumeration.tag_type, wanted_type, expr_val); + } + zig_unreachable(); +} + static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, IrInstruction *instruction) { set_debug_source_node(g, instruction->source_node); switch (instruction->id) { @@ -2907,13 +2805,14 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_load_var(g, executable, (IrInstructionLoadVar *)instruction); case IrInstructionIdBinOp: return ir_render_bin_op(g, executable, (IrInstructionBinOp *)instruction); + case IrInstructionIdCast: + return ir_render_cast(g, executable, (IrInstructionCast *)instruction); case IrInstructionIdCondBr: case IrInstructionIdSwitchBr: case IrInstructionIdPhi: case IrInstructionIdStoreVar: case IrInstructionIdCall: case IrInstructionIdBuiltinCall: - case IrInstructionIdCast: zig_panic("TODO render more IR instructions to LLVM"); } zig_unreachable(); @@ -3592,7 +3491,7 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { 1, ""); LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, LLVMPointerType(enum_field->type_entry->type_ref, 0), ""); - LLVMValueRef handle_val = get_handle_value(g, var_node, bitcasted_union_field_ptr, + LLVMValueRef handle_val = get_handle_value(g, bitcasted_union_field_ptr, enum_field->type_entry); gen_assign_raw(g, var_node, BinOpTypeAssign, @@ -3602,8 +3501,7 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { // variable is the payload LLVMValueRef err_payload_ptr = LLVMBuildStructGEP(g->builder, target_value_handle, 1, ""); - LLVMValueRef handle_val = get_handle_value(g, var_node, - err_payload_ptr, prong_var->type); + LLVMValueRef handle_val = get_handle_value(g, err_payload_ptr, prong_var->type); gen_assign_raw(g, var_node, BinOpTypeAssign, prong_var->value_ref, handle_val, prong_var->type, prong_var->type); } else { @@ -4375,10 +4273,8 @@ static void do_code_gen(CodeGen *g) { // allocate structs which are the result of casts for (size_t cea_i = 0; cea_i < fn_table_entry->cast_alloca_list.length; cea_i += 1) { - AstNode *fn_call_node = fn_table_entry->cast_alloca_list.at(cea_i); - Expr *expr = &fn_call_node->data.fn_call_expr.resolved_expr; - fn_call_node->data.fn_call_expr.tmp_ptr = LLVMBuildAlloca(g->builder, - expr->type_entry->type_ref, ""); + IrInstructionCast *cast_instruction = fn_table_entry->cast_alloca_list.at(cea_i); + cast_instruction->tmp_ptr = LLVMBuildAlloca(g->builder, cast_instruction->base.type_entry->type_ref, ""); } // allocate structs which are struct value expressions diff --git a/src/eval.cpp b/src/eval.cpp index b74fb38af9..414bdfffb5 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -773,6 +773,7 @@ void eval_const_expr_implicit_cast(CastOp cast_op, case CastOpEnumToInt: bignum_init_unsigned(&const_val->data.x_bignum, other_val->data.x_enum.tag); const_val->ok = true; + break; } } diff --git a/src/ir.cpp b/src/ir.cpp index 184522e4aa..ffde57a6de 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1,6 +1,7 @@ #include "analyze.hpp" -#include "ir.hpp" #include "error.hpp" +#include "eval.hpp" +#include "ir.hpp" struct IrVarSlot { ConstExprValue value; @@ -100,12 +101,12 @@ static T *ir_build_instruction(IrBuilder *irb, AstNode *source_node) { } static IrInstruction *ir_build_cast(IrBuilder *irb, AstNode *source_node, IrInstruction *dest_type, - IrInstruction *value, bool is_implicit) + IrInstruction *value, CastOp cast_op) { IrInstructionCast *cast_instruction = ir_build_instruction<IrInstructionCast>(irb, source_node); cast_instruction->dest_type = dest_type; cast_instruction->value = value; - cast_instruction->is_implicit = is_implicit; + cast_instruction->cast_op = cast_op; return &cast_instruction->base; } @@ -117,6 +118,13 @@ static IrInstruction *ir_build_return(IrBuilder *irb, AstNode *source_node, IrIn return &return_instruction->base; } +static IrInstruction *ir_build_const(IrBuilder *irb, AstNode *source_node, TypeTableEntry *type_entry) { + IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, source_node); + const_instruction->base.type_entry = type_entry; + const_instruction->base.static_value.ok = true; + return &const_instruction->base; +} + static IrInstruction *ir_build_const_void(IrBuilder *irb, AstNode *source_node) { IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, source_node); const_instruction->base.type_entry = irb->codegen->builtin_types.entry_void; @@ -133,14 +141,20 @@ static IrInstruction *ir_build_const_bignum(IrBuilder *irb, AstNode *source_node return &const_instruction->base; } -static IrInstruction *ir_build_const_type(IrBuilder *irb, AstNode *source_node, TypeTableEntry *type_entry) { - IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, source_node); +static IrInstruction *ir_create_const_type(IrBuilder *irb, AstNode *source_node, TypeTableEntry *type_entry) { + IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(irb->exec, source_node); const_instruction->base.type_entry = irb->codegen->builtin_types.entry_type; const_instruction->base.static_value.ok = true; const_instruction->base.static_value.data.x_type = type_entry; return &const_instruction->base; } +static IrInstruction *ir_build_const_type(IrBuilder *irb, AstNode *source_node, TypeTableEntry *type_entry) { + IrInstruction *instruction = ir_create_const_type(irb, source_node, type_entry); + ir_instruction_append(irb->current_basic_block, instruction); + return instruction; +} + static IrInstruction *ir_build_const_fn(IrBuilder *irb, AstNode *source_node, FnTableEntry *fn_entry) { IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, source_node); const_instruction->base.type_entry = fn_entry->type_entry; @@ -174,6 +188,16 @@ static IrInstruction *ir_build_load_var(IrBuilder *irb, AstNode *source_node, Va return &load_var_instruction->base; } +static IrInstruction *ir_build_call(IrBuilder *irb, AstNode *source_node, + IrInstruction *fn, size_t arg_count, IrInstruction **args) +{ + IrInstructionCall *call_instruction = ir_build_instruction<IrInstructionCall>(irb, source_node); + call_instruction->fn = fn; + call_instruction->arg_count = arg_count; + call_instruction->args = args; + return &call_instruction->base; +} + //static size_t get_conditional_defer_count(BlockContext *inner_block, BlockContext *outer_block) { // size_t result = 0; @@ -409,6 +433,26 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, AstNode *node, bool pointer_ return irb->codegen->invalid_instruction; } +static IrInstruction *ir_gen_fn_call(IrBuilder *irb, AstNode *node) { + assert(node->type == NodeTypeFnCallExpr); + + if (node->data.fn_call_expr.is_builtin) { + zig_panic("TODO ir gen builtin fn"); + } + + AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr; + IrInstruction *fn = ir_gen_node(irb, fn_ref_node, node->block_context); + + size_t arg_count = node->data.fn_call_expr.params.length; + IrInstruction **args = allocate<IrInstruction*>(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, node->block_context); + } + + return ir_build_call(irb, node, fn, arg_count, args); +} + static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockContext *block_context, bool pointer_only) { @@ -423,12 +467,13 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockCont return ir_gen_num_lit(irb, node); case NodeTypeSymbol: return ir_gen_symbol(irb, node, pointer_only); + case NodeTypeFnCallExpr: + return ir_gen_fn_call(irb, node); case NodeTypeUnwrapErrorExpr: case NodeTypeReturnExpr: case NodeTypeDefer: case NodeTypeVariableDeclaration: case NodeTypePrefixOpExpr: - case NodeTypeFnCallExpr: case NodeTypeArrayAccessExpr: case NodeTypeSliceExpr: case NodeTypeFieldAccessExpr: @@ -782,6 +827,296 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, IrInstruction *pare return ir_determine_peer_types(ira, parent_instruction, instructions, instruction_count); } +static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, + IrInstruction *dest_type, CastOp cast_op, bool need_alloca) +{ + assert(dest_type->type_entry->id == TypeTableEntryIdMetaType); + assert(dest_type->static_value.ok); + TypeTableEntry *wanted_type = dest_type->static_value.data.x_type; + + if (value->static_value.ok) { + IrInstruction *result = ir_build_const(&ira->new_irb, source_instr->source_node, wanted_type); + 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->source_node, + dest_type->other, value->other, cast_op); + result->type_entry = wanted_type; + if (need_alloca && source_instr->source_node->block_context->fn_entry) { + IrInstructionCast *cast_instruction = (IrInstructionCast *)result; + source_instr->source_node->block_context->fn_entry->cast_alloca_list.append(cast_instruction); + } + return result; + } +} + +static bool is_slice(TypeTableEntry *type) { + return type->id == TypeTableEntryIdStruct && type->data.structure.is_slice; +} + +static bool is_u8(TypeTableEntry *type) { + return type->id == TypeTableEntryIdInt && + !type->data.integral.is_signed && type->data.integral.bit_count == 8; +} + +static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *dest_type, IrInstruction *value) +{ + assert(dest_type->type_entry->id == TypeTableEntryIdMetaType); + assert(dest_type->static_value.ok); + + TypeTableEntry *wanted_type = dest_type->static_value.data.x_type; + 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, dest_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, dest_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, dest_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, dest_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, dest_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, dest_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, dest_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, dest_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)) + { + mark_impure_fn(ira->codegen, source_instr->source_node->block_context, source_instr->source_node); + return ir_resolve_cast(ira, source_instr, value, dest_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)) + { + mark_impure_fn(ira->codegen, source_instr->source_node->block_context, source_instr->source_node); + 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, dest_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, dest_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, dest_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, dest_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, dest_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, dest_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, dest_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, dest_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, dest_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, dest_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, dest_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, dest_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, dest_type, CastOpEnumToInt, 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_get_casted_value(IrAnalyze *ira, IrInstruction *value, TypeTableEntry *expected_type) { assert(value); assert(value != ira->old_irb.codegen->invalid_instruction); @@ -806,10 +1141,8 @@ static IrInstruction *ir_get_casted_value(IrAnalyze *ira, IrInstruction *value, case ImplicitCastMatchResultYes: { - IrInstruction *dest_type = ir_build_const_type(&ira->new_irb, value->source_node, expected_type); - bool is_implicit = true; - IrInstruction *cast_instruction = ir_build_cast(&ira->new_irb, value->source_node, dest_type, - value, is_implicit); + IrInstruction *dest_type = ir_create_const_type(&ira->new_irb, value->source_node, expected_type); + IrInstruction *cast_instruction = ir_analyze_cast(ira, value, dest_type, value); return cast_instruction; } case ImplicitCastMatchResultReportedError: @@ -849,18 +1182,41 @@ static TypeTableEntry *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstructionBinOp IrInstruction *op1 = bin_op_instruction->op1; IrInstruction *op2 = bin_op_instruction->op2; - IrInstruction *casted_op1 = ir_get_casted_value(ira, op1->other, ira->old_irb.codegen->builtin_types.entry_bool); + TypeTableEntry *bool_type = ira->old_irb.codegen->builtin_types.entry_bool; + + IrInstruction *casted_op1 = ir_get_casted_value(ira, op1->other, bool_type); if (casted_op1 == ira->old_irb.codegen->invalid_instruction) return ira->old_irb.codegen->builtin_types.entry_invalid; - IrInstruction *casted_op2 = ir_get_casted_value(ira, op2->other, ira->old_irb.codegen->builtin_types.entry_bool); + IrInstruction *casted_op2 = ir_get_casted_value(ira, op2->other, bool_type); if (casted_op2 == ira->old_irb.codegen->invalid_instruction) return ira->old_irb.codegen->builtin_types.entry_invalid; + ConstExprValue *op1_val = &casted_op1->static_value; + ConstExprValue *op2_val = &casted_op2->static_value; + if (op1_val->ok && op2_val->ok) { + ConstExprValue *out_val = &bin_op_instruction->base.static_value; + bin_op_instruction->base.other = &bin_op_instruction->base; + + assert(op1->type_entry->id == TypeTableEntryIdBool); + assert(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(); + } + out_val->ok = true; + out_val->depends_on_compile_var = op1_val->depends_on_compile_var || + op2_val->depends_on_compile_var; + return bool_type; + } + ir_link_new(ir_build_bin_op(&ira->new_irb, bin_op_instruction->base.source_node, bin_op_instruction->op_id, op1->other, op2->other), &bin_op_instruction->base); - return ira->old_irb.codegen->builtin_types.entry_bool; + return bool_type; } static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { @@ -925,12 +1281,109 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp zig_unreachable(); } + zig_panic("TODO interpret bin_op_cmp"); + ir_link_new(ir_build_bin_op(&ira->new_irb, bin_op_instruction->base.source_node, op_id, op1->other, op2->other), &bin_op_instruction->base); return ira->old_irb.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->ok = true; + 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; IrInstruction *op2 = bin_op_instruction->op2; @@ -955,12 +1408,39 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp // float } else { AstNode *source_node = bin_op_instruction->base.source_node; - add_node_error(ira->old_irb.codegen, source_node, buf_sprintf("invalid operands to binary expression: '%s' and '%s'", + add_node_error(ira->old_irb.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->old_irb.codegen->builtin_types.entry_invalid; } + if (op1->static_value.ok && op2->static_value.ok) { + ConstExprValue *op1_val = &op1->static_value; + ConstExprValue *op2_val = &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_link_new(ir_build_bin_op(&ira->new_irb, bin_op_instruction->base.source_node, op_id, op1->other, op2->other), &bin_op_instruction->base); @@ -1011,6 +1491,40 @@ static TypeTableEntry *ir_analyze_instruction_load_var(IrAnalyze *ira, IrInstruc return load_var_instruction->var->type; } +static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionCall *call_instruction) { + IrInstruction *fn_ref = call_instruction->fn->other; + if (fn_ref->type_entry->id == TypeTableEntryIdInvalid) + return ira->codegen->builtin_types.entry_invalid; + + if (fn_ref->static_value.ok) { + if (fn_ref->type_entry->id == TypeTableEntryIdMetaType) { + 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]; + IrInstruction *cast_instruction = ir_analyze_cast(ira, &call_instruction->base, fn_ref, arg); + if (cast_instruction == ira->codegen->invalid_instruction) + return ira->codegen->builtin_types.entry_invalid; + + ir_link_new(cast_instruction, &call_instruction->base); + return cast_instruction->type_entry; + } else { + zig_panic("TODO analyze more fn call types"); + } + } else { + //ir_link_new(ir_build_call(&ira->new_irb, call_instruction->base.source_node, + // call_instruction->fn, call_instruction->arg_count, call_instruction->args), + // &call_instruction->base); + + zig_panic("TODO analyze fn call"); + } +} + static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -1023,11 +1537,12 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_bin_op(ira, (IrInstructionBinOp *)instruction); case IrInstructionIdLoadVar: return ir_analyze_instruction_load_var(ira, (IrInstructionLoadVar *)instruction); + case IrInstructionIdCall: + return ir_analyze_instruction_call(ira, (IrInstructionCall *)instruction); case IrInstructionIdCondBr: case IrInstructionIdSwitchBr: case IrInstructionIdPhi: case IrInstructionIdStoreVar: - case IrInstructionIdCall: case IrInstructionIdBuiltinCall: case IrInstructionIdCast: zig_panic("TODO analyze more instructions"); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 75ebf27eb6..14edaf8b50 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -20,7 +20,7 @@ static void ir_print_prefix(IrPrint *irp, IrInstruction *instruction) { static void ir_print_return(IrPrint *irp, IrInstructionReturn *return_instruction) { ir_print_prefix(irp, &return_instruction->base); assert(return_instruction->value); - fprintf(irp->f, "return #%zu;\n", return_instruction->value->debug_id); + fprintf(irp->f, "return #%zu\n", return_instruction->value->debug_id); } static void ir_print_const(IrPrint *irp, IrInstructionConst *const_instruction) { @@ -43,8 +43,10 @@ static void ir_print_const(IrPrint *irp, IrInstructionConst *const_instruction) fprintf(irp->f, "%s%llu\n", negative_str, bignum->data.x_uint); break; } - case TypeTableEntryIdVar: case TypeTableEntryIdMetaType: + fprintf(irp->f, "%s\n", buf_ptr(&const_instruction->base.static_value.data.x_type->name)); + break; + case TypeTableEntryIdVar: case TypeTableEntryIdBool: case TypeTableEntryIdUnreachable: case TypeTableEntryIdInt: @@ -139,6 +141,25 @@ static void ir_print_load_var(IrPrint *irp, IrInstructionLoadVar *load_var_instr buf_ptr(&load_var_instruction->var->name)); } +static void ir_print_cast(IrPrint *irp, IrInstructionCast *cast_instruction) { + ir_print_prefix(irp, &cast_instruction->base); + fprintf(irp->f, "cast #%zu to #%zu\n", + cast_instruction->value->debug_id, + cast_instruction->dest_type->debug_id); +} + +static void ir_print_call(IrPrint *irp, IrInstructionCall *call_instruction) { + ir_print_prefix(irp, &call_instruction->base); + fprintf(irp->f, "#%zu(", call_instruction->fn->debug_id); + for (size_t i = 0; i < call_instruction->arg_count; i += 1) { + IrInstruction *arg = call_instruction->args[i]; + if (i != 0) + fprintf(irp->f, ", "); + fprintf(irp->f, "#%zu", arg->debug_id); + } + fprintf(irp->f, ")\n"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -155,13 +176,17 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdLoadVar: ir_print_load_var(irp, (IrInstructionLoadVar *)instruction); break; + case IrInstructionIdCast: + ir_print_cast(irp, (IrInstructionCast *)instruction); + break; + case IrInstructionIdCall: + ir_print_call(irp, (IrInstructionCall *)instruction); + break; case IrInstructionIdCondBr: case IrInstructionIdSwitchBr: case IrInstructionIdPhi: case IrInstructionIdStoreVar: - case IrInstructionIdCall: case IrInstructionIdBuiltinCall: - case IrInstructionIdCast: zig_panic("TODO print more IR instructions"); } } |
