Skip to content

Commit

Permalink
implement @TagName as a switch instead of table lookup
Browse files Browse the repository at this point in the history
closes #976
closes #1080
  • Loading branch information
andrewrk committed Jun 11, 2018
1 parent 854f90a commit 03c16c6
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 80 deletions.
6 changes: 2 additions & 4 deletions src/all_types.hpp
Expand Up @@ -1091,8 +1091,7 @@ struct TypeTableEntryEnum {
bool zero_bits_loop_flag;
bool zero_bits_known;

bool generate_name_table;
LLVMValueRef name_table;
LLVMValueRef name_function;

HashMap<Buf *, TypeEnumField *, buf_hash, buf_eql_buf> fields_by_name;
};
Expand Down Expand Up @@ -1411,6 +1410,7 @@ enum PanicMsgId {
PanicMsgIdInvalidErrorCode,
PanicMsgIdIncorrectAlignment,
PanicMsgIdBadUnionField,
PanicMsgIdBadEnumValue,

PanicMsgIdCount,
};
Expand Down Expand Up @@ -1730,8 +1730,6 @@ struct CodeGen {
ZigList<Buf *> link_objects;
ZigList<Buf *> assembly_files;

ZigList<TypeTableEntry *> name_table_enums;

Buf *test_filter;
Buf *test_name_prefix;

Expand Down
172 changes: 101 additions & 71 deletions src/codegen.cpp
Expand Up @@ -875,6 +875,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
return buf_create_from_str("incorrect alignment");
case PanicMsgIdBadUnionField:
return buf_create_from_str("access of inactive union field");
case PanicMsgIdBadEnumValue:
return buf_create_from_str("invalid enum value");
}
zig_unreachable();
}
Expand Down Expand Up @@ -3516,34 +3518,112 @@ static LLVMValueRef ir_render_err_name(CodeGen *g, IrExecutable *executable, IrI
return LLVMBuildInBoundsGEP(g->builder, g->err_name_table, indices, 2, "");
}

static LLVMValueRef get_enum_tag_name_function(CodeGen *g, TypeTableEntry *enum_type) {
assert(enum_type->id == TypeTableEntryIdEnum);
if (enum_type->data.enumeration.name_function)
return enum_type->data.enumeration.name_function;

TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, false, false,
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
TypeTableEntry *u8_slice_type = get_slice_type(g, u8_ptr_type);
TypeTableEntry *tag_int_type = enum_type->data.enumeration.tag_int_type;

LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMPointerType(u8_slice_type->type_ref, 0),
&tag_int_type->type_ref, 1, false);

Buf *fn_name = get_mangled_name(g, buf_sprintf("__zig_tag_name_%s", buf_ptr(&enum_type->name)), false);
LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);
LLVMSetLinkage(fn_val, LLVMInternalLinkage);
LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified));
addLLVMFnAttr(fn_val, "nounwind");
add_uwtable_attr(g, fn_val);
if (g->build_mode == BuildModeDebug) {
ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true");
ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim-non-leaf", nullptr);
}

LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder);
LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder);
FnTableEntry *prev_cur_fn = g->cur_fn;
LLVMValueRef prev_cur_fn_val = g->cur_fn_val;

LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry");
LLVMPositionBuilderAtEnd(g->builder, entry_block);
ZigLLVMClearCurrentDebugLocation(g->builder);
g->cur_fn = nullptr;
g->cur_fn_val = fn_val;

size_t field_count = enum_type->data.enumeration.src_field_count;
LLVMBasicBlockRef bad_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadValue");
LLVMValueRef tag_int_value = LLVMGetParam(fn_val, 0);
LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, tag_int_value, bad_value_block, field_count);


TypeTableEntry *usize = g->builtin_types.entry_usize;
LLVMValueRef array_ptr_indices[] = {
LLVMConstNull(usize->type_ref),
LLVMConstNull(usize->type_ref),
};

