overflow intrinsics take type as first argument

This commit is contained in:
Andrew Kelley 2016-01-14 17:04:35 -07:00
parent 68c4f617ed
commit 0c9afede9e
6 changed files with 183 additions and 199 deletions

View File

@ -630,6 +630,9 @@ struct TypeTableEntryPointer {
struct TypeTableEntryInt { struct TypeTableEntryInt {
bool is_signed; bool is_signed;
LLVMValueRef add_with_overflow_fn;
LLVMValueRef sub_with_overflow_fn;
LLVMValueRef mul_with_overflow_fn;
}; };
struct TypeTableEntryArray { struct TypeTableEntryArray {
@ -791,7 +794,6 @@ struct FnTableEntry {
enum BuiltinFnId { enum BuiltinFnId {
BuiltinFnIdInvalid, BuiltinFnIdInvalid,
BuiltinFnIdArithmeticWithOverflow,
BuiltinFnIdMemcpy, BuiltinFnIdMemcpy,
BuiltinFnIdMemset, BuiltinFnIdMemset,
BuiltinFnIdSizeof, BuiltinFnIdSizeof,
@ -799,6 +801,9 @@ enum BuiltinFnId {
BuiltinFnIdMinValue, BuiltinFnIdMinValue,
BuiltinFnIdValueCount, BuiltinFnIdValueCount,
BuiltinFnIdTypeof, BuiltinFnIdTypeof,
BuiltinFnIdAddWithOverflow,
BuiltinFnIdSubWithOverflow,
BuiltinFnIdMulWithOverflow,
}; };
struct BuiltinFnEntry { struct BuiltinFnEntry {
@ -831,6 +836,7 @@ struct CodeGen {
struct { struct {
TypeTableEntry *entry_bool; TypeTableEntry *entry_bool;
TypeTableEntry *entry_int[2][4]; // [signed,unsigned][8,16,32,64]
TypeTableEntry *entry_u8; TypeTableEntry *entry_u8;
TypeTableEntry *entry_u16; TypeTableEntry *entry_u16;
TypeTableEntry *entry_u32; TypeTableEntry *entry_u32;

View File

@ -155,18 +155,7 @@ static TypeTableEntry *get_number_literal_type_unsigned(CodeGen *g, uint64_t x)
} }
static TypeTableEntry *get_int_type_unsigned(CodeGen *g, uint64_t x) { static TypeTableEntry *get_int_type_unsigned(CodeGen *g, uint64_t x) {
switch (get_number_literal_kind_unsigned(x)) { return get_int_type(g, false, num_lit_bit_count(get_number_literal_kind_unsigned(x)));
case NumLitU8:
return g->builtin_types.entry_u8;
case NumLitU16:
return g->builtin_types.entry_u16;
case NumLitU32:
return g->builtin_types.entry_u32;
case NumLitU64:
return g->builtin_types.entry_u64;
default:
zig_unreachable();
}
} }
static TypeTableEntry *get_meta_type(CodeGen *g, TypeTableEntry *child_type) { static TypeTableEntry *get_meta_type(CodeGen *g, TypeTableEntry *child_type) {
@ -464,7 +453,9 @@ static void eval_const_expr_builtin(CodeGen *g, BlockContext *context, AstNode *
switch (node->data.fn_call_expr.builtin_fn->id) { switch (node->data.fn_call_expr.builtin_fn->id) {
case BuiltinFnIdInvalid: case BuiltinFnIdInvalid:
zig_unreachable(); zig_unreachable();
case BuiltinFnIdArithmeticWithOverflow: case BuiltinFnIdAddWithOverflow:
case BuiltinFnIdSubWithOverflow:
case BuiltinFnIdMulWithOverflow:
case BuiltinFnIdMemcpy: case BuiltinFnIdMemcpy:
case BuiltinFnIdMemset: case BuiltinFnIdMemset:
break; break;
@ -2476,18 +2467,36 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
add_node_error(g, node, add_node_error(g, node,
buf_sprintf("expected %d arguments, got %d", buf_sprintf("expected %d arguments, got %d",
builtin_fn->param_count, actual_param_count)); builtin_fn->param_count, actual_param_count));
return g->builtin_types.entry_invalid;
} }
switch (builtin_fn->id) { switch (builtin_fn->id) {
case BuiltinFnIdInvalid: case BuiltinFnIdInvalid:
zig_unreachable(); zig_unreachable();
case BuiltinFnIdArithmeticWithOverflow: case BuiltinFnIdAddWithOverflow:
for (int i = 0; i < actual_param_count; i += 1) { case BuiltinFnIdSubWithOverflow:
AstNode *child = node->data.fn_call_expr.params.at(i); case BuiltinFnIdMulWithOverflow:
TypeTableEntry *expected_param_type = builtin_fn->param_types[i]; {
analyze_expression(g, import, context, expected_param_type, child); AstNode *type_node = node->data.fn_call_expr.params.at(0);
TypeTableEntry *int_type = analyze_type_expr(g, import, context, type_node);
if (int_type->id == TypeTableEntryIdInvalid) {
return g->builtin_types.entry_bool;
} else if (int_type->id == TypeTableEntryIdInt) {
AstNode *op1_node = node->data.fn_call_expr.params.at(1);
AstNode *op2_node = node->data.fn_call_expr.params.at(2);
AstNode *result_node = node->data.fn_call_expr.params.at(3);
analyze_expression(g, import, context, int_type, op1_node);
analyze_expression(g, import, context, int_type, op2_node);
analyze_expression(g, import, context, get_pointer_to_type(g, int_type, false),
result_node);
} else {
add_node_error(g, type_node,
buf_sprintf("expected integer type, got '%s'", buf_ptr(&int_type->name)));
}
return g->builtin_types.entry_bool;
} }
return builtin_fn->return_type;
case BuiltinFnIdMemcpy: case BuiltinFnIdMemcpy:
{ {
AstNode *dest_node = node->data.fn_call_expr.params.at(0); AstNode *dest_node = node->data.fn_call_expr.params.at(0);
@ -2796,7 +2805,8 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
} else if (expr_type->id == TypeTableEntryIdNumberLiteral) { } else if (expr_type->id == TypeTableEntryIdNumberLiteral) {
return expr_type; return expr_type;
} else { } else {
add_node_error(g, operand_node, buf_sprintf("invalid negation type: '%s'", BREAKPOINT;
add_node_error(g, node, buf_sprintf("invalid negation type: '%s'",
buf_ptr(&expr_type->name))); buf_ptr(&expr_type->name)));
return g->builtin_types.entry_invalid; return g->builtin_types.entry_invalid;
} }
@ -3842,3 +3852,22 @@ bool is_node_void_expr(AstNode *node) {
return false; return false;
} }
TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, int size_in_bits) {
int index;
if (size_in_bits == 8) {
index = 0;
} else if (size_in_bits == 16) {
index = 1;
} else if (size_in_bits == 32) {
index = 2;
} else if (size_in_bits == 64) {
index = 3;
} else {
zig_unreachable();
}
return &g->builtin_types.entry_int[is_signed ? 0 : 1][index];
}
TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, int size_in_bits) {
return *get_int_type_ptr(g, is_signed, size_in_bits);
}

View File

@ -21,5 +21,7 @@ Expr *get_resolved_expr(AstNode *node);
NumLitCodeGen *get_resolved_num_lit(AstNode *node); NumLitCodeGen *get_resolved_num_lit(AstNode *node);
TopLevelDecl *get_resolved_top_level_decl(AstNode *node); TopLevelDecl *get_resolved_top_level_decl(AstNode *node);
bool is_node_void_expr(AstNode *node); bool is_node_void_expr(AstNode *node);
TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, int size_in_bits);
TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, int size_in_bits);
#endif #endif

View File

@ -184,14 +184,28 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
case BuiltinFnIdInvalid: case BuiltinFnIdInvalid:
case BuiltinFnIdTypeof: case BuiltinFnIdTypeof:
zig_unreachable(); zig_unreachable();
case BuiltinFnIdArithmeticWithOverflow: case BuiltinFnIdAddWithOverflow:
case BuiltinFnIdSubWithOverflow:
case BuiltinFnIdMulWithOverflow:
{ {
int fn_call_param_count = node->data.fn_call_expr.params.length; int fn_call_param_count = node->data.fn_call_expr.params.length;
assert(fn_call_param_count == 3); assert(fn_call_param_count == 4);
LLVMValueRef op1 = gen_expr(g, node->data.fn_call_expr.params.at(0)); TypeTableEntry *int_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0));
LLVMValueRef op2 = gen_expr(g, node->data.fn_call_expr.params.at(1)); LLVMValueRef fn_val;
LLVMValueRef ptr_result = gen_expr(g, node->data.fn_call_expr.params.at(2)); if (builtin_fn->id == BuiltinFnIdAddWithOverflow) {
fn_val = int_type->data.integral.add_with_overflow_fn;
} else if (builtin_fn->id == BuiltinFnIdSubWithOverflow) {
fn_val = int_type->data.integral.sub_with_overflow_fn;
} else if (builtin_fn->id == BuiltinFnIdMulWithOverflow) {
fn_val = int_type->data.integral.mul_with_overflow_fn;
} else {
zig_unreachable();
}
LLVMValueRef op1 = gen_expr(g, node->data.fn_call_expr.params.at(1));
LLVMValueRef op2 = gen_expr(g, node->data.fn_call_expr.params.at(2));
LLVMValueRef ptr_result = gen_expr(g, node->data.fn_call_expr.params.at(3));
LLVMValueRef params[] = { LLVMValueRef params[] = {
op1, op1,
@ -199,7 +213,7 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
}; };
add_debug_source_node(g, node); add_debug_source_node(g, node);
LLVMValueRef result_struct = LLVMBuildCall(g->builder, builtin_fn->fn_val, params, 2, ""); LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, "");
LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, ""); LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, "");
LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, ""); LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, "");
LLVMBuildStore(g->builder, result, ptr_result); LLVMBuildStore(g->builder, result, ptr_result);
@ -2184,6 +2198,35 @@ static void do_code_gen(CodeGen *g) {
#endif #endif
} }
static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, TypeTableEntry *type_entry,
const char *signed_name, const char *unsigned_name)
{
const char *signed_str = type_entry->data.integral.is_signed ? signed_name : unsigned_name;
Buf *llvm_name = buf_sprintf("llvm.%s.with.overflow.i%" PRIu64, signed_str, type_entry->size_in_bits);
LLVMTypeRef return_elem_types[] = {
type_entry->type_ref,
LLVMInt1Type(),
};
LLVMTypeRef param_types[] = {
type_entry->type_ref,
type_entry->type_ref,
};
LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false);
LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false);
LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(llvm_name), fn_type);
assert(LLVMGetIntrinsicID(fn_val));
return fn_val;
}
static void add_int_overflow_fns(CodeGen *g, TypeTableEntry *type_entry) {
assert(type_entry->id == TypeTableEntryIdInt);
type_entry->data.integral.add_with_overflow_fn = get_arithmetic_overflow_fn(g, type_entry, "sadd", "uadd");
type_entry->data.integral.sub_with_overflow_fn = get_arithmetic_overflow_fn(g, type_entry, "ssub", "usub");
type_entry->data.integral.mul_with_overflow_fn = get_arithmetic_overflow_fn(g, type_entry, "smul", "umul");
}
static const NumLit num_lit_kinds[] = { static const NumLit num_lit_kinds[] = {
NumLitF32, NumLitF32,
NumLitF64, NumLitF64,
@ -2198,6 +2241,13 @@ static const NumLit num_lit_kinds[] = {
NumLitI64, NumLitI64,
}; };
static const int int_sizes_in_bits[] = {
8,
16,
32,
64,
};
static void define_builtin_types(CodeGen *g) { static void define_builtin_types(CodeGen *g) {
{ {
// if this type is anywhere in the AST, we should never hit codegen. // if this type is anywhere in the AST, we should never hit codegen.
@ -2219,6 +2269,37 @@ static void define_builtin_types(CodeGen *g) {
g->num_lit_types[i] = entry; g->num_lit_types[i] = entry;
} }
for (int i = 0; i < array_length(int_sizes_in_bits); i += 1) {
int size_in_bits = int_sizes_in_bits[i];
bool is_signed = true;
for (;;) {
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMIntType(size_in_bits);
const char u_or_i = is_signed ? 'i' : 'u';
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "%c%d", u_or_i, size_in_bits);
entry->size_in_bits = size_in_bits;
entry->align_in_bits = size_in_bits;
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits,
is_signed ? LLVMZigEncoding_DW_ATE_signed() : LLVMZigEncoding_DW_ATE_unsigned());
entry->data.integral.is_signed = is_signed;
g->primitive_type_table.put(&entry->name, entry);
get_int_type_ptr(g, is_signed, size_in_bits)[0] = entry;
add_int_overflow_fns(g, entry);
if (!is_signed) {
break;
} else {
is_signed = false;
}
}
}
{ {
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdBool); TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdBool);
entry->type_ref = LLVMInt1Type(); entry->type_ref = LLVMInt1Type();
@ -2231,110 +2312,6 @@ static void define_builtin_types(CodeGen *g) {
g->builtin_types.entry_bool = entry; g->builtin_types.entry_bool = entry;
g->primitive_type_table.put(&entry->name, entry); g->primitive_type_table.put(&entry->name, entry);
} }
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMInt8Type();
buf_init_from_str(&entry->name, "u8");
entry->size_in_bits = 8;
entry->align_in_bits = 8;
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_unsigned());
g->builtin_types.entry_u8 = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMInt16Type();
buf_init_from_str(&entry->name, "u16");
entry->size_in_bits = 16;
entry->align_in_bits = 16;
entry->data.integral.is_signed = false;
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_unsigned());
g->builtin_types.entry_u16 = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMInt32Type();
buf_init_from_str(&entry->name, "u32");
entry->size_in_bits = 32;
entry->align_in_bits = 32;
entry->data.integral.is_signed = false;
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_unsigned());
g->builtin_types.entry_u32 = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMInt64Type();
buf_init_from_str(&entry->name, "u64");
entry->size_in_bits = 64;
entry->align_in_bits = 64;
entry->data.integral.is_signed = false;
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_unsigned());
g->builtin_types.entry_u64 = entry;
g->primitive_type_table.put(&entry->name, entry);
}
g->builtin_types.entry_c_string_literal = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMInt8Type();
buf_init_from_str(&entry->name, "i8");
entry->size_in_bits = 8;
entry->align_in_bits = 8;
entry->data.integral.is_signed = true;
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_signed());
g->builtin_types.entry_i8 = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMInt16Type();
buf_init_from_str(&entry->name, "i16");
entry->size_in_bits = 16;
entry->align_in_bits = 16;
entry->data.integral.is_signed = true;
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_signed());
g->builtin_types.entry_i16 = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMInt32Type();
buf_init_from_str(&entry->name, "i32");
entry->size_in_bits = 32;
entry->align_in_bits = 32;
entry->data.integral.is_signed = true;
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_signed());
g->builtin_types.entry_i32 = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMInt64Type();
buf_init_from_str(&entry->name, "i64");
entry->size_in_bits = 64;
entry->align_in_bits = 64;
entry->data.integral.is_signed = true;
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_signed());
g->builtin_types.entry_i64 = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{ {
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8); entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8);
@ -2342,6 +2319,12 @@ static void define_builtin_types(CodeGen *g) {
entry->size_in_bits = g->pointer_size_bytes * 8; entry->size_in_bits = g->pointer_size_bytes * 8;
entry->align_in_bits = g->pointer_size_bytes * 8; entry->align_in_bits = g->pointer_size_bytes * 8;
entry->data.integral.is_signed = true; entry->data.integral.is_signed = true;
TypeTableEntry *fixed_width_entry = get_int_type(g, entry->data.integral.is_signed, entry->size_in_bits);
entry->data.integral.add_with_overflow_fn = fixed_width_entry->data.integral.add_with_overflow_fn;
entry->data.integral.sub_with_overflow_fn = fixed_width_entry->data.integral.sub_with_overflow_fn;
entry->data.integral.mul_with_overflow_fn = fixed_width_entry->data.integral.mul_with_overflow_fn;
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits, entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_signed()); LLVMZigEncoding_DW_ATE_signed());
@ -2355,6 +2338,12 @@ static void define_builtin_types(CodeGen *g) {
entry->size_in_bits = g->pointer_size_bytes * 8; entry->size_in_bits = g->pointer_size_bytes * 8;
entry->align_in_bits = g->pointer_size_bytes * 8; entry->align_in_bits = g->pointer_size_bytes * 8;
entry->data.integral.is_signed = false; entry->data.integral.is_signed = false;
TypeTableEntry *fixed_width_entry = get_int_type(g, entry->data.integral.is_signed, entry->size_in_bits);
entry->data.integral.add_with_overflow_fn = fixed_width_entry->data.integral.add_with_overflow_fn;
entry->data.integral.sub_with_overflow_fn = fixed_width_entry->data.integral.sub_with_overflow_fn;
entry->data.integral.mul_with_overflow_fn = fixed_width_entry->data.integral.mul_with_overflow_fn;
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits, entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_unsigned()); LLVMZigEncoding_DW_ATE_unsigned());
@ -2403,54 +2392,19 @@ static void define_builtin_types(CodeGen *g) {
g->builtin_types.entry_unreachable = entry; g->builtin_types.entry_unreachable = entry;
g->primitive_type_table.put(&entry->name, entry); g->primitive_type_table.put(&entry->name, entry);
} }
g->builtin_types.entry_c_string_literal = get_pointer_to_type(g, get_int_type(g, false, 8), true);
g->builtin_types.entry_u8 = get_int_type(g, false, 8);
g->builtin_types.entry_u16 = get_int_type(g, false, 16);
g->builtin_types.entry_u32 = get_int_type(g, false, 32);
g->builtin_types.entry_u64 = get_int_type(g, false, 64);
g->builtin_types.entry_i8 = get_int_type(g, true, 8);
g->builtin_types.entry_i16 = get_int_type(g, true, 16);
g->builtin_types.entry_i32 = get_int_type(g, true, 32);
g->builtin_types.entry_i64 = get_int_type(g, true, 64);
} }
static void define_builtin_fns_int(CodeGen *g, TypeTableEntry *type_entry) {
assert(type_entry->id == TypeTableEntryIdInt);
struct OverflowFn {
const char *bare_name;
const char *signed_name;
const char *unsigned_name;
};
OverflowFn overflow_fns[] = {
{"add", "sadd", "uadd"},
{"sub", "ssub", "usub"},
{"mul", "smul", "umul"},
};
for (size_t i = 0; i < sizeof(overflow_fns)/sizeof(overflow_fns[0]); i += 1) {
OverflowFn *overflow_fn = &overflow_fns[i];
BuiltinFnEntry *builtin_fn = allocate<BuiltinFnEntry>(1);
buf_resize(&builtin_fn->name, 0);
buf_appendf(&builtin_fn->name, "%s_with_overflow_%s", overflow_fn->bare_name, buf_ptr(&type_entry->name));
builtin_fn->id = BuiltinFnIdArithmeticWithOverflow;
builtin_fn->return_type = g->builtin_types.entry_bool;
builtin_fn->param_count = 3;
builtin_fn->param_types = allocate<TypeTableEntry *>(builtin_fn->param_count);
builtin_fn->param_types[0] = type_entry;
builtin_fn->param_types[1] = type_entry;
builtin_fn->param_types[2] = get_pointer_to_type(g, type_entry, false);
const char *signed_str = type_entry->data.integral.is_signed ?
overflow_fn->signed_name : overflow_fn->unsigned_name;
Buf *llvm_name = buf_sprintf("llvm.%s.with.overflow.i%" PRIu64, signed_str, type_entry->size_in_bits);
LLVMTypeRef return_elem_types[] = {
type_entry->type_ref,
LLVMInt1Type(),
};
LLVMTypeRef param_types[] = {
type_entry->type_ref,
type_entry->type_ref,
};
LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false);
LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false);
builtin_fn->fn_val = LLVMAddFunction(g->module, buf_ptr(llvm_name), fn_type);
assert(LLVMGetIntrinsicID(builtin_fn->fn_val));
g->builtin_fn_table.put(&builtin_fn->name, builtin_fn);
}
}
static BuiltinFnEntry *create_builtin_fn(CodeGen *g, BuiltinFnId id, const char *name) { static BuiltinFnEntry *create_builtin_fn(CodeGen *g, BuiltinFnId id, const char *name) {
BuiltinFnEntry *builtin_fn = allocate<BuiltinFnEntry>(1); BuiltinFnEntry *builtin_fn = allocate<BuiltinFnEntry>(1);
@ -2460,24 +2414,14 @@ static BuiltinFnEntry *create_builtin_fn(CodeGen *g, BuiltinFnId id, const char
return builtin_fn; return builtin_fn;
} }
static BuiltinFnEntry *create_one_arg_builtin_fn(CodeGen *g, BuiltinFnId id, const char *name) { static BuiltinFnEntry *create_builtin_fn_with_arg_count(CodeGen *g, BuiltinFnId id, const char *name, int count) {
BuiltinFnEntry *builtin_fn = create_builtin_fn(g, id, name); BuiltinFnEntry *builtin_fn = create_builtin_fn(g, id, name);
builtin_fn->return_type = nullptr; // manually determined later builtin_fn->param_count = count;
builtin_fn->param_count = 1; builtin_fn->param_types = allocate<TypeTableEntry *>(count);
builtin_fn->param_types = allocate<TypeTableEntry *>(builtin_fn->param_count);
builtin_fn->param_types[0] = nullptr; // manually checked later
return builtin_fn; return builtin_fn;
} }
static void define_builtin_fns(CodeGen *g) { static void define_builtin_fns(CodeGen *g) {
define_builtin_fns_int(g, g->builtin_types.entry_u8);
define_builtin_fns_int(g, g->builtin_types.entry_u16);
define_builtin_fns_int(g, g->builtin_types.entry_u32);
define_builtin_fns_int(g, g->builtin_types.entry_u64);
define_builtin_fns_int(g, g->builtin_types.entry_i8);
define_builtin_fns_int(g, g->builtin_types.entry_i16);
define_builtin_fns_int(g, g->builtin_types.entry_i32);
define_builtin_fns_int(g, g->builtin_types.entry_i64);
{ {
BuiltinFnEntry *builtin_fn = create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy"); BuiltinFnEntry *builtin_fn = create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy");
builtin_fn->return_type = g->builtin_types.entry_void; builtin_fn->return_type = g->builtin_types.entry_void;
@ -2524,11 +2468,14 @@ static void define_builtin_fns(CodeGen *g) {
g->memset_fn_val = builtin_fn->fn_val; g->memset_fn_val = builtin_fn->fn_val;
} }
create_one_arg_builtin_fn(g, BuiltinFnIdSizeof, "sizeof"); create_builtin_fn_with_arg_count(g, BuiltinFnIdSizeof, "sizeof", 1);
create_one_arg_builtin_fn(g, BuiltinFnIdMaxValue, "max_value"); create_builtin_fn_with_arg_count(g, BuiltinFnIdMaxValue, "max_value", 1);
create_one_arg_builtin_fn(g, BuiltinFnIdMinValue, "min_value"); create_builtin_fn_with_arg_count(g, BuiltinFnIdMinValue, "min_value", 1);
create_one_arg_builtin_fn(g, BuiltinFnIdValueCount, "value_count"); create_builtin_fn_with_arg_count(g, BuiltinFnIdValueCount, "value_count", 1);
create_one_arg_builtin_fn(g, BuiltinFnIdTypeof, "typeof"); create_builtin_fn_with_arg_count(g, BuiltinFnIdTypeof, "typeof", 1);
create_builtin_fn_with_arg_count(g, BuiltinFnIdAddWithOverflow, "add_with_overflow", 4);
create_builtin_fn_with_arg_count(g, BuiltinFnIdSubWithOverflow, "sub_with_overflow", 4);
create_builtin_fn_with_arg_count(g, BuiltinFnIdMulWithOverflow, "mul_with_overflow", 4);
} }

View File

@ -61,12 +61,12 @@ pub fn parse_u64(buf: []u8, radix: u8, result: &u64) bool => {
} }
// x *= radix // x *= radix
if (@mul_with_overflow_u64(x, radix, &x)) { if (@mul_with_overflow(u64, x, radix, &x)) {
return true; return true;
} }
// x += digit // x += digit
if (@add_with_overflow_u64(x, digit, &x)) { if (@add_with_overflow(u64, x, digit, &x)) {
return true; return true;
} }

View File

@ -986,10 +986,10 @@ fn f(c: u8) u8 => {
use "std.zig"; use "std.zig";
pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => { pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
var result: u8; var result: u8;
if (!@add_with_overflow_u8(250, 100, &result)) { if (!@add_with_overflow(u8, 250, 100, &result)) {
print_str("BAD\n"); print_str("BAD\n");
} }
if (@add_with_overflow_u8(100, 150, &result)) { if (@add_with_overflow(u8, 100, 150, &result)) {
print_str("BAD\n"); print_str("BAD\n");
} }
if (result != 250) { if (result != 250) {