| author | |
| committer | |
| log | 898d65baa9198f2fb1c5df91fba51a58c3148626 |
| tree | fd4f7178f974b7be7236782aacde4981df03238b |
| parent | 910a96f0468c635a135d9fccd39f139ba0775ef9 |
* add alignment capability for fn protos
* add @alignCast
* fix some ast rendering code
* fix some ir rendering code
* add error for pointer cast increasing alignment
* update allocators in std to correctly align
See #3713 files changed, 419 insertions(+), 70 deletions(-)
src/all_types.hpp+11| ... | ... | @@ -1252,6 +1252,7 @@ enum BuiltinFnId { |
| 1252 | 1252 | BuiltinFnIdShlExact, |
| 1253 | 1253 | BuiltinFnIdShrExact, |
| 1254 | 1254 | BuiltinFnIdSetEvalBranchQuota, |
| 1255 | BuiltinFnIdAlignCast, | |
| 1255 | 1256 | }; |
| 1256 | 1257 | |
| 1257 | 1258 | struct BuiltinFnEntry { |
| ... | ... | @@ -1274,6 +1275,7 @@ enum PanicMsgId { |
| 1274 | 1275 | PanicMsgIdSliceWidenRemainder, |
| 1275 | 1276 | PanicMsgIdUnwrapMaybeFail, |
| 1276 | 1277 | PanicMsgIdInvalidErrorCode, |
| 1278 | PanicMsgIdIncorrectAlignment, | |
| 1277 | 1279 | |
| 1278 | 1280 | PanicMsgIdCount, |
| 1279 | 1281 | }; |
| ... | ... | @@ -1856,6 +1858,7 @@ enum IrInstructionId { |
| 1856 | 1858 | IrInstructionIdTypeId, |
| 1857 | 1859 | IrInstructionIdSetEvalBranchQuota, |
| 1858 | 1860 | IrInstructionIdPtrTypeOf, |
| 1861 | IrInstructionIdAlignCast, | |
| 1859 | 1862 | }; |
| 1860 | 1863 | |
| 1861 | 1864 | struct IrInstruction { |
| ... | ... | @@ -2462,6 +2465,7 @@ struct IrInstructionFnProto { |
| 2462 | 2465 | IrInstruction base; |
| 2463 | 2466 | |
| 2464 | 2467 | IrInstruction **param_types; |
| 2468 | IrInstruction *align_value; | |
| 2465 | 2469 | IrInstruction *return_type; |
| 2466 | 2470 | bool is_var_args; |
| 2467 | 2471 | }; |
| ... | ... | @@ -2638,6 +2642,13 @@ struct IrInstructionPtrTypeOf { |
| 2638 | 2642 | bool is_volatile; |
| 2639 | 2643 | }; |
| 2640 | 2644 | |
| 2645 | struct IrInstructionAlignCast { | |
| 2646 | IrInstruction base; | |
| 2647 | ||
| 2648 | IrInstruction *align_bytes; | |
| 2649 | IrInstruction *target; | |
| 2650 | }; | |
| 2651 | ||
| 2641 | 2652 | static const size_t slice_ptr_index = 0; |
| 2642 | 2653 | static const size_t slice_len_index = 1; |
| 2643 | 2654 |
src/analyze.cpp+20-11| ... | ... | @@ -884,6 +884,9 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { |
| 884 | 884 | buf_appendf(&fn_type->name, "%s...", comma); |
| 885 | 885 | } |
| 886 | 886 | buf_appendf(&fn_type->name, ")"); |
| 887 | if (fn_type_id->alignment != 0) { | |
| 888 | buf_appendf(&fn_type->name, " align %" PRIu32, fn_type_id->alignment); | |
| 889 | } | |
| 887 | 890 | if (fn_type_id->return_type->id != TypeTableEntryIdVoid) { |
| 888 | 891 | buf_appendf(&fn_type->name, " -> %s", buf_ptr(&fn_type_id->return_type->name)); |
| 889 | 892 | } |
| ... | ... | @@ -1058,12 +1061,13 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou |
| 1058 | 1061 | fn_type_id->is_var_args = fn_proto->is_var_args; |
| 1059 | 1062 | } |
| 1060 | 1063 | |
| 1061 | static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope) { | |
| 1064 | static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope, uint32_t alignment) { | |
| 1062 | 1065 | assert(proto_node->type == NodeTypeFnProto); |
| 1063 | 1066 | AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; |
| 1064 | 1067 | |
| 1065 | 1068 | FnTypeId fn_type_id = {0}; |
| 1066 | 1069 | init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length); |
| 1070 | fn_type_id.alignment = alignment; | |
| 1067 | 1071 | |
| 1068 | 1072 | for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) { |
| 1069 | 1073 | AstNode *param_node = fn_proto->params.at(fn_type_id.next_param_index); |
| ... | ... | @@ -2056,23 +2060,23 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { |
| 2056 | 2060 | } |
| 2057 | 2061 | |
| 2058 | 2062 | Scope *child_scope = fn_table_entry->fndef_scope ? &fn_table_entry->fndef_scope->base : tld_fn->base.parent_scope; |
| 2059 | fn_table_entry->type_entry = analyze_fn_type(g, source_node, child_scope); | |
| 2060 | ||
| 2061 | if (fn_table_entry->type_entry->id == TypeTableEntryIdInvalid) { | |
| 2062 | tld_fn->base.resolution = TldResolutionInvalid; | |
| 2063 | return; | |
| 2064 | } | |
| 2065 | 2063 | |
| 2064 | uint32_t alignment = 0; | |
| 2066 | 2065 | if (fn_proto->align_expr != nullptr) { |
| 2067 | if (!analyze_const_align(g, tld_fn->base.parent_scope, fn_proto->align_expr, | |
| 2068 | &fn_table_entry->align_bytes)) | |
| 2069 | { | |
| 2066 | if (!analyze_const_align(g, child_scope, fn_proto->align_expr, &alignment)) { | |
| 2070 | 2067 | fn_table_entry->type_entry = g->builtin_types.entry_invalid; |
| 2071 | 2068 | tld_fn->base.resolution = TldResolutionInvalid; |
| 2072 | 2069 | return; |
| 2073 | 2070 | } |
| 2074 | 2071 | } |
| 2075 | 2072 | |
| 2073 | fn_table_entry->type_entry = analyze_fn_type(g, source_node, child_scope, alignment); | |
| 2074 | ||
| 2075 | if (fn_table_entry->type_entry->id == TypeTableEntryIdInvalid) { | |
| 2076 | tld_fn->base.resolution = TldResolutionInvalid; | |
| 2077 | return; | |
| 2078 | } | |
| 2079 | ||
| 2076 | 2080 | if (!fn_table_entry->type_entry->data.fn.is_generic) { |
| 2077 | 2081 | g->fn_protos.append(fn_table_entry); |
| 2078 | 2082 | |
| ... | ... | @@ -2663,6 +2667,9 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry * |
| 2663 | 2667 | if (expected_type->id == TypeTableEntryIdFn && |
| 2664 | 2668 | actual_type->id == TypeTableEntryIdFn) |
| 2665 | 2669 | { |
| 2670 | if (expected_type->data.fn.fn_type_id.alignment > actual_type->data.fn.fn_type_id.alignment) { | |
| 2671 | return false; | |
| 2672 | } | |
| 2666 | 2673 | if (expected_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) { |
| 2667 | 2674 | return false; |
| 2668 | 2675 | } |
| ... | ... | @@ -3384,6 +3391,7 @@ uint32_t fn_type_id_hash(FnTypeId *id) { |
| 3384 | 3391 | result += ((uint32_t)(id->cc)) * (uint32_t)3349388391; |
| 3385 | 3392 | result += id->is_var_args ? (uint32_t)1931444534 : 0; |
| 3386 | 3393 | result += hash_ptr(id->return_type); |
| 3394 | result += id->alignment * 0xd3b3f3e2; | |
| 3387 | 3395 | for (size_t i = 0; i < id->param_count; i += 1) { |
| 3388 | 3396 | FnTypeParamInfo *info = &id->param_info[i]; |
| 3389 | 3397 | result += info->is_noalias ? (uint32_t)892356923 : 0; |
| ... | ... | @@ -3396,7 +3404,8 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) { |
| 3396 | 3404 | if (a->cc != b->cc || |
| 3397 | 3405 | a->return_type != b->return_type || |
| 3398 | 3406 | a->is_var_args != b->is_var_args || |
| 3399 | a->param_count != b->param_count) | |
| 3407 | a->param_count != b->param_count || | |
| 3408 | a->alignment != b->alignment) | |
| 3400 | 3409 | { |
| 3401 | 3410 | return false; |
| 3402 | 3411 | } |
src/ast_render.cpp+1-1| ... | ... | @@ -953,7 +953,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { |
| 953 | 953 | render_node_ungrouped(ar, node->data.slice_expr.array_ref_expr); |
| 954 | 954 | fprintf(ar->f, "["); |
| 955 | 955 | render_node_grouped(ar, node->data.slice_expr.start); |
| 956 | fprintf(ar->f, "..."); | |
| 956 | fprintf(ar->f, ".."); | |
| 957 | 957 | if (node->data.slice_expr.end) |
| 958 | 958 | render_node_grouped(ar, node->data.slice_expr.end); |
| 959 | 959 | fprintf(ar->f, "]"); |
src/codegen.cpp+71| ... | ... | @@ -688,6 +688,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { |
| 688 | 688 | return buf_create_from_str("reached unreachable code"); |
| 689 | 689 | case PanicMsgIdInvalidErrorCode: |
| 690 | 690 | return buf_create_from_str("invalid error code"); |
| 691 | case PanicMsgIdIncorrectAlignment: | |
| 692 | return buf_create_from_str("incorrect alignment"); | |
| 691 | 693 | } |
| 692 | 694 | zig_unreachable(); |
| 693 | 695 | } |
| ... | ... | @@ -2605,6 +2607,72 @@ static LLVMValueRef ir_render_field_parent_ptr(CodeGen *g, IrExecutable *executa |
| 2605 | 2607 | } |
| 2606 | 2608 | } |
| 2607 | 2609 | |
| 2610 | static LLVMValueRef get_default_aligned_load(CodeGen *g, LLVMValueRef ptr) { | |
| 2611 | LLVMValueRef result = LLVMBuildLoad(g->builder, ptr, ""); | |
| 2612 | LLVMSetAlignment(result, LLVMABIAlignmentOfType(g->target_data_ref, LLVMGetElementType(LLVMTypeOf(ptr)))); | |
| 2613 | return result; | |
| 2614 | } | |
| 2615 | ||
| 2616 | static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, IrInstructionAlignCast *instruction) { | |
| 2617 | LLVMValueRef target_val = ir_llvm_value(g, instruction->target); | |
| 2618 | assert(target_val); | |
| 2619 | ||
| 2620 | bool want_debug_safety = ir_want_debug_safety(g, &instruction->base); | |
| 2621 | if (!want_debug_safety) { | |
| 2622 | return target_val; | |
| 2623 | } | |
| 2624 | ||
| 2625 | TypeTableEntry *target_type = instruction->base.value.type; | |
| 2626 | uint32_t align_bytes; | |
| 2627 | LLVMValueRef ptr_val; | |
| 2628 | ||
| 2629 | if (target_type->id == TypeTableEntryIdPointer) { | |
| 2630 | align_bytes = target_type->data.pointer.alignment; | |
| 2631 | ptr_val = target_val; | |
| 2632 | } else if (target_type->id == TypeTableEntryIdFn) { | |
| 2633 | align_bytes = target_type->data.fn.fn_type_id.alignment; | |
| 2634 | ptr_val = target_val; | |
| 2635 | } else if (target_type->id == TypeTableEntryIdMaybe && | |
| 2636 | target_type->data.maybe.child_type->id == TypeTableEntryIdPointer) | |
| 2637 | { | |
| 2638 | align_bytes = target_type->data.maybe.child_type->data.pointer.alignment; | |
| 2639 | ptr_val = target_val; | |
| 2640 | } else if (target_type->id == TypeTableEntryIdMaybe && | |
| 2641 | target_type->data.maybe.child_type->id == TypeTableEntryIdFn) | |
| 2642 | { | |
| 2643 | align_bytes = target_type->data.maybe.child_type->data.fn.fn_type_id.alignment; | |
| 2644 | ptr_val = target_val; | |
| 2645 | } else if (target_type->id == TypeTableEntryIdStruct && target_type->data.structure.is_slice) { | |
| 2646 | TypeTableEntry *slice_ptr_type = target_type->data.structure.fields[slice_ptr_index].type_entry; | |
| 2647 | align_bytes = slice_ptr_type->data.pointer.alignment; | |
| 2648 | ||
| 2649 | size_t ptr_index = target_type->data.structure.fields[slice_ptr_index].gen_index; | |
| 2650 | LLVMValueRef ptr_val_ptr = LLVMBuildStructGEP(g->builder, target_val, (unsigned)ptr_index, ""); | |
| 2651 | ptr_val = get_default_aligned_load(g, ptr_val_ptr); | |
| 2652 | } else { | |
| 2653 | zig_unreachable(); | |
| 2654 | } | |
| 2655 | ||
| 2656 | assert(align_bytes != 1); | |
| 2657 | ||
| 2658 | TypeTableEntry *usize = g->builtin_types.entry_usize; | |
| 2659 | LLVMValueRef ptr_as_int_val = LLVMBuildPtrToInt(g->builder, ptr_val, usize->type_ref, ""); | |
| 2660 | LLVMValueRef alignment_minus_1 = LLVMConstInt(usize->type_ref, align_bytes - 1, false); | |
| 2661 | LLVMValueRef anded_val = LLVMBuildAnd(g->builder, ptr_as_int_val, alignment_minus_1, ""); | |
| 2662 | LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, anded_val, LLVMConstNull(usize->type_ref), ""); | |
| 2663 | ||
| 2664 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "AlignCastOk"); | |
| 2665 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "AlignCastFail"); | |
| 2666 | ||
| 2667 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); | |
| 2668 | ||
| 2669 | LLVMPositionBuilderAtEnd(g->builder, fail_block); | |
| 2670 | gen_debug_safety_crash(g, PanicMsgIdIncorrectAlignment); | |
| 2671 | ||
| 2672 | LLVMPositionBuilderAtEnd(g->builder, ok_block); | |
| 2673 | ||
| 2674 | return target_val; | |
| 2675 | } | |
| 2608 | 2676 | |
| 2609 | 2677 | static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) { |
| 2610 | 2678 | switch (atomic_order) { |
| ... | ... | @@ -3350,6 +3418,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 3350 | 3418 | return ir_render_enum_tag_name(g, executable, (IrInstructionEnumTagName *)instruction); |
| 3351 | 3419 | case IrInstructionIdFieldParentPtr: |
| 3352 | 3420 | return ir_render_field_parent_ptr(g, executable, (IrInstructionFieldParentPtr *)instruction); |
| 3421 | case IrInstructionIdAlignCast: | |
| 3422 | return ir_render_align_cast(g, executable, (IrInstructionAlignCast *)instruction); | |
| 3353 | 3423 | } |
| 3354 | 3424 | zig_unreachable(); |
| 3355 | 3425 | } |
| ... | ... | @@ -4633,6 +4703,7 @@ static void define_builtin_fns(CodeGen *g) { |
| 4633 | 4703 | create_builtin_fn(g, BuiltinFnIdShlExact, "shlExact", 2); |
| 4634 | 4704 | create_builtin_fn(g, BuiltinFnIdShrExact, "shrExact", 2); |
| 4635 | 4705 | create_builtin_fn(g, BuiltinFnIdSetEvalBranchQuota, "setEvalBranchQuota", 1); |
| 4706 | create_builtin_fn(g, BuiltinFnIdAlignCast, "alignCast", 2); | |
| 4636 | 4707 | } |
| 4637 | 4708 | |
| 4638 | 4709 | static const char *bool_to_str(bool b) { |
src/ir.cpp+181-21| ... | ... | @@ -555,6 +555,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrTypeOf *) { |
| 555 | 555 | return IrInstructionIdPtrTypeOf; |
| 556 | 556 | } |
| 557 | 557 | |
| 558 | static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignCast *) { | |
| 559 | return IrInstructionIdAlignCast; | |
| 560 | } | |
| 561 | ||
| 558 | 562 | template<typename T> |
| 559 | 563 | static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { |
| 560 | 564 | T *special_instruction = allocate<T>(1); |
| ... | ... | @@ -1899,10 +1903,11 @@ static IrInstruction *ir_build_unwrap_err_payload_from(IrBuilder *irb, IrInstruc |
| 1899 | 1903 | } |
| 1900 | 1904 | |
| 1901 | 1905 | static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 1902 | IrInstruction **param_types, IrInstruction *return_type, bool is_var_args) | |
| 1906 | IrInstruction **param_types, IrInstruction *align_value, IrInstruction *return_type, bool is_var_args) | |
| 1903 | 1907 | { |
| 1904 | 1908 | IrInstructionFnProto *instruction = ir_build_instruction<IrInstructionFnProto>(irb, scope, source_node); |
| 1905 | 1909 | instruction->param_types = param_types; |
| 1910 | instruction->align_value = align_value; | |
| 1906 | 1911 | instruction->return_type = return_type; |
| 1907 | 1912 | instruction->is_var_args = is_var_args; |
| 1908 | 1913 | |
| ... | ... | @@ -1912,6 +1917,7 @@ static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *s |
| 1912 | 1917 | for (size_t i = 0; i < param_count; i += 1) { |
| 1913 | 1918 | ir_ref_instruction(param_types[i], irb->current_basic_block); |
| 1914 | 1919 | } |
| 1920 | if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block); | |
| 1915 | 1921 | ir_ref_instruction(return_type, irb->current_basic_block); |
| 1916 | 1922 | |
| 1917 | 1923 | return &instruction->base; |
| ... | ... | @@ -2219,6 +2225,19 @@ static IrInstruction *ir_build_set_eval_branch_quota(IrBuilder *irb, Scope *scop |
| 2219 | 2225 | return &instruction->base; |
| 2220 | 2226 | } |
| 2221 | 2227 | |
| 2228 | static IrInstruction *ir_build_align_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, | |
| 2229 | IrInstruction *align_bytes, IrInstruction *target) | |
| 2230 | { | |
| 2231 | IrInstructionAlignCast *instruction = ir_build_instruction<IrInstructionAlignCast>(irb, scope, source_node); | |
| 2232 | instruction->align_bytes = align_bytes; | |
| 2233 | instruction->target = target; | |
| 2234 | ||
| 2235 | ir_ref_instruction(align_bytes, irb->current_basic_block); | |
| 2236 | ir_ref_instruction(target, irb->current_basic_block); | |
| 2237 | ||
| 2238 | return &instruction->base; | |
| 2239 | } | |
| 2240 | ||
| 2222 | 2241 | static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) { |
| 2223 | 2242 | return nullptr; |
| 2224 | 2243 | } |
| ... | ... | @@ -2738,6 +2757,10 @@ static IrInstruction *ir_instruction_fnproto_get_dep(IrInstructionFnProto *instr |
| 2738 | 2757 | if (param_index < instruction->base.source_node->data.fn_proto.params.length) { |
| 2739 | 2758 | return instruction->param_types[param_index]; |
| 2740 | 2759 | } |
| 2760 | size_t next_index = param_index - instruction->base.source_node->data.fn_proto.params.length; | |
| 2761 | if (next_index == 0 && instruction->align_value != nullptr) { | |
| 2762 | return instruction->align_value; | |
| 2763 | } | |
| 2741 | 2764 | return nullptr; |
| 2742 | 2765 | } |
| 2743 | 2766 | |
| ... | ... | @@ -2925,6 +2948,14 @@ static IrInstruction *ir_instruction_ptrtypeof_get_dep(IrInstructionPtrTypeOf *i |
| 2925 | 2948 | } |
| 2926 | 2949 | } |
| 2927 | 2950 | |
| 2951 | static IrInstruction *ir_instruction_aligncast_get_dep(IrInstructionAlignCast *instruction, size_t index) { | |
| 2952 | switch (index) { | |
| 2953 | case 0: return instruction->align_bytes; | |
| 2954 | case 1: return instruction->target; | |
| 2955 | default: return nullptr; | |
| 2956 | } | |
| 2957 | } | |
| 2958 | ||
| 2928 | 2959 | static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) { |
| 2929 | 2960 | switch (instruction->id) { |
| 2930 | 2961 | case IrInstructionIdInvalid: |
| ... | ... | @@ -3121,6 +3152,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t |
| 3121 | 3152 | return ir_instruction_setevalbranchquota_get_dep((IrInstructionSetEvalBranchQuota *) instruction, index); |
| 3122 | 3153 | case IrInstructionIdPtrTypeOf: |
| 3123 | 3154 | return ir_instruction_ptrtypeof_get_dep((IrInstructionPtrTypeOf *) instruction, index); |
| 3155 | case IrInstructionIdAlignCast: | |
| 3156 | return ir_instruction_aligncast_get_dep((IrInstructionAlignCast *) instruction, index); | |
| 3124 | 3157 | } |
| 3125 | 3158 | zig_unreachable(); |
| 3126 | 3159 | } |
| ... | ... | @@ -4531,6 +4564,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4531 | 4564 | |
| 4532 | 4565 | return ir_build_set_eval_branch_quota(irb, scope, node, arg0_value); |
| 4533 | 4566 | } |
| 4567 | case BuiltinFnIdAlignCast: | |
| 4568 | { | |
| 4569 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | |
| 4570 | IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); | |
| 4571 | if (arg0_value == irb->codegen->invalid_instruction) | |
| 4572 | return arg0_value; | |
| 4573 | ||
| 4574 | AstNode *arg1_node = node->data.fn_call_expr.params.at(1); | |
| 4575 | IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); | |
| 4576 | if (arg1_value == irb->codegen->invalid_instruction) | |
| 4577 | return arg1_value; | |
| 4578 | ||
| 4579 | return ir_build_align_cast(irb, scope, node, arg0_value, arg1_value); | |
| 4580 | } | |
| 4534 | 4581 | } |
| 4535 | 4582 | zig_unreachable(); |
| 4536 | 4583 | } |
| ... | ... | @@ -6060,11 +6107,18 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo |
| 6060 | 6107 | param_types[i] = type_value; |
| 6061 | 6108 | } |
| 6062 | 6109 | |
| 6110 | IrInstruction *align_value = nullptr; | |
| 6111 | if (node->data.fn_proto.align_expr != nullptr) { | |
| 6112 | align_value = ir_gen_node(irb, node->data.fn_proto.align_expr, parent_scope); | |
| 6113 | if (align_value == irb->codegen->invalid_instruction) | |
| 6114 | return irb->codegen->invalid_instruction; | |
| 6115 | } | |
| 6116 | ||
| 6063 | 6117 | IrInstruction *return_type = ir_gen_node(irb, node->data.fn_proto.return_type, parent_scope); |
| 6064 | 6118 | if (return_type == irb->codegen->invalid_instruction) |
| 6065 | 6119 | return irb->codegen->invalid_instruction; |
| 6066 | 6120 | |
| 6067 | return ir_build_fn_proto(irb, parent_scope, node, param_types, return_type, is_var_args); | |
| 6121 | return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, return_type, is_var_args); | |
| 6068 | 6122 | } |
| 6069 | 6123 | |
| 6070 | 6124 | static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope, |
| ... | ... | @@ -8316,15 +8370,29 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst |
| 8316 | 8370 | } |
| 8317 | 8371 | |
| 8318 | 8372 | // explicit cast from []T to []u8 or []u8 to []T |
| 8319 | if (is_slice(wanted_type) && is_slice(actual_type) && | |
| 8320 | (is_u8(wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type) || | |
| 8321 | is_u8(actual_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type)) && | |
| 8322 | (wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const || | |
| 8323 | !actual_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const)) | |
| 8324 | { | |
| 8325 | if (!ir_emit_global_runtime_side_effect(ira, source_instr)) | |
| 8326 | return ira->codegen->invalid_instruction; | |
| 8327 | return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpResizeSlice, true); | |
| 8373 | if (is_slice(wanted_type) && is_slice(actual_type)) { | |
| 8374 | TypeTableEntry *wanted_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; | |
| 8375 | TypeTableEntry *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry; | |
| 8376 | if ((is_u8(wanted_ptr_type->data.pointer.child_type) || is_u8(actual_ptr_type->data.pointer.child_type)) && | |
| 8377 | (wanted_ptr_type->data.pointer.is_const || !actual_ptr_type->data.pointer.is_const)) | |
| 8378 | { | |
| 8379 | uint32_t src_align_bytes = get_ptr_align(actual_ptr_type); | |
| 8380 | uint32_t dest_align_bytes = get_ptr_align(wanted_ptr_type); | |
| 8381 | ||
| 8382 | if (dest_align_bytes > src_align_bytes) { | |
| 8383 | ErrorMsg *msg = ir_add_error(ira, source_instr, | |
| 8384 | buf_sprintf("cast increases pointer alignment")); | |
| 8385 | add_error_note(ira->codegen, msg, source_instr->source_node, | |
| 8386 | buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&actual_type->name), src_align_bytes)); | |
| 8387 | add_error_note(ira->codegen, msg, source_instr->source_node, | |
| 8388 | buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&wanted_type->name), dest_align_bytes)); | |
| 8389 | return ira->codegen->invalid_instruction; | |
| 8390 | } | |
| 8391 | ||
| 8392 | if (!ir_emit_global_runtime_side_effect(ira, source_instr)) | |
| 8393 | return ira->codegen->invalid_instruction; | |
| 8394 | return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpResizeSlice, true); | |
| 8395 | } | |
| 8328 | 8396 | } |
| 8329 | 8397 | |
| 8330 | 8398 | // explicit cast from [N]u8 to []const T |
| ... | ... | @@ -10226,7 +10294,10 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal |
| 10226 | 10294 | ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, |
| 10227 | 10295 | nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec); |
| 10228 | 10296 | |
| 10229 | ir_resolve_align(ira, align_result, &impl_fn->align_bytes); | |
| 10297 | uint32_t align_bytes = 0; | |
| 10298 | ir_resolve_align(ira, align_result, &align_bytes); | |
| 10299 | impl_fn->align_bytes = align_bytes; | |
| 10300 | inst_fn_type_id.alignment = align_bytes; | |
| 10230 | 10301 | } |
| 10231 | 10302 | |
| 10232 | 10303 | { |
| ... | ... | @@ -13728,11 +13799,9 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio |
| 13728 | 13799 | TypeTableEntry *return_type; |
| 13729 | 13800 | |
| 13730 | 13801 | if (array_type->id == TypeTableEntryIdArray) { |
| 13731 | uint32_t normal_array_alignment = get_abi_alignment(ira->codegen, array_type); | |
| 13732 | uint32_t align_bytes = (ptr_type->data.pointer.alignment >= normal_array_alignment) ? | |
| 13733 | normal_array_alignment : 1; | |
| 13734 | 13802 | TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type, |
| 13735 | ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, align_bytes, 0, 0); | |
| 13803 | ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, | |
| 13804 | ptr_type->data.pointer.alignment, 0, 0); | |
| 13736 | 13805 | return_type = get_slice_type(ira->codegen, slice_ptr_type); |
| 13737 | 13806 | } else if (array_type->id == TypeTableEntryIdPointer) { |
| 13738 | 13807 | TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.pointer.child_type, |
| ... | ... | @@ -14237,6 +14306,11 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc |
| 14237 | 14306 | } |
| 14238 | 14307 | } |
| 14239 | 14308 | |
| 14309 | if (instruction->align_value != nullptr) { | |
| 14310 | if (!ir_resolve_align(ira, instruction->align_value->other, &fn_type_id.alignment)) | |
| 14311 | return ira->codegen->builtin_types.entry_invalid; | |
| 14312 | } | |
| 14313 | ||
| 14240 | 14314 | IrInstruction *return_type_value = instruction->return_type->other; |
| 14241 | 14315 | fn_type_id.return_type = ir_resolve_type(ira, return_type_value); |
| 14242 | 14316 | if (type_is_invalid(fn_type_id.return_type)) |
| ... | ... | @@ -14866,6 +14940,90 @@ static TypeTableEntry *ir_analyze_instruction_ptr_type_of(IrAnalyze *ira, IrInst |
| 14866 | 14940 | return ira->codegen->builtin_types.entry_type; |
| 14867 | 14941 | } |
| 14868 | 14942 | |
| 14943 | static TypeTableEntry *ir_analyze_instruction_align_cast(IrAnalyze *ira, IrInstructionAlignCast *instruction) { | |
| 14944 | uint32_t align_bytes; | |
| 14945 | IrInstruction *align_bytes_inst = instruction->align_bytes->other; | |
| 14946 | if (!ir_resolve_align(ira, align_bytes_inst, &align_bytes)) | |
| 14947 | return ira->codegen->builtin_types.entry_invalid; | |
| 14948 | ||
| 14949 | IrInstruction *target = instruction->target->other; | |
| 14950 | TypeTableEntry *target_type = target->value.type; | |
| 14951 | if (type_is_invalid(target_type)) | |
| 14952 | return ira->codegen->builtin_types.entry_invalid; | |
| 14953 | ||
| 14954 | TypeTableEntry *result_type; | |
| 14955 | uint32_t old_align_bytes; | |
| 14956 | ||
| 14957 | if (target_type->id == TypeTableEntryIdPointer) { | |
| 14958 | result_type = get_pointer_to_type_extra(ira->codegen, | |
| 14959 | target_type->data.pointer.child_type, | |
| 14960 | target_type->data.pointer.is_const, target_type->data.pointer.is_volatile, | |
| 14961 | align_bytes, | |
| 14962 | target_type->data.pointer.bit_offset, target_type->data.pointer.unaligned_bit_count); | |
| 14963 | } else if (target_type->id == TypeTableEntryIdFn) { | |
| 14964 | FnTypeId fn_type_id = target_type->data.fn.fn_type_id; | |
| 14965 | old_align_bytes = fn_type_id.alignment; | |
| 14966 | fn_type_id.alignment = align_bytes; | |
| 14967 | result_type = get_fn_type(ira->codegen, &fn_type_id); | |
| 14968 | } else if (target_type->id == TypeTableEntryIdMaybe && | |
| 14969 | target_type->data.maybe.child_type->id == TypeTableEntryIdPointer) | |
| 14970 | { | |
| 14971 | TypeTableEntry *ptr_type = target_type->data.maybe.child_type; | |
| 14972 | old_align_bytes = ptr_type->data.pointer.alignment; | |
| 14973 | TypeTableEntry *better_ptr_type = get_pointer_to_type_extra(ira->codegen, | |
| 14974 | ptr_type->data.pointer.child_type, | |
| 14975 | ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, | |
| 14976 | align_bytes, | |
| 14977 | ptr_type->data.pointer.bit_offset, ptr_type->data.pointer.unaligned_bit_count); | |
| 14978 | ||
| 14979 | result_type = get_maybe_type(ira->codegen, better_ptr_type); | |
| 14980 | } else if (target_type->id == TypeTableEntryIdMaybe && | |
| 14981 | target_type->data.maybe.child_type->id == TypeTableEntryIdFn) | |
| 14982 | { | |
| 14983 | FnTypeId fn_type_id = target_type->data.maybe.child_type->data.fn.fn_type_id; | |
| 14984 | old_align_bytes = fn_type_id.alignment; | |
| 14985 | fn_type_id.alignment = align_bytes; | |
| 14986 | TypeTableEntry *fn_type = get_fn_type(ira->codegen, &fn_type_id); | |
| 14987 | result_type = get_maybe_type(ira->codegen, fn_type); | |
| 14988 | } else if (is_slice(target_type)) { | |
| 14989 | TypeTableEntry *slice_ptr_type = target_type->data.structure.fields[slice_ptr_index].type_entry; | |
| 14990 | old_align_bytes = slice_ptr_type->data.pointer.alignment; | |
| 14991 | TypeTableEntry *result_ptr_type = get_pointer_to_type_extra(ira->codegen, | |
| 14992 | slice_ptr_type->data.pointer.child_type, | |
| 14993 | slice_ptr_type->data.pointer.is_const, slice_ptr_type->data.pointer.is_volatile, | |
| 14994 | align_bytes, | |
| 14995 | slice_ptr_type->data.pointer.bit_offset, slice_ptr_type->data.pointer.unaligned_bit_count); | |
| 14996 | result_type = get_slice_type(ira->codegen, result_ptr_type); | |
| 14997 | } else { | |
| 14998 | ir_add_error(ira, target, | |
| 14999 | buf_sprintf("expected pointer or slice, found '%s'", buf_ptr(&target_type->name))); | |
| 15000 | return ira->codegen->builtin_types.entry_invalid; | |
| 15001 | } | |
| 15002 | ||
| 15003 | if (instr_is_comptime(target)) { | |
| 15004 | ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); | |
| 15005 | if (!val) | |
| 15006 | return ira->codegen->builtin_types.entry_invalid; | |
| 15007 | ||
| 15008 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); | |
| 15009 | copy_const_val(out_val, val, false); | |
| 15010 | out_val->type = result_type; | |
| 15011 | return result_type; | |
| 15012 | } | |
| 15013 | ||
| 15014 | IrInstruction *result; | |
| 15015 | if (align_bytes > old_align_bytes && align_bytes != 1) { | |
| 15016 | result = ir_build_align_cast(&ira->new_irb, instruction->base.scope, instruction->base.source_node, | |
| 15017 | align_bytes_inst, target); | |
| 15018 | } else { | |
| 15019 | result = ir_build_cast(&ira->new_irb, instruction->base.scope, instruction->base.source_node, | |
| 15020 | result_type, target, CastOpNoop); | |
| 15021 | } | |
| 15022 | ir_link_new_instruction(result, &instruction->base); | |
| 15023 | result->value.type = result_type; | |
| 15024 | return result_type; | |
| 15025 | } | |
| 15026 | ||
| 14869 | 15027 | static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { |
| 14870 | 15028 | switch (instruction->id) { |
| 14871 | 15029 | case IrInstructionIdInvalid: |
| ... | ... | @@ -14877,6 +15035,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi |
| 14877 | 15035 | case IrInstructionIdStructFieldPtr: |
| 14878 | 15036 | case IrInstructionIdEnumFieldPtr: |
| 14879 | 15037 | case IrInstructionIdInitEnum: |
| 15038 | case IrInstructionIdMaybeWrap: | |
| 15039 | case IrInstructionIdErrWrapCode: | |
| 15040 | case IrInstructionIdErrWrapPayload: | |
| 15041 | case IrInstructionIdCast: | |
| 14880 | 15042 | zig_unreachable(); |
| 14881 | 15043 | case IrInstructionIdReturn: |
| 14882 | 15044 | return ir_analyze_instruction_return(ira, (IrInstructionReturn *)instruction); |
| ... | ... | @@ -15046,11 +15208,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi |
| 15046 | 15208 | return ir_analyze_instruction_set_eval_branch_quota(ira, (IrInstructionSetEvalBranchQuota *)instruction); |
| 15047 | 15209 | case IrInstructionIdPtrTypeOf: |
| 15048 | 15210 | return ir_analyze_instruction_ptr_type_of(ira, (IrInstructionPtrTypeOf *)instruction); |
| 15049 | case IrInstructionIdMaybeWrap: | |
| 15050 | case IrInstructionIdErrWrapCode: | |
| 15051 | case IrInstructionIdErrWrapPayload: | |
| 15052 | case IrInstructionIdCast: | |
| 15053 | zig_panic("TODO analyze more instructions"); | |
| 15211 | case IrInstructionIdAlignCast: | |
| 15212 | return ir_analyze_instruction_align_cast(ira, (IrInstructionAlignCast *)instruction); | |
| 15054 | 15213 | } |
| 15055 | 15214 | zig_unreachable(); |
| 15056 | 15215 | } |
| ... | ... | @@ -15228,6 +15387,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 15228 | 15387 | case IrInstructionIdFieldParentPtr: |
| 15229 | 15388 | case IrInstructionIdOffsetOf: |
| 15230 | 15389 | case IrInstructionIdTypeId: |
| 15390 | case IrInstructionIdAlignCast: | |
| 15231 | 15391 | return false; |
| 15232 | 15392 | case IrInstructionIdAsm: |
| 15233 | 15393 | { |
src/ir_print.cpp+27-4| ... | ... | @@ -174,10 +174,16 @@ static void ir_print_decl_var(IrPrint *irp, IrInstructionDeclVar *decl_var_instr |
| 174 | 174 | if (decl_var_instruction->var_type) { |
| 175 | 175 | fprintf(irp->f, "%s %s: ", var_or_const, name); |
| 176 | 176 | ir_print_other_instruction(irp, decl_var_instruction->var_type); |
| 177 | fprintf(irp->f, " = "); | |
| 177 | fprintf(irp->f, " "); | |
| 178 | 178 | } else { |
| 179 | fprintf(irp->f, "%s %s = ", var_or_const, name); | |
| 179 | fprintf(irp->f, "%s %s ", var_or_const, name); | |
| 180 | } | |
| 181 | if (decl_var_instruction->align_value) { | |
| 182 | fprintf(irp->f, "align "); | |
| 183 | ir_print_other_instruction(irp, decl_var_instruction->align_value); | |
| 184 | fprintf(irp->f, " "); | |
| 180 | 185 | } |
| 186 | fprintf(irp->f, "= "); | |
| 181 | 187 | ir_print_other_instruction(irp, decl_var_instruction->init_value); |
| 182 | 188 | if (decl_var_instruction->var->is_comptime != nullptr) { |
| 183 | 189 | fprintf(irp->f, " // comptime = "); |
| ... | ... | @@ -640,7 +646,7 @@ static void ir_print_slice(IrPrint *irp, IrInstructionSlice *instruction) { |
| 640 | 646 | ir_print_other_instruction(irp, instruction->ptr); |
| 641 | 647 | fprintf(irp->f, "["); |
| 642 | 648 | ir_print_other_instruction(irp, instruction->start); |
| 643 | fprintf(irp->f, "..."); | |
| 649 | fprintf(irp->f, ".."); | |
| 644 | 650 | if (instruction->end) |
| 645 | 651 | ir_print_other_instruction(irp, instruction->end); |
| 646 | 652 | fprintf(irp->f, "]"); |
| ... | ... | @@ -745,7 +751,13 @@ static void ir_print_fn_proto(IrPrint *irp, IrInstructionFnProto *instruction) { |
| 745 | 751 | ir_print_other_instruction(irp, instruction->param_types[i]); |
| 746 | 752 | } |
| 747 | 753 | } |
| 748 | fprintf(irp->f, ")->"); | |
| 754 | fprintf(irp->f, ")"); | |
| 755 | if (instruction->align_value != nullptr) { | |
| 756 | fprintf(irp->f, " align "); | |
| 757 | ir_print_other_instruction(irp, instruction->align_value); | |
| 758 | fprintf(irp->f, " "); | |
| 759 | } | |
| 760 | fprintf(irp->f, "->"); | |
| 749 | 761 | ir_print_other_instruction(irp, instruction->return_type); |
| 750 | 762 | } |
| 751 | 763 | |
| ... | ... | @@ -920,6 +932,14 @@ static void ir_print_set_eval_branch_quota(IrPrint *irp, IrInstructionSetEvalBra |
| 920 | 932 | fprintf(irp->f, ")"); |
| 921 | 933 | } |
| 922 | 934 | |
| 935 | static void ir_print_align_cast(IrPrint *irp, IrInstructionAlignCast *instruction) { | |
| 936 | fprintf(irp->f, "@alignCast("); | |
| 937 | ir_print_other_instruction(irp, instruction->align_bytes); | |
| 938 | fprintf(irp->f, ","); | |
| 939 | ir_print_other_instruction(irp, instruction->target); | |
| 940 | fprintf(irp->f, ")"); | |
| 941 | } | |
| 942 | ||
| 923 | 943 | static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 924 | 944 | ir_print_prefix(irp, instruction); |
| 925 | 945 | switch (instruction->id) { |
| ... | ... | @@ -1213,6 +1233,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1213 | 1233 | case IrInstructionIdSetEvalBranchQuota: |
| 1214 | 1234 | ir_print_set_eval_branch_quota(irp, (IrInstructionSetEvalBranchQuota *)instruction); |
| 1215 | 1235 | break; |
| 1236 | case IrInstructionIdAlignCast: | |
| 1237 | ir_print_align_cast(irp, (IrInstructionAlignCast *)instruction); | |
| 1238 | break; | |
| 1216 | 1239 | } |
| 1217 | 1240 | fprintf(irp->f, "\n"); |
| 1218 | 1241 | } |
std/debug.zig+11-6| ... | ... | @@ -957,16 +957,21 @@ pub var global_allocator = mem.Allocator { |
| 957 | 957 | var some_mem: [100 * 1024]u8 = undefined; |
| 958 | 958 | var some_mem_index: usize = 0; |
| 959 | 959 | |
| 960 | fn globalAlloc(self: &mem.Allocator, n: usize) -> %[]u8 { | |
| 961 | const result = some_mem[some_mem_index .. some_mem_index + n]; | |
| 962 | some_mem_index += n; | |
| 960 | fn globalAlloc(self: &mem.Allocator, n: usize, alignment: usize) -> %[]u8 { | |
| 961 | const addr = @ptrToInt(&some_mem[some_mem_index]); | |
| 962 | const rem = @rem(addr, alignment); | |
| 963 | const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); | |
| 964 | const adjusted_index = some_mem_index + march_forward_bytes; | |
| 965 | const end_index = adjusted_index + n; | |
| 966 | const result = some_mem[adjusted_index .. end_index]; | |
| 967 | some_mem_index = end_index; | |
| 963 | 968 | return result; |
| 964 | 969 | } |
| 965 | 970 | |
| 966 | fn globalRealloc(self: &mem.Allocator, old_mem: []u8, new_size: usize) -> %[]u8 { | |
| 967 | const result = %return globalAlloc(self, new_size); | |
| 971 | fn globalRealloc(self: &mem.Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 { | |
| 972 | const result = %return globalAlloc(self, new_size, alignment); | |
| 968 | 973 | @memcpy(result.ptr, old_mem.ptr, old_mem.len); |
| 969 | 974 | return result; |
| 970 | 975 | } |
| 971 | 976 | |
| 972 | fn globalFree(self: &mem.Allocator, old_mem: []u8) { } | |
| 977 | fn globalFree(self: &mem.Allocator, ptr: &u8) { } |
std/mem.zig+37-23| ... | ... | @@ -11,21 +11,18 @@ pub const Cmp = math.Cmp; |
| 11 | 11 | error NoMem; |
| 12 | 12 | |
| 13 | 13 | pub const Allocator = struct { |
| 14 | allocFn: fn (self: &Allocator, n: usize) -> %[]u8, | |
| 15 | /// Note that old_mem may be a slice of length 0, in which case reallocFn | |
| 16 | /// should simply call allocFn. | |
| 17 | reallocFn: fn (self: &Allocator, old_mem: []u8, new_size: usize) -> %[]u8, | |
| 18 | /// Note that mem may be a slice of length 0, in which case freeFn | |
| 19 | /// should do nothing. | |
| 20 | freeFn: fn (self: &Allocator, mem: []u8), | |
| 21 | ||
| 22 | /// Aborts the program if an allocation fails. | |
| 23 | fn checkedAlloc(self: &Allocator, comptime T: type, n: usize) -> []T { | |
| 24 | alloc(self, T, n) %% |err| debug.panic("allocation failure: {}", @errorName(err)) | |
| 25 | } | |
| 14 | /// Allocate byte_count bytes and return them in a slice, with the | |
| 15 | /// slicer's pointer aligned at least to alignment bytes. | |
| 16 | allocFn: fn (self: &Allocator, byte_count: usize, alignment: usize) -> %[]u8, | |
| 17 | ||
| 18 | /// Guaranteed: old_mem.len > 0 and alignment >= alignment of old_mem.ptr | |
| 19 | reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: usize) -> %[]u8, | |
| 20 | ||
| 21 | freeFn: fn (self: &Allocator, ptr: &u8), | |
| 26 | 22 | |
| 27 | 23 | fn create(self: &Allocator, comptime T: type) -> %&T { |
| 28 | &(%return self.alloc(T, 1))[0] | |
| 24 | const slice = %return self.alloc(T, 1); | |
| 25 | &slice[0] | |
| 29 | 26 | } |
| 30 | 27 | |
| 31 | 28 | fn destroy(self: &Allocator, ptr: var) { |
| ... | ... | @@ -34,16 +31,29 @@ pub const Allocator = struct { |
| 34 | 31 | |
| 35 | 32 | fn alloc(self: &Allocator, comptime T: type, n: usize) -> %[]T { |
| 36 | 33 | const byte_count = %return math.mul(usize, @sizeOf(T), n); |
| 37 | ([]T)(%return self.allocFn(self, byte_count)) | |
| 34 | const byte_slice = %return self.allocFn(self, byte_count, @alignOf(T)); | |
| 35 | ([]T)(@alignCast(@alignOf(T), byte_slice)) | |
| 38 | 36 | } |
| 39 | 37 | |
| 40 | 38 | fn realloc(self: &Allocator, comptime T: type, old_mem: []T, n: usize) -> %[]T { |
| 39 | if (old_mem.len == 0) { | |
| 40 | return self.alloc(T, n); | |
| 41 | } | |
| 42 | ||
| 43 | // Assert that old_mem.ptr is properly aligned. | |
| 44 | _ = @alignCast(@alignOf(T), old_mem.ptr); | |
| 45 | ||
| 41 | 46 | const byte_count = %return math.mul(usize, @sizeOf(T), n); |
| 42 | ([]T)(%return self.reallocFn(self, ([]u8)(old_mem), byte_count)) | |
| 47 | const byte_slice = %return self.reallocFn(self, ([]u8)(old_mem), byte_count, @alignOf(T)); | |
| 48 | ([]T)(@alignCast(@alignOf(T), byte_slice)) | |
| 43 | 49 | } |
| 44 | 50 | |
| 45 | fn free(self: &Allocator, mem: var) { | |
| 46 | self.freeFn(self, ([]u8)(mem)); | |
| 51 | fn free(self: &Allocator, memory: var) { | |
| 52 | const const_slice = ([]const u8)(memory); | |
| 53 | if (memory.len == 0) | |
| 54 | return; | |
| 55 | const ptr = @intToPtr(&u8, @ptrToInt(const_slice.ptr)); | |
| 56 | self.freeFn(self, ptr); | |
| 47 | 57 | } |
| 48 | 58 | }; |
| 49 | 59 | |
| ... | ... | @@ -79,24 +89,28 @@ pub const IncrementingAllocator = struct { |
| 79 | 89 | _ = os.posix.munmap(self.bytes.ptr, self.bytes.len); |
| 80 | 90 | } |
| 81 | 91 | |
| 82 | fn alloc(allocator: &Allocator, n: usize) -> %[]u8 { | |
| 92 | fn alloc(allocator: &Allocator, n: usize, alignment: usize) -> %[]u8 { | |
| 83 | 93 | const self = @fieldParentPtr(IncrementingAllocator, "allocator", allocator); |
| 84 | const new_end_index = self.end_index + n; | |
| 94 | const addr = @ptrToInt(&self.bytes[self.end_index]); | |
| 95 | const rem = @rem(addr, alignment); | |
| 96 | const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); | |
| 97 | const adjusted_index = self.end_index + march_forward_bytes; | |
| 98 | const new_end_index = adjusted_index + n; | |
| 85 | 99 | if (new_end_index > self.bytes.len) { |
| 86 | 100 | return error.NoMem; |
| 87 | 101 | } |
| 88 | const result = self.bytes[self.end_index..new_end_index]; | |
| 102 | const result = self.bytes[adjusted_index .. new_end_index]; | |
| 89 | 103 | self.end_index = new_end_index; |
| 90 | 104 | return result; |
| 91 | 105 | } |
| 92 | 106 | |
| 93 | fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize) -> %[]u8 { | |
| 94 | const result = %return alloc(allocator, new_size); | |
| 107 | fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 { | |
| 108 | const result = %return alloc(allocator, new_size, alignment); | |
| 95 | 109 | copy(u8, result, old_mem); |
| 96 | 110 | return result; |
| 97 | 111 | } |
| 98 | 112 | |
| 99 | fn free(allocator: &Allocator, bytes: []u8) { | |
| 113 | fn free(allocator: &Allocator, bytes: &u8) { | |
| 100 | 114 | // Do nothing. That's the point of an incrementing allocator. |
| 101 | 115 | } |
| 102 | 116 | }; |
test/cases/align.zig+21| ... | ... | @@ -62,3 +62,24 @@ fn testBytesAlign(b: u8) { |
| 62 | 62 | const ptr = @ptrCast(&u32, &bytes[0]); |
| 63 | 63 | assert(*ptr == 0x33333333); |
| 64 | 64 | } |
| 65 | ||
| 66 | test "specifying alignment allows slice cast" { | |
| 67 | testBytesAlignSlice(0x33); | |
| 68 | } | |
| 69 | fn testBytesAlignSlice(b: u8) { | |
| 70 | var bytes align 4 = []u8{b, b, b, b}; | |
| 71 | const slice = ([]u32)(bytes[0..]); | |
| 72 | assert(slice[0] == 0x33333333); | |
| 73 | } | |
| 74 | ||
| 75 | test "@alignCast" { | |
| 76 | var x: u32 align 4 = 1; | |
| 77 | expectsOnly1(&x); | |
| 78 | assert(x == 2); | |
| 79 | } | |
| 80 | fn expectsOnly1(x: &align 1 u32) { | |
| 81 | expects4(@alignCast(4, x)); | |
| 82 | } | |
| 83 | fn expects4(x: &align 4 u32) { | |
| 84 | *x += 1; | |
| 85 | } |
test/cases/cast.zig+1-1| ... | ... | @@ -277,7 +277,7 @@ fn cast128Float(x: u128) -> f128 { |
| 277 | 277 | } |
| 278 | 278 | |
| 279 | 279 | test "const slice widen cast" { |
| 280 | const bytes = []u8{0x12, 0x12, 0x12, 0x12}; | |
| 280 | const bytes align 4 = []u8{0x12, 0x12, 0x12, 0x12}; | |
| 281 | 281 | |
| 282 | 282 | const u32_value = ([]const u32)(bytes[0..])[0]; |
| 283 | 283 | assert(u32_value == 0x12121212); |
test/cases/misc.zig+1-1| ... | ... | @@ -404,7 +404,7 @@ test "cast slice to u8 slice" { |
| 404 | 404 | bytes[6] = 0; |
| 405 | 405 | bytes[7] = 0; |
| 406 | 406 | assert(big_thing_slice[1] == 0); |
| 407 | const big_thing_again = ([]i32)(bytes); | |
| 407 | const big_thing_again = ([]align 1 i32)(bytes); | |
| 408 | 408 | assert(big_thing_again[2] == 3); |
| 409 | 409 | big_thing_again[2] = -1; |
| 410 | 410 | assert(bytes[8] == @maxValue(u8)); |
test/compile_errors.zig+17| ... | ... | @@ -2022,4 +2022,21 @@ pub fn addCases(cases: &tests.CompileErrorContext) { |
| 2022 | 2022 | ".tmp_source.zig:3:17: error: cast increases pointer alignment", |
| 2023 | 2023 | ".tmp_source.zig:3:38: note: '&u8' has alignment 1", |
| 2024 | 2024 | ".tmp_source.zig:3:27: note: '&u32' has alignment 4"); |
| 2025 | ||
| 2026 | cases.add("increase pointer alignment in slice resize", | |
| 2027 | \\export fn entry() -> u32 { | |
| 2028 | \\ var bytes = []u8{0x01, 0x02, 0x03, 0x04}; | |
| 2029 | \\ return ([]u32)(bytes[0..])[0]; | |
| 2030 | \\} | |
| 2031 | , | |
| 2032 | ".tmp_source.zig:3:19: error: cast increases pointer alignment", | |
| 2033 | ".tmp_source.zig:3:19: note: '[]u8' has alignment 1", | |
| 2034 | ".tmp_source.zig:3:19: note: '[]u32' has alignment 4"); | |
| 2035 | ||
| 2036 | cases.add("@alignCast expects pointer or slice", | |
| 2037 | \\export fn entry() { | |
| 2038 | \\ @alignCast(4, u32(3)) | |
| 2039 | \\} | |
| 2040 | , | |
| 2041 | ".tmp_source.zig:2:22: error: expected pointer or slice, found 'u32'"); | |
| 2025 | 2042 | } |
test/debug_safety.zig+20-2| ... | ... | @@ -200,8 +200,8 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 200 | 200 | \\ const x = widenSlice([]u8{1, 2, 3, 4, 5}); |
| 201 | 201 | \\ if (x.len == 0) return error.Whatever; |
| 202 | 202 | \\} |
| 203 | \\fn widenSlice(slice: []const u8) -> []const i32 { | |
| 204 | \\ ([]const i32)(slice) | |
| 203 | \\fn widenSlice(slice: []align 1 const u8) -> []align 1 const i32 { | |
| 204 | \\ ([]align 1 const i32)(slice) | |
| 205 | 205 | \\} |
| 206 | 206 | ); |
| 207 | 207 | |
| ... | ... | @@ -261,4 +261,22 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 261 | 261 | \\ return error(x); |
| 262 | 262 | \\} |
| 263 | 263 | ); |
| 264 | ||
| 265 | cases.addDebugSafety("@alignCast misaligned", | |
| 266 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 267 | \\ @breakpoint(); | |
| 268 | \\ while (true) {} | |
| 269 | \\} | |
| 270 | \\error Wrong; | |
| 271 | \\pub fn main() -> %void { | |
| 272 | \\ var array align 4 = []u32{0x11111111, 0x11111111}; | |
| 273 | \\ const bytes = ([]u8)(array[0..]); | |
| 274 | \\ if (foo(bytes) != 0x11111111) return error.Wrong; | |
| 275 | \\} | |
| 276 | \\fn foo(bytes: []u8) -> u32 { | |
| 277 | \\ const slice4 = bytes[1..5]; | |
| 278 | \\ const int_slice = ([]u32)(@alignCast(4, slice4)); | |
| 279 | \\ return int_slice[0]; | |
| 280 | \\} | |
| 281 | ); | |
| 264 | 282 | } |