for (size_t field_i = 0; field_i < field_count; field_i += 1) {
Buf *name = enum_type->data.enumeration.fields[field_i].name;
LLVMValueRef str_init = LLVMConstString(buf_ptr(name), (unsigned)buf_len(name), true);
LLVMValueRef str_global = LLVMAddGlobal(g->module, LLVMTypeOf(str_init), "");
LLVMSetInitializer(str_global, str_init);
LLVMSetLinkage(str_global, LLVMPrivateLinkage);
LLVMSetGlobalConstant(str_global, true);
LLVMSetUnnamedAddr(str_global, true);
LLVMSetAlignment(str_global, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(str_init)));

LLVMValueRef fields[] = {
LLVMConstGEP(str_global, array_ptr_indices, 2),
LLVMConstInt(g->builtin_types.entry_usize->type_ref, buf_len(name), false),
};
LLVMValueRef slice_init_value = LLVMConstNamedStruct(u8_slice_type->type_ref, fields, 2);

LLVMValueRef slice_global = LLVMAddGlobal(g->module, LLVMTypeOf(slice_init_value), "");
LLVMSetInitializer(slice_global, slice_init_value);
LLVMSetLinkage(slice_global, LLVMPrivateLinkage);
LLVMSetGlobalConstant(slice_global, true);
LLVMSetUnnamedAddr(slice_global, true);
LLVMSetAlignment(slice_global, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(slice_init_value)));

LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn_val, "Name");
LLVMValueRef this_tag_int_value = bigint_to_llvm_const(tag_int_type->type_ref,
&enum_type->data.enumeration.fields[field_i].value);
LLVMAddCase(switch_instr, this_tag_int_value, return_block);

LLVMPositionBuilderAtEnd(g->builder, return_block);
LLVMBuildRet(g->builder, slice_global);
}

LLVMPositionBuilderAtEnd(g->builder, bad_value_block);
if (g->build_mode == BuildModeDebug || g->build_mode == BuildModeSafeRelease) {
gen_safety_crash(g, PanicMsgIdBadEnumValue);
} else {
LLVMBuildUnreachable(g->builder);
}

g->cur_fn = prev_cur_fn;
g->cur_fn_val = prev_cur_fn_val;
LLVMPositionBuilderAtEnd(g->builder, prev_block);
LLVMSetCurrentDebugLocation(g->builder, prev_debug_location);

enum_type->data.enumeration.name_function = fn_val;
return fn_val;
}

static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutable *executable,
IrInstructionTagName *instruction)
{
TypeTableEntry *enum_type = instruction->target->value.type;
assert(enum_type->id == TypeTableEntryIdEnum);
assert(enum_type->data.enumeration.generate_name_table);

TypeTableEntry *tag_int_type = enum_type->data.enumeration.tag_int_type;
LLVMValueRef enum_tag_value = ir_llvm_value(g, instruction->target);
if (ir_want_runtime_safety(g, &instruction->base)) {
size_t field_count = enum_type->data.enumeration.src_field_count;

// if the field_count can't fit in the bits of the enum_type, then it can't possibly
// be the wrong value
BigInt field_bi;
bigint_init_unsigned(&field_bi, field_count);
if (bigint_fits_in_bits(&field_bi, tag_int_type->data.integral.bit_count, false)) {
LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(enum_tag_value), field_count, false);
add_bounds_check(g, enum_tag_value, LLVMIntEQ, nullptr, LLVMIntULT, end_val);
}
}
LLVMValueRef enum_name_function = get_enum_tag_name_function(g, enum_type);

LLVMValueRef indices[] = {
LLVMConstNull(g->builtin_types.entry_usize->type_ref),
gen_widen_or_shorten(g, false, tag_int_type,
g->builtin_types.entry_usize, enum_tag_value),
};
return LLVMBuildInBoundsGEP(g->builder, enum_type->data.enumeration.name_table, indices, 2, "");
LLVMValueRef enum_tag_value = ir_llvm_value(g, instruction->target);
return ZigLLVMBuildCall(g->builder, enum_name_function, &enum_tag_value, 1,
get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
}

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

