authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-08-29 23:33:25-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-08-29 23:33:25-04:00
log898d65baa9198f2fb1c5df91fba51a58c3148626
treefd4f7178f974b7be7236782aacde4981df03238b
parent910a96f0468c635a135d9fccd39f139ba0775ef9

more alignment improvements

* 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 #37

13 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};
12561257
1257struct BuiltinFnEntry {1258struct 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,
12771279
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};
18601863
1861struct IrInstruction {1864struct IrInstruction {
...@@ -2462,6 +2465,7 @@ struct IrInstructionFnProto {...@@ -2462,6 +2465,7 @@ struct IrInstructionFnProto {
2462 IrInstruction base;2465 IrInstruction base;
24632466
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};
26402644
2645struct IrInstructionAlignCast {
2646 IrInstruction base;
2647
2648 IrInstruction *align_bytes;
2649 IrInstruction *target;
2650};
2651
2641static const size_t slice_ptr_index = 0;2652static const size_t slice_ptr_index = 0;
2642static const size_t slice_len_index = 1;2653static const size_t slice_len_index = 1;
26432654
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}
10601063
1061static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope) {1064static 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;
10641067
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;
10671071
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 }
20572061
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 }
20652063
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 }
20752072
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);
20782082
...@@ -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}
26072609
2610static 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
2616static 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}
26082676
2609static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) {2677static 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}
46374708
4638static const char *bool_to_str(bool b) {4709static 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}
557557
558static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignCast *) {
559 return IrInstructionIdAlignCast;
560}
561
558template<typename T>562template<typename T>
559static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {563static 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}
19001904
1901static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *source_node,1905static 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;
19081913
...@@ -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);
19161922
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}
22212227
2228static 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
2222static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) {2241static 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}
27432766
...@@ -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}
29272950
2951static 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
2928static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) {2959static 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
45314564
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 }
60626109
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;
60666120
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}
60696123
6070static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope,6124static 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 }
83178371
8318 // explicit cast from []T to []u8 or []u8 to []T8372 // 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 }
83298397
8330 // explicit cast from [N]u8 to []const T8398 // 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);
1022810296
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 }
1023110302
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;
1372913800
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 }
1423914308
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}
1486814942
14943static 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
14869static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {15027static 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}
751763
...@@ -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}
922934
935static 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
923static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {943static 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 {
957var some_mem: [100 * 1024]u8 = undefined;957var some_mem: [100 * 1024]u8 = undefined;
958var some_mem_index: usize = 0;958var some_mem_index: usize = 0;
959959
960fn globalAlloc(self: &mem.Allocator, n: usize) -> %[]u8 {960fn 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}
965970
966fn globalRealloc(self: &mem.Allocator, old_mem: []u8, new_size: usize) -> %[]u8 {971fn 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}
971976
972fn globalFree(self: &mem.Allocator, old_mem: []u8) { }977fn 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;
11error NoMem;11error NoMem;
1212
13pub const Allocator = struct {13pub 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 reallocFn15 /// 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 freeFn18 /// 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
2121 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 }
2622
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 }
3027
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 {
3431
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 }
3937
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 }
4450
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};
4959
...@@ -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 }
8191
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 }
92106
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 }
98112
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
66test "specifying alignment allows slice cast" {
67 testBytesAlignSlice(0x33);
68}
69fn 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
75test "@alignCast" {
76 var x: u32 align 4 = 1;
77 expectsOnly1(&x);
78 assert(x == 2);
79}
80fn expectsOnly1(x: &align 1 u32) {
81 expects4(@alignCast(4, x));
82}
83fn 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}
278278
279test "const slice widen cast" {279test "const slice widen cast" {
280 const bytes = []u8{0x12, 0x12, 0x12, 0x12};280 const bytes align 4 = []u8{0x12, 0x12, 0x12, 0x12};
281281
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 );
207207
...@@ -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}