Skip to content

Commit 03c16c6

Browse files
committedJun 11, 2018
implement @TagName as a switch instead of table lookup
closes #976 closes #1080
·
0.15.10.3.0
1 parent 854f90a commit 03c16c6

File tree

4 files changed

+112
-80
lines changed

4 files changed

+112
-80
lines changed
 

‎src/all_types.hpp‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,8 +1091,7 @@ struct TypeTableEntryEnum {
10911091
bool zero_bits_loop_flag;
10921092
bool zero_bits_known;
10931093

1094-
bool generate_name_table;
1095-
LLVMValueRef name_table;
1094+
LLVMValueRef name_function;
10961095

10971096
HashMap<Buf *, TypeEnumField *, buf_hash, buf_eql_buf> fields_by_name;
10981097
};
@@ -1411,6 +1410,7 @@ enum PanicMsgId {
14111410
PanicMsgIdInvalidErrorCode,
14121411
PanicMsgIdIncorrectAlignment,
14131412
PanicMsgIdBadUnionField,
1413+
PanicMsgIdBadEnumValue,
14141414

14151415
PanicMsgIdCount,
14161416
};
@@ -1730,8 +1730,6 @@ struct CodeGen {
17301730
ZigList<Buf *> link_objects;
17311731
ZigList<Buf *> assembly_files;
17321732

1733-
ZigList<TypeTableEntry *> name_table_enums;
1734-
17351733
Buf *test_filter;
17361734
Buf *test_name_prefix;
17371735

‎src/codegen.cpp‎

Lines changed: 101 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
875875
return buf_create_from_str("incorrect alignment");
876876
case PanicMsgIdBadUnionField:
877877
return buf_create_from_str("access of inactive union field");
878+
case PanicMsgIdBadEnumValue:
879+
return buf_create_from_str("invalid enum value");
878880
}
879881
zig_unreachable();
880882
}
@@ -3516,34 +3518,112 @@ static LLVMValueRef ir_render_err_name(CodeGen *g, IrExecutable *executable, IrI
35163518
return LLVMBuildInBoundsGEP(g->builder, g->err_name_table, indices, 2, "");
35173519
}
35183520