static void generate_enum_name_tables(CodeGen *g) {
TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
TypeTableEntry *str_type = get_slice_type(g, u8_ptr_type);

TypeTableEntry *usize = g->builtin_types.entry_usize;
LLVMValueRef array_ptr_indices[] = {
LLVMConstNull(usize->type_ref),
LLVMConstNull(usize->type_ref),
};


for (size_t enum_i = 0; enum_i < g->name_table_enums.length; enum_i += 1) {
TypeTableEntry *enum_type = g->name_table_enums.at(enum_i);
assert(enum_type->id == TypeTableEntryIdEnum);

size_t field_count = enum_type->data.enumeration.src_field_count;
LLVMValueRef *values = allocate<LLVMValueRef>(field_count);
for (size_t field_i = 0; field_i < field_count; field_i += 1) {
Buf *name = enum_type->data.enumeration.fields[field_i].name;

LLVMValueRef str_init = LLVMConstString(buf_ptr(name), (unsigned)buf_len(name), true);
LLVMValueRef str_global = LLVMAddGlobal(g->module, LLVMTypeOf(str_init), "");
LLVMSetInitializer(str_global, str_init);
LLVMSetLinkage(str_global, LLVMPrivateLinkage);
LLVMSetGlobalConstant(str_global, true);
LLVMSetUnnamedAddr(str_global, true);
LLVMSetAlignment(str_global, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(str_init)));

LLVMValueRef fields[] = {
LLVMConstGEP(str_global, array_ptr_indices, 2),
LLVMConstInt(g->builtin_types.entry_usize->type_ref, buf_len(name), false),
};
values[field_i] = LLVMConstNamedStruct(str_type->type_ref, fields, 2);
}

LLVMValueRef name_table_init = LLVMConstArray(str_type->type_ref, values, (unsigned)field_count);

Buf *table_name = get_mangled_name(g, buf_sprintf("%s_name_table", buf_ptr(&enum_type->name)), false);
LLVMValueRef name_table = LLVMAddGlobal(g->module, LLVMTypeOf(name_table_init), buf_ptr(table_name));
LLVMSetInitializer(name_table, name_table_init);
LLVMSetLinkage(name_table, LLVMPrivateLinkage);
LLVMSetGlobalConstant(name_table, true);
LLVMSetUnnamedAddr(name_table, true);
LLVMSetAlignment(name_table, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(name_table_init)));
enum_type->data.enumeration.name_table = name_table;
}
}

static void build_all_basic_blocks(CodeGen *g, FnTableEntry *fn) {
IrExecutable *executable = &fn->analyzed_executable;
assert(executable->basic_block_list.length > 0);
Expand Down Expand Up @@ -5616,7 +5647,6 @@ static void do_code_gen(CodeGen *g) {
}

generate_error_name_table(g);
generate_enum_name_tables(g);

// Generate module level variables
for (size_t i = 0; i < g->global_vars.length; i += 1) {
Expand Down
5 changes: 0 additions & 5 deletions src/ir.cpp
Expand Up @@ -15837,11 +15837,6 @@ static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIn
return out_val->type;
}

if (!target->value.type->data.enumeration.generate_name_table) {
target->value.type->data.enumeration.generate_name_table = true;
ira->codegen->name_table_enums.append(target->value.type);
}

IrInstruction *result = ir_build_tag_name(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, target);
ir_link_new_instruction(result, &instruction->base);
Expand Down
9 changes: 9 additions & 0 deletions test/cases/enum.zig
Expand Up @@ -883,3 +883,12 @@ test "empty extern enum with members" {
};
assert(@sizeOf(E) == @sizeOf(c_int));
}

test "aoeu" {
const LocalFoo = enum {
A = 1,
B = 0,
};
var b = LocalFoo.B;
assert(mem.eql(u8, @tagName(b), "B"));
}

0 comments on commit 03c16c6

Please sign in to comment.