From c5c9d98065890eaeb9070cba20e5a1ed48b392af Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 29 Aug 2017 07:30:22 -0400 Subject: [PATCH] introduce align keyword * remove `@setGlobalAlign` * add align keyword for setting alignment on functions and variables. * loads and stores use alignment from pointer * memcpy, memset use alignment from pointer * add syntax for pointer alignment * slices can have volatile * add u2, i2 primitives * ignore preferred align and use abi align everywhere * back to only having alignOf builtin. preferredAlignOf is too tricky to be useful. See #432. Partial revert of e726925e802eddab53cbfd9aacbc5eefe95c356f. See #37 --- src/all_types.hpp | 87 +++--- src/analyze.cpp | 492 ++++++++++++++++++++------------- src/analyze.hpp | 7 +- src/ast_render.cpp | 38 ++- src/codegen.cpp | 224 +++++++-------- src/ir.cpp | 583 +++++++++++++++++++++------------------- src/ir_print.cpp | 33 +-- src/parseh.cpp | 8 + src/parser.cpp | 130 ++++++--- src/tokenizer.cpp | 2 + src/tokenizer.hpp | 1 + src/zig_llvm.cpp | 5 - src/zig_llvm.hpp | 2 - std/hash_map.zig | 2 +- std/mem.zig | 5 +- test/cases/alignof.zig | 9 +- test/cases/enum.zig | 4 +- test/cases/struct.zig | 2 - test/cases/switch.zig | 1 - test/compile_errors.zig | 37 +-- 20 files changed, 944 insertions(+), 728 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 2fbf7c1a44..b43ecedcd6 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -303,8 +303,6 @@ struct TldVar { Tld base; VariableTableEntry *var; - AstNode *set_global_align_node; - uint32_t alignment; AstNode *set_global_section_node; Buf *section_name; AstNode *set_global_linkage_node; @@ -358,6 +356,7 @@ enum NodeType { NodeTypeCharLiteral, NodeTypeSymbol, NodeTypePrefixOpExpr, + NodeTypeAddrOfExpr, NodeTypeFnCallExpr, NodeTypeArrayAccessExpr, NodeTypeSliceExpr, @@ -415,6 +414,8 @@ struct AstNodeFnProto { AstNode *fn_def_node; // populated if this is an extern declaration Buf *lib_name; + // populated if the "align A" is present + AstNode *align_expr; }; struct AstNodeFnDef { @@ -470,6 +471,8 @@ struct AstNodeVariableDeclaration { AstNode *expr; // populated if this is an extern declaration Buf *lib_name; + // populated if the "align A" is present + AstNode *align_expr; }; struct AstNodeErrorValueDecl { @@ -579,10 +582,6 @@ enum PrefixOp { PrefixOpBinNot, PrefixOpNegation, PrefixOpNegationWrap, - PrefixOpAddressOf, - PrefixOpConstAddressOf, - PrefixOpVolatileAddressOf, - PrefixOpConstVolatileAddressOf, PrefixOpDereference, PrefixOpMaybe, PrefixOpError, @@ -595,6 +594,23 @@ struct AstNodePrefixOpExpr { AstNode *primary_expr; }; +struct AstNodeAddrOfExpr { + AstNode *align_expr; + BigInt *bit_offset_start; + BigInt *bit_offset_end; + bool is_const; + bool is_volatile; + AstNode *op_expr; +}; + +struct AstNodeArrayType { + AstNode *size; + AstNode *child_type; + AstNode *align_expr; + bool is_const; + bool is_volatile; +}; + struct AstNodeUse { VisibMod visib_mod; AstNode *expr; @@ -807,12 +823,6 @@ struct AstNodeUnreachableExpr { }; -struct AstNodeArrayType { - AstNode *size; - AstNode *child_type; - bool is_const; -}; - struct AstNodeErrorType { }; @@ -841,6 +851,7 @@ struct AstNode { AstNodeBinOpExpr bin_op_expr; AstNodeUnwrapErrorExpr unwrap_err_expr; AstNodePrefixOpExpr prefix_op_expr; + AstNodeAddrOfExpr addr_of_expr; AstNodeFnCallExpr fn_call_expr; AstNodeArrayAccessExpr array_access_expr; AstNodeSliceExpr slice_expr; @@ -911,8 +922,10 @@ struct TypeTableEntryPointer { TypeTableEntry *child_type; bool is_const; bool is_volatile; + uint32_t alignment; uint32_t bit_offset; uint32_t unaligned_bit_count; + TypeTableEntry *slice_parent; }; struct TypeTableEntryInt { @@ -958,6 +971,7 @@ struct TypeTableEntryStruct { bool zero_bits_loop_flag; bool zero_bits_known; + uint32_t abi_alignment; // also figured out with zero_bits pass }; struct TypeTableEntryMaybe { @@ -989,6 +1003,7 @@ struct TypeTableEntryEnum { bool zero_bits_loop_flag; bool zero_bits_known; + uint32_t abi_alignment; // also figured out with zero_bits pass size_t gen_union_index; size_t gen_tag_index; @@ -1101,7 +1116,6 @@ struct TypeTableEntry { // use these fields to make sure we don't duplicate type table entries for the same type TypeTableEntry *pointer_parent[2]; // [0 - mut, 1 - const] - TypeTableEntry *slice_parent[2]; // [0 - mut, 1 - const] TypeTableEntry *maybe_parent; TypeTableEntry *error_parent; // If we generate a constant name value for this type, we memoize it here. @@ -1164,6 +1178,7 @@ struct FnTableEntry { size_t prealloc_bbc; AstNode **param_source_nodes; Buf **param_names; + uint32_t align_bytes; AstNode *fn_no_inline_set_node; AstNode *fn_static_eval_set_node; @@ -1171,8 +1186,6 @@ struct FnTableEntry { ZigList alloca_list; ZigList variable_list; - AstNode *set_global_align_node; - uint32_t alignment; AstNode *set_global_section_node; Buf *section_name; AstNode *set_global_linkage_node; @@ -1187,8 +1200,7 @@ enum BuiltinFnId { BuiltinFnIdMemcpy, BuiltinFnIdMemset, BuiltinFnIdSizeof, - BuiltinFnIdPreferredAlignOf, - BuiltinFnIdAbiAlignOf, + BuiltinFnIdAlignOf, BuiltinFnIdMaxValue, BuiltinFnIdMinValue, BuiltinFnIdMemberCount, @@ -1224,7 +1236,6 @@ enum BuiltinFnId { BuiltinFnIdSetFloatMode, BuiltinFnIdTypeName, BuiltinFnIdCanImplicitCast, - BuiltinFnIdSetGlobalAlign, BuiltinFnIdSetGlobalSection, BuiltinFnIdSetGlobalLinkage, BuiltinFnIdPanic, @@ -1277,6 +1288,7 @@ struct TypeId { TypeTableEntry *child_type; bool is_const; bool is_volatile; + uint32_t alignment; uint32_t bit_offset; uint32_t unaligned_bit_count; } pointer; @@ -1392,7 +1404,7 @@ struct CodeGen { struct { TypeTableEntry *entry_bool; - TypeTableEntry *entry_int[2][10]; // [signed,unsigned][3,4,5,6,7,8,16,32,64,128] + TypeTableEntry *entry_int[2][11]; // [signed,unsigned][2,3,4,5,6,7,8,16,32,64,128] TypeTableEntry *entry_c_int[CIntTypeCount]; TypeTableEntry *entry_c_longdouble; TypeTableEntry *entry_c_void; @@ -1547,6 +1559,8 @@ struct CodeGen { ZigList inline_fns; ZigList tld_ref_source_node_stack; + + TypeTableEntry *align_amt_type; }; enum VarLinkage { @@ -1575,6 +1589,7 @@ struct VariableTableEntry { size_t ref_count; VarLinkage linkage; IrInstruction *decl_instruction; + uint32_t align_bytes; }; struct ErrorTableEntry { @@ -1808,8 +1823,7 @@ enum IrInstructionId { IrInstructionIdBreakpoint, IrInstructionIdReturnAddress, IrInstructionIdFrameAddress, - IrInstructionIdPreferredAlignOf, - IrInstructionIdAbiAlignOf, + IrInstructionIdAlignOf, IrInstructionIdOverflowOp, IrInstructionIdTestErr, IrInstructionIdUnwrapErrCode, @@ -1831,7 +1845,6 @@ enum IrInstructionId { IrInstructionIdCheckStatementIsVoid, IrInstructionIdTypeName, IrInstructionIdCanImplicitCast, - IrInstructionIdSetGlobalAlign, IrInstructionIdSetGlobalSection, IrInstructionIdSetGlobalLinkage, IrInstructionIdDeclRef, @@ -1841,6 +1854,7 @@ enum IrInstructionId { IrInstructionIdOffsetOf, IrInstructionIdTypeId, IrInstructionIdSetEvalBranchQuota, + IrInstructionIdPtrTypeOf, }; struct IrInstruction { @@ -1976,6 +1990,7 @@ struct IrInstructionDeclVar { VariableTableEntry *var; IrInstruction *var_type; + IrInstruction *align_value; IrInstruction *init_value; }; @@ -2152,7 +2167,9 @@ struct IrInstructionArrayType { struct IrInstructionSliceType { IrInstruction base; + IrInstruction *align_value; bool is_const; + bool is_volatile; IrInstruction *child_type; }; @@ -2393,13 +2410,7 @@ struct IrInstructionOverflowOp { TypeTableEntry *result_ptr_type; }; -struct IrInstructionPreferredAlignOf { - IrInstruction base; - - IrInstruction *type_value; -}; - -struct IrInstructionAbiAlignOf { +struct IrInstructionAlignOf { IrInstruction base; IrInstruction *type_value; @@ -2554,13 +2565,6 @@ struct IrInstructionCanImplicitCast { IrInstruction *target_value; }; -struct IrInstructionSetGlobalAlign { - IrInstruction base; - - Tld *tld; - IrInstruction *value; -}; - struct IrInstructionSetGlobalSection { IrInstruction base; @@ -2622,6 +2626,17 @@ struct IrInstructionSetEvalBranchQuota { IrInstruction *new_quota; }; +struct IrInstructionPtrTypeOf { + IrInstruction base; + + IrInstruction *align_value; + IrInstruction *child_type; + uint32_t bit_offset_start; + uint32_t bit_offset_end; + bool is_const; + bool is_volatile; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/analyze.cpp b/src/analyze.cpp index bfa5038ab2..5fcbd92142 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -320,17 +320,21 @@ TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) { } TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type, bool is_const, - bool is_volatile, uint32_t bit_offset, uint32_t unaligned_bit_count) + bool is_volatile, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count) { assert(child_type->id != TypeTableEntryIdInvalid); TypeId type_id = {}; TypeTableEntry **parent_pointer = nullptr; - if (unaligned_bit_count != 0 || is_volatile) { + uint32_t abi_alignment; + if (unaligned_bit_count != 0 || is_volatile || + byte_alignment != (abi_alignment = get_abi_alignment(g, child_type))) + { type_id.id = TypeTableEntryIdPointer; type_id.data.pointer.child_type = child_type; type_id.data.pointer.is_const = is_const; type_id.data.pointer.is_volatile = is_volatile; + type_id.data.pointer.alignment = byte_alignment; type_id.data.pointer.bit_offset = bit_offset; type_id.data.pointer.unaligned_bit_count = unaligned_bit_count; @@ -352,11 +356,14 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type const char *const_str = is_const ? "const " : ""; const char *volatile_str = is_volatile ? "volatile " : ""; buf_resize(&entry->name, 0); - if (unaligned_bit_count == 0) { + if (unaligned_bit_count == 0 && byte_alignment == abi_alignment) { buf_appendf(&entry->name, "&%s%s%s", const_str, volatile_str, buf_ptr(&child_type->name)); + } else if (unaligned_bit_count == 0) { + buf_appendf(&entry->name, "&align %" PRIu32 " %s%s%s", byte_alignment, + const_str, volatile_str, buf_ptr(&child_type->name)); } else { - buf_appendf(&entry->name, "&:%" PRIu32 ":%" PRIu32 " %s%s%s", bit_offset, - bit_offset + unaligned_bit_count, const_str, volatile_str, buf_ptr(&child_type->name)); + buf_appendf(&entry->name, "&align %" PRIu32 ":%" PRIu32 ":%" PRIu32 " %s%s%s", byte_alignment, + bit_offset, bit_offset + unaligned_bit_count, const_str, volatile_str, buf_ptr(&child_type->name)); } assert(child_type->id != TypeTableEntryIdInvalid); @@ -364,20 +371,29 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type entry->zero_bits = !type_has_bits(child_type); if (!entry->zero_bits) { - entry->type_ref = LLVMPointerType(child_type->type_ref, 0); + assert(byte_alignment > 0); + if (is_const || is_volatile || unaligned_bit_count != 0 || byte_alignment != abi_alignment) { + TypeTableEntry *peer_type = get_pointer_to_type(g, child_type, false); + entry->type_ref = peer_type->type_ref; + entry->di_type = peer_type->di_type; + } else { + entry->type_ref = LLVMPointerType(child_type->type_ref, 0); - uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); - uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref); - assert(child_type->di_type); - entry->di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, child_type->di_type, - debug_size_in_bits, debug_align_in_bits, buf_ptr(&entry->name)); + uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); + uint64_t debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, entry->type_ref); + assert(child_type->di_type); + entry->di_type = ZigLLVMCreateDebugPointerType(g->dbuilder, child_type->di_type, + debug_size_in_bits, debug_align_in_bits, buf_ptr(&entry->name)); + } } else { + assert(byte_alignment == 0); entry->di_type = g->builtin_types.entry_void->di_type; } entry->data.pointer.child_type = child_type; entry->data.pointer.is_const = is_const; entry->data.pointer.is_volatile = is_volatile; + entry->data.pointer.alignment = byte_alignment; entry->data.pointer.bit_offset = bit_offset; entry->data.pointer.unaligned_bit_count = unaligned_bit_count; @@ -390,7 +406,7 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type } TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const) { - return get_pointer_to_type_extra(g, child_type, is_const, false, 0, 0); + return get_pointer_to_type_extra(g, child_type, is_const, false, get_abi_alignment(g, child_type), 0, 0); } TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { @@ -592,11 +608,7 @@ TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t return entry; } -static void slice_type_common_init(CodeGen *g, TypeTableEntry *child_type, - bool is_const, TypeTableEntry *entry) -{ - TypeTableEntry *pointer_type = get_pointer_to_type(g, child_type, is_const); - +static void slice_type_common_init(CodeGen *g, TypeTableEntry *pointer_type, TypeTableEntry *entry) { unsigned element_count = 2; entry->data.structure.layout = ContainerLayoutAuto; entry->data.structure.is_slice = true; @@ -612,156 +624,167 @@ static void slice_type_common_init(CodeGen *g, TypeTableEntry *child_type, entry->data.structure.fields[slice_len_index].src_index = slice_len_index; entry->data.structure.fields[slice_len_index].gen_index = 1; - assert(type_has_zero_bits_known(child_type)); - if (child_type->zero_bits) { + assert(type_has_zero_bits_known(pointer_type->data.pointer.child_type)); + if (pointer_type->data.pointer.child_type->zero_bits) { entry->data.structure.gen_field_count = 1; entry->data.structure.fields[slice_ptr_index].gen_index = SIZE_MAX; entry->data.structure.fields[slice_len_index].gen_index = 0; } } -TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_const) { - assert(child_type->id != TypeTableEntryIdInvalid); - TypeTableEntry **parent_pointer = &child_type->slice_parent[(is_const ? 1 : 0)]; +TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *ptr_type) { + assert(ptr_type->id == TypeTableEntryIdPointer); + TypeTableEntry **parent_pointer = &ptr_type->data.pointer.slice_parent; if (*parent_pointer) { return *parent_pointer; - } else if (is_const) { - TypeTableEntry *var_peer = get_slice_type(g, child_type, false); - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct); - entry->is_copyable = true; + } - buf_resize(&entry->name, 0); - buf_appendf(&entry->name, "[]const %s", buf_ptr(&child_type->name)); - - slice_type_common_init(g, child_type, is_const, entry); - - entry->type_ref = var_peer->type_ref; - entry->di_type = var_peer->di_type; - entry->data.structure.complete = true; - entry->data.structure.zero_bits_known = true; - - *parent_pointer = entry; - return entry; - } else { - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct); - entry->is_copyable = true; - - // If the child type is []const T then we need to make sure the type ref - // and debug info is the same as if the child type were []T. - if (is_slice(child_type)) { - TypeTableEntry *ptr_type = child_type->data.structure.fields[slice_ptr_index].type_entry; - assert(ptr_type->id == TypeTableEntryIdPointer); - if (ptr_type->data.pointer.is_const) { - TypeTableEntry *non_const_child_type = get_slice_type(g, - ptr_type->data.pointer.child_type, false); - TypeTableEntry *var_peer = get_slice_type(g, non_const_child_type, false); - - entry->type_ref = var_peer->type_ref; - entry->di_type = var_peer->di_type; - } - } - - buf_resize(&entry->name, 0); - buf_appendf(&entry->name, "[]%s", buf_ptr(&child_type->name)); - - slice_type_common_init(g, child_type, is_const, entry); - - if (!entry->type_ref) { - entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&entry->name)); - - ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit); - ZigLLVMDIFile *di_file = nullptr; - unsigned line = 0; - entry->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder, - ZigLLVMTag_DW_structure_type(), buf_ptr(&entry->name), - compile_unit_scope, di_file, line); - - if (child_type->zero_bits) { - LLVMTypeRef element_types[] = { - g->builtin_types.entry_usize->type_ref, - }; - LLVMStructSetBody(entry->type_ref, element_types, 1, false); - - TypeTableEntry *usize_type = g->builtin_types.entry_usize; - uint64_t len_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, usize_type->type_ref); - uint64_t len_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, usize_type->type_ref); - uint64_t len_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, 0); - - uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); - uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref); - - ZigLLVMDIType *di_element_types[] = { - ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type), - "len", di_file, line, - len_debug_size_in_bits, - len_debug_align_in_bits, - len_offset_in_bits, - 0, usize_type->di_type), - }; - ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, - compile_unit_scope, - buf_ptr(&entry->name), - di_file, line, debug_size_in_bits, debug_align_in_bits, 0, - nullptr, di_element_types, 1, 0, nullptr, ""); - - ZigLLVMReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type); - entry->di_type = replacement_di_type; - } else { - TypeTableEntry *pointer_type = get_pointer_to_type(g, child_type, is_const); - - unsigned element_count = 2; - LLVMTypeRef element_types[] = { - pointer_type->type_ref, - g->builtin_types.entry_usize->type_ref, - }; - LLVMStructSetBody(entry->type_ref, element_types, element_count, false); - - - uint64_t ptr_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, pointer_type->type_ref); - uint64_t ptr_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, pointer_type->type_ref); - uint64_t ptr_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, 0); - - TypeTableEntry *usize_type = g->builtin_types.entry_usize; - uint64_t len_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, usize_type->type_ref); - uint64_t len_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, usize_type->type_ref); - uint64_t len_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, 1); - - uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); - uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref); - - ZigLLVMDIType *di_element_types[] = { - ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type), - "ptr", di_file, line, - ptr_debug_size_in_bits, - ptr_debug_align_in_bits, - ptr_offset_in_bits, - 0, pointer_type->di_type), - ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type), - "len", di_file, line, - len_debug_size_in_bits, - len_debug_align_in_bits, - len_offset_in_bits, - 0, usize_type->di_type), - }; - ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, - compile_unit_scope, - buf_ptr(&entry->name), - di_file, line, debug_size_in_bits, debug_align_in_bits, 0, - nullptr, di_element_types, 2, 0, nullptr, ""); - - ZigLLVMReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type); - entry->di_type = replacement_di_type; - } - } + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct); + entry->is_copyable = true; + // replace the & with [] to go from a ptr type name to a slice type name + buf_resize(&entry->name, 0); + buf_appendf(&entry->name, "[]%s", buf_ptr(&ptr_type->name) + 1); + TypeTableEntry *child_type = ptr_type->data.pointer.child_type; + uint32_t abi_alignment; + if (ptr_type->data.pointer.is_const || ptr_type->data.pointer.is_volatile || + ptr_type->data.pointer.alignment != (abi_alignment = get_abi_alignment(g, child_type))) + { + TypeTableEntry *peer_ptr_type = get_pointer_to_type(g, child_type, false); + TypeTableEntry *peer_slice_type = get_slice_type(g, peer_ptr_type); + + slice_type_common_init(g, ptr_type, entry); + + entry->type_ref = peer_slice_type->type_ref; + entry->di_type = peer_slice_type->di_type; entry->data.structure.complete = true; entry->data.structure.zero_bits_known = true; + entry->data.structure.abi_alignment = peer_slice_type->data.structure.abi_alignment; *parent_pointer = entry; return entry; } + + // If the child type is []const T then we need to make sure the type ref + // and debug info is the same as if the child type were []T. + if (is_slice(child_type)) { + TypeTableEntry *child_ptr_type = child_type->data.structure.fields[slice_ptr_index].type_entry; + assert(child_ptr_type->id == TypeTableEntryIdPointer); + TypeTableEntry *grand_child_type = child_ptr_type->data.pointer.child_type; + if (child_ptr_type->data.pointer.is_const || child_ptr_type->data.pointer.is_volatile || + child_ptr_type->data.pointer.alignment != get_abi_alignment(g, grand_child_type)) + { + TypeTableEntry *bland_child_ptr_type = get_pointer_to_type(g, grand_child_type, false); + TypeTableEntry *bland_child_slice = get_slice_type(g, bland_child_ptr_type); + TypeTableEntry *peer_ptr_type = get_pointer_to_type(g, bland_child_slice, false); + TypeTableEntry *peer_slice_type = get_slice_type(g, peer_ptr_type); + + entry->type_ref = peer_slice_type->type_ref; + entry->di_type = peer_slice_type->di_type; + entry->data.structure.abi_alignment = peer_slice_type->data.structure.abi_alignment; + } + } + + slice_type_common_init(g, ptr_type, entry); + + if (!entry->type_ref) { + entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&entry->name)); + + ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit); + ZigLLVMDIFile *di_file = nullptr; + unsigned line = 0; + entry->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder, + ZigLLVMTag_DW_structure_type(), buf_ptr(&entry->name), + compile_unit_scope, di_file, line); + + if (child_type->zero_bits) { + LLVMTypeRef element_types[] = { + g->builtin_types.entry_usize->type_ref, + }; + LLVMStructSetBody(entry->type_ref, element_types, 1, false); + + TypeTableEntry *usize_type = g->builtin_types.entry_usize; + uint64_t len_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, usize_type->type_ref); + uint64_t len_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, usize_type->type_ref); + uint64_t len_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, 0); + + uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); + uint64_t debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, entry->type_ref); + + ZigLLVMDIType *di_element_types[] = { + ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type), + "len", di_file, line, + len_debug_size_in_bits, + len_debug_align_in_bits, + len_offset_in_bits, + 0, usize_type->di_type), + }; + ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, + compile_unit_scope, + buf_ptr(&entry->name), + di_file, line, debug_size_in_bits, debug_align_in_bits, 0, + nullptr, di_element_types, 1, 0, nullptr, ""); + + ZigLLVMReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type); + entry->di_type = replacement_di_type; + + entry->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, usize_type->type_ref); + } else { + unsigned element_count = 2; + LLVMTypeRef element_types[] = { + ptr_type->type_ref, + g->builtin_types.entry_usize->type_ref, + }; + LLVMStructSetBody(entry->type_ref, element_types, element_count, false); + + + uint64_t ptr_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, ptr_type->type_ref); + uint64_t ptr_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, ptr_type->type_ref); + uint64_t ptr_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, 0); + + TypeTableEntry *usize_type = g->builtin_types.entry_usize; + uint64_t len_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, usize_type->type_ref); + uint64_t len_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, usize_type->type_ref); + uint64_t len_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, 1); + + uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); + uint64_t debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, entry->type_ref); + + ZigLLVMDIType *di_element_types[] = { + ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type), + "ptr", di_file, line, + ptr_debug_size_in_bits, + ptr_debug_align_in_bits, + ptr_offset_in_bits, + 0, ptr_type->di_type), + ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type), + "len", di_file, line, + len_debug_size_in_bits, + len_debug_align_in_bits, + len_offset_in_bits, + 0, usize_type->di_type), + }; + ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, + compile_unit_scope, + buf_ptr(&entry->name), + di_file, line, debug_size_in_bits, debug_align_in_bits, 0, + nullptr, di_element_types, 2, 0, nullptr, ""); + + ZigLLVMReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type); + entry->di_type = replacement_di_type; + + entry->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, entry->type_ref); + } + } + + + entry->data.structure.complete = true; + entry->data.structure.zero_bits_known = true; + + *parent_pointer = entry; + return entry; } TypeTableEntry *get_opaque_type(CodeGen *g, Scope *scope, AstNode *source_node, const char *name) { @@ -1273,26 +1296,24 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) { continue; uint64_t store_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, field_type->type_ref); - uint64_t preferred_align_in_bits = 8*LLVMPreferredAlignmentOfType(g->target_data_ref, field_type->type_ref); + uint64_t abi_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, field_type->type_ref); assert(store_size_in_bits > 0); - assert(preferred_align_in_bits > 0); + assert(abi_align_in_bits > 0); union_inner_di_types[type_enum_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(enum_type->di_type), buf_ptr(type_enum_field->name), import->di_file, (unsigned)(field_node->line + 1), store_size_in_bits, - preferred_align_in_bits, + abi_align_in_bits, 0, 0, field_type->di_type); biggest_size_in_bits = max(biggest_size_in_bits, store_size_in_bits); - if (!most_aligned_union_member || - preferred_align_in_bits > biggest_align_in_bits) - { + if (!most_aligned_union_member || abi_align_in_bits > biggest_align_in_bits) { most_aligned_union_member = field_type; - biggest_align_in_bits = preferred_align_in_bits; + biggest_align_in_bits = abi_align_in_bits; size_of_most_aligned_member_in_bits = store_size_in_bits; } } @@ -1306,7 +1327,7 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) { TypeTableEntry *tag_type_entry = create_enum_tag_type(g, enum_type, tag_int_type); enum_type->data.enumeration.tag_type = tag_type_entry; - uint64_t align_of_tag_in_bits = 8*LLVMPreferredAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); + uint64_t align_of_tag_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); if (most_aligned_union_member) { // create llvm type for union @@ -1328,7 +1349,7 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) { } enum_type->data.enumeration.union_type_ref = union_type_ref; - assert(8*LLVMPreferredAlignmentOfType(g->target_data_ref, union_type_ref) >= biggest_align_in_bits); + assert(8*LLVMABIAlignmentOfType(g->target_data_ref, union_type_ref) >= biggest_align_in_bits); assert(8*LLVMStoreSizeOfType(g->target_data_ref, union_type_ref) >= biggest_size_in_bits); if (align_of_tag_in_bits >= biggest_align_in_bits) { @@ -1347,7 +1368,7 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) { // create debug type for tag uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref); - uint64_t tag_debug_align_in_bits = 8*LLVMPreferredAlignmentOfType(g->target_data_ref, tag_type_entry->type_ref); + uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type_entry->type_ref); ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, ZigLLVMTypeToScope(enum_type->di_type), "AnonEnum", import->di_file, (unsigned)(decl_node->line + 1), @@ -1405,7 +1426,7 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) { // create debug type for tag uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref); - uint64_t tag_debug_align_in_bits = 8*LLVMPreferredAlignmentOfType(g->target_data_ref, tag_type_entry->type_ref); + uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type_entry->type_ref); ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, ZigLLVMFileToScope(import->di_file), buf_ptr(&enum_type->name), import->di_file, (unsigned)(decl_node->line + 1), @@ -1497,7 +1518,7 @@ TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *f TypeStructField *type_struct_field = &struct_type->data.structure.fields[i]; TypeTableEntry *field_type = type_struct_field->type_entry; uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, field_type->type_ref); - uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, field_type->type_ref); + uint64_t debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, field_type->type_ref); uint64_t debug_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, struct_type->type_ref, i); di_element_types[i] = ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(struct_type->di_type), buf_ptr(type_struct_field->name), @@ -1522,6 +1543,7 @@ TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *f ZigLLVMReplaceTemporary(g->dbuilder, struct_type->di_type, replacement_di_type); struct_type->di_type = replacement_di_type; + struct_type->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, struct_type->type_ref); return struct_type; } @@ -1663,6 +1685,10 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) { struct_type->data.structure.gen_field_count = (uint32_t)gen_field_count; LLVMStructSetBody(struct_type->type_ref, element_types, (unsigned)gen_field_count, packed); + + // if you hit this assert then probably this type or a related type didn't + // get ensure_complete_type called on it before using it with something that + // requires a complete type assert(LLVMStoreSizeOfType(g->target_data_ref, struct_type->type_ref) > 0); ZigLLVMDIType **di_element_types = allocate(debug_field_count); @@ -1760,6 +1786,8 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) { enum_type->data.enumeration.src_field_count = field_count; enum_type->data.enumeration.fields = allocate(field_count); + uint32_t biggest_align_bytes = 0; + Scope *scope = &enum_type->data.enumeration.decls_scope->base; uint32_t gen_field_index = 0; @@ -1782,12 +1810,22 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) { type_enum_field->gen_index = gen_field_index; gen_field_index += 1; + + uint32_t field_align_bytes = get_abi_alignment(g, field_type); + if (field_align_bytes > biggest_align_bytes) { + biggest_align_bytes = field_align_bytes; + } } enum_type->data.enumeration.zero_bits_loop_flag = false; enum_type->data.enumeration.gen_field_count = gen_field_index; enum_type->zero_bits = (gen_field_index == 0 && field_count < 2); enum_type->data.enumeration.zero_bits_known = true; + + // also compute abi_alignment + TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count); + uint32_t align_of_tag_in_bytes = LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); + enum_type->data.enumeration.abi_alignment = max(align_of_tag_in_bytes, biggest_align_bytes); } static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) { @@ -1797,7 +1835,19 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) { return; if (struct_type->data.structure.zero_bits_loop_flag) { + // If we get here it's due to recursion. From this we conclude that the struct is + // not zero bits, and if abi_alignment == 0 we further conclude that the first field + // is a pointer to this very struct, or a function pointer with parameters that + // reference such a type. struct_type->data.structure.zero_bits_known = true; + if (struct_type->data.structure.abi_alignment == 0) { + if (struct_type->data.structure.layout == ContainerLayoutPacked) { + struct_type->data.structure.abi_alignment = 1; + } else { + struct_type->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, + LLVMPointerType(LLVMInt8Type(), 0)); + } + } return; } @@ -1833,6 +1883,17 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) { if (!type_has_bits(field_type)) continue; + if (gen_field_index == 0) { + if (struct_type->data.structure.layout == ContainerLayoutPacked) { + struct_type->data.structure.abi_alignment = 1; + } else { + // Alignment of structs is the alignment of the first field, for now. + // TODO change this when we re-order struct fields (issue #168) + struct_type->data.structure.abi_alignment = get_abi_alignment(g, field_type); + assert(struct_type->data.structure.abi_alignment != 0); + } + } + type_struct_field->gen_index = gen_field_index; gen_field_index += 1; } @@ -1925,7 +1986,8 @@ static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) { if (fn_type_id->param_count != 1) { return wrong_panic_prototype(g, proto_node, fn_type); } - TypeTableEntry *const_u8_slice = get_slice_type(g, g->builtin_types.entry_u8, true); + TypeTableEntry *const_u8_ptr = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + TypeTableEntry *const_u8_slice = get_slice_type(g, const_u8_ptr); if (fn_type_id->param_info[0].type != const_u8_slice) { return wrong_panic_prototype(g, proto_node, fn_type); } @@ -1946,6 +2008,25 @@ TypeTableEntry *get_test_fn_type(CodeGen *g) { return g->test_fn_type; } +static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_t *result) { + IrInstruction *align_result = analyze_const_value(g, scope, node, get_align_amt_type(g), nullptr); + if (type_is_invalid(align_result->value.type)) + return false; + + uint32_t align_bytes = bigint_as_unsigned(&align_result->value.data.x_bigint); + if (align_bytes == 0) { + add_node_error(g, node, buf_sprintf("alignment must be >= 1")); + return false; + } + if (!is_power_of_2(align_bytes)) { + add_node_error(g, node, buf_sprintf("alignment value %" PRIu32 " is not a power of 2", align_bytes)); + return false; + } + + *result = align_bytes; + return true; +} + static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { ImportTableEntry *import = tld_fn->base.import; AstNode *source_node = tld_fn->base.source_node; @@ -1982,6 +2063,16 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { return; } + if (fn_proto->align_expr != nullptr) { + if (!analyze_const_align(g, tld_fn->base.parent_scope, fn_proto->align_expr, + &fn_table_entry->align_bytes)) + { + fn_table_entry->type_entry = g->builtin_types.entry_invalid; + tld_fn->base.resolution = TldResolutionInvalid; + return; + } + } + if (!fn_table_entry->type_entry->data.fn.is_generic) { g->fn_protos.append(fn_table_entry); @@ -2149,6 +2240,7 @@ void update_compile_var(CodeGen *g, Buf *name, ConstExprValue *value) { assert(tld->id == TldIdVar); TldVar *tld_var = (TldVar *)tld; tld_var->var->value = value; + tld_var->var->align_bytes = get_abi_alignment(g, value->type); } void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { @@ -2236,6 +2328,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeThisLiteral: case NodeTypeSymbol: case NodeTypePrefixOpExpr: + case NodeTypeAddrOfExpr: case NodeTypeIfBoolExpr: case NodeTypeWhileExpr: case NodeTypeForExpr: @@ -2331,6 +2424,7 @@ VariableTableEntry *add_variable(CodeGen *g, AstNode *source_node, Scope *parent variable_entry->shadowable = false; variable_entry->mem_slot_index = SIZE_MAX; variable_entry->src_arg_index = SIZE_MAX; + variable_entry->align_bytes = get_abi_alignment(g, value->type); assert(name); @@ -2389,7 +2483,8 @@ VariableTableEntry *add_variable(CodeGen *g, AstNode *source_node, Scope *parent } static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { - AstNodeVariableDeclaration *var_decl = &tld_var->base.source_node->data.variable_declaration; + AstNode *source_node = tld_var->base.source_node; + AstNodeVariableDeclaration *var_decl = &source_node->data.variable_declaration; bool is_const = var_decl->is_const; bool is_export = (tld_var->base.visib_mod == VisibModExport); @@ -2401,8 +2496,6 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { explicit_type = validate_var_type(g, var_decl->type, proposed_type); } - AstNode *source_node = tld_var->base.source_node; - if (is_export && is_extern) { add_node_error(g, source_node, buf_sprintf("variable is both export and extern")); } @@ -2458,6 +2551,12 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { is_const, init_val, &tld_var->base); tld_var->var->linkage = linkage; + if (var_decl->align_expr != nullptr) { + if (!analyze_const_align(g, tld_var->base.parent_scope, var_decl->align_expr, &tld_var->var->align_bytes)) { + tld_var->var->value->type = g->builtin_types.entry_invalid; + } + } + g->global_vars.append(tld_var); } @@ -3129,26 +3228,28 @@ void semantic_analyze(CodeGen *g) { TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, uint32_t size_in_bits) { size_t index; - if (size_in_bits == 3) { + if (size_in_bits == 2) { index = 0; - } else if (size_in_bits == 4) { + } else if (size_in_bits == 3) { index = 1; - } else if (size_in_bits == 5) { + } else if (size_in_bits == 4) { index = 2; - } else if (size_in_bits == 6) { + } else if (size_in_bits == 5) { index = 3; - } else if (size_in_bits == 7) { + } else if (size_in_bits == 6) { index = 4; - } else if (size_in_bits == 8) { + } else if (size_in_bits == 7) { index = 5; - } else if (size_in_bits == 16) { + } else if (size_in_bits == 8) { index = 6; - } else if (size_in_bits == 32) { + } else if (size_in_bits == 16) { index = 7; - } else if (size_in_bits == 64) { + } else if (size_in_bits == 32) { index = 8; - } else if (size_in_bits == 128) { + } else if (size_in_bits == 64) { index = 9; + } else if (size_in_bits == 128) { + index = 10; } else { return nullptr; } @@ -3723,8 +3824,10 @@ void init_const_slice(CodeGen *g, ConstExprValue *const_val, ConstExprValue *arr { assert(array_val->type->id == TypeTableEntryIdArray); + TypeTableEntry *ptr_type = get_pointer_to_type(g, array_val->type->data.array.child_type, is_const); + const_val->special = ConstValSpecialStatic; - const_val->type = get_slice_type(g, array_val->type->data.array.child_type, is_const); + const_val->type = get_slice_type(g, ptr_type); const_val->data.x_struct.fields = create_const_vals(2); init_const_ptr_array(g, &const_val->data.x_struct.fields[slice_ptr_index], array_val, start, is_const); @@ -4342,14 +4445,15 @@ uint32_t type_id_hash(TypeId x) { return hash_ptr(x.data.pointer.child_type) + (x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) + (x.data.pointer.is_volatile ? (uint32_t)536730450 : (uint32_t)1685612214) + - (((uint32_t)x.data.pointer.bit_offset) * (uint32_t)2639019452) + - (((uint32_t)x.data.pointer.unaligned_bit_count) * (uint32_t)529908881); + (((uint32_t)x.data.pointer.alignment) ^ (uint32_t)0x777fbe0e) + + (((uint32_t)x.data.pointer.bit_offset) ^ (uint32_t)2639019452) + + (((uint32_t)x.data.pointer.unaligned_bit_count) ^ (uint32_t)529908881); case TypeTableEntryIdArray: return hash_ptr(x.data.array.child_type) + - ((uint32_t)x.data.array.size * (uint32_t)2122979968); + ((uint32_t)x.data.array.size ^ (uint32_t)2122979968); case TypeTableEntryIdInt: return (x.data.integer.is_signed ? (uint32_t)2652528194 : (uint32_t)163929201) + - (((uint32_t)x.data.integer.bit_count) * (uint32_t)2998081557); + (((uint32_t)x.data.integer.bit_count) ^ (uint32_t)2998081557); } zig_unreachable(); } @@ -4387,6 +4491,7 @@ bool type_id_eql(TypeId a, TypeId b) { return a.data.pointer.child_type == b.data.pointer.child_type && a.data.pointer.is_const == b.data.pointer.is_const && a.data.pointer.is_volatile == b.data.pointer.is_volatile && + a.data.pointer.alignment == b.data.pointer.alignment && a.data.pointer.bit_offset == b.data.pointer.bit_offset && a.data.pointer.unaligned_bit_count == b.data.pointer.unaligned_bit_count; case TypeTableEntryIdArray: @@ -4692,3 +4797,30 @@ void add_link_lib_symbol(CodeGen *g, Buf *lib_name, Buf *symbol_name) { } link_lib->symbols.append(symbol_name); } + +uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) { + type_ensure_zero_bits_known(g, type_entry); + if (type_entry->zero_bits) return 0; + + // We need to make this function work without requiring ensure_complete_type + // so that we can have structs with fields that are pointers to their own type. + if (type_entry->id == TypeTableEntryIdStruct) { + assert(type_entry->data.structure.abi_alignment != 0); + return type_entry->data.structure.abi_alignment; + } else if (type_entry->id == TypeTableEntryIdEnum) { + assert(type_entry->data.enumeration.abi_alignment != 0); + return type_entry->data.enumeration.abi_alignment; + } else if (type_entry->id == TypeTableEntryIdUnion) { + zig_panic("TODO"); + } else { + return LLVMABIAlignmentOfType(g->target_data_ref, type_entry->type_ref); + } +} + +TypeTableEntry *get_align_amt_type(CodeGen *g) { + if (g->align_amt_type == nullptr) { + // according to LLVM the maximum alignment is 1 << 29. + g->align_amt_type = get_int_type(g, false, 29); + } + return g->align_amt_type; +} diff --git a/src/analyze.hpp b/src/analyze.hpp index 90600da790..79a2cf1daf 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -16,7 +16,7 @@ ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *m TypeTableEntry *new_type_table_entry(TypeTableEntryId id); TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const); TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type, bool is_const, - bool is_volatile, uint32_t bit_offset, uint32_t unaligned_bit_count); + bool is_volatile, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count); uint64_t type_size(CodeGen *g, TypeTableEntry *type_entry); uint64_t type_size_bits(CodeGen *g, TypeTableEntry *type_entry); TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, uint32_t size_in_bits); @@ -26,7 +26,7 @@ TypeTableEntry *get_c_int_type(CodeGen *g, CIntType c_int_type); TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id); TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type); TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size); -TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_const); +TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *ptr_type); TypeTableEntry *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind, AstNode *decl_node, const char *name, ContainerLayout layout); TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x); @@ -171,4 +171,7 @@ bool calling_convention_does_first_arg_return(CallingConvention cc); LinkLib *add_link_lib(CodeGen *codegen, Buf *lib); void add_link_lib_symbol(CodeGen *g, Buf *lib_name, Buf *symbol_name); +uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry); +TypeTableEntry *get_align_amt_type(CodeGen *g); + #endif diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 23a83fadd0..b053a4e8af 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -65,10 +65,6 @@ static const char *prefix_op_str(PrefixOp prefix_op) { case PrefixOpNegationWrap: return "-%"; case PrefixOpBoolNot: return "!"; case PrefixOpBinNot: return "~"; - case PrefixOpAddressOf: return "&"; - case PrefixOpConstAddressOf: return "&const "; - case PrefixOpVolatileAddressOf: return "&volatile "; - case PrefixOpConstVolatileAddressOf: return "&const volatile "; case PrefixOpDereference: return "*"; case PrefixOpMaybe: return "?"; case PrefixOpError: return "%"; @@ -192,6 +188,8 @@ static const char *node_type_str(NodeType node_type) { return "Symbol"; case NodeTypePrefixOpExpr: return "PrefixOpExpr"; + case NodeTypeAddrOfExpr: + return "AddrOfExpr"; case NodeTypeUse: return "Use"; case NodeTypeBoolLiteral: @@ -583,6 +581,38 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { render_node_ungrouped(ar, node->data.prefix_op_expr.primary_expr); break; } + case NodeTypeAddrOfExpr: + { + fprintf(ar->f, "&"); + if (node->data.addr_of_expr.align_expr != nullptr) { + fprintf(ar->f, "align "); + render_node_grouped(ar, node->data.addr_of_expr.align_expr); + if (node->data.addr_of_expr.bit_offset_start != nullptr) { + assert(node->data.addr_of_expr.bit_offset_end != nullptr); + + Buf offset_start_buf = BUF_INIT; + buf_resize(&offset_start_buf, 0); + bigint_append_buf(&offset_start_buf, node->data.addr_of_expr.bit_offset_start, 10); + + Buf offset_end_buf = BUF_INIT; + buf_resize(&offset_end_buf, 0); + bigint_append_buf(&offset_end_buf, node->data.addr_of_expr.bit_offset_end, 10); + + fprintf(ar->f, ":%s:%s ", buf_ptr(&offset_start_buf), buf_ptr(&offset_end_buf)); + } else { + fprintf(ar->f, " "); + } + } + if (node->data.addr_of_expr.is_const) { + fprintf(ar->f, "const "); + } + if (node->data.addr_of_expr.is_volatile) { + fprintf(ar->f, "volatile "); + } + + render_node_ungrouped(ar, node->data.addr_of_expr.op_expr); + break; + } case NodeTypeFnCallExpr: { if (node->data.fn_call_expr.is_builtin) { diff --git a/src/codegen.cpp b/src/codegen.cpp index b00d6f6474..840836c622 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -350,6 +350,12 @@ static LLVMCallConv get_llvm_cc(CodeGen *g, CallingConvention cc) { zig_unreachable(); } +static uint32_t get_pref_fn_align(CodeGen *g, LLVMTypeRef fn_type_ref) { + uint32_t pref_align = LLVMPreferredAlignmentOfType(g->target_data_ref, fn_type_ref); + uint32_t abi_align = LLVMABIAlignmentOfType(g->target_data_ref, fn_type_ref); + return (pref_align > abi_align) ? pref_align : abi_align; +} + static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { if (fn_table_entry->llvm_value) return fn_table_entry->llvm_value; @@ -442,14 +448,14 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { if (fn_table_entry->section_name) { LLVMSetSection(fn_table_entry->llvm_value, buf_ptr(fn_table_entry->section_name)); } - if (fn_table_entry->alignment) { - LLVMSetAlignment(fn_table_entry->llvm_value, (unsigned)fn_table_entry->alignment); - } else if (external_linkage) { + if (fn_table_entry->align_bytes > 0) { + LLVMSetAlignment(fn_table_entry->llvm_value, (unsigned)fn_table_entry->align_bytes); + } else if (fn_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionUnspecified) { LLVMSetAlignment(fn_table_entry->llvm_value, - LLVMABIAlignmentOfType(g->target_data_ref, fn_table_entry->type_entry->data.fn.raw_type_ref)); + get_pref_fn_align(g, fn_table_entry->type_entry->data.fn.raw_type_ref)); } else { LLVMSetAlignment(fn_table_entry->llvm_value, - LLVMPreferredAlignmentOfType(g->target_data_ref, fn_table_entry->type_entry->data.fn.raw_type_ref)); + LLVMABIAlignmentOfType(g->target_data_ref, fn_table_entry->type_entry->data.fn.raw_type_ref)); } return fn_table_entry->llvm_value; @@ -604,13 +610,15 @@ static LLVMValueRef get_floor_ceil_fn(CodeGen *g, TypeTableEntry *type_entry, Zi return fn_val; } -static LLVMValueRef get_handle_value(CodeGen *g, LLVMValueRef ptr, TypeTableEntry *type, bool is_volatile) { +static LLVMValueRef get_handle_value(CodeGen *g, LLVMValueRef ptr, TypeTableEntry *type, TypeTableEntry *ptr_type) { if (type_has_bits(type)) { if (handle_is_ptr(type)) { return ptr; } else { + assert(ptr_type->id == TypeTableEntryIdPointer); LLVMValueRef result = LLVMBuildLoad(g->builder, ptr, ""); - LLVMSetVolatile(result, is_volatile); + LLVMSetVolatile(result, ptr_type->data.pointer.is_volatile); + LLVMSetAlignment(result, ptr_type->data.pointer.alignment); return result; } } else { @@ -657,34 +665,6 @@ static bool ir_want_debug_safety(CodeGen *g, IrInstruction *instruction) { return true; } -static bool is_array_of_at_least_n_bytes(CodeGen *g, TypeTableEntry *type_entry, uint32_t n) { - if (type_entry->id != TypeTableEntryIdArray) - return false; - - TypeTableEntry *child_type = type_entry->data.array.child_type; - if (child_type->id != TypeTableEntryIdInt) - return false; - - if (child_type != g->builtin_types.entry_u8) - return false; - - if (type_entry->data.array.len < n) - return false; - - return true; -} - -static uint32_t get_type_alignment(CodeGen *g, TypeTableEntry *type_entry) { - uint32_t alignment = ZigLLVMGetPrefTypeAlignment(g->target_data_ref, type_entry->type_ref); - uint32_t dbl_ptr_bytes = g->pointer_size_bytes * 2; - if (is_array_of_at_least_n_bytes(g, type_entry, dbl_ptr_bytes)) { - return (alignment < dbl_ptr_bytes) ? dbl_ptr_bytes : alignment; - } else { - return alignment; - } -} - - static Buf *panic_msg_buf(PanicMsgId msg_id) { switch (msg_id) { case PanicMsgIdCount: @@ -745,7 +725,8 @@ static void gen_panic_raw(CodeGen *g, LLVMValueRef msg_ptr, LLVMValueRef msg_len } static void gen_panic(CodeGen *g, LLVMValueRef msg_arg) { - TypeTableEntry *str_type = get_slice_type(g, g->builtin_types.entry_u8, true); + TypeTableEntry *ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + TypeTableEntry *str_type = get_slice_type(g, ptr_type); size_t ptr_index = str_type->data.structure.fields[slice_ptr_index].gen_index; size_t len_index = str_type->data.structure.fields[slice_len_index].gen_index; LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, msg_arg, (unsigned)ptr_index, ""); @@ -806,7 +787,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { LLVMSetLinkage(global_value, LLVMInternalLinkage); LLVMSetGlobalConstant(global_value, false); LLVMSetUnnamedAddr(global_value, true); - LLVMSetAlignment(global_value, get_type_alignment(g, g->builtin_types.entry_u8)); + LLVMSetAlignment(global_value, get_abi_alignment(g, g->builtin_types.entry_u8)); TypeTableEntry *usize = g->builtin_types.entry_usize; LLVMValueRef full_buf_ptr_indices[] = { @@ -833,7 +814,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true"); ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim-non-leaf", nullptr); } - LLVMSetAlignment(fn_val, LLVMPreferredAlignmentOfType(g->target_data_ref, fn_type_ref)); + LLVMSetAlignment(fn_val, get_pref_fn_align(g, fn_type_ref)); LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry"); LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder); @@ -1056,50 +1037,49 @@ static LLVMRealPredicate cmp_op_to_real_predicate(IrBinOp cmp_op) { } } -static LLVMValueRef gen_struct_memcpy(CodeGen *g, LLVMValueRef src, LLVMValueRef dest, - TypeTableEntry *type_entry) -{ - assert(handle_is_ptr(type_entry)); - - assert(LLVMGetTypeKind(LLVMTypeOf(src)) == LLVMPointerTypeKind); - assert(LLVMGetTypeKind(LLVMTypeOf(dest)) == LLVMPointerTypeKind); - - LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); - - LLVMValueRef src_ptr = LLVMBuildBitCast(g->builder, src, ptr_u8, ""); - LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, dest, ptr_u8, ""); - - TypeTableEntry *usize = g->builtin_types.entry_usize; - uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, type_entry->type_ref); - uint64_t align_bytes = get_type_alignment(g, type_entry); - assert(size_bytes > 0); - assert(align_bytes > 0); - - LLVMValueRef params[] = { - dest_ptr, // dest pointer - src_ptr, // source pointer - LLVMConstInt(usize->type_ref, size_bytes, false), - LLVMConstInt(LLVMInt32Type(), align_bytes, false), - LLVMConstNull(LLVMInt1Type()), // is volatile - }; - - return LLVMBuildCall(g->builder, get_memcpy_fn_val(g), params, 5, ""); -} - static LLVMValueRef gen_assign_raw(CodeGen *g, LLVMValueRef ptr, TypeTableEntry *ptr_type, LLVMValueRef value) { + assert(ptr_type->id == TypeTableEntryIdPointer); TypeTableEntry *child_type = ptr_type->data.pointer.child_type; if (!type_has_bits(child_type)) return nullptr; - if (handle_is_ptr(child_type)) - return gen_struct_memcpy(g, value, ptr, child_type); + if (handle_is_ptr(child_type)) { + assert(LLVMGetTypeKind(LLVMTypeOf(value)) == LLVMPointerTypeKind); + assert(LLVMGetTypeKind(LLVMTypeOf(ptr)) == LLVMPointerTypeKind); + + LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); + + LLVMValueRef src_ptr = LLVMBuildBitCast(g->builder, value, ptr_u8, ""); + LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, ptr, ptr_u8, ""); + + TypeTableEntry *usize = g->builtin_types.entry_usize; + uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, child_type->type_ref); + uint64_t align_bytes = ptr_type->data.pointer.alignment; + assert(size_bytes > 0); + assert(align_bytes > 0); + + LLVMValueRef volatile_bit = ptr_type->data.pointer.is_volatile ? + LLVMConstAllOnes(LLVMInt1Type()) : LLVMConstNull(LLVMInt1Type()); + + LLVMValueRef params[] = { + dest_ptr, // dest pointer + src_ptr, // source pointer + LLVMConstInt(usize->type_ref, size_bytes, false), + LLVMConstInt(LLVMInt32Type(), align_bytes, false), + volatile_bit, + }; + + LLVMBuildCall(g->builder, get_memcpy_fn_val(g), params, 5, ""); + return nullptr; + } uint32_t unaligned_bit_count = ptr_type->data.pointer.unaligned_bit_count; if (unaligned_bit_count == 0) { LLVMValueRef llvm_instruction = LLVMBuildStore(g->builder, value, ptr); + LLVMSetAlignment(llvm_instruction, ptr_type->data.pointer.alignment); LLVMSetVolatile(llvm_instruction, ptr_type->data.pointer.is_volatile); return nullptr; } @@ -1122,6 +1102,7 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, LLVMValueRef ptr, TypeTableEntry LLVMValueRef ored_value = LLVMBuildOr(g->builder, shifted_value, anded_containing_int, ""); LLVMValueRef llvm_instruction = LLVMBuildStore(g->builder, ored_value, ptr); + LLVMSetAlignment(llvm_instruction, ptr_type->data.pointer.alignment); LLVMSetVolatile(llvm_instruction, ptr_type->data.pointer.is_volatile); return nullptr; } @@ -2010,23 +1991,24 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, if (have_init_expr) { assert(var->value->type == init_value->value.type); - gen_assign_raw(g, var->value_ref, get_pointer_to_type(g, var->value->type, false), - ir_llvm_value(g, init_value)); + TypeTableEntry *var_ptr_type = get_pointer_to_type_extra(g, var->value->type, false, false, + var->align_bytes, 0, 0); + gen_assign_raw(g, var->value_ref, var_ptr_type, ir_llvm_value(g, init_value)); } else { - bool ignore_uninit = false; - // handle runtime stack allocation bool want_safe = ir_want_debug_safety(g, &decl_var_instruction->base); - if (!ignore_uninit && want_safe) { + if (want_safe) { TypeTableEntry *usize = g->builtin_types.entry_usize; uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, var->value->type->type_ref); - uint64_t align_bytes = get_type_alignment(g, var->value->type); + assert(size_bytes > 0); + + assert(var->align_bytes > 0); // memset uninitialized memory to 0xa LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false); LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, var->value_ref, ptr_u8, ""); LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false); - LLVMValueRef align_in_bytes = LLVMConstInt(LLVMInt32Type(), align_bytes, false); + LLVMValueRef align_in_bytes = LLVMConstInt(LLVMInt32Type(), var->align_bytes, false); LLVMValueRef params[] = { dest_ptr, fill_char, @@ -2051,15 +2033,14 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); TypeTableEntry *ptr_type = instruction->ptr->value.type; assert(ptr_type->id == TypeTableEntryIdPointer); - bool is_volatile = ptr_type->data.pointer.is_volatile; uint32_t unaligned_bit_count = ptr_type->data.pointer.unaligned_bit_count; if (unaligned_bit_count == 0) - return get_handle_value(g, ptr, child_type, is_volatile); + return get_handle_value(g, ptr, child_type, ptr_type); assert(!handle_is_ptr(child_type)); LLVMValueRef containing_int = LLVMBuildLoad(g->builder, ptr, ""); - LLVMSetVolatile(containing_int, is_volatile); + LLVMSetVolatile(containing_int, ptr_type->data.pointer.is_volatile); uint32_t bit_offset = ptr_type->data.pointer.bit_offset; uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int)); @@ -2097,9 +2078,8 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrI LLVMValueRef array_ptr_ptr = ir_llvm_value(g, instruction->array_ptr); TypeTableEntry *array_ptr_type = instruction->array_ptr->value.type; assert(array_ptr_type->id == TypeTableEntryIdPointer); - bool is_volatile = array_ptr_type->data.pointer.is_volatile; TypeTableEntry *array_type = array_ptr_type->data.pointer.child_type; - LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, is_volatile); + LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); LLVMValueRef subscript_value = ir_llvm_value(g, instruction->elem_index); assert(subscript_value); @@ -2427,12 +2407,11 @@ static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable, { TypeTableEntry *ptr_type = instruction->value->value.type; assert(ptr_type->id == TypeTableEntryIdPointer); - bool is_volatile = ptr_type->data.pointer.is_volatile; TypeTableEntry *maybe_type = ptr_type->data.pointer.child_type; assert(maybe_type->id == TypeTableEntryIdMaybe); TypeTableEntry *child_type = maybe_type->data.maybe.child_type; LLVMValueRef maybe_ptr = ir_llvm_value(g, instruction->value); - LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, is_volatile); + LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, ptr_type); if (ir_want_debug_safety(g, &instruction->base) && instruction->safety_check_on) { LLVMValueRef non_null_bit = gen_non_null_bit(g, maybe_type, maybe_handle); LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapMaybeOk"); @@ -2451,7 +2430,7 @@ static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable, if (maybe_is_ptr) { return maybe_ptr; } else { - LLVMValueRef maybe_struct_ref = get_handle_value(g, maybe_ptr, maybe_type, is_volatile); + LLVMValueRef maybe_struct_ref = get_handle_value(g, maybe_ptr, maybe_type, ptr_type); return LLVMBuildStructGEP(g->builder, maybe_struct_ref, maybe_child_index, ""); } } @@ -2694,11 +2673,13 @@ static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutable *executable, IrIns LLVMValueRef is_volatile = ptr_type->data.pointer.is_volatile ? LLVMConstAllOnes(LLVMInt1Type()) : LLVMConstNull(LLVMInt1Type()); + LLVMValueRef align_val = LLVMConstInt(LLVMInt32Type(), ptr_type->data.pointer.alignment, false); + LLVMValueRef params[] = { - dest_ptr_casted, // dest pointer - char_val, // source pointer - len_val, // byte count - LLVMConstInt(LLVMInt32Type(), 1, false), // align in bytes + dest_ptr_casted, + char_val, + len_val, + align_val, is_volatile, }; @@ -2725,11 +2706,14 @@ static LLVMValueRef ir_render_memcpy(CodeGen *g, IrExecutable *executable, IrIns LLVMValueRef is_volatile = (dest_ptr_type->data.pointer.is_volatile || src_ptr_type->data.pointer.is_volatile) ? LLVMConstAllOnes(LLVMInt1Type()) : LLVMConstNull(LLVMInt1Type()); + uint32_t min_align_bytes = min(src_ptr_type->data.pointer.alignment, dest_ptr_type->data.pointer.alignment); + LLVMValueRef align_val = LLVMConstInt(LLVMInt32Type(), min_align_bytes, false); + LLVMValueRef params[] = { - dest_ptr_casted, // dest pointer - src_ptr_casted, // source pointer - len_val, // byte count - LLVMConstInt(LLVMInt32Type(), 1, false), // align in bytes + dest_ptr_casted, + src_ptr_casted, + len_val, + align_val, is_volatile, }; @@ -2743,9 +2727,8 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst LLVMValueRef array_ptr_ptr = ir_llvm_value(g, instruction->ptr); TypeTableEntry *array_ptr_type = instruction->ptr->value.type; assert(array_ptr_type->id == TypeTableEntryIdPointer); - bool is_volatile = array_ptr_type->data.pointer.is_volatile; TypeTableEntry *array_type = array_ptr_type->data.pointer.child_type; - LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, is_volatile); + LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); LLVMValueRef tmp_struct_ptr = instruction->tmp_ptr; @@ -2989,11 +2972,10 @@ static LLVMValueRef ir_render_test_err(CodeGen *g, IrExecutable *executable, IrI static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executable, IrInstructionUnwrapErrCode *instruction) { TypeTableEntry *ptr_type = instruction->value->value.type; assert(ptr_type->id == TypeTableEntryIdPointer); - bool is_volatile = ptr_type->data.pointer.is_volatile; TypeTableEntry *err_union_type = ptr_type->data.pointer.child_type; TypeTableEntry *child_type = err_union_type->data.error.child_type; LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value); - LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, is_volatile); + LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type); if (type_has_bits(child_type)) { LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, ""); @@ -3006,11 +2988,10 @@ static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executab static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *executable, IrInstructionUnwrapErrPayload *instruction) { TypeTableEntry *ptr_type = instruction->value->value.type; assert(ptr_type->id == TypeTableEntryIdPointer); - bool is_volatile = ptr_type->data.pointer.is_volatile; TypeTableEntry *err_union_type = ptr_type->data.pointer.child_type; TypeTableEntry *child_type = err_union_type->data.error.child_type; LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value); - LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, is_volatile); + LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type); if (ir_want_debug_safety(g, &instruction->base) && instruction->safety_check_on && g->error_decls.length > 1) { LLVMValueRef err_val; @@ -3123,7 +3104,8 @@ static LLVMValueRef ir_render_enum_tag(CodeGen *g, IrExecutable *executable, IrI return enum_val; LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, enum_val, enum_type->data.enumeration.gen_tag_index, ""); - return get_handle_value(g, tag_field_ptr, tag_type, false); + TypeTableEntry *ptr_type = get_pointer_to_type(g, tag_type, false); + return get_handle_value(g, tag_field_ptr, tag_type, ptr_type); } static LLVMValueRef ir_render_init_enum(CodeGen *g, IrExecutable *executable, IrInstructionInitEnum *instruction) { @@ -3164,8 +3146,10 @@ static LLVMValueRef ir_render_struct_init(CodeGen *g, IrExecutable *executable, (unsigned)type_struct_field->gen_index, ""); LLVMValueRef value = ir_llvm_value(g, field->value); + uint32_t field_align_bytes = get_abi_alignment(g, type_struct_field->type_entry); + TypeTableEntry *ptr_type = get_pointer_to_type_extra(g, type_struct_field->type_entry, - false, false, + false, false, field_align_bytes, (uint32_t)type_struct_field->packed_bits_offset, (uint32_t)type_struct_field->unaligned_bit_count); gen_assign_raw(g, field_ptr, ptr_type, value); @@ -3243,15 +3227,13 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdEmbedFile: case IrInstructionIdIntType: case IrInstructionIdMemberCount: - case IrInstructionIdPreferredAlignOf: - case IrInstructionIdAbiAlignOf: + case IrInstructionIdAlignOf: case IrInstructionIdFnProto: case IrInstructionIdTestComptime: case IrInstructionIdCheckSwitchProngs: case IrInstructionIdCheckStatementIsVoid: case IrInstructionIdTypeName: case IrInstructionIdCanImplicitCast: - case IrInstructionIdSetGlobalAlign: case IrInstructionIdSetGlobalSection: case IrInstructionIdSetGlobalLinkage: case IrInstructionIdDeclRef: @@ -3259,6 +3241,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdOffsetOf: case IrInstructionIdTypeId: case IrInstructionIdSetEvalBranchQuota: + case IrInstructionIdPtrTypeOf: zig_unreachable(); case IrInstructionIdReturn: return ir_render_return(g, executable, (IrInstructionReturn *)instruction); @@ -3840,7 +3823,7 @@ static void render_const_val_global(CodeGen *g, ConstExprValue *const_val, const LLVMSetLinkage(global_value, LLVMInternalLinkage); LLVMSetGlobalConstant(global_value, true); LLVMSetUnnamedAddr(global_value, true); - LLVMSetAlignment(global_value, get_type_alignment(g, const_val->type)); + LLVMSetAlignment(global_value, get_abi_alignment(g, const_val->type)); const_val->global_refs->llvm_global = global_value; } @@ -3856,8 +3839,8 @@ static void generate_error_name_table(CodeGen *g) { assert(g->error_decls.length > 0); - TypeTableEntry *str_type = get_slice_type(g, g->builtin_types.entry_u8, true); - TypeTableEntry *u8_ptr_type = str_type->data.structure.fields[0].type_entry; + TypeTableEntry *u8_ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + TypeTableEntry *str_type = get_slice_type(g, u8_ptr_type); LLVMValueRef *values = allocate(g->error_decls.length); values[0] = LLVMGetUndef(str_type->type_ref); @@ -3893,8 +3876,8 @@ static void generate_error_name_table(CodeGen *g) { } static void generate_enum_name_tables(CodeGen *g) { - TypeTableEntry *str_type = get_slice_type(g, g->builtin_types.entry_u8, true); - TypeTableEntry *u8_ptr_type = str_type->data.structure.fields[0].type_entry; + TypeTableEntry *u8_ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + TypeTableEntry *str_type = get_slice_type(g, u8_ptr_type); for (size_t enum_i = 0; enum_i < g->name_table_enums.length; enum_i += 1) { TypeTableEntry *enum_tag_type = g->name_table_enums.at(enum_i); @@ -3965,9 +3948,10 @@ static void gen_global_var(CodeGen *g, VariableTableEntry *var, LLVMValueRef ini // TODO ^^ make an actual global variable } -static LLVMValueRef build_alloca(CodeGen *g, TypeTableEntry *type_entry, const char *name) { +static LLVMValueRef build_alloca(CodeGen *g, TypeTableEntry *type_entry, const char *name, uint32_t alignment) { + assert(alignment > 0); LLVMValueRef result = LLVMBuildAlloca(g->builder, type_entry->type_ref, name); - LLVMSetAlignment(result, get_type_alignment(g, type_entry)); + LLVMSetAlignment(result, alignment); return result; } @@ -4056,6 +4040,7 @@ static void do_code_gen(CodeGen *g) { // TODO debug info for the extern variable LLVMSetLinkage(global_value, LLVMExternalLinkage); + LLVMSetAlignment(global_value, var->align_bytes); } else { bool exported = (var->linkage == VarLinkageExport); render_const_val(g, var->value); @@ -4068,8 +4053,7 @@ static void do_code_gen(CodeGen *g) { if (tld_var->section_name) { LLVMSetSection(global_value, buf_ptr(tld_var->section_name)); } - LLVMSetAlignment(global_value, tld_var->alignment ? - tld_var->alignment : get_type_alignment(g, var->value->type)); + LLVMSetAlignment(global_value, var->align_bytes); // TODO debug info for function pointers if (var->gen_is_const && var->value->type->id != TypeTableEntryIdFn) { @@ -4189,7 +4173,7 @@ static void do_code_gen(CodeGen *g) { } else { zig_unreachable(); } - *slot = build_alloca(g, slot_type, ""); + *slot = build_alloca(g, slot_type, "", get_abi_alignment(g, slot_type)); } ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base); @@ -4207,7 +4191,7 @@ static void do_code_gen(CodeGen *g) { continue; if (var->src_arg_index == SIZE_MAX) { - var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name)); + var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name), var->align_bytes); var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, get_di_scope(g, var->parent_scope), buf_ptr(&var->name), import->di_file, (unsigned)(var->decl_node->line + 1), @@ -4227,7 +4211,7 @@ static void do_code_gen(CodeGen *g) { var->value_ref = LLVMGetParam(fn, (unsigned)var->gen_arg_index); } else { gen_type = var->value->type; - var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name)); + var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name), var->align_bytes); } if (var->decl_node) { var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope), @@ -4309,6 +4293,7 @@ static void do_code_gen(CodeGen *g) { } static const uint8_t int_sizes_in_bits[] = { + 2, 3, 4, 5, @@ -4605,8 +4590,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy", 3); create_builtin_fn(g, BuiltinFnIdMemset, "memset", 3); create_builtin_fn(g, BuiltinFnIdSizeof, "sizeOf", 1); - create_builtin_fn(g, BuiltinFnIdPreferredAlignOf, "preferredAlignOf", 1); - create_builtin_fn(g, BuiltinFnIdAbiAlignOf, "cAbiAlignOf", 1); + create_builtin_fn(g, BuiltinFnIdAlignOf, "alignOf", 1); create_builtin_fn(g, BuiltinFnIdMaxValue, "maxValue", 1); create_builtin_fn(g, BuiltinFnIdMinValue, "minValue", 1); create_builtin_fn(g, BuiltinFnIdMemberCount, "memberCount", 1); @@ -4634,7 +4618,6 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); create_builtin_fn(g, BuiltinFnIdSetDebugSafety, "setDebugSafety", 2); create_builtin_fn(g, BuiltinFnIdSetFloatMode, "setFloatMode", 2); - create_builtin_fn(g, BuiltinFnIdSetGlobalAlign, "setGlobalAlign", 2); create_builtin_fn(g, BuiltinFnIdSetGlobalSection, "setGlobalSection", 2); create_builtin_fn(g, BuiltinFnIdSetGlobalLinkage, "setGlobalLinkage", 2); create_builtin_fn(g, BuiltinFnIdPanic, "panic", 1); @@ -4989,7 +4972,8 @@ static void create_test_compile_var_and_add_test_runner(CodeGen *g) { exit(0); } - TypeTableEntry *str_type = get_slice_type(g, g->builtin_types.entry_u8, true); + TypeTableEntry *u8_ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + TypeTableEntry *str_type = get_slice_type(g, u8_ptr_type); TypeTableEntry *fn_type = get_test_fn_type(g); const char *field_names[] = { "name", "func", }; diff --git a/src/ir.cpp b/src/ir.cpp index 4256afa9b7..39c0b84bc5 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -51,6 +51,7 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *sc static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction); static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, TypeTableEntry *expected_type); static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr); +static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutable *exec, AstNode *source_node, Buf *msg); ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { assert(const_val->type->id == TypeTableEntryIdPointer); @@ -422,12 +423,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameAddress *) return IrInstructionIdFrameAddress; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionPreferredAlignOf *) { - return IrInstructionIdPreferredAlignOf; -} - -static constexpr IrInstructionId ir_instruction_id(IrInstructionAbiAlignOf *) { - return IrInstructionIdAbiAlignOf; +static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignOf *) { + return IrInstructionIdAlignOf; } static constexpr IrInstructionId ir_instruction_id(IrInstructionOverflowOp *) { @@ -518,10 +515,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCanImplicitCast return IrInstructionIdCanImplicitCast; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionSetGlobalAlign *) { - return IrInstructionIdSetGlobalAlign; -} - static constexpr IrInstructionId ir_instruction_id(IrInstructionSetGlobalSection *) { return IrInstructionIdSetGlobalSection; } @@ -558,6 +551,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSetEvalBranchQuo return IrInstructionIdSetEvalBranchQuota; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrTypeOf *) { + return IrInstructionIdPtrTypeOf; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -988,6 +985,24 @@ static IrInstruction *ir_build_br_from(IrBuilder *irb, IrInstruction *old_instru return new_instruction; } +static IrInstruction *ir_build_ptr_type_of(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *child_type, bool is_const, bool is_volatile, IrInstruction *align_value, + uint32_t bit_offset_start, uint32_t bit_offset_end) +{ + IrInstructionPtrTypeOf *ptr_type_of_instruction = ir_build_instruction(irb, scope, source_node); + ptr_type_of_instruction->align_value = align_value; + ptr_type_of_instruction->child_type = child_type; + ptr_type_of_instruction->is_const = is_const; + ptr_type_of_instruction->is_volatile = is_volatile; + ptr_type_of_instruction->bit_offset_start = bit_offset_start; + ptr_type_of_instruction->bit_offset_end = bit_offset_end; + + ir_ref_instruction(align_value, irb->current_basic_block); + ir_ref_instruction(child_type, irb->current_basic_block); + + return &ptr_type_of_instruction->base; +} + static IrInstruction *ir_build_un_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrUnOp op_id, IrInstruction *value) { IrInstructionUnOp *br_instruction = ir_build_instruction(irb, scope, source_node); br_instruction->op_id = op_id; @@ -1112,26 +1127,28 @@ static IrInstruction *ir_build_store_ptr_from(IrBuilder *irb, IrInstruction *old } static IrInstruction *ir_build_var_decl(IrBuilder *irb, Scope *scope, AstNode *source_node, - VariableTableEntry *var, IrInstruction *var_type, IrInstruction *init_value) + VariableTableEntry *var, IrInstruction *var_type, IrInstruction *align_value, IrInstruction *init_value) { IrInstructionDeclVar *decl_var_instruction = ir_build_instruction(irb, scope, source_node); decl_var_instruction->base.value.special = ConstValSpecialStatic; decl_var_instruction->base.value.type = irb->codegen->builtin_types.entry_void; decl_var_instruction->var = var; decl_var_instruction->var_type = var_type; + decl_var_instruction->align_value = align_value; decl_var_instruction->init_value = init_value; if (var_type) ir_ref_instruction(var_type, irb->current_basic_block); + if (align_value) ir_ref_instruction(align_value, irb->current_basic_block); ir_ref_instruction(init_value, irb->current_basic_block); return &decl_var_instruction->base; } static IrInstruction *ir_build_var_decl_from(IrBuilder *irb, IrInstruction *old_instruction, - VariableTableEntry *var, IrInstruction *var_type, IrInstruction *init_value) + VariableTableEntry *var, IrInstruction *var_type, IrInstruction *align_value, IrInstruction *init_value) { IrInstruction *new_instruction = ir_build_var_decl(irb, old_instruction->scope, - old_instruction->source_node, var, var_type, init_value); + old_instruction->source_node, var, var_type, align_value, init_value); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } @@ -1221,14 +1238,17 @@ static IrInstruction *ir_build_array_type(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } -static IrInstruction *ir_build_slice_type(IrBuilder *irb, Scope *scope, AstNode *source_node, bool is_const, - IrInstruction *child_type) +static IrInstruction *ir_build_slice_type(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *child_type, bool is_const, bool is_volatile, IrInstruction *align_value) { IrInstructionSliceType *instruction = ir_build_instruction(irb, scope, source_node); instruction->is_const = is_const; + instruction->is_volatile = is_volatile; instruction->child_type = child_type; + instruction->align_value = align_value; ir_ref_instruction(child_type, irb->current_basic_block); + if (align_value) ir_ref_instruction(align_value, irb->current_basic_block); return &instruction->base; } @@ -1810,17 +1830,8 @@ static IrInstruction *ir_build_overflow_op_from(IrBuilder *irb, IrInstruction *o return new_instruction; } -static IrInstruction *ir_build_preferred_align_of(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value) { - IrInstructionPreferredAlignOf *instruction = ir_build_instruction(irb, scope, source_node); - instruction->type_value = type_value; - - ir_ref_instruction(type_value, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstruction *ir_build_abi_align_of(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value) { - IrInstructionAbiAlignOf *instruction = ir_build_instruction(irb, scope, source_node); +static IrInstruction *ir_build_align_of(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value) { + IrInstructionAlignOf *instruction = ir_build_instruction(irb, scope, source_node); instruction->type_value = type_value; ir_ref_instruction(type_value, irb->current_basic_block); @@ -2097,19 +2108,6 @@ static IrInstruction *ir_build_can_implicit_cast(IrBuilder *irb, Scope *scope, A return &instruction->base; } -static IrInstruction *ir_build_set_global_align(IrBuilder *irb, Scope *scope, AstNode *source_node, - Tld *tld, IrInstruction *value) -{ - IrInstructionSetGlobalAlign *instruction = ir_build_instruction( - irb, scope, source_node); - instruction->tld = tld; - instruction->value = value; - - ir_ref_instruction(value, irb->current_basic_block); - - return &instruction->base; -} - static IrInstruction *ir_build_set_global_section(IrBuilder *irb, Scope *scope, AstNode *source_node, Tld *tld, IrInstruction *value) { @@ -2277,11 +2275,20 @@ static IrInstruction *ir_instruction_binop_get_dep(IrInstructionBinOp *instructi } static IrInstruction *ir_instruction_declvar_get_dep(IrInstructionDeclVar *instruction, size_t index) { - switch (index) { - case 0: return instruction->init_value; - case 1: return instruction->var_type; - default: return nullptr; + if (index == 0) return instruction->init_value; + index -= 1; + + if (instruction->align_value != nullptr) { + if (index == 0) return instruction->align_value; + index -= 1; } + + if (instruction->var_type != nullptr) { + if (index == 0) return instruction->var_type; + index -= 1; + } + + return nullptr; } static IrInstruction *ir_instruction_loadptr_get_dep(IrInstructionLoadPtr *instruction, size_t index) { @@ -2671,14 +2678,7 @@ static IrInstruction *ir_instruction_frameaddress_get_dep(IrInstructionFrameAddr return nullptr; } -static IrInstruction *ir_instruction_preferredalignof_get_dep(IrInstructionPreferredAlignOf *instruction, size_t index) { - switch (index) { - case 0: return instruction->type_value; - default: return nullptr; - } -} - -static IrInstruction *ir_instruction_abialignof_get_dep(IrInstructionAbiAlignOf *instruction, size_t index) { +static IrInstruction *ir_instruction_alignof_get_dep(IrInstructionAlignOf *instruction, size_t index) { switch (index) { case 0: return instruction->type_value; default: return nullptr; @@ -2854,13 +2854,6 @@ static IrInstruction *ir_instruction_canimplicitcast_get_dep(IrInstructionCanImp } } -static IrInstruction *ir_instruction_setglobalalign_get_dep(IrInstructionSetGlobalAlign *instruction, size_t index) { - switch (index) { - case 0: return instruction->value; - default: return nullptr; - } -} - static IrInstruction *ir_instruction_setglobalsection_get_dep(IrInstructionSetGlobalSection *instruction, size_t index) { switch (index) { case 0: return instruction->value; @@ -2924,6 +2917,14 @@ static IrInstruction *ir_instruction_setevalbranchquota_get_dep(IrInstructionSet } } +static IrInstruction *ir_instruction_ptrtypeof_get_dep(IrInstructionPtrTypeOf *instruction, size_t index) { + switch (index) { + case 0: return instruction->align_value; + case 1: return instruction->child_type; + default: return nullptr; + } +} + static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -3056,10 +3057,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t return ir_instruction_returnaddress_get_dep((IrInstructionReturnAddress *) instruction, index); case IrInstructionIdFrameAddress: return ir_instruction_frameaddress_get_dep((IrInstructionFrameAddress *) instruction, index); - case IrInstructionIdPreferredAlignOf: - return ir_instruction_preferredalignof_get_dep((IrInstructionPreferredAlignOf *) instruction, index); - case IrInstructionIdAbiAlignOf: - return ir_instruction_abialignof_get_dep((IrInstructionAbiAlignOf *) instruction, index); + case IrInstructionIdAlignOf: + return ir_instruction_alignof_get_dep((IrInstructionAlignOf *) instruction, index); case IrInstructionIdOverflowOp: return ir_instruction_overflowop_get_dep((IrInstructionOverflowOp *) instruction, index); case IrInstructionIdTestErr: @@ -3102,8 +3101,6 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t return ir_instruction_typename_get_dep((IrInstructionTypeName *) instruction, index); case IrInstructionIdCanImplicitCast: return ir_instruction_canimplicitcast_get_dep((IrInstructionCanImplicitCast *) instruction, index); - case IrInstructionIdSetGlobalAlign: - return ir_instruction_setglobalalign_get_dep((IrInstructionSetGlobalAlign *) instruction, index); case IrInstructionIdSetGlobalSection: return ir_instruction_setglobalsection_get_dep((IrInstructionSetGlobalSection *) instruction, index); case IrInstructionIdSetGlobalLinkage: @@ -3122,6 +3119,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t return ir_instruction_typeid_get_dep((IrInstructionTypeId *) instruction, index); case IrInstructionIdSetEvalBranchQuota: return ir_instruction_setevalbranchquota_get_dep((IrInstructionSetEvalBranchQuota *) instruction, index); + case IrInstructionIdPtrTypeOf: + return ir_instruction_ptrtypeof_get_dep((IrInstructionPtrTypeOf *) instruction, index); } zig_unreachable(); } @@ -4286,23 +4285,14 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_build_return_address(irb, scope, node); case BuiltinFnIdFrameAddress: return ir_build_frame_address(irb, scope, node); - case BuiltinFnIdPreferredAlignOf: + case BuiltinFnIdAlignOf: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_preferred_align_of(irb, scope, node, arg0_value); - } - case BuiltinFnIdAbiAlignOf: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_instruction) - return arg0_value; - - return ir_build_abi_align_of(irb, scope, node, arg0_value); + return ir_build_align_of(irb, scope, node, arg0_value); } case BuiltinFnIdAddWithOverflow: return ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd); @@ -4335,7 +4325,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_build_can_implicit_cast(irb, scope, node, arg0_value, arg1_value); } - case BuiltinFnIdSetGlobalAlign: case BuiltinFnIdSetGlobalSection: case BuiltinFnIdSetGlobalLinkage: { @@ -4361,9 +4350,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - if (builtin_fn->id == BuiltinFnIdSetGlobalAlign) { - return ir_build_set_global_align(irb, scope, node, tld, arg1_value); - } else if (builtin_fn->id == BuiltinFnIdSetGlobalSection) { + if (builtin_fn->id == BuiltinFnIdSetGlobalSection) { return ir_build_set_global_section(irb, scope, node, tld, arg1_value); } else if (builtin_fn->id == BuiltinFnIdSetGlobalLinkage) { return ir_build_set_global_linkage(irb, scope, node, tld, arg1_value); @@ -4652,17 +4639,51 @@ static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction * return ir_build_ref(irb, scope, value->source_node, value, lval.is_const, lval.is_volatile); } -static IrInstruction *ir_gen_address_of(IrBuilder *irb, Scope *scope, AstNode *node, - bool is_const, bool is_volatile, LVal lval) -{ - assert(node->type == NodeTypePrefixOpExpr); - AstNode *expr_node = node->data.prefix_op_expr.primary_expr; +static IrInstruction *ir_gen_address_of(IrBuilder *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeAddrOfExpr); + bool is_const = node->data.addr_of_expr.is_const; + bool is_volatile = node->data.addr_of_expr.is_volatile; + AstNode *expr_node = node->data.addr_of_expr.op_expr; + AstNode *align_expr = node->data.addr_of_expr.align_expr; - IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, make_lval_addr(is_const, is_volatile)); - if (value == irb->codegen->invalid_instruction) - return value; + if (align_expr == nullptr) { + return ir_gen_node_extra(irb, expr_node, scope, make_lval_addr(is_const, is_volatile)); + } - return ir_lval_wrap(irb, scope, value, lval); + IrInstruction *align_value = ir_gen_node(irb, align_expr, scope); + if (align_value == irb->codegen->invalid_instruction) + return align_value; + + IrInstruction *child_type = ir_gen_node(irb, expr_node, scope); + if (child_type == irb->codegen->invalid_instruction) + return child_type; + + uint32_t bit_offset_start = 0; + if (node->data.addr_of_expr.bit_offset_start != nullptr) { + if (!bigint_fits_in_bits(node->data.addr_of_expr.bit_offset_start, 32, false)) { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, node->data.addr_of_expr.bit_offset_start, 10); + exec_add_error_node(irb->codegen, irb->exec, node, + buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf))); + return irb->codegen->invalid_instruction; + } + bit_offset_start = bigint_as_unsigned(node->data.addr_of_expr.bit_offset_start); + } + + uint32_t bit_offset_end = 0; + if (node->data.addr_of_expr.bit_offset_end != nullptr) { + if (!bigint_fits_in_bits(node->data.addr_of_expr.bit_offset_end, 32, false)) { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, node->data.addr_of_expr.bit_offset_end, 10); + exec_add_error_node(irb->codegen, irb->exec, node, + buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf))); + return irb->codegen->invalid_instruction; + } + bit_offset_end = bigint_as_unsigned(node->data.addr_of_expr.bit_offset_end); + } + + return ir_build_ptr_type_of(irb, scope, node, child_type, is_const, is_volatile, + align_value, bit_offset_start, bit_offset_end); } static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { @@ -4725,14 +4746,6 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegation), lval); case PrefixOpNegationWrap: return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap), lval); - case PrefixOpAddressOf: - return ir_gen_address_of(irb, scope, node, false, false, lval); - case PrefixOpConstAddressOf: - return ir_gen_address_of(irb, scope, node, true, false, lval); - case PrefixOpVolatileAddressOf: - return ir_gen_address_of(irb, scope, node, false, true, lval); - case PrefixOpConstVolatileAddressOf: - return ir_gen_address_of(irb, scope, node, true, true, lval); case PrefixOpDereference: return ir_gen_prefix_op_id_lval(irb, scope, node, IrUnOpDereference, lval); case PrefixOpMaybe: @@ -4822,11 +4835,18 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod return irb->codegen->invalid_instruction; } + IrInstruction *align_value = nullptr; + if (variable_declaration->align_expr != nullptr) { + align_value = ir_gen_node(irb, variable_declaration->align_expr, scope); + if (align_value == irb->codegen->invalid_instruction) + return align_value; + } + IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope); if (init_value == irb->codegen->invalid_instruction) return init_value; - IrInstruction *result = ir_build_var_decl(irb, scope, node, var, type_instruction, init_value); + IrInstruction *result = ir_build_var_decl(irb, scope, node, var, type_instruction, align_value, init_value); var->decl_instruction = result; return result; } @@ -4883,7 +4903,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n err_val_ptr, false); IrInstruction *var_value = node->data.while_expr.var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, payload_scope, symbol_node, var_ptr_value); - ir_build_var_decl(irb, payload_scope, symbol_node, payload_var, nullptr, var_value); + ir_build_var_decl(irb, payload_scope, symbol_node, payload_var, nullptr, nullptr, var_value); } ZigList incoming_values = {0}; @@ -4922,7 +4942,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n true, false, false, is_comptime); Scope *err_scope = err_var->child_scope; IrInstruction *err_var_value = ir_build_unwrap_err_code(irb, err_scope, err_symbol_node, err_val_ptr); - ir_build_var_decl(irb, err_scope, symbol_node, err_var, nullptr, err_var_value); + ir_build_var_decl(irb, err_scope, symbol_node, err_var, nullptr, nullptr, err_var_value); else_result = ir_gen_node(irb, else_node, err_scope); if (else_result == irb->codegen->invalid_instruction) @@ -4964,7 +4984,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, child_scope, symbol_node, maybe_val_ptr, false); IrInstruction *var_value = node->data.while_expr.var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, child_scope, symbol_node, var_ptr_value); - ir_build_var_decl(irb, child_scope, symbol_node, payload_var, nullptr, var_value); + ir_build_var_decl(irb, child_scope, symbol_node, payload_var, nullptr, nullptr, var_value); ZigList incoming_values = {0}; ZigList incoming_blocks = {0}; @@ -5115,7 +5135,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo Scope *child_scope = elem_var->child_scope; IrInstruction *undefined_value = ir_build_const_undefined(irb, child_scope, elem_node); - ir_build_var_decl(irb, child_scope, elem_node, elem_var, elem_var_type, undefined_value); + ir_build_var_decl(irb, child_scope, elem_node, elem_var, elem_var_type, nullptr, undefined_value); IrInstruction *elem_var_ptr = ir_build_var_ptr(irb, child_scope, node, elem_var, false, false); AstNode *index_var_source_node; @@ -5133,7 +5153,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo IrInstruction *usize = ir_build_const_type(irb, child_scope, node, irb->codegen->builtin_types.entry_usize); IrInstruction *zero = ir_build_const_usize(irb, child_scope, node, 0); IrInstruction *one = ir_build_const_usize(irb, child_scope, node, 1); - ir_build_var_decl(irb, child_scope, index_var_source_node, index_var, usize, zero); + ir_build_var_decl(irb, child_scope, index_var_source_node, index_var, usize, nullptr, zero); IrInstruction *index_ptr = ir_build_var_ptr(irb, child_scope, node, index_var, false, false); @@ -5254,12 +5274,22 @@ static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *n AstNode *size_node = node->data.array_type.size; AstNode *child_type_node = node->data.array_type.child_type; bool is_const = node->data.array_type.is_const; + bool is_volatile = node->data.array_type.is_volatile; + AstNode *align_expr = node->data.array_type.align_expr; if (size_node) { if (is_const) { add_node_error(irb->codegen, node, buf_create_from_str("const qualifier invalid on array type")); return irb->codegen->invalid_instruction; } + if (is_volatile) { + add_node_error(irb->codegen, node, buf_create_from_str("volatile qualifier invalid on array type")); + return irb->codegen->invalid_instruction; + } + if (align_expr != nullptr) { + add_node_error(irb->codegen, node, buf_create_from_str("align qualifier invalid on array type")); + return irb->codegen->invalid_instruction; + } IrInstruction *size_value = ir_gen_node(irb, size_node, scope); if (size_value == irb->codegen->invalid_instruction) @@ -5271,11 +5301,20 @@ static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *n return ir_build_array_type(irb, scope, node, size_value, child_type); } else { + IrInstruction *align_value; + if (align_expr != nullptr) { + align_value = ir_gen_node(irb, align_expr, scope); + if (align_value == irb->codegen->invalid_instruction) + return align_value; + } else { + align_value = nullptr; + } + IrInstruction *child_type = ir_gen_node(irb, child_type_node, scope); if (child_type == irb->codegen->invalid_instruction) return child_type; - return ir_build_slice_type(irb, scope, node, is_const, child_type); + return ir_build_slice_type(irb, scope, node, child_type, is_const, is_volatile, align_value); } } @@ -5375,7 +5414,7 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, scope, node, maybe_val_ptr, false); IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, scope, node, var_ptr_value); - ir_build_var_decl(irb, scope, node, var, var_type, var_value); + ir_build_var_decl(irb, scope, node, var, var_type, nullptr, var_value); var_scope = var->child_scope; } else { var_scope = scope; @@ -5452,7 +5491,7 @@ static IrInstruction *ir_gen_try_expr(IrBuilder *irb, Scope *scope, AstNode *nod IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, scope, node, err_val_ptr, false); IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, scope, node, var_ptr_value); - ir_build_var_decl(irb, scope, node, var, var_type, var_value); + ir_build_var_decl(irb, scope, node, var, var_type, nullptr, var_value); var_scope = var->child_scope; } else { var_scope = scope; @@ -5477,7 +5516,7 @@ static IrInstruction *ir_gen_try_expr(IrBuilder *irb, Scope *scope, AstNode *nod err_symbol, is_const, is_const, is_shadowable, is_comptime); IrInstruction *var_value = ir_build_unwrap_err_code(irb, scope, node, err_val_ptr); - ir_build_var_decl(irb, scope, node, var, var_type, var_value); + ir_build_var_decl(irb, scope, node, var, var_type, nullptr, var_value); err_var_scope = var->child_scope; } else { err_var_scope = scope; @@ -5531,7 +5570,7 @@ static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *swit var_value = var_is_ptr ? target_value_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, target_value_ptr); } IrInstruction *var_type = nullptr; // infer the type - ir_build_var_decl(irb, scope, var_symbol_node, var, var_type, var_value); + ir_build_var_decl(irb, scope, var_symbol_node, var, var_type, nullptr, var_value); } else { child_scope = scope; } @@ -5912,7 +5951,7 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN is_const, is_const, is_shadowable, is_comptime); err_scope = var->child_scope; IrInstruction *err_val = ir_build_unwrap_err_code(irb, err_scope, node, err_union_ptr); - ir_build_var_decl(irb, err_scope, var_node, var, var_type, err_val); + ir_build_var_decl(irb, err_scope, var_node, var, var_type, nullptr, err_val); } else { err_scope = parent_scope; } @@ -6056,6 +6095,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_lval_wrap(irb, scope, ir_gen_if_bool_expr(irb, scope, node), lval); case NodeTypePrefixOpExpr: return ir_gen_prefix_op_expr(irb, scope, node, lval); + case NodeTypeAddrOfExpr: + return ir_lval_wrap(irb, scope, ir_gen_address_of(irb, scope, node), lval); case NodeTypeContainerInitExpr: return ir_lval_wrap(irb, scope, ir_gen_container_init_expr(irb, scope, node), lval); case NodeTypeVariableDeclaration: @@ -7264,7 +7305,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } if (convert_to_const_slice) { assert(prev_inst->value.type->id == TypeTableEntryIdArray); - TypeTableEntry *slice_type = get_slice_type(ira->codegen, prev_inst->value.type->data.array.child_type, true); + TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, prev_inst->value.type->data.array.child_type, true); + TypeTableEntry *slice_type = get_slice_type(ira->codegen, ptr_type); if (any_are_pure_error) { return get_error_type(ira->codegen, slice_type); } else { @@ -7569,7 +7611,7 @@ static TypeTableEntry *ir_analyze_void(IrAnalyze *ira, IrInstruction *instructio static IrInstruction *ir_get_const_ptr(IrAnalyze *ira, IrInstruction *instruction, ConstExprValue *pointee, TypeTableEntry *pointee_type, - ConstPtrMut ptr_mut, bool ptr_is_const, bool ptr_is_volatile) + ConstPtrMut ptr_mut, bool ptr_is_const, bool ptr_is_volatile, uint32_t ptr_align) { if (pointee_type->id == TypeTableEntryIdMetaType) { TypeTableEntry *type_entry = pointee->data.x_type; @@ -7583,11 +7625,11 @@ static IrInstruction *ir_get_const_ptr(IrAnalyze *ira, IrInstruction *instructio const_val->type = pointee_type; type_ensure_zero_bits_known(ira->codegen, type_entry); const_val->data.x_type = get_pointer_to_type_extra(ira->codegen, type_entry, - ptr_is_const, ptr_is_volatile, 0, 0); + ptr_is_const, ptr_is_volatile, get_abi_alignment(ira->codegen, type_entry), 0, 0); return const_instr; } else { TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, pointee_type, - ptr_is_const, ptr_is_volatile, 0, 0); + ptr_is_const, ptr_is_volatile, ptr_align, 0, 0); IrInstruction *const_instr = ir_get_const(ira, instruction); ConstExprValue *const_val = &const_instr->value; const_val->type = ptr_type; @@ -7603,7 +7645,8 @@ static TypeTableEntry *ir_analyze_const_ptr(IrAnalyze *ira, IrInstruction *instr ConstPtrMut ptr_mut, bool ptr_is_const, bool ptr_is_volatile) { IrInstruction *const_instr = ir_get_const_ptr(ira, instruction, pointee, - pointee_type, ptr_mut, ptr_is_const, ptr_is_volatile); + pointee_type, ptr_mut, ptr_is_const, ptr_is_volatile, + get_abi_alignment(ira->codegen, pointee_type)); ir_link_new_instruction(const_instr, instruction); return const_instr->value.type; } @@ -7876,10 +7919,12 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi return ira->codegen->invalid_instruction; bool final_is_const = (value->value.type->id == TypeTableEntryIdMetaType) ? is_const : true; return ir_get_const_ptr(ira, source_instruction, val, value->value.type, - ConstPtrMutComptimeConst, final_is_const, is_volatile); + ConstPtrMutComptimeConst, final_is_const, is_volatile, + get_abi_alignment(ira->codegen, value->value.type)); } - TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, value->value.type, is_const, is_volatile, 0, 0); + TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, value->value.type, + is_const, is_volatile, get_abi_alignment(ira->codegen, value->value.type), 0, 0); FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec); assert(fn_entry); IrInstruction *new_instruction = ir_build_ref(&ira->new_irb, source_instruction->scope, @@ -8579,6 +8624,33 @@ static TypeTableEntry *ir_analyze_ref(IrAnalyze *ira, IrInstruction *source_inst return result->value.type; } +static bool ir_resolve_align(IrAnalyze *ira, IrInstruction *value, uint32_t *out) { + if (type_is_invalid(value->value.type)) + return false; + + IrInstruction *casted_value = ir_implicit_cast(ira, value, get_align_amt_type(ira->codegen)); + if (type_is_invalid(casted_value->value.type)) + return false; + + ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); + if (!const_val) + return false; + + uint32_t align_bytes = bigint_as_unsigned(&const_val->data.x_bigint); + if (align_bytes == 0) { + ir_add_error(ira, value, buf_sprintf("alignment must be >= 1")); + return false; + } + + if (!is_power_of_2(align_bytes)) { + ir_add_error(ira, value, buf_sprintf("alignment value %" PRIu32 " is not a power of 2", align_bytes)); + return false; + } + + *out = align_bytes; + return true; +} + static bool ir_resolve_usize(IrAnalyze *ira, IrInstruction *value, uint64_t *out) { if (type_is_invalid(value->value.type)) return false; @@ -8656,7 +8728,8 @@ static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) { if (type_is_invalid(value->value.type)) return nullptr; - TypeTableEntry *str_type = get_slice_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true); + TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true); + TypeTableEntry *str_type = get_slice_type(ira->codegen, ptr_type); IrInstruction *casted_value = ir_implicit_cast(ira, value, str_type); if (type_is_invalid(casted_value->value.type)) return nullptr; @@ -9715,6 +9788,14 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc bool is_comptime = ir_get_var_is_comptime(var); + if (decl_var_instruction->align_value == nullptr) { + var->align_bytes = get_abi_alignment(ira->codegen, result_type); + } else { + if (!ir_resolve_align(ira, decl_var_instruction->align_value->other, &var->align_bytes)) { + var->value->type = ira->codegen->builtin_types.entry_invalid; + } + } + if (casted_init_value->value.special != ConstValSpecialRuntime) { if (var->mem_slot_index != SIZE_MAX) { assert(var->mem_slot_index < ira->exec_context.mem_slot_count); @@ -9733,7 +9814,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc return ira->codegen->builtin_types.entry_invalid; } - ir_build_var_decl_from(&ira->new_irb, &decl_var_instruction->base, var, var_type, casted_init_value); + ir_build_var_decl_from(&ira->new_irb, &decl_var_instruction->base, var, var_type, nullptr, casted_init_value); FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec); if (fn_entry) @@ -9892,11 +9973,12 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, ptr_mut = ConstPtrMutRuntimeVar; } return ir_get_const_ptr(ira, instruction, mem_slot, var->value->type, - ptr_mut, is_const, is_volatile); + ptr_mut, is_const, is_volatile, var->align_bytes); } else { IrInstruction *var_ptr_instruction = ir_build_var_ptr(&ira->new_irb, instruction->scope, instruction->source_node, var, is_const, is_volatile); - var_ptr_instruction->value.type = get_pointer_to_type(ira->codegen, var->value->type, var->src_is_const); + var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->value->type, + var->src_is_const, is_volatile, var->align_bytes, 0, 0); type_ensure_zero_bits_known(ira->codegen, var->value->type); bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr); @@ -10131,6 +10213,16 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal impl_fn->child_scope, param_name, true, var_args_val, nullptr); impl_fn->child_scope = var->child_scope; } + + if (fn_proto_node->data.fn_proto.align_expr != nullptr) { + IrInstruction *align_result = ir_eval_const_value(ira->codegen, impl_fn->child_scope, + fn_proto_node->data.fn_proto.align_expr, get_align_amt_type(ira->codegen), + ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, + nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec); + + ir_resolve_align(ira, align_result, &impl_fn->align_bytes); + } + { AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type; TypeTableEntry *return_type = analyze_type_expr(ira->codegen, impl_fn->child_scope, return_type_node); @@ -10731,7 +10823,8 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc TypeTableEntry *child_type = array_type->data.array.child_type; if (ptr_type->data.pointer.unaligned_bit_count == 0) { return_type = get_pointer_to_type_extra(ira->codegen, child_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, 0, 0); + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + get_abi_alignment(ira->codegen, child_type), 0, 0); } else { uint64_t elem_val_scalar; if (!ir_resolve_usize(ira, elem_index, &elem_val_scalar)) @@ -10742,6 +10835,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc return_type = get_pointer_to_type_extra(ira->codegen, child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + get_abi_alignment(ira->codegen, child_type), (uint32_t)bit_offset, (uint32_t)bit_width); } } else if (array_type->id == TypeTableEntryIdPointer) { @@ -10964,6 +11058,8 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field TypeStructField *field = find_struct_type_field(bare_type, field_name); if (field) { + bool is_packed = (bare_type->data.structure.layout == ContainerLayoutPacked); + uint32_t align_bytes = is_packed ? 1 : get_abi_alignment(ira->codegen, field->type_entry); if (instr_is_comptime(container_ptr)) { ConstExprValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); if (!ptr_val) @@ -10973,7 +11069,7 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field ConstExprValue *struct_val = const_ptr_pointee(ira->codegen, ptr_val); ConstExprValue *field_val = &struct_val->data.x_struct.fields[field->src_index]; TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, field_val->type, - is_const, is_volatile, 0, 0); + is_const, is_volatile, align_bytes, 0, 0); ConstExprValue *const_val = ir_build_const_from(ira, &field_ptr_instruction->base); const_val->data.x_ptr.special = ConstPtrSpecialBaseStruct; const_val->data.x_ptr.mut = container_ptr->value.data.x_ptr.mut; @@ -10988,6 +11084,7 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field field->unaligned_bit_count : type_size_bits(ira->codegen, field->type_entry); ir_build_struct_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field); return get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile, + align_bytes, (uint32_t)(ptr_bit_offset + field->packed_bits_offset), (uint32_t)unaligned_bit_count_for_result_type); } else { @@ -11001,7 +11098,8 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field TypeEnumField *field = find_enum_type_field(bare_type, field_name); if (field) { ir_build_enum_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field); - return get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile, 0, 0); + return get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile, + get_abi_alignment(ira->codegen, field->type_entry), 0, 0); } else { return ir_analyze_container_member_access_inner(ira, bare_type, field_name, field_ptr_instruction, container_ptr, container_type); @@ -11427,7 +11525,6 @@ static TypeTableEntry *ir_analyze_instruction_ptr_type_child(IrAnalyze *ira, if (type_is_invalid(type_entry)) return type_entry; - // TODO handle typedefs if (type_entry->id != TypeTableEntryIdPointer) { ir_add_error_node(ira, ptr_type_child_instruction->base.source_node, buf_sprintf("expected pointer type, found '%s'", buf_ptr(&type_entry->name))); @@ -11439,69 +11536,6 @@ static TypeTableEntry *ir_analyze_instruction_ptr_type_child(IrAnalyze *ira, return ira->codegen->builtin_types.entry_type; } -static TypeTableEntry *ir_analyze_instruction_set_global_align(IrAnalyze *ira, - IrInstructionSetGlobalAlign *instruction) -{ - Tld *tld = instruction->tld; - IrInstruction *align_value = instruction->value->other; - - resolve_top_level_decl(ira->codegen, tld, true, instruction->base.source_node); - if (tld->resolution == TldResolutionInvalid) - return ira->codegen->builtin_types.entry_invalid; - - uint64_t scalar_align; - if (!ir_resolve_usize(ira, align_value, &scalar_align)) - return ira->codegen->builtin_types.entry_invalid; - - if (!is_power_of_2(scalar_align)) { - ir_add_error(ira, instruction->value, buf_sprintf("alignment value must be power of 2")); - return ira->codegen->builtin_types.entry_invalid; - } - - AstNode **set_global_align_node; - uint32_t *alignment_ptr; - if (tld->id == TldIdVar) { - TldVar *tld_var = (TldVar *)tld; - set_global_align_node = &tld_var->set_global_align_node; - alignment_ptr = &tld_var->alignment; - - if (tld_var->var->linkage == VarLinkageExternal) { - ErrorMsg *msg = ir_add_error(ira, &instruction->base, - buf_sprintf("cannot set alignment of external variable '%s'", buf_ptr(&tld_var->var->name))); - add_error_note(ira->codegen, msg, tld->source_node, buf_sprintf("declared here")); - return ira->codegen->builtin_types.entry_invalid; - } - } else if (tld->id == TldIdFn) { - TldFn *tld_fn = (TldFn *)tld; - FnTableEntry *fn_entry = tld_fn->fn_entry; - set_global_align_node = &fn_entry->set_global_align_node; - alignment_ptr = &fn_entry->alignment; - - if (fn_entry->def_scope == nullptr) { - ErrorMsg *msg = ir_add_error(ira, &instruction->base, - buf_sprintf("cannot set alignment of external function '%s'", buf_ptr(&fn_entry->symbol_name))); - add_error_note(ira->codegen, msg, tld->source_node, buf_sprintf("declared here")); - return ira->codegen->builtin_types.entry_invalid; - } - } else { - // error is caught in pass1 IR gen - zig_unreachable(); - } - - AstNode *source_node = instruction->base.source_node; - if (*set_global_align_node) { - ErrorMsg *msg = ir_add_error_node(ira, source_node, - buf_sprintf("alignment set twice")); - add_error_note(ira->codegen, msg, *set_global_align_node, buf_sprintf("first set here")); - return ira->codegen->builtin_types.entry_invalid; - } - *set_global_align_node = source_node; - *alignment_ptr = (uint32_t)scalar_align; - - ir_build_const_from(ira, &instruction->base); - return ira->codegen->builtin_types.entry_void; -} - static TypeTableEntry *ir_analyze_instruction_set_global_section(IrAnalyze *ira, IrInstructionSetGlobalSection *instruction) { @@ -11750,16 +11784,24 @@ static TypeTableEntry *ir_analyze_instruction_set_float_mode(IrAnalyze *ira, static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira, IrInstructionSliceType *slice_type_instruction) { - IrInstruction *child_type = slice_type_instruction->child_type->other; - if (type_is_invalid(child_type->value.type)) + uint32_t align_bytes; + if (slice_type_instruction->align_value != nullptr) { + if (!ir_resolve_align(ira, slice_type_instruction->align_value->other, &align_bytes)) + return ira->codegen->builtin_types.entry_invalid; + } + + TypeTableEntry *child_type = ir_resolve_type(ira, slice_type_instruction->child_type->other); + if (type_is_invalid(child_type)) return ira->codegen->builtin_types.entry_invalid; + + if (slice_type_instruction->align_value == nullptr) { + align_bytes = get_abi_alignment(ira->codegen, child_type); + } + bool is_const = slice_type_instruction->is_const; + bool is_volatile = slice_type_instruction->is_volatile; - TypeTableEntry *resolved_child_type = ir_resolve_type(ira, child_type); - if (type_is_invalid(resolved_child_type)) - return ira->codegen->builtin_types.entry_invalid; - - switch (resolved_child_type->id) { + switch (child_type->id) { case TypeTableEntryIdInvalid: // handled above zig_unreachable(); case TypeTableEntryIdVar: @@ -11770,7 +11812,7 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira, case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: ir_add_error_node(ira, slice_type_instruction->base.source_node, - buf_sprintf("slice of type '%s' not allowed", buf_ptr(&resolved_child_type->name))); + buf_sprintf("slice of type '%s' not allowed", buf_ptr(&child_type->name))); return ira->codegen->builtin_types.entry_invalid; case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: @@ -11792,8 +11834,10 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira, case TypeTableEntryIdBoundFn: case TypeTableEntryIdEnumTag: { - type_ensure_zero_bits_known(ira->codegen, resolved_child_type); - TypeTableEntry *result_type = get_slice_type(ira->codegen, resolved_child_type, is_const); + type_ensure_zero_bits_known(ira->codegen, child_type); + TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, child_type, + is_const, is_volatile, align_bytes, 0, 0); + TypeTableEntry *result_type = get_slice_type(ira->codegen, slice_ptr_type); ConstExprValue *out_val = ir_build_const_from(ira, &slice_type_instruction->base); out_val->data.x_type = result_type; return ira->codegen->builtin_types.entry_type; @@ -12018,7 +12062,6 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, assert(ptr_type->id == TypeTableEntryIdPointer); TypeTableEntry *type_entry = ptr_type->data.pointer.child_type; - // TODO handle typedef if (type_is_invalid(type_entry)) { return ira->codegen->builtin_types.entry_invalid; } else if (type_entry->id != TypeTableEntryIdMaybe) { @@ -12028,7 +12071,8 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, } TypeTableEntry *child_type = type_entry->data.maybe.child_type; TypeTableEntry *result_type = get_pointer_to_type_extra(ira->codegen, child_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, 0, 0); + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + get_abi_alignment(ira->codegen, child_type), 0, 0); if (instr_is_comptime(value)) { ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); @@ -12887,7 +12931,8 @@ static TypeTableEntry *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruc if (type_is_invalid(casted_value->value.type)) return ira->codegen->builtin_types.entry_invalid; - TypeTableEntry *str_type = get_slice_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true); + TypeTableEntry *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true); + TypeTableEntry *str_type = get_slice_type(ira->codegen, u8_ptr_type); if (casted_value->value.special == ConstValSpecialStatic) { ErrorTableEntry *err = casted_value->value.data.x_pure_err; if (!err->cached_error_name_val) { @@ -12929,7 +12974,8 @@ static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIn IrInstruction *result = ir_build_enum_tag_name(&ira->new_irb, instruction->base.scope, instruction->base.source_node, target); ir_link_new_instruction(result, &instruction->base); - result->value.type = get_slice_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true); + TypeTableEntry *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true); + result->value.type = get_slice_type(ira->codegen, u8_ptr_type); return result->value.type; } @@ -12972,16 +13018,22 @@ static TypeTableEntry *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, return ira->codegen->builtin_types.entry_invalid; } + bool is_packed = (container_type->data.structure.layout == ContainerLayoutPacked); + uint32_t field_ptr_align = is_packed ? 1 : get_abi_alignment(ira->codegen, field->type_entry); + uint32_t parent_ptr_align = is_packed ? 1 : get_abi_alignment(ira->codegen, container_type); + TypeTableEntry *field_ptr_type = get_pointer_to_type_extra(ira->codegen, field->type_entry, field_ptr->value.type->data.pointer.is_const, - field_ptr->value.type->data.pointer.is_volatile, 0, 0); + field_ptr->value.type->data.pointer.is_volatile, + field_ptr_align, 0, 0); IrInstruction *casted_field_ptr = ir_implicit_cast(ira, field_ptr, field_ptr_type); if (type_is_invalid(casted_field_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *result_type = get_pointer_to_type_extra(ira->codegen, container_type, casted_field_ptr->value.type->data.pointer.is_const, - casted_field_ptr->value.type->data.pointer.is_volatile, 0, 0); + casted_field_ptr->value.type->data.pointer.is_volatile, + parent_ptr_align, 0, 0); if (instr_is_comptime(casted_field_ptr)) { ConstExprValue *field_ptr_val = ir_resolve_const(ira, casted_field_ptr, UndefBad); @@ -13436,7 +13488,9 @@ static TypeTableEntry *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructi TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize; TypeTableEntry *u8 = ira->codegen->builtin_types.entry_u8; - TypeTableEntry *u8_ptr = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile, 0, 0); + uint32_t dest_align = (dest_uncasted_type->id == TypeTableEntryIdPointer) ? + dest_uncasted_type->data.pointer.alignment : get_abi_alignment(ira->codegen, u8); + TypeTableEntry *u8_ptr = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile, dest_align, 0, 0); IrInstruction *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr); if (type_is_invalid(casted_dest_ptr->value.type)) @@ -13517,17 +13571,21 @@ static TypeTableEntry *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructi if (type_is_invalid(count_value->value.type)) return ira->codegen->builtin_types.entry_invalid; + TypeTableEntry *u8 = ira->codegen->builtin_types.entry_u8; TypeTableEntry *dest_uncasted_type = dest_ptr->value.type; TypeTableEntry *src_uncasted_type = src_ptr->value.type; bool dest_is_volatile = (dest_uncasted_type->id == TypeTableEntryIdPointer) && dest_uncasted_type->data.pointer.is_volatile; bool src_is_volatile = (src_uncasted_type->id == TypeTableEntryIdPointer) && src_uncasted_type->data.pointer.is_volatile; + uint32_t dest_align = (dest_uncasted_type->id == TypeTableEntryIdPointer) ? + dest_uncasted_type->data.pointer.alignment : get_abi_alignment(ira->codegen, u8); + uint32_t src_align = (src_uncasted_type->id == TypeTableEntryIdPointer) ? + src_uncasted_type->data.pointer.alignment : get_abi_alignment(ira->codegen, u8); TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize; - TypeTableEntry *u8 = ira->codegen->builtin_types.entry_u8; - TypeTableEntry *u8_ptr_mut = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile, 0, 0); - TypeTableEntry *u8_ptr_const = get_pointer_to_type_extra(ira->codegen, u8, true, src_is_volatile, 0, 0); + TypeTableEntry *u8_ptr_mut = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile, dest_align, 0, 0); + TypeTableEntry *u8_ptr_const = get_pointer_to_type_extra(ira->codegen, u8, true, src_is_volatile, src_align, 0, 0); IrInstruction *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr_mut); if (type_is_invalid(casted_dest_ptr->value.type)) @@ -13662,17 +13720,24 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio TypeTableEntry *return_type; if (array_type->id == TypeTableEntryIdArray) { - return_type = get_slice_type(ira->codegen, array_type->data.array.child_type, ptr_type->data.pointer.is_const); + uint32_t normal_array_alignment = get_abi_alignment(ira->codegen, array_type); + uint32_t align_bytes = (ptr_type->data.pointer.alignment >= normal_array_alignment) ? + normal_array_alignment : 1; + TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, align_bytes, 0, 0); + return_type = get_slice_type(ira->codegen, slice_ptr_type); } else if (array_type->id == TypeTableEntryIdPointer) { - return_type = get_slice_type(ira->codegen, array_type->data.pointer.child_type, - array_type->data.pointer.is_const); + TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.pointer.child_type, + array_type->data.pointer.is_const, array_type->data.pointer.is_volatile, + array_type->data.pointer.alignment, 0, 0); + return_type = get_slice_type(ira->codegen, slice_ptr_type); if (!end) { ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value")); return ira->codegen->builtin_types.entry_invalid; } } else if (is_slice(array_type)) { TypeTableEntry *ptr_type = array_type->data.structure.fields[slice_ptr_index].type_entry; - return_type = get_slice_type(ira->codegen, ptr_type->data.pointer.child_type, ptr_type->data.pointer.is_const); + return_type = get_slice_type(ira->codegen, ptr_type); } else { ir_add_error(ira, &instruction->base, buf_sprintf("slice of non-array type '%s'", buf_ptr(&array_type->name))); @@ -13860,7 +13925,7 @@ static TypeTableEntry *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrIn return u8_ptr_const; } -static TypeTableEntry *ir_analyze_instruction_preferred_align_of(IrAnalyze *ira, IrInstructionPreferredAlignOf *instruction) { +static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) { IrInstruction *type_value = instruction->type_value->other; if (type_is_invalid(type_value->value.type)) return ira->codegen->builtin_types.entry_invalid; @@ -13884,10 +13949,11 @@ static TypeTableEntry *ir_analyze_instruction_preferred_align_of(IrAnalyze *ira, case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: + case TypeTableEntryIdVoid: + case TypeTableEntryIdOpaque: ir_add_error(ira, instruction->type_value, buf_sprintf("no align available for type '%s'", buf_ptr(&type_entry->name))); return ira->codegen->builtin_types.entry_invalid; - case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: @@ -13901,61 +13967,8 @@ static TypeTableEntry *ir_analyze_instruction_preferred_align_of(IrAnalyze *ira, case TypeTableEntryIdEnumTag: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: - case TypeTableEntryIdOpaque: { - uint64_t align_in_bytes = LLVMPreferredAlignmentOfType(ira->codegen->target_data_ref, type_entry->type_ref); - ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - bigint_init_unsigned(&out_val->data.x_bigint, align_in_bytes); - return ira->codegen->builtin_types.entry_num_lit_int; - } - } - zig_unreachable(); -} - -static TypeTableEntry *ir_analyze_instruction_abi_align_of(IrAnalyze *ira, IrInstructionAbiAlignOf *instruction) { - IrInstruction *type_value = instruction->type_value->other; - if (type_is_invalid(type_value->value.type)) - return ira->codegen->builtin_types.entry_invalid; - TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); - - ensure_complete_type(ira->codegen, type_entry); - if (type_is_invalid(type_entry)) - return ira->codegen->builtin_types.entry_invalid; - - switch (type_entry->id) { - case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: - zig_unreachable(); - case TypeTableEntryIdMetaType: - case TypeTableEntryIdUnreachable: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: - case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: - case TypeTableEntryIdNamespace: - case TypeTableEntryIdBlock: - case TypeTableEntryIdBoundFn: - case TypeTableEntryIdArgTuple: - ir_add_error(ira, instruction->type_value, - buf_sprintf("no align available for type '%s'", buf_ptr(&type_entry->name))); - return ira->codegen->builtin_types.entry_invalid; - case TypeTableEntryIdVoid: - case TypeTableEntryIdBool: - case TypeTableEntryIdInt: - case TypeTableEntryIdFloat: - case TypeTableEntryIdPointer: - case TypeTableEntryIdArray: - case TypeTableEntryIdStruct: - case TypeTableEntryIdMaybe: - case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: - case TypeTableEntryIdEnum: - case TypeTableEntryIdEnumTag: - case TypeTableEntryIdUnion: - case TypeTableEntryIdFn: - case TypeTableEntryIdOpaque: - { - uint64_t align_in_bytes = LLVMABIAlignmentOfType(ira->codegen->target_data_ref, type_entry->type_ref); + uint64_t align_in_bytes = get_abi_alignment(ira->codegen, type_entry); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); bigint_init_unsigned(&out_val->data.x_bigint, align_in_bytes); return ira->codegen->builtin_types.entry_num_lit_int; @@ -14143,7 +14156,8 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, } else if (type_entry->id == TypeTableEntryIdErrorUnion) { TypeTableEntry *child_type = type_entry->data.error.child_type; TypeTableEntry *result_type = get_pointer_to_type_extra(ira->codegen, child_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, 0, 0); + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + get_abi_alignment(ira->codegen, child_type), 0, 0); if (instr_is_comptime(value)) { ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); if (!ptr_val) @@ -14390,7 +14404,8 @@ static TypeTableEntry *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstructio if (type_is_invalid(msg->value.type)) return ira->codegen->builtin_types.entry_invalid; - TypeTableEntry *str_type = get_slice_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true); + TypeTableEntry *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true); + TypeTableEntry *str_type = get_slice_type(ira->codegen, u8_ptr_type); IrInstruction *casted_msg = ir_implicit_cast(ira, msg, str_type); if (type_is_invalid(casted_msg->value.type)) return ira->codegen->builtin_types.entry_invalid; @@ -14814,6 +14829,23 @@ static TypeTableEntry *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstr return usize; } +static TypeTableEntry *ir_analyze_instruction_ptr_type_of(IrAnalyze *ira, IrInstructionPtrTypeOf *instruction) { + TypeTableEntry *child_type = ir_resolve_type(ira, instruction->child_type->other); + if (type_is_invalid(child_type)) + return ira->codegen->builtin_types.entry_invalid; + + uint32_t align_bytes; + if (!ir_resolve_align(ira, instruction->align_value->other, &align_bytes)) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + out_val->data.x_type = get_pointer_to_type_extra(ira->codegen, child_type, + instruction->is_const, instruction->is_volatile, align_bytes, + instruction->bit_offset_start, instruction->bit_offset_end); + + return ira->codegen->builtin_types.entry_type; +} + static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -14862,8 +14894,6 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_to_ptr_type(ira, (IrInstructionToPtrType *)instruction); case IrInstructionIdPtrTypeChild: return ir_analyze_instruction_ptr_type_child(ira, (IrInstructionPtrTypeChild *)instruction); - case IrInstructionIdSetGlobalAlign: - return ir_analyze_instruction_set_global_align(ira, (IrInstructionSetGlobalAlign *)instruction); case IrInstructionIdSetGlobalSection: return ir_analyze_instruction_set_global_section(ira, (IrInstructionSetGlobalSection *)instruction); case IrInstructionIdSetGlobalLinkage: @@ -14952,10 +14982,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_return_address(ira, (IrInstructionReturnAddress *)instruction); case IrInstructionIdFrameAddress: return ir_analyze_instruction_frame_address(ira, (IrInstructionFrameAddress *)instruction); - case IrInstructionIdPreferredAlignOf: - return ir_analyze_instruction_preferred_align_of(ira, (IrInstructionPreferredAlignOf *)instruction); - case IrInstructionIdAbiAlignOf: - return ir_analyze_instruction_abi_align_of(ira, (IrInstructionAbiAlignOf *)instruction); + case IrInstructionIdAlignOf: + return ir_analyze_instruction_align_of(ira, (IrInstructionAlignOf *)instruction); case IrInstructionIdOverflowOp: return ir_analyze_instruction_overflow_op(ira, (IrInstructionOverflowOp *)instruction); case IrInstructionIdTestErr: @@ -14996,6 +15024,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_type_id(ira, (IrInstructionTypeId *)instruction); case IrInstructionIdSetEvalBranchQuota: return ir_analyze_instruction_set_eval_branch_quota(ira, (IrInstructionSetEvalBranchQuota *)instruction); + case IrInstructionIdPtrTypeOf: + return ir_analyze_instruction_ptr_type_of(ira, (IrInstructionPtrTypeOf *)instruction); case IrInstructionIdMaybeWrap: case IrInstructionIdErrWrapCode: case IrInstructionIdErrWrapPayload: @@ -15108,11 +15138,11 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdOverflowOp: // TODO when we support multiple returns this can be side effect free case IrInstructionIdCheckSwitchProngs: case IrInstructionIdCheckStatementIsVoid: - case IrInstructionIdSetGlobalAlign: case IrInstructionIdSetGlobalSection: case IrInstructionIdSetGlobalLinkage: case IrInstructionIdPanic: case IrInstructionIdSetEvalBranchQuota: + case IrInstructionIdPtrTypeOf: return true; case IrInstructionIdPhi: case IrInstructionIdUnOp: @@ -15151,8 +15181,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdBoolNot: case IrInstructionIdSlice: case IrInstructionIdMemberCount: - case IrInstructionIdPreferredAlignOf: - case IrInstructionIdAbiAlignOf: + case IrInstructionIdAlignOf: case IrInstructionIdReturnAddress: case IrInstructionIdFrameAddress: case IrInstructionIdTestErr: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 16afc192e0..bfaf45835b 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -664,14 +664,8 @@ static void ir_print_return_address(IrPrint *irp, IrInstructionReturnAddress *in fprintf(irp->f, "@returnAddress()"); } -static void ir_print_preferred_align_of(IrPrint *irp, IrInstructionPreferredAlignOf *instruction) { - fprintf(irp->f, "@preferredAlignOf("); - ir_print_other_instruction(irp, instruction->type_value); - fprintf(irp->f, ")"); -} - -static void ir_print_abi_align_of(IrPrint *irp, IrInstructionAbiAlignOf *instruction) { - fprintf(irp->f, "@abiAlignOf("); +static void ir_print_align_of(IrPrint *irp, IrInstructionAlignOf *instruction) { + fprintf(irp->f, "@alignOf("); ir_print_other_instruction(irp, instruction->type_value); fprintf(irp->f, ")"); } @@ -860,10 +854,14 @@ static void ir_print_can_implicit_cast(IrPrint *irp, IrInstructionCanImplicitCas fprintf(irp->f, ")"); } -static void ir_print_set_global_align(IrPrint *irp, IrInstructionSetGlobalAlign *instruction) { - fprintf(irp->f, "@setGlobalAlign(%s,", buf_ptr(instruction->tld->name)); - ir_print_other_instruction(irp, instruction->value); - fprintf(irp->f, ")"); +static void ir_print_ptr_type_of(IrPrint *irp, IrInstructionPtrTypeOf *instruction) { + fprintf(irp->f, "&align "); + ir_print_other_instruction(irp, instruction->align_value); + const char *const_str = instruction->is_const ? "const " : ""; + const char *volatile_str = instruction->is_volatile ? "volatile " : ""; + fprintf(irp->f, ":%" PRIu32 ":%" PRIu32 " %s%s", instruction->bit_offset_start, instruction->bit_offset_end, + const_str, volatile_str); + ir_print_other_instruction(irp, instruction->child_type); } static void ir_print_set_global_section(IrPrint *irp, IrInstructionSetGlobalSection *instruction) { @@ -1116,11 +1114,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdFrameAddress: ir_print_frame_address(irp, (IrInstructionFrameAddress *)instruction); break; - case IrInstructionIdPreferredAlignOf: - ir_print_preferred_align_of(irp, (IrInstructionPreferredAlignOf *)instruction); - break; - case IrInstructionIdAbiAlignOf: - ir_print_abi_align_of(irp, (IrInstructionAbiAlignOf *)instruction); + case IrInstructionIdAlignOf: + ir_print_align_of(irp, (IrInstructionAlignOf *)instruction); break; case IrInstructionIdOverflowOp: ir_print_overflow_op(irp, (IrInstructionOverflowOp *)instruction); @@ -1191,8 +1186,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdCanImplicitCast: ir_print_can_implicit_cast(irp, (IrInstructionCanImplicitCast *)instruction); break; - case IrInstructionIdSetGlobalAlign: - ir_print_set_global_align(irp, (IrInstructionSetGlobalAlign *)instruction); + case IrInstructionIdPtrTypeOf: + ir_print_ptr_type_of(irp, (IrInstructionPtrTypeOf *)instruction); break; case IrInstructionIdSetGlobalSection: ir_print_set_global_section(irp, (IrInstructionSetGlobalSection *)instruction); diff --git a/src/parseh.cpp b/src/parseh.cpp index 7b88c901ba..9acbc7c57c 100644 --- a/src/parseh.cpp +++ b/src/parseh.cpp @@ -710,6 +710,7 @@ static TypeTableEntry *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) TypeTableEntry *enum_type = get_partial_container_type(c->codegen, &c->import->decls_scope->base, ContainerKindEnum, c->source_node, buf_ptr(full_type_name), ContainerLayoutExtern); enum_type->data.enumeration.zero_bits_known = true; + enum_type->data.enumeration.abi_alignment = 1; c->enum_type_table.put(bare_name, enum_type); c->decl_table.put(enum_decl, enum_type); replace_with_fwd_decl(c, enum_type, full_type_name); @@ -741,6 +742,7 @@ static TypeTableEntry *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) enum_type->data.enumeration.gen_field_count = 0; enum_type->data.enumeration.complete = true; enum_type->data.enumeration.zero_bits_known = true; + enum_type->data.enumeration.abi_alignment = 1; enum_type->data.enumeration.tag_type = tag_type_entry; enum_type->data.enumeration.src_field_count = field_count; @@ -778,6 +780,9 @@ static TypeTableEntry *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) // create llvm type for root struct enum_type->type_ref = tag_type_entry->type_ref; + enum_type->data.enumeration.abi_alignment = LLVMABIAlignmentOfType(c->codegen->target_data_ref, + enum_type->type_ref); + // create debug type for tag unsigned line = c->source_node ? (c->source_node->line + 1) : 0; uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(c->codegen->target_data_ref, enum_type->type_ref); @@ -864,6 +869,7 @@ static TypeTableEntry *resolve_record_decl(Context *c, const RecordDecl *record_ TypeTableEntry *struct_type = get_partial_container_type(c->codegen, &c->import->decls_scope->base, ContainerKindStruct, c->source_node, buf_ptr(full_type_name), ContainerLayoutExtern); struct_type->data.structure.zero_bits_known = true; + struct_type->data.structure.abi_alignment = 1; c->struct_type_table.put(bare_name, struct_type); c->decl_table.put(record_decl, struct_type); @@ -950,6 +956,8 @@ static TypeTableEntry *resolve_record_decl(Context *c, const RecordDecl *record_ struct_type->data.structure.gen_field_count = field_count; struct_type->data.structure.complete = true; + struct_type->data.structure.abi_alignment = LLVMABIAlignmentOfType(c->codegen->target_data_ref, + struct_type->type_ref); uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(c->codegen->target_data_ref, struct_type->type_ref); uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(c->codegen->target_data_ref, struct_type->type_ref); diff --git a/src/parser.cpp b/src/parser.cpp index f06e9af4e8..23d0fb8971 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -228,6 +228,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m static AstNode *ast_parse_return_expr(ParseContext *pc, size_t *token_index); static AstNode *ast_parse_grouped_expr(ParseContext *pc, size_t *token_index, bool mandatory); static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, bool mandatory); +static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory); static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) { if (token->id == token_id) { @@ -384,7 +385,7 @@ static AstNode *ast_parse_grouped_expr(ParseContext *pc, size_t *token_index, bo } /* -ArrayType : "[" option(Expression) "]" option("const") PrefixOpExpression +ArrayType : "[" option(Expression) "]" option("align" PrimaryExpression)) option("const") option("volatile") PrefixOpExpression */ static AstNode *ast_parse_array_type_expr(ParseContext *pc, size_t *token_index, bool mandatory) { Token *l_bracket = &pc->tokens->at(*token_index); @@ -403,10 +404,22 @@ static AstNode *ast_parse_array_type_expr(ParseContext *pc, size_t *token_index, ast_eat_token(pc, token_index, TokenIdRBracket); - Token *const_tok = &pc->tokens->at(*token_index); - if (const_tok->id == TokenIdKeywordConst) { + Token *token = &pc->tokens->at(*token_index); + if (token->id == TokenIdKeywordAlign) { + *token_index += 1; + node->data.array_type.align_expr = ast_parse_primary_expr(pc, token_index, true); + + token = &pc->tokens->at(*token_index); + } + if (token->id == TokenIdKeywordConst) { *token_index += 1; node->data.array_type.is_const = true; + + token = &pc->tokens->at(*token_index); + } + if (token->id == TokenIdKeywordVolatile) { + *token_index += 1; + node->data.array_type.is_volatile = true; } node->data.array_type.child_type = ast_parse_type_expr(pc, token_index, true); @@ -953,7 +966,6 @@ static PrefixOp tok_to_prefix_op(Token *token) { case TokenIdDash: return PrefixOpNegation; case TokenIdMinusPercent: return PrefixOpNegationWrap; case TokenIdTilde: return PrefixOpBinNot; - case TokenIdAmpersand: return PrefixOpAddressOf; case TokenIdStar: return PrefixOpDereference; case TokenIdMaybe: return PrefixOpMaybe; case TokenIdPercent: return PrefixOpError; @@ -964,12 +976,52 @@ static PrefixOp tok_to_prefix_op(Token *token) { } } +static AstNode *ast_parse_addr_of(ParseContext *pc, size_t *token_index) { + Token *ampersand_tok = ast_eat_token(pc, token_index, TokenIdAmpersand); + + AstNode *node = ast_create_node(pc, NodeTypeAddrOfExpr, ampersand_tok); + + Token *token = &pc->tokens->at(*token_index); + if (token->id == TokenIdKeywordAlign) { + *token_index += 1; + node->data.addr_of_expr.align_expr = ast_parse_primary_expr(pc, token_index, true); + + token = &pc->tokens->at(*token_index); + if (token->id == TokenIdColon) { + *token_index += 1; + Token *bit_offset_start_tok = ast_eat_token(pc, token_index, TokenIdIntLiteral); + ast_eat_token(pc, token_index, TokenIdColon); + Token *bit_offset_end_tok = ast_eat_token(pc, token_index, TokenIdIntLiteral); + token = &pc->tokens->at(*token_index); + + node->data.addr_of_expr.bit_offset_start = token_bigint(bit_offset_start_tok); + node->data.addr_of_expr.bit_offset_end = token_bigint(bit_offset_end_tok); + } + } + if (token->id == TokenIdKeywordConst) { + *token_index += 1; + node->data.addr_of_expr.is_const = true; + + token = &pc->tokens->at(*token_index); + } + if (token->id == TokenIdKeywordVolatile) { + *token_index += 1; + node->data.addr_of_expr.is_volatile = true; + } + + node->data.addr_of_expr.op_expr = ast_parse_prefix_op_expr(pc, token_index, true); + return node; +} + /* PrefixOpExpression : PrefixOp PrefixOpExpression | SuffixOpExpression -PrefixOp = "!" | "-" | "~" | "*" | ("&" option("const") option("volatile")) | "?" | "%" | "%%" | "??" | "-%" +PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" PrimaryExpression option(":" Integer ":" Integer)) option("const") option("volatile")) | "?" | "%" | "%%" | "??" | "-%" */ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); + if (token->id == TokenIdAmpersand) { + return ast_parse_addr_of(pc, token_index); + } PrefixOp prefix_op = tok_to_prefix_op(token); if (prefix_op == PrefixOpInvalid) { return ast_parse_suffix_op_expr(pc, token_index, mandatory); @@ -997,23 +1049,6 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, node->column += 1; } - if (prefix_op == PrefixOpAddressOf) { - Token *const_or_volatile_tok = &pc->tokens->at(*token_index); - if (const_or_volatile_tok->id == TokenIdKeywordConst) { - *token_index += 1; - Token *volatile_token = &pc->tokens->at(*token_index); - if (volatile_token->id == TokenIdKeywordVolatile) { - *token_index += 1; - prefix_op = PrefixOpConstVolatileAddressOf; - } else { - prefix_op = PrefixOpConstAddressOf; - } - } else if (const_or_volatile_tok->id == TokenIdKeywordVolatile) { - prefix_op = PrefixOpVolatileAddressOf; - *token_index += 1; - } - } - AstNode *prefix_op_expr = ast_parse_prefix_op_expr(pc, token_index, true); node->data.prefix_op_expr.primary_expr = prefix_op_expr; node->data.prefix_op_expr.prefix_op = prefix_op; @@ -1499,7 +1534,7 @@ static AstNode *ast_parse_defer_expr(ParseContext *pc, size_t *token_index) { } /* -VariableDeclaration = option("comptime") ("var" | "const") Symbol option(":" TypeExpr) "=" Expression +VariableDeclaration = option("comptime") ("var" | "const") Symbol option(":" TypeExpr) option("align" PrimaryExpression) "=" Expression */ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) @@ -1549,25 +1584,28 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, size_t *to Token *name_token = ast_eat_token(pc, token_index, TokenIdSymbol); node->data.variable_declaration.symbol = token_buf(name_token); - Token *eq_or_colon = &pc->tokens->at(*token_index); - *token_index += 1; - if (eq_or_colon->id == TokenIdEq) { - node->data.variable_declaration.expr = ast_parse_expression(pc, token_index, true); - } else if (eq_or_colon->id == TokenIdColon) { - node->data.variable_declaration.type = ast_parse_type_expr(pc, token_index, true); - Token *eq_token = &pc->tokens->at(*token_index); - if (eq_token->id == TokenIdEq) { - *token_index += 1; + Token *next_token = &pc->tokens->at(*token_index); - node->data.variable_declaration.expr = ast_parse_expression(pc, token_index, true); - } - } else { - ast_invalid_token_error(pc, eq_or_colon); + if (next_token->id == TokenIdColon) { + *token_index += 1; + node->data.variable_declaration.type = ast_parse_type_expr(pc, token_index, true); + next_token = &pc->tokens->at(*token_index); + } + + if (next_token->id == TokenIdKeywordAlign) { + *token_index += 1; + node->data.variable_declaration.align_expr = ast_parse_primary_expr(pc, token_index, true); + next_token = &pc->tokens->at(*token_index); + } + + if (next_token->id == TokenIdEq) { + *token_index += 1; + node->data.variable_declaration.expr = ast_parse_expression(pc, token_index, true); + next_token = &pc->tokens->at(*token_index); } // peek ahead and ensure that all variable declarations are followed by a semicolon - Token *semicolon_token = &pc->tokens->at(*token_index); - ast_expect_token(pc, semicolon_token, TokenIdSemicolon); + ast_expect_token(pc, next_token, TokenIdSemicolon); return node; } @@ -2165,7 +2203,7 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand } /* -FnProto = option("coldcc" | "nakedcc" | "stdcallcc") "fn" option(Symbol) ParamDeclList option("->" TypeExpr) +FnProto = option("coldcc" | "nakedcc" | "stdcallcc") "fn" option(Symbol) ParamDeclList option("align" PrimaryExpression) option("->" TypeExpr) */ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) { Token *first_token = &pc->tokens->at(*token_index); @@ -2200,6 +2238,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m node->data.fn_proto.cc = cc; Token *fn_name = &pc->tokens->at(*token_index); + if (fn_name->id == TokenIdSymbol) { *token_index += 1; node->data.fn_proto.name = token_buf(fn_name); @@ -2210,6 +2249,12 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m ast_parse_param_decl_list(pc, token_index, &node->data.fn_proto.params, &node->data.fn_proto.is_var_args); Token *next_token = &pc->tokens->at(*token_index); + if (next_token->id == TokenIdKeywordAlign) { + *token_index += 1; + + node->data.fn_proto.align_expr = ast_parse_primary_expr(pc, token_index, true); + next_token = &pc->tokens->at(*token_index); + } if (next_token->id == TokenIdArrow) { *token_index += 1; node->data.fn_proto.return_type = ast_parse_type_expr(pc, token_index, false); @@ -2595,6 +2640,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypeFnProto: visit_field(&node->data.fn_proto.return_type, visit, context); visit_node_list(&node->data.fn_proto.params, visit, context); + visit_field(&node->data.fn_proto.align_expr, visit, context); break; case NodeTypeFnDef: visit_field(&node->data.fn_def.fn_proto, visit, context); @@ -2621,6 +2667,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypeVariableDeclaration: visit_field(&node->data.variable_declaration.type, visit, context); visit_field(&node->data.variable_declaration.expr, visit, context); + visit_field(&node->data.variable_declaration.align_expr, visit, context); break; case NodeTypeErrorValueDecl: // none @@ -2769,6 +2816,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypeArrayType: visit_field(&node->data.array_type.size, visit, context); visit_field(&node->data.array_type.child_type, visit, context); + visit_field(&node->data.array_type.align_expr, visit, context); break; case NodeTypeErrorType: // none @@ -2776,5 +2824,9 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypeVarLiteral: // none break; + case NodeTypeAddrOfExpr: + visit_field(&node->data.addr_of_expr.align_expr, visit, context); + visit_field(&node->data.addr_of_expr.op_expr, visit, context); + break; } } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index c8b8c6eb31..c0c78c15f9 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -107,6 +107,7 @@ struct ZigKeyword { }; static const struct ZigKeyword zig_keywords[] = { + {"align", TokenIdKeywordAlign}, {"and", TokenIdKeywordAnd}, {"asm", TokenIdKeywordAsm}, {"break", TokenIdKeywordBreak}, @@ -1454,6 +1455,7 @@ const char * token_name(TokenId id) { case TokenIdFatArrow: return "=>"; case TokenIdFloatLiteral: return "FloatLiteral"; case TokenIdIntLiteral: return "IntLiteral"; + case TokenIdKeywordAlign: return "align"; case TokenIdKeywordAnd: return "and"; case TokenIdKeywordAsm: return "asm"; case TokenIdKeywordBreak: return "break"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index c113a152c0..bcad977864 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -46,6 +46,7 @@ enum TokenId { TokenIdFatArrow, TokenIdFloatLiteral, TokenIdIntLiteral, + TokenIdKeywordAlign, TokenIdKeywordAnd, TokenIdKeywordAsm, TokenIdKeywordBreak, diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 4f37444a3b..3bef0ff699 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -713,11 +713,6 @@ void ZigLLVMAddModuleDebugInfoFlag(LLVMModuleRef module) { unwrap(module)->addModuleFlag(Module::Warning, "Debug Info Version", DEBUG_METADATA_VERSION); } -unsigned ZigLLVMGetPrefTypeAlignment(LLVMTargetDataRef TD, LLVMTypeRef Ty) { - return unwrap(TD)->getPrefTypeAlignment(unwrap(Ty)); -} - - static AtomicOrdering mapFromLLVMOrdering(LLVMAtomicOrdering Ordering) { switch (Ordering) { case LLVMAtomicOrderingNotAtomic: return AtomicOrdering::NotAtomic; diff --git a/src/zig_llvm.hpp b/src/zig_llvm.hpp index ded420fd5b..857f0cdcb8 100644 --- a/src/zig_llvm.hpp +++ b/src/zig_llvm.hpp @@ -167,8 +167,6 @@ void ZigLLVMSetFastMath(LLVMBuilderRef builder_wrapped, bool on_state); void ZigLLVMAddFunctionAttr(LLVMValueRef fn, const char *attr_name, const char *attr_value); void ZigLLVMAddFunctionAttrCold(LLVMValueRef fn); -unsigned ZigLLVMGetPrefTypeAlignment(LLVMTargetDataRef TD, LLVMTypeRef Ty); - // copied from include/llvm/ADT/Triple.h diff --git a/std/hash_map.zig b/std/hash_map.zig index a68413538d..582aa7ccaf 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -251,7 +251,7 @@ test "basicHashMapTest" { } fn hash_i32(x: i32) -> u32 { - *@ptrCast(&u32, &x) + @bitCast(u32, x) } fn eql_i32(a: i32, b: i32) -> bool { diff --git a/std/mem.zig b/std/mem.zig index 4e275bed63..26fb2a3cd6 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -21,10 +21,7 @@ pub const Allocator = struct { /// Aborts the program if an allocation fails. fn checkedAlloc(self: &Allocator, comptime T: type, n: usize) -> []T { - alloc(self, T, n) %% |err| { - %%io.stderr.printf("allocation failure: {}\n", @errorName(err)); - os.abort() - } + alloc(self, T, n) %% |err| debug.panic("allocation failure: {}", @errorName(err)) } fn create(self: &Allocator, comptime T: type) -> %&T { diff --git a/test/cases/alignof.zig b/test/cases/alignof.zig index 97f8efe503..27b95c7fdc 100644 --- a/test/cases/alignof.zig +++ b/test/cases/alignof.zig @@ -3,12 +3,9 @@ const builtin = @import("builtin"); const Foo = struct { x: u32, y: u32, z: u32, }; -test "@abiAlignOf(T) before referencing T" { - comptime assert(@cAbiAlignOf(Foo) != @maxValue(usize)); +test "@alignOf(T) before referencing T" { + comptime assert(@alignOf(Foo) != @maxValue(usize)); if (builtin.arch == builtin.Arch.x86_64) { - comptime { - assert(@cAbiAlignOf(Foo) == 4); - assert(@preferredAlignOf(Foo) == 8); - } + comptime assert(@alignOf(Foo) == 4); } } diff --git a/test/cases/enum.zig b/test/cases/enum.zig index a07efaf61d..66fe69e02a 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -124,8 +124,8 @@ const BareNumber = enum { test "enum alignment" { comptime { - assert(@cAbiAlignOf(AlignTestEnum) >= @cAbiAlignOf([9]u8)); - assert(@cAbiAlignOf(AlignTestEnum) >= @cAbiAlignOf(u64)); + assert(@alignOf(AlignTestEnum) >= @alignOf([9]u8)); + assert(@alignOf(AlignTestEnum) >= @alignOf(u64)); } } diff --git a/test/cases/struct.zig b/test/cases/struct.zig index 792ac9fd98..308b75672c 100644 --- a/test/cases/struct.zig +++ b/test/cases/struct.zig @@ -201,8 +201,6 @@ test "packed struct" { } -const u2 = @IntType(false, 2); - const BitField1 = packed struct { a: u3, b: u3, diff --git a/test/cases/switch.zig b/test/cases/switch.zig index d441434876..154e251a04 100644 --- a/test/cases/switch.zig +++ b/test/cases/switch.zig @@ -172,7 +172,6 @@ test "switch handles all cases of number" { comptime testSwitchHandleAllCases(); } -const u2 = @IntType(false, 2); fn testSwitchHandleAllCases() { assert(testSwitchHandleAllCasesExhaustive(0) == 3); assert(testSwitchHandleAllCasesExhaustive(1) == 2); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index e2f4489b0f..ac5e06d9f3 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1316,13 +1316,15 @@ pub fn addCases(cases: &tests.CompileErrorContext) { \\} , ".tmp_source.zig:2:24: error: integer value 753664 cannot be implicitly casted to type 'u16'"); - cases.add("set global variable alignment to non power of 2", - \\const some_data: [100]u8 = undefined; - \\comptime { - \\ @setGlobalAlign(some_data, 3); - \\} + cases.add("global variable alignment non power of 2", + \\const some_data: [100]u8 align 3 = undefined; \\export fn entry() -> usize { @sizeOf(@typeOf(some_data)) } - , ".tmp_source.zig:3:32: error: alignment value must be power of 2"); + , ".tmp_source.zig:1:32: error: alignment value 3 is not a power of 2"); + + cases.add("function alignment non power of 2", + \\extern fn foo() align 3; + \\export fn entry() { foo() } + , ".tmp_source.zig:1:23: error: alignment value 3 is not a power of 2"); cases.add("compile log", \\export fn foo() { @@ -1342,9 +1344,6 @@ pub fn addCases(cases: &tests.CompileErrorContext) { ".tmp_source.zig:2:17: note: called from here"); cases.add("casting bit offset pointer to regular pointer", - \\const u2 = @IntType(false, 2); - \\const u3 = @IntType(false, 3); - \\ \\const BitField = packed struct { \\ a: u3, \\ b: u3, @@ -1360,7 +1359,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) { \\} \\ \\export fn entry() -> usize { @sizeOf(@typeOf(foo)) } - , ".tmp_source.zig:11:26: error: expected type '&const u3', found '&:3:6 const u3'"); + , ".tmp_source.zig:8:26: error: expected type '&const u3', found '&align 1:3:6 const u3'"); cases.add("referring to a struct that is invalid", \\const UsbDeviceRequest = struct { @@ -1626,24 +1625,6 @@ pub fn addCases(cases: &tests.CompileErrorContext) { "error: 'main' is private", ".tmp_source.zig:1:1: note: declared here"); - cases.add("@setGlobalAlign extern variable", - \\extern var foo: i32; - \\comptime { - \\ @setGlobalAlign(foo, 4); - \\} - , - ".tmp_source.zig:3:5: error: cannot set alignment of external variable 'foo'", - ".tmp_source.zig:1:8: note: declared here"); - - cases.add("@setGlobalAlign extern fn", - \\extern fn foo(); - \\comptime { - \\ @setGlobalAlign(foo, 4); - \\} - , - ".tmp_source.zig:3:5: error: cannot set alignment of external function 'foo'", - ".tmp_source.zig:1:8: note: declared here"); - cases.add("@setGlobalSection extern variable", \\extern var foo: i32; \\comptime {