add the C integer types

This commit is contained in:
Andrew Kelley
2016-01-25 23:21:13 -07:00
parent 01428d4a72
commit a37bb4a4da
7 changed files with 150 additions and 56 deletions

View File

@@ -100,6 +100,74 @@ static TypeTableEntry *fn_proto_type_from_type_node(CodeGen *g, AstNode *type_no
}
}
enum AddSubMul {
AddSubMulAdd = 0,
AddSubMulSub = 1,
AddSubMulMul = 2,
};
static int bits_index(int size_in_bits) {
switch (size_in_bits) {
case 8:
return 0;
case 16:
return 1;
case 32:
return 2;
case 64:
return 3;
default:
zig_unreachable();
}
}
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 LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry, AddSubMul add_sub_mul) {
assert(type_entry->id == TypeTableEntryIdInt);
// [0-signed,1-unsigned][0-add,1-sub,2-mul][0-8,1-16,2-32,3-64]
int index0 = type_entry->data.integral.is_signed ? 0 : 1;
int index1 = add_sub_mul;
int index2 = bits_index(type_entry->size_in_bits);
LLVMValueRef *fn = &g->int_overflow_fns[index0][index1][index2];
if (*fn) {
return *fn;
}
switch (add_sub_mul) {
case AddSubMulAdd:
*fn = get_arithmetic_overflow_fn(g, type_entry, "sadd", "uadd");
break;
case AddSubMulSub:
*fn = get_arithmetic_overflow_fn(g, type_entry, "ssub", "usub");
break;
case AddSubMulMul:
*fn = get_arithmetic_overflow_fn(g, type_entry, "smul", "umul");
break;
}
return *fn;
}
static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeFnCallExpr);
AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
@@ -118,16 +186,17 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
assert(fn_call_param_count == 4);
TypeTableEntry *int_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0));
LLVMValueRef fn_val;
AddSubMul add_sub_mul;
if (builtin_fn->id == BuiltinFnIdAddWithOverflow) {
fn_val = int_type->data.integral.add_with_overflow_fn;
add_sub_mul = AddSubMulAdd;
} else if (builtin_fn->id == BuiltinFnIdSubWithOverflow) {
fn_val = int_type->data.integral.sub_with_overflow_fn;
add_sub_mul = AddSubMulSub;
} else if (builtin_fn->id == BuiltinFnIdMulWithOverflow) {
fn_val = int_type->data.integral.mul_with_overflow_fn;
add_sub_mul = AddSubMulMul;
} else {
zig_unreachable();
}
LLVMValueRef fn_val = get_int_overflow_fn(g, int_type, add_sub_mul);
LLVMValueRef op1 = gen_expr(g, node->data.fn_call_expr.params.at(1));
LLVMValueRef op2 = gen_expr(g, node->data.fn_call_expr.params.at(2));
@@ -2559,35 +2628,6 @@ static void do_code_gen(CodeGen *g) {
#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 int int_sizes_in_bits[] = {
8,
16,
@@ -2595,6 +2635,52 @@ static const int int_sizes_in_bits[] = {
64,
};
enum CIntType {
CIntTypeShort,
CIntTypeUShort,
CIntTypeInt,
CIntTypeUInt,
CIntTypeLong,
CIntTypeULong,
CIntTypeLongLong,
CIntTypeULongLong,
};
struct CIntTypeInfo {
CIntType id;
const char *name;
bool is_signed;
};
static const CIntTypeInfo c_int_type_infos[] = {
{CIntTypeShort, "c_short", true},
{CIntTypeUShort, "c_ushort", false},
{CIntTypeInt, "c_int", true},
{CIntTypeUInt, "c_uint", false},
{CIntTypeLong, "c_long", true},
{CIntTypeULong, "c_ulong", false},
{CIntTypeLongLong, "c_longlong", true},
{CIntTypeULongLong, "c_ulonglong", false},
};
static int get_c_type_size_in_bits(CodeGen *g, CIntType id) {
// TODO other architectures besides x86_64
switch (id) {
case CIntTypeShort:
case CIntTypeUShort:
return 16;
case CIntTypeInt:
case CIntTypeUInt:
return 32;
case CIntTypeLong:
case CIntTypeULong:
case CIntTypeLongLong:
case CIntTypeULongLong:
return 64;
}
zig_unreachable();
}
static void define_builtin_types(CodeGen *g) {
{
// if this type is anywhere in the AST, we should never hit codegen.
@@ -2639,8 +2725,6 @@ static void define_builtin_types(CodeGen *g) {
get_int_type_ptr(g, is_signed, size_in_bits)[0] = entry;
add_int_overflow_fns(g, entry);
if (!is_signed) {
break;
} else {
@@ -2649,6 +2733,26 @@ static void define_builtin_types(CodeGen *g) {
}
}
for (int i = 0; i < array_length(c_int_type_infos); i += 1) {
const CIntTypeInfo *info = &c_int_type_infos[i];
uint64_t size_in_bits = get_c_type_size_in_bits(g, info->id);
bool is_signed = info->is_signed;
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMIntType(size_in_bits);
buf_init_from_str(&entry->name, info->name);
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);
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdBool);
entry->type_ref = LLVMInt1Type();
@@ -2669,11 +2773,6 @@ static void define_builtin_types(CodeGen *g) {
entry->align_in_bits = g->pointer_size_bytes * 8;
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->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_signed());
@@ -2688,11 +2787,6 @@ static void define_builtin_types(CodeGen *g) {
entry->align_in_bits = g->pointer_size_bytes * 8;
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->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_unsigned());