| 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,6 +1252,7 @@ enum BuiltinFnId { |
| 1252 | BuiltinFnIdShlExact, | 1252 | BuiltinFnIdShlExact, |
| 1253 | BuiltinFnIdShrExact, | 1253 | BuiltinFnIdShrExact, |
| 1254 | BuiltinFnIdSetEvalBranchQuota, | 1254 | BuiltinFnIdSetEvalBranchQuota, |
| 1255 | BuiltinFnIdAlignCast, | ||
| 1255 | }; | 1256 | }; |
| 1256 | 1257 | ||
| 1257 | struct BuiltinFnEntry { | 1258 | struct BuiltinFnEntry { |
| ... | @@ -1274,6 +1275,7 @@ enum PanicMsgId { | ... | @@ -1274,6 +1275,7 @@ enum PanicMsgId { |
| 1274 | PanicMsgIdSliceWidenRemainder, | 1275 | PanicMsgIdSliceWidenRemainder, |
| 1275 | PanicMsgIdUnwrapMaybeFail, | 1276 | PanicMsgIdUnwrapMaybeFail, |
| 1276 | PanicMsgIdInvalidErrorCode, | 1277 | PanicMsgIdInvalidErrorCode, |
| 1278 | PanicMsgIdIncorrectAlignment, | ||
| 1277 | 1279 | ||
| 1278 | PanicMsgIdCount, | 1280 | PanicMsgIdCount, |
| 1279 | }; | 1281 | }; |
| ... | @@ -1856,6 +1858,7 @@ enum IrInstructionId { | ... | @@ -1856,6 +1858,7 @@ enum IrInstructionId { |
| 1856 | IrInstructionIdTypeId, | 1858 | IrInstructionIdTypeId, |
| 1857 | IrInstructionIdSetEvalBranchQuota, | 1859 | IrInstructionIdSetEvalBranchQuota, |
| 1858 | IrInstructionIdPtrTypeOf, | 1860 | IrInstructionIdPtrTypeOf, |
| 1861 | IrInstructionIdAlignCast, | ||
| 1859 | }; | 1862 | }; |
| 1860 | 1863 | ||
| 1861 | struct IrInstruction { | 1864 | struct IrInstruction { |
| ... | @@ -2462,6 +2465,7 @@ struct IrInstructionFnProto { | ... | @@ -2462,6 +2465,7 @@ struct IrInstructionFnProto { |
| 2462 | IrInstruction base; | 2465 | IrInstruction base; |
| 2463 | 2466 | ||
| 2464 | IrInstruction **param_types; | 2467 | IrInstruction **param_types; |
| 2468 | IrInstruction *align_value; | ||
| 2465 | IrInstruction *return_type; | 2469 | IrInstruction *return_type; |
| 2466 | bool is_var_args; | 2470 | bool is_var_args; |
| 2467 | }; | 2471 | }; |
| ... | @@ -2638,6 +2642,13 @@ struct IrInstructionPtrTypeOf { | ... | @@ -2638,6 +2642,13 @@ struct IrInstructionPtrTypeOf { |
| 2638 | bool is_volatile; | 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 | static const size_t slice_ptr_index = 0; | 2652 | static const size_t slice_ptr_index = 0; |
| 2642 | static const size_t slice_len_index = 1; | 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,6 +884,9 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { |
| 884 | buf_appendf(&fn_type->name, "%s...", comma); | 884 | buf_appendf(&fn_type->name, "%s...", comma); |
| 885 | } | 885 | } |
| 886 | buf_appendf(&fn_type->name, ")"); | 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 | if (fn_type_id->return_type->id != TypeTableEntryIdVoid) { | 890 | if (fn_type_id->return_type->id != TypeTableEntryIdVoid) { |
| 888 | buf_appendf(&fn_type->name, " -> %s", buf_ptr(&fn_type_id->return_type->name)); | 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,12 +1061,13 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou |
| 1058 | fn_type_id->is_var_args = fn_proto->is_var_args; | 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 | assert(proto_node->type == NodeTypeFnProto); | 1065 | assert(proto_node->type == NodeTypeFnProto); |
| 1063 | AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; | 1066 | AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; |
| 1064 | 1067 | ||
| 1065 | FnTypeId fn_type_id = {0}; | 1068 | FnTypeId fn_type_id = {0}; |
| 1066 | init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length); | 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 | for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) { | 1072 | for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) { |
| 1069 | AstNode *param_node = fn_proto->params.at(fn_type_id.next_param_index); | 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,23 +2060,23 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { |
| 2056 | } | 2060 | } |
| 2057 | 2061 | ||
| 2058 | Scope *child_scope = fn_table_entry->fndef_scope ? &fn_table_entry->fndef_scope->base : tld_fn->base.parent_scope; | 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 | if (fn_proto->align_expr != nullptr) { | 2065 | if (fn_proto->align_expr != nullptr) { |
| 2067 | if (!analyze_const_align(g, tld_fn->base.parent_scope, fn_proto->align_expr, | 2066 | if (!analyze_const_align(g, child_scope, fn_proto->align_expr, &alignment)) { |
| 2068 | &fn_table_entry->align_bytes)) | ||
| 2069 | { | ||
| 2070 | fn_table_entry->type_entry = g->builtin_types.entry_invalid; | 2067 | fn_table_entry->type_entry = g->builtin_types.entry_invalid; |
| 2071 | tld_fn->base.resolution = TldResolutionInvalid; | 2068 | tld_fn->base.resolution = TldResolutionInvalid; |
| 2072 | return; | 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 | if (!fn_table_entry->type_entry->data.fn.is_generic) { | 2080 | if (!fn_table_entry->type_entry->data.fn.is_generic) { |
| 2077 | g->fn_protos.append(fn_table_entry); | 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,6 +2667,9 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry * |
| 2663 | if (expected_type->id == TypeTableEntryIdFn && | 2667 | if (expected_type->id == TypeTableEntryIdFn && |
| 2664 | actual_type->id == TypeTableEntryIdFn) | 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 | if (expected_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) { | 2673 | if (expected_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) { |
| 2667 | return false; | 2674 | return false; |
| 2668 | } | 2675 | } |
| ... | @@ -3384,6 +3391,7 @@ uint32_t fn_type_id_hash(FnTypeId *id) { | ... | @@ -3384,6 +3391,7 @@ uint32_t fn_type_id_hash(FnTypeId *id) { |
| 3384 | result += ((uint32_t)(id->cc)) * (uint32_t)3349388391; | 3391 | result += ((uint32_t)(id->cc)) * (uint32_t)3349388391; |
| 3385 | result += id->is_var_args ? (uint32_t)1931444534 : 0; | 3392 | result += id->is_var_args ? (uint32_t)1931444534 : 0; |
| 3386 | result += hash_ptr(id->return_type); | 3393 | result += hash_ptr(id->return_type); |
| 3394 | result += id->alignment * 0xd3b3f3e2; | ||
| 3387 | for (size_t i = 0; i < id->param_count; i += 1) { | 3395 | for (size_t i = 0; i < id->param_count; i += 1) { |
| 3388 | FnTypeParamInfo *info = &id->param_info[i]; | 3396 | FnTypeParamInfo *info = &id->param_info[i]; |
| 3389 | result += info->is_noalias ? (uint32_t)892356923 : 0; | 3397 | result += info->is_noalias ? (uint32_t)892356923 : 0; |
| ... | @@ -3396,7 +3404,8 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) { | ... | @@ -3396,7 +3404,8 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) { |
| 3396 | if (a->cc != b->cc || | 3404 | if (a->cc != b->cc || |
| 3397 | a->return_type != b->return_type || | 3405 | a->return_type != b->return_type || |
| 3398 | a->is_var_args != b->is_var_args || | 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 | return false; | 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,7 +953,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { |
| 953 | render_node_ungrouped(ar, node->data.slice_expr.array_ref_expr); | 953 | render_node_ungrouped(ar, node->data.slice_expr.array_ref_expr); |
| 954 | fprintf(ar->f, "["); | 954 | fprintf(ar->f, "["); |
| 955 | render_node_grouped(ar, node->data.slice_expr.start); | 955 | render_node_grouped(ar, node->data.slice_expr.start); |
| 956 | fprintf(ar->f, "..."); | 956 | fprintf(ar->f, ".."); |
| 957 | if (node->data.slice_expr.end) | 957 | if (node->data.slice_expr.end) |
| 958 | render_node_grouped(ar, node->data.slice_expr.end); | 958 | render_node_grouped(ar, node->data.slice_expr.end); |
| 959 | fprintf(ar->f, "]"); | 959 | fprintf(ar->f, "]"); |
src/codegen.cpp+71| ... | @@ -688,6 +688,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { | ... | @@ -688,6 +688,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { |
| 688 | return buf_create_from_str("reached unreachable code"); | 688 | return buf_create_from_str("reached unreachable code"); |
| 689 | case PanicMsgIdInvalidErrorCode: | 689 | case PanicMsgIdInvalidErrorCode: |
| 690 | return buf_create_from_str("invalid error code"); | 690 | return buf_create_from_str("invalid error code"); |
| 691 | case PanicMsgIdIncorrectAlignment: | ||
| 692 | return buf_create_from_str("incorrect alignment"); | ||
| 691 | } | 693 | } |
| 692 | zig_unreachable(); | 694 | zig_unreachable(); |
| 693 | } | 695 | } |
| ... | @@ -2605,6 +2607,72 @@ static LLVMValueRef ir_render_field_parent_ptr(CodeGen *g, IrExecutable *executa | ... | @@ -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 | static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) { | 2677 | static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) { |
| 2610 | switch (atomic_order) { | 2678 | switch (atomic_order) { |
| ... | @@ -3350,6 +3418,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, | ... | @@ -3350,6 +3418,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 3350 | return ir_render_enum_tag_name(g, executable, (IrInstructionEnumTagName *)instruction); | 3418 | return ir_render_enum_tag_name(g, executable, (IrInstructionEnumTagName *)instruction); |
| 3351 | case IrInstructionIdFieldParentPtr: | 3419 | case IrInstructionIdFieldParentPtr: |
| 3352 | return ir_render_field_parent_ptr(g, executable, (IrInstructionFieldParentPtr *)instruction); | 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 | zig_unreachable(); | 3424 | zig_unreachable(); |
| 3355 | } | 3425 | } |
| ... | @@ -4633,6 +4703,7 @@ static void define_builtin_fns(CodeGen *g) { | ... | @@ -4633,6 +4703,7 @@ static void define_builtin_fns(CodeGen *g) { |
| 4633 | create_builtin_fn(g, BuiltinFnIdShlExact, "shlExact", 2); | 4703 | create_builtin_fn(g, BuiltinFnIdShlExact, "shlExact", 2); |
| 4634 | create_builtin_fn(g, BuiltinFnIdShrExact, "shrExact", 2); | 4704 | create_builtin_fn(g, BuiltinFnIdShrExact, "shrExact", 2); |
| 4635 | create_builtin_fn(g, BuiltinFnIdSetEvalBranchQuota, "setEvalBranchQuota", 1); | 4705 | create_builtin_fn(g, BuiltinFnIdSetEvalBranchQuota, "setEvalBranchQuota", 1); |
| 4706 | create_builtin_fn(g, BuiltinFnIdAlignCast, "alignCast", 2); | ||
| 4636 | } | 4707 | } |
| 4637 | 4708 | ||
| 4638 | static const char *bool_to_str(bool b) { | 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,6 +555,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrTypeOf *) { |
| 555 | return IrInstructionIdPtrTypeOf; | 555 | return IrInstructionIdPtrTypeOf; |
| 556 | } | 556 | } |
| 557 | 557 | ||
| 558 | static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignCast *) { | ||
| 559 | return IrInstructionIdAlignCast; | ||
| 560 | } | ||
| 561 | |||
| 558 | template<typename T> | 562 | template<typename T> |
| 559 | static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { | 563 | static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { |
| 560 | T *special_instruction = allocate<T>(1); | 564 | T *special_instruction = allocate<T>(1); |
| ... | @@ -1899,10 +1903,11 @@ static IrInstruction *ir_build_unwrap_err_payload_from(IrBuilder *irb, IrInstruc | ... | @@ -1899,10 +1903,11 @@ static IrInstruction *ir_build_unwrap_err_payload_from(IrBuilder *irb, IrInstruc |
| 1899 | } | 1903 | } |
| 1900 | 1904 | ||
| 1901 | static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *source_node, | 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 | IrInstructionFnProto *instruction = ir_build_instruction<IrInstructionFnProto>(irb, scope, source_node); | 1908 | IrInstructionFnProto *instruction = ir_build_instruction<IrInstructionFnProto>(irb, scope, source_node); |
| 1905 | instruction->param_types = param_types; | 1909 | instruction->param_types = param_types; |
| 1910 | instruction->align_value = align_value; | ||
| 1906 | instruction->return_type = return_type; | 1911 | instruction->return_type = return_type; |
| 1907 | instruction->is_var_args = is_var_args; | 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,6 +1917,7 @@ static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *s |
| 1912 | for (size_t i = 0; i < param_count; i += 1) { | 1917 | for (size_t i = 0; i < param_count; i += 1) { |
| 1913 | ir_ref_instruction(param_types[i], irb->current_basic_block); | 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 | ir_ref_instruction(return_type, irb->current_basic_block); | 1921 | ir_ref_instruction(return_type, irb->current_basic_block); |
| 1916 | 1922 | ||
| 1917 | return &instruction->base; | 1923 | return &instruction->base; |
| ... | @@ -2219,6 +2225,19 @@ static IrInstruction *ir_build_set_eval_branch_quota(IrBuilder *irb, Scope *scop | ... | @@ -2219,6 +2225,19 @@ static IrInstruction *ir_build_set_eval_branch_quota(IrBuilder *irb, Scope *scop |
| 2219 | return &instruction->base; | 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 | static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) { | 2241 | static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) { |
| 2223 | return nullptr; | 2242 | return nullptr; |
| 2224 | } | 2243 | } |
| ... | @@ -2738,6 +2757,10 @@ static IrInstruction *ir_instruction_fnproto_get_dep(IrInstructionFnProto *instr | ... | @@ -2738,6 +2757,10 @@ static IrInstruction *ir_instruction_fnproto_get_dep(IrInstructionFnProto *instr |
| 2738 | if (param_index < instruction->base.source_node->data.fn_proto.params.length) { | 2757 | if (param_index < instruction->base.source_node->data.fn_proto.params.length) { |
| 2739 | return instruction->param_types[param_index]; | 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 | return nullptr; | 2764 | return nullptr; |
| 2742 | } | 2765 | } |
| 2743 | 2766 | ||
| ... | @@ -2925,6 +2948,14 @@ static IrInstruction *ir_instruction_ptrtypeof_get_dep(IrInstructionPtrTypeOf *i | ... | @@ -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 | static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) { | 2959 | static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) { |
| 2929 | switch (instruction->id) { | 2960 | switch (instruction->id) { |
| 2930 | case IrInstructionIdInvalid: | 2961 | case IrInstructionIdInvalid: |
| ... | @@ -3121,6 +3152,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t | ... | @@ -3121,6 +3152,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t |
| 3121 | return ir_instruction_setevalbranchquota_get_dep((IrInstructionSetEvalBranchQuota *) instruction, index); | 3152 | return ir_instruction_setevalbranchquota_get_dep((IrInstructionSetEvalBranchQuota *) instruction, index); |
| 3122 | case IrInstructionIdPtrTypeOf: | 3153 | case IrInstructionIdPtrTypeOf: |
| 3123 | return ir_instruction_ptrtypeof_get_dep((IrInstructionPtrTypeOf *) instruction, index); | 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 | zig_unreachable(); | 3158 | zig_unreachable(); |
| 3126 | } | 3159 | } |
| ... | @@ -4531,6 +4564,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo | ... | @@ -4531,6 +4564,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4531 | 4564 | ||
| 4532 | return ir_build_set_eval_branch_quota(irb, scope, node, arg0_value); | 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 | zig_unreachable(); | 4582 | zig_unreachable(); |
| 4536 | } | 4583 | } |
| ... | @@ -6060,11 +6107,18 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo | ... | @@ -6060,11 +6107,18 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo |
| 6060 | param_types[i] = type_value; | 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 | IrInstruction *return_type = ir_gen_node(irb, node->data.fn_proto.return_type, parent_scope); | 6117 | IrInstruction *return_type = ir_gen_node(irb, node->data.fn_proto.return_type, parent_scope); |
| 6064 | if (return_type == irb->codegen->invalid_instruction) | 6118 | if (return_type == irb->codegen->invalid_instruction) |
| 6065 | return irb->codegen->invalid_instruction; | 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 | static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope, | 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,15 +8370,29 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst |
| 8316 | } | 8370 | } |
| 8317 | 8371 | ||
| 8318 | // explicit cast from []T to []u8 or []u8 to []T | 8372 | // explicit cast from []T to []u8 or []u8 to []T |
| 8319 | if (is_slice(wanted_type) && is_slice(actual_type) && | 8373 | 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) || | 8374 | TypeTableEntry *wanted_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; |
| 8321 | is_u8(actual_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type)) && | 8375 | TypeTableEntry *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry; |
| 8322 | (wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const || | 8376 | if ((is_u8(wanted_ptr_type->data.pointer.child_type) || is_u8(actual_ptr_type->data.pointer.child_type)) && |
| 8323 | !actual_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const)) | 8377 | (wanted_ptr_type->data.pointer.is_const || !actual_ptr_type->data.pointer.is_const)) |
| 8324 | { | 8378 | { |
| 8325 | if (!ir_emit_global_runtime_side_effect(ira, source_instr)) | 8379 | uint32_t src_align_bytes = get_ptr_align(actual_ptr_type); |
| 8326 | return ira->codegen->invalid_instruction; | 8380 | uint32_t dest_align_bytes = get_ptr_align(wanted_ptr_type); |
| 8327 | return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpResizeSlice, true); | 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 | // explicit cast from [N]u8 to []const T | 8398 | // explicit cast from [N]u8 to []const T |
| ... | @@ -10226,7 +10294,10 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal | ... | @@ -10226,7 +10294,10 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal |
| 10226 | ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, | 10294 | ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, |
| 10227 | nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec); | 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,11 +13799,9 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio |
| 13728 | TypeTableEntry *return_type; | 13799 | TypeTableEntry *return_type; |
| 13729 | 13800 | ||
| 13730 | if (array_type->id == TypeTableEntryIdArray) { | 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 | TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type, | 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 | return_type = get_slice_type(ira->codegen, slice_ptr_type); | 13805 | return_type = get_slice_type(ira->codegen, slice_ptr_type); |
| 13737 | } else if (array_type->id == TypeTableEntryIdPointer) { | 13806 | } else if (array_type->id == TypeTableEntryIdPointer) { |
| 13738 | TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.pointer.child_type, | 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,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 | IrInstruction *return_type_value = instruction->return_type->other; | 14314 | IrInstruction *return_type_value = instruction->return_type->other; |
| 14241 | fn_type_id.return_type = ir_resolve_type(ira, return_type_value); | 14315 | fn_type_id.return_type = ir_resolve_type(ira, return_type_value); |
| 14242 | if (type_is_invalid(fn_type_id.return_type)) | 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,6 +14940,90 @@ static TypeTableEntry *ir_analyze_instruction_ptr_type_of(IrAnalyze *ira, IrInst |
| 14866 | return ira->codegen->builtin_types.entry_type; | 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 | static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { | 15027 | static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { |
| 14870 | switch (instruction->id) { | 15028 | switch (instruction->id) { |
| 14871 | case IrInstructionIdInvalid: | 15029 | case IrInstructionIdInvalid: |
| ... | @@ -14877,6 +15035,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi | ... | @@ -14877,6 +15035,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi |
| 14877 | case IrInstructionIdStructFieldPtr: | 15035 | case IrInstructionIdStructFieldPtr: |
| 14878 | case IrInstructionIdEnumFieldPtr: | 15036 | case IrInstructionIdEnumFieldPtr: |
| 14879 | case IrInstructionIdInitEnum: | 15037 | case IrInstructionIdInitEnum: |
| 15038 | case IrInstructionIdMaybeWrap: | ||
| 15039 | case IrInstructionIdErrWrapCode: | ||
| 15040 | case IrInstructionIdErrWrapPayload: | ||
| 15041 | case IrInstructionIdCast: | ||
| 14880 | zig_unreachable(); | 15042 | zig_unreachable(); |
| 14881 | case IrInstructionIdReturn: | 15043 | case IrInstructionIdReturn: |
| 14882 | return ir_analyze_instruction_return(ira, (IrInstructionReturn *)instruction); | 15044 | return ir_analyze_instruction_return(ira, (IrInstructionReturn *)instruction); |
| ... | @@ -15046,11 +15208,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi | ... | @@ -15046,11 +15208,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi |
| 15046 | return ir_analyze_instruction_set_eval_branch_quota(ira, (IrInstructionSetEvalBranchQuota *)instruction); | 15208 | return ir_analyze_instruction_set_eval_branch_quota(ira, (IrInstructionSetEvalBranchQuota *)instruction); |
| 15047 | case IrInstructionIdPtrTypeOf: | 15209 | case IrInstructionIdPtrTypeOf: |
| 15048 | return ir_analyze_instruction_ptr_type_of(ira, (IrInstructionPtrTypeOf *)instruction); | 15210 | return ir_analyze_instruction_ptr_type_of(ira, (IrInstructionPtrTypeOf *)instruction); |
| 15049 | case IrInstructionIdMaybeWrap: | 15211 | case IrInstructionIdAlignCast: |
| 15050 | case IrInstructionIdErrWrapCode: | 15212 | return ir_analyze_instruction_align_cast(ira, (IrInstructionAlignCast *)instruction); |
| 15051 | case IrInstructionIdErrWrapPayload: | ||
| 15052 | case IrInstructionIdCast: | ||
| 15053 | zig_panic("TODO analyze more instructions"); | ||
| 15054 | } | 15213 | } |
| 15055 | zig_unreachable(); | 15214 | zig_unreachable(); |
| 15056 | } | 15215 | } |
| ... | @@ -15228,6 +15387,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { | ... | @@ -15228,6 +15387,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 15228 | case IrInstructionIdFieldParentPtr: | 15387 | case IrInstructionIdFieldParentPtr: |
| 15229 | case IrInstructionIdOffsetOf: | 15388 | case IrInstructionIdOffsetOf: |
| 15230 | case IrInstructionIdTypeId: | 15389 | case IrInstructionIdTypeId: |
| 15390 | case IrInstructionIdAlignCast: | ||
| 15231 | return false; | 15391 | return false; |
| 15232 | case IrInstructionIdAsm: | 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,10 +174,16 @@ static void ir_print_decl_var(IrPrint *irp, IrInstructionDeclVar *decl_var_instr |
| 174 | if (decl_var_instruction->var_type) { | 174 | if (decl_var_instruction->var_type) { |
| 175 | fprintf(irp->f, "%s %s: ", var_or_const, name); | 175 | fprintf(irp->f, "%s %s: ", var_or_const, name); |
| 176 | ir_print_other_instruction(irp, decl_var_instruction->var_type); | 176 | ir_print_other_instruction(irp, decl_var_instruction->var_type); |
| 177 | fprintf(irp->f, " = "); | 177 | fprintf(irp->f, " "); |
| 178 | } else { | 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 | ir_print_other_instruction(irp, decl_var_instruction->init_value); | 187 | ir_print_other_instruction(irp, decl_var_instruction->init_value); |
| 182 | if (decl_var_instruction->var->is_comptime != nullptr) { | 188 | if (decl_var_instruction->var->is_comptime != nullptr) { |
| 183 | fprintf(irp->f, " // comptime = "); | 189 | fprintf(irp->f, " // comptime = "); |
| ... | @@ -640,7 +646,7 @@ static void ir_print_slice(IrPrint *irp, IrInstructionSlice *instruction) { | ... | @@ -640,7 +646,7 @@ static void ir_print_slice(IrPrint *irp, IrInstructionSlice *instruction) { |
| 640 | ir_print_other_instruction(irp, instruction->ptr); | 646 | ir_print_other_instruction(irp, instruction->ptr); |
| 641 | fprintf(irp->f, "["); | 647 | fprintf(irp->f, "["); |
| 642 | ir_print_other_instruction(irp, instruction->start); | 648 | ir_print_other_instruction(irp, instruction->start); |
| 643 | fprintf(irp->f, "..."); | 649 | fprintf(irp->f, ".."); |
| 644 | if (instruction->end) | 650 | if (instruction->end) |
| 645 | ir_print_other_instruction(irp, instruction->end); | 651 | ir_print_other_instruction(irp, instruction->end); |
| 646 | fprintf(irp->f, "]"); | 652 | fprintf(irp->f, "]"); |
| ... | @@ -745,7 +751,13 @@ static void ir_print_fn_proto(IrPrint *irp, IrInstructionFnProto *instruction) { | ... | @@ -745,7 +751,13 @@ static void ir_print_fn_proto(IrPrint *irp, IrInstructionFnProto *instruction) { |
| 745 | ir_print_other_instruction(irp, instruction->param_types[i]); | 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 | ir_print_other_instruction(irp, instruction->return_type); | 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,6 +932,14 @@ static void ir_print_set_eval_branch_quota(IrPrint *irp, IrInstructionSetEvalBra |
| 920 | fprintf(irp->f, ")"); | 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 | static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { | 943 | static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 924 | ir_print_prefix(irp, instruction); | 944 | ir_print_prefix(irp, instruction); |
| 925 | switch (instruction->id) { | 945 | switch (instruction->id) { |
| ... | @@ -1213,6 +1233,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { | ... | @@ -1213,6 +1233,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1213 | case IrInstructionIdSetEvalBranchQuota: | 1233 | case IrInstructionIdSetEvalBranchQuota: |
| 1214 | ir_print_set_eval_branch_quota(irp, (IrInstructionSetEvalBranchQuota *)instruction); | 1234 | ir_print_set_eval_branch_quota(irp, (IrInstructionSetEvalBranchQuota *)instruction); |
| 1215 | break; | 1235 | break; |
| 1236 | case IrInstructionIdAlignCast: | ||
| 1237 | ir_print_align_cast(irp, (IrInstructionAlignCast *)instruction); | ||
| 1238 | break; | ||
| 1216 | } | 1239 | } |
| 1217 | fprintf(irp->f, "\n"); | 1240 | fprintf(irp->f, "\n"); |
| 1218 | } | 1241 | } |
std/debug.zig+11-6| ... | @@ -957,16 +957,21 @@ pub var global_allocator = mem.Allocator { | ... | @@ -957,16 +957,21 @@ pub var global_allocator = mem.Allocator { |
| 957 | var some_mem: [100 * 1024]u8 = undefined; | 957 | var some_mem: [100 * 1024]u8 = undefined; |
| 958 | var some_mem_index: usize = 0; | 958 | var some_mem_index: usize = 0; |
| 959 | 959 | ||
| 960 | fn globalAlloc(self: &mem.Allocator, n: usize) -> %[]u8 { | 960 | fn globalAlloc(self: &mem.Allocator, n: usize, alignment: usize) -> %[]u8 { |
| 961 | const result = some_mem[some_mem_index .. some_mem_index + n]; | 961 | const addr = @ptrToInt(&some_mem[some_mem_index]); |
| 962 | some_mem_index += n; | 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 | return result; | 968 | return result; |
| 964 | } | 969 | } |
| 965 | 970 | ||
| 966 | fn globalRealloc(self: &mem.Allocator, old_mem: []u8, new_size: usize) -> %[]u8 { | 971 | fn globalRealloc(self: &mem.Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 { |
| 967 | const result = %return globalAlloc(self, new_size); | 972 | const result = %return globalAlloc(self, new_size, alignment); |
| 968 | @memcpy(result.ptr, old_mem.ptr, old_mem.len); | 973 | @memcpy(result.ptr, old_mem.ptr, old_mem.len); |
| 969 | return result; | 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,21 +11,18 @@ pub const Cmp = math.Cmp; |
| 11 | error NoMem; | 11 | error NoMem; |
| 12 | 12 | ||
| 13 | pub const Allocator = struct { | 13 | pub const Allocator = struct { |
| 14 | allocFn: fn (self: &Allocator, n: usize) -> %[]u8, | 14 | /// Allocate byte_count bytes and return them in a slice, with the |
| 15 | /// Note that old_mem may be a slice of length 0, in which case reallocFn | 15 | /// slicer's pointer aligned at least to alignment bytes. |
| 16 | /// should simply call allocFn. | 16 | allocFn: fn (self: &Allocator, byte_count: usize, alignment: usize) -> %[]u8, |
| 17 | reallocFn: fn (self: &Allocator, old_mem: []u8, new_size: usize) -> %[]u8, | 17 | |
| 18 | /// Note that mem may be a slice of length 0, in which case freeFn | 18 | /// Guaranteed: old_mem.len > 0 and alignment >= alignment of old_mem.ptr |
| 19 | /// should do nothing. | 19 | reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: usize) -> %[]u8, |
| 20 | freeFn: fn (self: &Allocator, mem: []u8), | 20 | |
| 21 | 21 | freeFn: fn (self: &Allocator, ptr: &u8), | |
| 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 | } | ||
| 26 | 22 | ||
| 27 | fn create(self: &Allocator, comptime T: type) -> %&T { | 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 | fn destroy(self: &Allocator, ptr: var) { | 28 | fn destroy(self: &Allocator, ptr: var) { |
| ... | @@ -34,16 +31,29 @@ pub const Allocator = struct { | ... | @@ -34,16 +31,29 @@ pub const Allocator = struct { |
| 34 | 31 | ||
| 35 | fn alloc(self: &Allocator, comptime T: type, n: usize) -> %[]T { | 32 | fn alloc(self: &Allocator, comptime T: type, n: usize) -> %[]T { |
| 36 | const byte_count = %return math.mul(usize, @sizeOf(T), n); | 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 | fn realloc(self: &Allocator, comptime T: type, old_mem: []T, n: usize) -> %[]T { | 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 | const byte_count = %return math.mul(usize, @sizeOf(T), n); | 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) { | 51 | fn free(self: &Allocator, memory: var) { |
| 46 | self.freeFn(self, ([]u8)(mem)); | 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,24 +89,28 @@ pub const IncrementingAllocator = struct { |
| 79 | _ = os.posix.munmap(self.bytes.ptr, self.bytes.len); | 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 | const self = @fieldParentPtr(IncrementingAllocator, "allocator", allocator); | 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 | if (new_end_index > self.bytes.len) { | 99 | if (new_end_index > self.bytes.len) { |
| 86 | return error.NoMem; | 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 | self.end_index = new_end_index; | 103 | self.end_index = new_end_index; |
| 90 | return result; | 104 | return result; |
| 91 | } | 105 | } |
| 92 | 106 | ||
| 93 | fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize) -> %[]u8 { | 107 | fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 { |
| 94 | const result = %return alloc(allocator, new_size); | 108 | const result = %return alloc(allocator, new_size, alignment); |
| 95 | copy(u8, result, old_mem); | 109 | copy(u8, result, old_mem); |
| 96 | return result; | 110 | return result; |
| 97 | } | 111 | } |
| 98 | 112 | ||
| 99 | fn free(allocator: &Allocator, bytes: []u8) { | 113 | fn free(allocator: &Allocator, bytes: &u8) { |
| 100 | // Do nothing. That's the point of an incrementing allocator. | 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,3 +62,24 @@ fn testBytesAlign(b: u8) { |
| 62 | const ptr = @ptrCast(&u32, &bytes[0]); | 62 | const ptr = @ptrCast(&u32, &bytes[0]); |
| 63 | assert(*ptr == 0x33333333); | 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,7 +277,7 @@ fn cast128Float(x: u128) -> f128 { |
| 277 | } | 277 | } |
| 278 | 278 | ||
| 279 | test "const slice widen cast" { | 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 | const u32_value = ([]const u32)(bytes[0..])[0]; | 282 | const u32_value = ([]const u32)(bytes[0..])[0]; |
| 283 | assert(u32_value == 0x12121212); | 283 | assert(u32_value == 0x12121212); |
test/cases/misc.zig+1-1| ... | @@ -404,7 +404,7 @@ test "cast slice to u8 slice" { | ... | @@ -404,7 +404,7 @@ test "cast slice to u8 slice" { |
| 404 | bytes[6] = 0; | 404 | bytes[6] = 0; |
| 405 | bytes[7] = 0; | 405 | bytes[7] = 0; |
| 406 | assert(big_thing_slice[1] == 0); | 406 | assert(big_thing_slice[1] == 0); |
| 407 | const big_thing_again = ([]i32)(bytes); | 407 | const big_thing_again = ([]align 1 i32)(bytes); |
| 408 | assert(big_thing_again[2] == 3); | 408 | assert(big_thing_again[2] == 3); |
| 409 | big_thing_again[2] = -1; | 409 | big_thing_again[2] = -1; |
| 410 | assert(bytes[8] == @maxValue(u8)); | 410 | assert(bytes[8] == @maxValue(u8)); |
test/compile_errors.zig+17| ... | @@ -2022,4 +2022,21 @@ pub fn addCases(cases: &tests.CompileErrorContext) { | ... | @@ -2022,4 +2022,21 @@ pub fn addCases(cases: &tests.CompileErrorContext) { |
| 2022 | ".tmp_source.zig:3:17: error: cast increases pointer alignment", | 2022 | ".tmp_source.zig:3:17: error: cast increases pointer alignment", |
| 2023 | ".tmp_source.zig:3:38: note: '&u8' has alignment 1", | 2023 | ".tmp_source.zig:3:38: note: '&u8' has alignment 1", |
| 2024 | ".tmp_source.zig:3:27: note: '&u32' has alignment 4"); | 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,8 +200,8 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 200 | \\ const x = widenSlice([]u8{1, 2, 3, 4, 5}); | 200 | \\ const x = widenSlice([]u8{1, 2, 3, 4, 5}); |
| 201 | \\ if (x.len == 0) return error.Whatever; | 201 | \\ if (x.len == 0) return error.Whatever; |
| 202 | \\} | 202 | \\} |
| 203 | \\fn widenSlice(slice: []const u8) -> []const i32 { | 203 | \\fn widenSlice(slice: []align 1 const u8) -> []align 1 const i32 { |
| 204 | \\ ([]const i32)(slice) | 204 | \\ ([]align 1 const i32)(slice) |
| 205 | \\} | 205 | \\} |
| 206 | ); | 206 | ); |
| 207 | 207 | ||
| ... | @@ -261,4 +261,22 @@ pub fn addCases(cases: &tests.CompareOutputContext) { | ... | @@ -261,4 +261,22 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 261 | \\ return error(x); | 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 | } |