aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/all_types.hpp1
-rw-r--r--src/analyze.cpp40
-rw-r--r--src/codegen.cpp54
-rw-r--r--src/eval.cpp1
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: