diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/all_types.hpp | 1 | ||||
| -rw-r--r-- | src/analyze.cpp | 40 | ||||
| -rw-r--r-- | src/codegen.cpp | 54 | ||||
| -rw-r--r-- | src/eval.cpp | 1 |
4 files changed, 95 insertions, 1 deletions
diff --git a/src/all_types.hpp b/src/all_types.hpp index daf37e5956..f6342cb8a0 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1129,6 +1129,7 @@ enum BuiltinFnId { BuiltinFnIdCmpExchange, BuiltinFnIdFence, BuiltinFnIdDivExact, + BuiltinFnIdTruncate, }; struct BuiltinFnEntry { diff --git a/src/analyze.cpp b/src/analyze.cpp index 133cbf47fa..233bc4e7b2 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4686,6 +4686,44 @@ static TypeTableEntry *analyze_div_exact(CodeGen *g, ImportTableEntry *import, } } +static TypeTableEntry *analyze_truncate(CodeGen *g, ImportTableEntry *import, + BlockContext *context, AstNode *node) +{ + assert(node->type == NodeTypeFnCallExpr); + + AstNode **op1 = &node->data.fn_call_expr.params.at(0); + AstNode **op2 = &node->data.fn_call_expr.params.at(1); + + TypeTableEntry *dest_type = analyze_type_expr(g, import, context, *op1); + TypeTableEntry *src_type = analyze_expression(g, import, context, nullptr, *op2); + + if (dest_type->id == TypeTableEntryIdInvalid || src_type->id == TypeTableEntryIdInvalid) { + return g->builtin_types.entry_invalid; + } else if (dest_type->id != TypeTableEntryIdInt) { + add_node_error(g, *op1, + buf_sprintf("expected integer type, got '%s'", buf_ptr(&dest_type->name))); + return g->builtin_types.entry_invalid; + } else if (src_type->id != TypeTableEntryIdInt) { + add_node_error(g, *op2, + buf_sprintf("expected integer type, got '%s'", buf_ptr(&src_type->name))); + return g->builtin_types.entry_invalid; + } else if (src_type->data.integral.is_signed != dest_type->data.integral.is_signed) { + const char *sign_str = dest_type->data.integral.is_signed ? "signed" : "unsigned"; + add_node_error(g, *op2, + buf_sprintf("expected %s integer type, got '%s'", sign_str, buf_ptr(&src_type->name))); + return g->builtin_types.entry_invalid; + } else if (src_type->data.integral.bit_count <= dest_type->data.integral.bit_count) { + add_node_error(g, *op2, + buf_sprintf("type '%s' has same or fewer bits than destination type '%s'", + buf_ptr(&src_type->name), buf_ptr(&dest_type->name))); + return g->builtin_types.entry_invalid; + } + + // TODO const expr eval + + return dest_type; +} + static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { @@ -5028,6 +5066,8 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry return analyze_fence(g, import, context, node); case BuiltinFnIdDivExact: return analyze_div_exact(g, import, context, node); + case BuiltinFnIdTruncate: + return analyze_truncate(g, import, context, node); } zig_unreachable(); } diff --git a/src/codegen.cpp b/src/codegen.cpp index 8163cf9fc9..13f0c9eee0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -473,6 +473,18 @@ static LLVMValueRef gen_div_exact(CodeGen *g, AstNode *node) { return gen_div(g, node, op1_val, op2_val, get_expr_type(op1_node), true); } +static LLVMValueRef gen_truncate(CodeGen *g, AstNode *node) { + assert(node->type == NodeTypeFnCallExpr); + + TypeTableEntry *dest_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0)); + AstNode *src_node = node->data.fn_call_expr.params.at(1); + + 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_shl_with_overflow(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); @@ -661,6 +673,8 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { return gen_fence(g, node); case BuiltinFnIdDivExact: return gen_div_exact(g, node); + case BuiltinFnIdTruncate: + return gen_truncate(g, node); } zig_unreachable(); } @@ -729,6 +743,24 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeT zig_unreachable(); } + if (actual_bits >= wanted_bits && actual_type->id == TypeTableEntryIdInt && + !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, ""); + + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SignCastOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SignCastFail"); + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_debug_safety_crash(g); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + if (actual_bits == wanted_bits) { return expr_val; } else if (actual_bits < wanted_bits) { @@ -752,7 +784,26 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeT return LLVMBuildFPTrunc(g->builder, expr_val, wanted_type->type_ref, ""); } else if (actual_type->id == TypeTableEntryIdInt) { set_debug_source_node(g, source_node); - return LLVMBuildTrunc(g->builder, expr_val, wanted_type->type_ref, ""); + LLVMValueRef trunc_val = LLVMBuildTrunc(g->builder, expr_val, wanted_type->type_ref, ""); + if (!want_debug_safety(g, source_node)) { + return trunc_val; + } + LLVMValueRef orig_val; + if (actual_type->data.integral.is_signed) { + orig_val = LLVMBuildSExt(g->builder, trunc_val, actual_type->type_ref, ""); + } else { + orig_val = LLVMBuildZExt(g->builder, trunc_val, actual_type->type_ref, ""); + } + LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, orig_val, ""); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "CastShortenOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "CastShortenFail"); + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_debug_safety_crash(g); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + return trunc_val; } else { zig_unreachable(); } @@ -4568,6 +4619,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn_with_arg_count(g, BuiltinFnIdCmpExchange, "cmpxchg", 5); create_builtin_fn_with_arg_count(g, BuiltinFnIdFence, "fence", 1); create_builtin_fn_with_arg_count(g, BuiltinFnIdDivExact, "div_exact", 2); + create_builtin_fn_with_arg_count(g, BuiltinFnIdTruncate, "truncate", 2); } static void init(CodeGen *g, Buf *source_path) { diff --git a/src/eval.cpp b/src/eval.cpp index b31cc47daa..4499b8529f 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -827,6 +827,7 @@ static bool eval_fn_call_builtin(EvalFn *ef, AstNode *node, ConstExprValue *out_ case BuiltinFnIdErrName: case BuiltinFnIdEmbedFile: case BuiltinFnIdCmpExchange: + case BuiltinFnIdTruncate: zig_panic("TODO"); case BuiltinFnIdBreakpoint: case BuiltinFnIdInvalid: |
