authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-06-11 14:58:42-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-06-11 14:58:42-04:00
log03c16c6c548a8f8246c3dcff540482b7612aab80
treed31170b7ce3b1f9f883980c1f84ee17bce9aa970
parent854f90aa3064e8bf7ab62a113de3ff3aafa3e6f6

implement @tagName as a switch instead of table lookup

closes #976 closes #1080

4 files changed, 112 insertions(+), 80 deletions(-)

src/all_types.hpp+2-4
......@@ -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+101-71
......@@ -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
3521static 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
35193616static 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
35493629static 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
5474static 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
55235554static 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-5
......@@ -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+9
......@@ -883,3 +883,12 @@ test "empty extern enum with members" {
883883 };
884884 assert(@sizeOf(E) == @sizeOf(c_int));
885885}
886
887test "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}