3521+
static LLVMValueRef get_enum_tag_name_function(CodeGen *g, TypeTableEntry *enum_type) {
3522+
assert(enum_type->id == TypeTableEntryIdEnum);
3523+
if (enum_type->data.enumeration.name_function)
3524+
return enum_type->data.enumeration.name_function;
3525+
3526+
TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, false, false,
3527+
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
3528+
TypeTableEntry *u8_slice_type = get_slice_type(g, u8_ptr_type);
3529+
TypeTableEntry *tag_int_type = enum_type->data.enumeration.tag_int_type;
3530+
3531+
LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMPointerType(u8_slice_type->type_ref, 0),
3532+
&tag_int_type->type_ref, 1, false);
3533+
3534+
Buf *fn_name = get_mangled_name(g, buf_sprintf("__zig_tag_name_%s", buf_ptr(&enum_type->name)), false);
3535+
LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);
3536+
LLVMSetLinkage(fn_val, LLVMInternalLinkage);
3537+
LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified));
3538+
addLLVMFnAttr(fn_val, "nounwind");
3539+
add_uwtable_attr(g, fn_val);
3540+
if (g->build_mode == BuildModeDebug) {
3541+
ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true");
3542+
ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim-non-leaf", nullptr);
3543+
}
3544+
3545+
LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder);
3546+
LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder);
3547+
FnTableEntry *prev_cur_fn = g->cur_fn;
3548+
LLVMValueRef prev_cur_fn_val = g->cur_fn_val;
3549+
3550+
LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry");
3551+
LLVMPositionBuilderAtEnd(g->builder, entry_block);
3552+
ZigLLVMClearCurrentDebugLocation(g->builder);
3553+
g->cur_fn = nullptr;
3554+
g->cur_fn_val = fn_val;
3555+
3556+
size_t field_count = enum_type->data.enumeration.src_field_count;
3557+
LLVMBasicBlockRef bad_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadValue");
3558+
LLVMValueRef tag_int_value = LLVMGetParam(fn_val, 0);
3559+
LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, tag_int_value, bad_value_block, field_count);
3560+
3561+
3562+
TypeTableEntry *usize = g->builtin_types.entry_usize;
3563+
LLVMValueRef array_ptr_indices[] = {
3564+
LLVMConstNull(usize->type_ref),
3565+
LLVMConstNull(usize->type_ref),
3566+
};
3567+
3568+
for (size_t field_i = 0; field_i < field_count; field_i += 1) {
3569+
Buf *name = enum_type->data.enumeration.fields[field_i].name;
3570+
LLVMValueRef str_init = LLVMConstString(buf_ptr(name), (unsigned)buf_len(name), true);
3571+
LLVMValueRef str_global = LLVMAddGlobal(g->module, LLVMTypeOf(str_init), "");
3572+
LLVMSetInitializer(str_global, str_init);
3573+
LLVMSetLinkage(str_global, LLVMPrivateLinkage);
3574+
LLVMSetGlobalConstant(str_global, true);
3575+
LLVMSetUnnamedAddr(str_global, true);
3576+
LLVMSetAlignment(str_global, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(str_init)));
3577+
3578+
LLVMValueRef fields[] = {
3579+
LLVMConstGEP(str_global, array_ptr_indices, 2),
3580+
LLVMConstInt(g->builtin_types.entry_usize->type_ref, buf_len(name), false),
3581+
};
3582+
LLVMValueRef slice_init_value = LLVMConstNamedStruct(u8_slice_type->type_ref, fields, 2);
3583+
3584+
LLVMValueRef slice_global = LLVMAddGlobal(g->module, LLVMTypeOf(slice_init_value), "");
3585+
LLVMSetInitializer(slice_global, slice_init_value);
3586+
LLVMSetLinkage(slice_global, LLVMPrivateLinkage);
3587+
LLVMSetGlobalConstant(slice_global, true);
3588+
LLVMSetUnnamedAddr(slice_global, true);
3589+
LLVMSetAlignment(slice_global, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(slice_init_value)));
3590+
3591+
LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn_val, "Name");
3592+
LLVMValueRef this_tag_int_value = bigint_to_llvm_const(tag_int_type->type_ref,
3593+
&enum_type->data.enumeration.fields[field_i].value);
3594+
LLVMAddCase(switch_instr, this_tag_int_value, return_block);
3595+
3596+
LLVMPositionBuilderAtEnd(g->builder, return_block);
3597+
LLVMBuildRet(g->builder, slice_global);
3598+
}
3599+
3600+
LLVMPositionBuilderAtEnd(g->builder, bad_value_block);
3601+
if (g->build_mode == BuildModeDebug || g->build_mode == BuildModeSafeRelease) {
3602+
gen_safety_crash(g, PanicMsgIdBadEnumValue);
3603+
} else {
3604+
LLVMBuildUnreachable(g->builder);
3605+
}
3606+
3607+
g->cur_fn = prev_cur_fn;
3608+
g->cur_fn_val = prev_cur_fn_val;
3609+
LLVMPositionBuilderAtEnd(g->builder, prev_block);
3610+
LLVMSetCurrentDebugLocation(g->builder, prev_debug_location);
3611+
3612+
enum_type->data.enumeration.name_function = fn_val;
3613+
return fn_val;
3614+
}
3615+
35193616
static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutable *executable,
35203617
IrInstructionTagName *instruction)
35213618
{
35223619
TypeTableEntry *enum_type = instruction->target->value.type;
35233620
assert(enum_type->id == TypeTableEntryIdEnum);
3524-
assert(enum_type->data.enumeration.generate_name_table);
35253621

3526-
TypeTableEntry *tag_int_type = enum_type->data.enumeration.tag_int_type;
3527-
LLVMValueRef enum_tag_value = ir_llvm_value(g, instruction->target);
3528-
if (ir_want_runtime_safety(g, &instruction->base)) {
3529-
size_t field_count = enum_type->data.enumeration.src_field_count;
3530-
3531-
// if the field_count can't fit in the bits of the enum_type, then it can't possibly
3532-
// be the wrong value
3533-
BigInt field_bi;
3534-
bigint_init_unsigned(&field_bi, field_count);
3535-
if (bigint_fits_in_bits(&field_bi, tag_int_type->data.integral.bit_count, false)) {
3536-
LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(enum_tag_value), field_count, false);
3537-
add_bounds_check(g, enum_tag_value, LLVMIntEQ, nullptr, LLVMIntULT, end_val);
3538-
}
3539-
}
3622+
LLVMValueRef enum_name_function = get_enum_tag_name_function(g, enum_type);
35403623

3541-
LLVMValueRef indices[] = {
3542-
LLVMConstNull(g->builtin_types.entry_usize->type_ref),
3543-
gen_widen_or_shorten(g, false, tag_int_type,
3544-
g->builtin_types.entry_usize, enum_tag_value),
3545-
};
3546-
return LLVMBuildInBoundsGEP(g->builder, enum_type->data.enumeration.name_table, indices, 2, "");
3624+
LLVMValueRef enum_tag_value = ir_llvm_value(g, instruction->target);
3625+
return ZigLLVMBuildCall(g->builder, enum_name_function, &enum_tag_value, 1,
3626+
get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
35473627
}
35483628

