Commit c5c9d98065

Andrew Kelley <superjoe30@gmail.com>
2017-08-29 13:30:22
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
1 parent b8ed0cb
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<IrInstruction *> alloca_list;
     ZigList<VariableTableEntry *> 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<FnTableEntry *> inline_fns;
     ZigList<AstNode *> 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;
 
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));
+    TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct);
+    entry->is_copyable = true;
 
-        slice_type_common_init(g, child_type, is_const, entry);
+    // 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);
 
-        entry->type_ref = var_peer->type_ref;
-        entry->di_type = var_peer->di_type;
+    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;
-    } 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;
-            }
+    }
+
+    // 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;
         }
+    }
 
-        buf_resize(&entry->name, 0);
-        buf_appendf(&entry->name, "[]%s", buf_ptr(&child_type->name));
+    slice_type_common_init(g, ptr_type, entry);
 
-        slice_type_common_init(g, child_type, is_const, entry);
+    if (!entry->type_ref) {
+        entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&entry->name));
 
-        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);
 
-            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);
 
-            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);
+            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);
 
-                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;
-            }
-        }
+            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, "");
 
-        entry->data.structure.complete = true;
-        entry->data.structure.zero_bits_known = true;
+            ZigLLVMReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type);
+            entry->di_type = replacement_di_type;
 
-        *parent_pointer = entry;
-        return entry;
+            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<ZigLLVMDIType*>(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<TypeEnumField>(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;
+}
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
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) {
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<LLVMValueRef>(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", };
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<typename T>
 static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
     T *special_instruction = allocate<T>(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<IrInstructionPtrTypeOf>(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<IrInstructionUnOp>(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<IrInstructionDeclVar>(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<IrInstructionSliceType>(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<IrInstructionPreferredAlignOf>(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<IrInstructionAbiAlignOf>(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<IrInstructionAlignOf>(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<IrInstructionSetGlobalAlign>(
-            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:
-            {
-                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:
+        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_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));
+    }
+
+    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_lval_wrap(irb, scope, value, lval);
+    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<IrInstruction *> 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<IrInstruction *> incoming_values = {0};
         ZigList<IrBasicBlock *> 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))
-        return ira->codegen->builtin_types.entry_invalid;
-    bool is_const = slice_type_instruction->is_const;
+    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 *resolved_child_type = ir_resolve_type(ira, child_type);
-    if (type_is_invalid(resolved_child_type))
+    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;
 
-    switch (resolved_child_type->id) {
+    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;
+
+    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,62 +13949,11 @@ static TypeTableEntry *ir_analyze_instruction_preferred_align_of(IrAnalyze *ira,
         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 = 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:
@@ -13953,9 +13967,8 @@ static TypeTableEntry *ir_analyze_instruction_abi_align_of(IrAnalyze *ira, IrIns
         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:
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);
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);
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) {
+    Token *next_token = &pc->tokens->at(*token_index);
+
+    if (next_token->id == TokenIdColon) {
+        *token_index += 1;
         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;
+        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 == 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;
     }
 }
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";
src/tokenizer.hpp
@@ -46,6 +46,7 @@ enum TokenId {
     TokenIdFatArrow,
     TokenIdFloatLiteral,
     TokenIdIntLiteral,
+    TokenIdKeywordAlign,
     TokenIdKeywordAnd,
     TokenIdKeywordAsm,
     TokenIdKeywordBreak,
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;
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
 
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 {
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 {
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);
     }
 }
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));
     }
 }
 
test/cases/struct.zig
@@ -201,8 +201,6 @@ test "packed struct" {
 }
 
 
-const u2 = @IntType(false, 2);
-
 const BitField1 = packed struct {
     a: u3,
     b: u3,
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);
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 {