35493629
static LLVMValueRef ir_render_field_parent_ptr(CodeGen *g, IrExecutable *executable,
@@ -5471,55 +5551,6 @@ static void generate_error_name_table(CodeGen *g) {
54715551
LLVMSetAlignment(g->err_name_table, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(err_name_table_init)));
54725552
}
54735553

5474-
static void generate_enum_name_tables(CodeGen *g) {
5475-
TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
5476-
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
5477-
TypeTableEntry *str_type = get_slice_type(g, u8_ptr_type);
5478-
5479-
TypeTableEntry *usize = g->builtin_types.entry_usize;
5480-
LLVMValueRef array_ptr_indices[] = {
5481-
LLVMConstNull(usize->type_ref),
5482-
LLVMConstNull(usize->type_ref),
5483-
};
5484-
5485-
5486-
for (size_t enum_i = 0; enum_i < g->name_table_enums.length; enum_i += 1) {
5487-
TypeTableEntry *enum_type = g->name_table_enums.at(enum_i);
5488-
assert(enum_type->id == TypeTableEntryIdEnum);
5489-
5490-
size_t field_count = enum_type->data.enumeration.src_field_count;
5491-
LLVMValueRef *values = allocate<LLVMValueRef>(field_count);
5492-
for (size_t field_i = 0; field_i < field_count; field_i += 1) {
5493-
Buf *name = enum_type->data.enumeration.fields[field_i].name;
5494-
5495-
LLVMValueRef str_init = LLVMConstString(buf_ptr(name), (unsigned)buf_len(name), true);
5496-
LLVMValueRef str_global = LLVMAddGlobal(g->module, LLVMTypeOf(str_init), "");
5497-
LLVMSetInitializer(str_global, str_init);
5498-
LLVMSetLinkage(str_global, LLVMPrivateLinkage);
5499-
LLVMSetGlobalConstant(str_global, true);
5500-
LLVMSetUnnamedAddr(str_global, true);
5501-
LLVMSetAlignment(str_global, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(str_init)));
5502-
5503-
LLVMValueRef fields[] = {
5504-
LLVMConstGEP(str_global, array_ptr_indices, 2),
5505-
LLVMConstInt(g->builtin_types.entry_usize->type_ref, buf_len(name), false),
5506-
};
5507-
values[field_i] = LLVMConstNamedStruct(str_type->type_ref, fields, 2);
5508-
}
5509-
5510-
LLVMValueRef name_table_init = LLVMConstArray(str_type->type_ref, values, (unsigned)field_count);
5511-
5512-
Buf *table_name = get_mangled_name(g, buf_sprintf("%s_name_table", buf_ptr(&enum_type->name)), false);
5513-
LLVMValueRef name_table = LLVMAddGlobal(g->module, LLVMTypeOf(name_table_init), buf_ptr(table_name));
5514-
LLVMSetInitializer(name_table, name_table_init);
5515-
LLVMSetLinkage(name_table, LLVMPrivateLinkage);
5516-
LLVMSetGlobalConstant(name_table, true);
5517-
LLVMSetUnnamedAddr(name_table, true);
5518-
LLVMSetAlignment(name_table, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(name_table_init)));
5519-
enum_type->data.enumeration.name_table = name_table;
5520-
}
5521-
}
5522-
55235554
static void build_all_basic_blocks(CodeGen *g, FnTableEntry *fn) {
55245555
IrExecutable *executable = &fn->analyzed_executable;
55255556
assert(executable->basic_block_list.length > 0);
@@ -5616,7 +5647,6 @@ static void do_code_gen(CodeGen *g) {
56165647
}
56175648

56185649
generate_error_name_table(g);
5619-
generate_enum_name_tables(g);
56205650

56215651
// Generate module level variables
56225652
for (size_t i = 0; i < g->global_vars.length; i += 1) {

‎src/ir.cpp‎

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15837,11 +15837,6 @@ static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIn
1583715837
return out_val->type;
1583815838
}
1583915839

15840-
if (!target->value.type->data.enumeration.generate_name_table) {
15841-
target->value.type->data.enumeration.generate_name_table = true;
15842-
ira->codegen->name_table_enums.append(target->value.type);
15843-
}
15844-
1584515840
IrInstruction *result = ir_build_tag_name(&ira->new_irb, instruction->base.scope,
1584615841
instruction->base.source_node, target);
1584715842
ir_link_new_instruction(result, &instruction->base);

‎test/cases/enum.zig‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -883,3 +883,12 @@ test "empty extern enum with members" {
883883
};
884884
assert(@sizeOf(E) == @sizeOf(c_int));
885885
}
886+
887+
test "aoeu" {
888+
const LocalFoo = enum {
889+
A = 1,
890+
B = 0,
891+
};
892+
var b = LocalFoo.B;
893+
assert(mem.eql(u8, @tagName(b), "B"));
894+
}

0 commit comments

Comments
 (0)
Please sign in to comment.