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 {
12521252 BuiltinFnIdShlExact,
12531253 BuiltinFnIdShrExact,
12541254 BuiltinFnIdSetEvalBranchQuota,
1255 BuiltinFnIdAlignCast,
12551256};
12561257
12571258struct BuiltinFnEntry {
......@@ -1274,6 +1275,7 @@ enum PanicMsgId {
12741275 PanicMsgIdSliceWidenRemainder,
12751276 PanicMsgIdUnwrapMaybeFail,
12761277 PanicMsgIdInvalidErrorCode,
1278 PanicMsgIdIncorrectAlignment,
12771279
12781280 PanicMsgIdCount,
12791281};
......@@ -1856,6 +1858,7 @@ enum IrInstructionId {
18561858 IrInstructionIdTypeId,
18571859 IrInstructionIdSetEvalBranchQuota,
18581860 IrInstructionIdPtrTypeOf,
1861 IrInstructionIdAlignCast,
18591862};
18601863
18611864struct IrInstruction {
......@@ -2462,6 +2465,7 @@ struct IrInstructionFnProto {
24622465 IrInstruction base;
24632466
24642467 IrInstruction **param_types;
2468 IrInstruction *align_value;
24652469 IrInstruction *return_type;
24662470 bool is_var_args;
24672471};
......@@ -2638,6 +2642,13 @@ struct IrInstructionPtrTypeOf {
26382642 bool is_volatile;
26392643};
26402644
2645struct IrInstructionAlignCast {
2646 IrInstruction base;
2647
2648 IrInstruction *align_bytes;
2649 IrInstruction *target;
2650};
2651
26412652static const size_t slice_ptr_index = 0;
26422653static 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) {
884884 buf_appendf(&fn_type->name, "%s...", comma);
885885 }
886886 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 }
887890 if (fn_type_id->return_type->id != TypeTableEntryIdVoid) {
888891 buf_appendf(&fn_type->name, " -> %s", buf_ptr(&fn_type_id->return_type->name));
889892 }
......@@ -1058,12 +1061,13 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou
10581061 fn_type_id->is_var_args = fn_proto->is_var_args;
10591062}
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) {
10621065 assert(proto_node->type == NodeTypeFnProto);
10631066 AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
10641067
10651068 FnTypeId fn_type_id = {0};
10661069 init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length);
1070 fn_type_id.alignment = alignment;
10671071
10681072 for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) {
10691073 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) {
20562060 }
20572061
20582062 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;
20662065 if (fn_proto->align_expr != nullptr) {
2067 if (!analyze_const_align(g, tld_fn->base.parent_scope, fn_proto->align_expr,
2068 &fn_table_entry->align_bytes))
2069 {
2066 if (!analyze_const_align(g, child_scope, fn_proto->align_expr, &alignment)) {
20702067 fn_table_entry->type_entry = g->builtin_types.entry_invalid;
20712068 tld_fn->base.resolution = TldResolutionInvalid;
20722069 return;
20732070 }
20742071 }
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
20762080 if (!fn_table_entry->type_entry->data.fn.is_generic) {
20772081 g->fn_protos.append(fn_table_entry);
20782082
......@@ -2663,6 +2667,9 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *
26632667 if (expected_type->id == TypeTableEntryIdFn &&
26642668 actual_type->id == TypeTableEntryIdFn)
26652669 {
2670 if (expected_type->data.fn.fn_type_id.alignment > actual_type->data.fn.fn_type_id.alignment) {
2671 return false;
2672 }
26662673 if (expected_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) {
26672674 return false;
26682675 }
......@@ -3384,6 +3391,7 @@ uint32_t fn_type_id_hash(FnTypeId *id) {
33843391 result += ((uint32_t)(id->cc)) * (uint32_t)3349388391;
33853392 result += id->is_var_args ? (uint32_t)1931444534 : 0;
33863393 result += hash_ptr(id->return_type);
3394 result += id->alignment * 0xd3b3f3e2;
33873395 for (size_t i = 0; i < id->param_count; i += 1) {
33883396 FnTypeParamInfo *info = &id->param_info[i];
33893397 result += info->is_noalias ? (uint32_t)892356923 : 0;
......@@ -3396,7 +3404,8 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) {
33963404 if (a->cc != b->cc ||
33973405 a->return_type != b->return_type ||
33983406 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)
34003409 {
34013410 return false;
34023411 }
src/ast_render.cpp+1-1
......@@ -953,7 +953,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
953953 render_node_ungrouped(ar, node->data.slice_expr.array_ref_expr);
954954 fprintf(ar->f, "[");
955955 render_node_grouped(ar, node->data.slice_expr.start);
956 fprintf(ar->f, "...");
956 fprintf(ar->f, "..");
957957 if (node->data.slice_expr.end)
958958 render_node_grouped(ar, node->data.slice_expr.end);
959959 fprintf(ar->f, "]");
src/codegen.cpp+71
......@@ -688,6 +688,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
688688 return buf_create_from_str("reached unreachable code");
689689 case PanicMsgIdInvalidErrorCode:
690690 return buf_create_from_str("invalid error code");
691 case PanicMsgIdIncorrectAlignment:
692 return buf_create_from_str("incorrect alignment");
691693 }
692694 zig_unreachable();
693695}
......@@ -2605,6 +2607,72 @@ static LLVMValueRef ir_render_field_parent_ptr(CodeGen *g, IrExecutable *executa
26052607 }
26062608}
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
26092677static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) {
26102678 switch (atomic_order) {
......@@ -3350,6 +3418,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
33503418 return ir_render_enum_tag_name(g, executable, (IrInstructionEnumTagName *)instruction);
33513419 case IrInstructionIdFieldParentPtr:
33523420 return ir_render_field_parent_ptr(g, executable, (IrInstructionFieldParentPtr *)instruction);
3421 case IrInstructionIdAlignCast:
3422 return ir_render_align_cast(g, executable, (IrInstructionAlignCast *)instruction);
33533423 }
33543424 zig_unreachable();
33553425}
......@@ -4633,6 +4703,7 @@ static void define_builtin_fns(CodeGen *g) {
46334703 create_builtin_fn(g, BuiltinFnIdShlExact, "shlExact", 2);
46344704 create_builtin_fn(g, BuiltinFnIdShrExact, "shrExact", 2);
46354705 create_builtin_fn(g, BuiltinFnIdSetEvalBranchQuota, "setEvalBranchQuota", 1);
4706 create_builtin_fn(g, BuiltinFnIdAlignCast, "alignCast", 2);
46364707}
46374708
46384709static const char *bool_to_str(bool b) {
src/ir.cpp+181-21
......@@ -555,6 +555,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrTypeOf *) {
555555 return IrInstructionIdPtrTypeOf;
556556}
557557
558static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignCast *) {
559 return IrInstructionIdAlignCast;
560}
561
558562template<typename T>
559563static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
560564 T *special_instruction = allocate<T>(1);
......@@ -1899,10 +1903,11 @@ static IrInstruction *ir_build_unwrap_err_payload_from(IrBuilder *irb, IrInstruc
18991903}
19001904
19011905static 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)
19031907{
19041908 IrInstructionFnProto *instruction = ir_build_instruction<IrInstructionFnProto>(irb, scope, source_node);
19051909 instruction->param_types = param_types;
1910 instruction->align_value = align_value;
19061911 instruction->return_type = return_type;
19071912 instruction->is_var_args = is_var_args;
19081913
......@@ -1912,6 +1917,7 @@ static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *s
19121917 for (size_t i = 0; i < param_count; i += 1) {
19131918 ir_ref_instruction(param_types[i], irb->current_basic_block);
19141919 }
1920 if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block);
19151921 ir_ref_instruction(return_type, irb->current_basic_block);
19161922
19171923 return &instruction->base;
......@@ -2219,6 +2225,19 @@ static IrInstruction *ir_build_set_eval_branch_quota(IrBuilder *irb, Scope *scop
22192225 return &instruction->base;
22202226}
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
22222241static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) {
22232242 return nullptr;
22242243}
......@@ -2738,6 +2757,10 @@ static IrInstruction *ir_instruction_fnproto_get_dep(IrInstructionFnProto *instr
27382757 if (param_index < instruction->base.source_node->data.fn_proto.params.length) {
27392758 return instruction->param_types[param_index];
27402759 }
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 }
27412764 return nullptr;
27422765}
27432766
......@@ -2925,6 +2948,14 @@ static IrInstruction *ir_instruction_ptrtypeof_get_dep(IrInstructionPtrTypeOf *i
29252948 }
29262949}
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
29282959static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) {
29292960 switch (instruction->id) {
29302961 case IrInstructionIdInvalid:
......@@ -3121,6 +3152,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
31213152 return ir_instruction_setevalbranchquota_get_dep((IrInstructionSetEvalBranchQuota *) instruction, index);
31223153 case IrInstructionIdPtrTypeOf:
31233154 return ir_instruction_ptrtypeof_get_dep((IrInstructionPtrTypeOf *) instruction, index);
3155 case IrInstructionIdAlignCast:
3156 return ir_instruction_aligncast_get_dep((IrInstructionAlignCast *) instruction, index);
31243157 }
31253158 zig_unreachable();
31263159}
......@@ -4531,6 +4564,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
45314564
45324565 return ir_build_set_eval_branch_quota(irb, scope, node, arg0_value);
45334566 }
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 }
45344581 }
45354582 zig_unreachable();
45364583}
......@@ -6060,11 +6107,18 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo
60606107 param_types[i] = type_value;
60616108 }
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
60636117 IrInstruction *return_type = ir_gen_node(irb, node->data.fn_proto.return_type, parent_scope);
60646118 if (return_type == irb->codegen->invalid_instruction)
60656119 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);
60686122}
60696123
60706124static 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
83168370 }
83178371
83188372 // explicit cast from []T to []u8 or []u8 to []T
8319 if (is_slice(wanted_type) && is_slice(actual_type) &&
8320 (is_u8(wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type) ||
8321 is_u8(actual_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type)) &&
8322 (wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||
8323 !actual_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const))
8324 {
8325 if (!ir_emit_global_runtime_side_effect(ira, source_instr))
8326 return ira->codegen->invalid_instruction;
8327 return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpResizeSlice, true);
8373 if (is_slice(wanted_type) && is_slice(actual_type)) {
8374 TypeTableEntry *wanted_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
8375 TypeTableEntry *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry;
8376 if ((is_u8(wanted_ptr_type->data.pointer.child_type) || is_u8(actual_ptr_type->data.pointer.child_type)) &&
8377 (wanted_ptr_type->data.pointer.is_const || !actual_ptr_type->data.pointer.is_const))
8378 {
8379 uint32_t src_align_bytes = get_ptr_align(actual_ptr_type);
8380 uint32_t dest_align_bytes = get_ptr_align(wanted_ptr_type);
8381
8382 if (dest_align_bytes > src_align_bytes) {
8383 ErrorMsg *msg = ir_add_error(ira, source_instr,
8384 buf_sprintf("cast increases pointer alignment"));
8385 add_error_note(ira->codegen, msg, source_instr->source_node,
8386 buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&actual_type->name), src_align_bytes));
8387 add_error_note(ira->codegen, msg, source_instr->source_node,
8388 buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&wanted_type->name), dest_align_bytes));
8389 return ira->codegen->invalid_instruction;
8390 }
8391
8392 if (!ir_emit_global_runtime_side_effect(ira, source_instr))
8393 return ira->codegen->invalid_instruction;
8394 return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpResizeSlice, true);
8395 }
83288396 }
83298397
83308398 // explicit cast from [N]u8 to []const T
......@@ -10226,7 +10294,10 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
1022610294 ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota,
1022710295 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;
1023010301 }
1023110302
1023210303 {
......@@ -13728,11 +13799,9 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio
1372813799 TypeTableEntry *return_type;
1372913800
1373013801 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;
1373413802 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);
1373613805 return_type = get_slice_type(ira->codegen, slice_ptr_type);
1373713806 } else if (array_type->id == TypeTableEntryIdPointer) {
1373813807 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
1423714306 }
1423814307 }
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
1424014314 IrInstruction *return_type_value = instruction->return_type->other;
1424114315 fn_type_id.return_type = ir_resolve_type(ira, return_type_value);
1424214316 if (type_is_invalid(fn_type_id.return_type))
......@@ -14866,6 +14940,90 @@ static TypeTableEntry *ir_analyze_instruction_ptr_type_of(IrAnalyze *ira, IrInst
1486614940 return ira->codegen->builtin_types.entry_type;
1486714941}
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
1486915027static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
1487015028 switch (instruction->id) {
1487115029 case IrInstructionIdInvalid:
......@@ -14877,6 +15035,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
1487715035 case IrInstructionIdStructFieldPtr:
1487815036 case IrInstructionIdEnumFieldPtr:
1487915037 case IrInstructionIdInitEnum:
15038 case IrInstructionIdMaybeWrap:
15039 case IrInstructionIdErrWrapCode:
15040 case IrInstructionIdErrWrapPayload:
15041 case IrInstructionIdCast:
1488015042 zig_unreachable();
1488115043 case IrInstructionIdReturn:
1488215044 return ir_analyze_instruction_return(ira, (IrInstructionReturn *)instruction);
......@@ -15046,11 +15208,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
1504615208 return ir_analyze_instruction_set_eval_branch_quota(ira, (IrInstructionSetEvalBranchQuota *)instruction);
1504715209 case IrInstructionIdPtrTypeOf:
1504815210 return ir_analyze_instruction_ptr_type_of(ira, (IrInstructionPtrTypeOf *)instruction);
15049 case IrInstructionIdMaybeWrap:
15050 case IrInstructionIdErrWrapCode:
15051 case IrInstructionIdErrWrapPayload:
15052 case IrInstructionIdCast:
15053 zig_panic("TODO analyze more instructions");
15211 case IrInstructionIdAlignCast:
15212 return ir_analyze_instruction_align_cast(ira, (IrInstructionAlignCast *)instruction);
1505415213 }
1505515214 zig_unreachable();
1505615215}
......@@ -15228,6 +15387,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
1522815387 case IrInstructionIdFieldParentPtr:
1522915388 case IrInstructionIdOffsetOf:
1523015389 case IrInstructionIdTypeId:
15390 case IrInstructionIdAlignCast:
1523115391 return false;
1523215392 case IrInstructionIdAsm:
1523315393 {
src/ir_print.cpp+27-4
......@@ -174,10 +174,16 @@ static void ir_print_decl_var(IrPrint *irp, IrInstructionDeclVar *decl_var_instr
174174 if (decl_var_instruction->var_type) {
175175 fprintf(irp->f, "%s %s: ", var_or_const, name);
176176 ir_print_other_instruction(irp, decl_var_instruction->var_type);
177 fprintf(irp->f, " = ");
177 fprintf(irp->f, " ");
178178 } 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, " ");
180185 }
186 fprintf(irp->f, "= ");
181187 ir_print_other_instruction(irp, decl_var_instruction->init_value);
182188 if (decl_var_instruction->var->is_comptime != nullptr) {
183189 fprintf(irp->f, " // comptime = ");
......@@ -640,7 +646,7 @@ static void ir_print_slice(IrPrint *irp, IrInstructionSlice *instruction) {
640646 ir_print_other_instruction(irp, instruction->ptr);
641647 fprintf(irp->f, "[");
642648 ir_print_other_instruction(irp, instruction->start);
643 fprintf(irp->f, "...");
649 fprintf(irp->f, "..");
644650 if (instruction->end)
645651 ir_print_other_instruction(irp, instruction->end);
646652 fprintf(irp->f, "]");
......@@ -745,7 +751,13 @@ static void ir_print_fn_proto(IrPrint *irp, IrInstructionFnProto *instruction) {
745751 ir_print_other_instruction(irp, instruction->param_types[i]);
746752 }
747753 }
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, "->");
749761 ir_print_other_instruction(irp, instruction->return_type);
750762}
751763
......@@ -920,6 +932,14 @@ static void ir_print_set_eval_branch_quota(IrPrint *irp, IrInstructionSetEvalBra
920932 fprintf(irp->f, ")");
921933}
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
923943static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
924944 ir_print_prefix(irp, instruction);
925945 switch (instruction->id) {
......@@ -1213,6 +1233,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
12131233 case IrInstructionIdSetEvalBranchQuota:
12141234 ir_print_set_eval_branch_quota(irp, (IrInstructionSetEvalBranchQuota *)instruction);
12151235 break;
1236 case IrInstructionIdAlignCast:
1237 ir_print_align_cast(irp, (IrInstructionAlignCast *)instruction);
1238 break;
12161239 }
12171240 fprintf(irp->f, "\n");
12181241}
std/debug.zig+11-6
......@@ -957,16 +957,21 @@ pub var global_allocator = mem.Allocator {
957957var some_mem: [100 * 1024]u8 = undefined;
958958var some_mem_index: usize = 0;
959959
960fn globalAlloc(self: &mem.Allocator, n: usize) -> %[]u8 {
961 const result = some_mem[some_mem_index .. some_mem_index + n];
962 some_mem_index += n;
960fn globalAlloc(self: &mem.Allocator, n: usize, alignment: usize) -> %[]u8 {
961 const addr = @ptrToInt(&some_mem[some_mem_index]);
962 const rem = @rem(addr, alignment);
963 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
964 const adjusted_index = some_mem_index + march_forward_bytes;
965 const end_index = adjusted_index + n;
966 const result = some_mem[adjusted_index .. end_index];
967 some_mem_index = end_index;
963968 return result;
964969}
965970
966fn globalRealloc(self: &mem.Allocator, old_mem: []u8, new_size: usize) -> %[]u8 {
967 const result = %return globalAlloc(self, new_size);
971fn globalRealloc(self: &mem.Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
972 const result = %return globalAlloc(self, new_size, alignment);
968973 @memcpy(result.ptr, old_mem.ptr, old_mem.len);
969974 return result;
970975}
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;
1111error NoMem;
1212
1313pub const Allocator = struct {
14 allocFn: fn (self: &Allocator, n: usize) -> %[]u8,
15 /// Note that old_mem may be a slice of length 0, in which case reallocFn
16 /// should simply call allocFn.
17 reallocFn: fn (self: &Allocator, old_mem: []u8, new_size: usize) -> %[]u8,
18 /// Note that mem may be a slice of length 0, in which case freeFn
19 /// should do nothing.
20 freeFn: fn (self: &Allocator, mem: []u8),
21
22 /// Aborts the program if an allocation fails.
23 fn checkedAlloc(self: &Allocator, comptime T: type, n: usize) -> []T {
24 alloc(self, T, n) %% |err| debug.panic("allocation failure: {}", @errorName(err))
25 }
14 /// Allocate byte_count bytes and return them in a slice, with the
15 /// slicer's pointer aligned at least to alignment bytes.
16 allocFn: fn (self: &Allocator, byte_count: usize, alignment: usize) -> %[]u8,
17
18 /// Guaranteed: old_mem.len > 0 and alignment >= alignment of old_mem.ptr
19 reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: usize) -> %[]u8,
20
21 freeFn: fn (self: &Allocator, ptr: &u8),
2622
2723 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]
2926 }
3027
3128 fn destroy(self: &Allocator, ptr: var) {
......@@ -34,16 +31,29 @@ pub const Allocator = struct {
3431
3532 fn alloc(self: &Allocator, comptime T: type, n: usize) -> %[]T {
3633 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))
3836 }
3937
4038 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
4146 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))
4349 }
4450
45 fn free(self: &Allocator, mem: var) {
46 self.freeFn(self, ([]u8)(mem));
51 fn free(self: &Allocator, memory: var) {
52 const const_slice = ([]const u8)(memory);
53 if (memory.len == 0)
54 return;
55 const ptr = @intToPtr(&u8, @ptrToInt(const_slice.ptr));
56 self.freeFn(self, ptr);
4757 }
4858};
4959
......@@ -79,24 +89,28 @@ pub const IncrementingAllocator = struct {
7989 _ = os.posix.munmap(self.bytes.ptr, self.bytes.len);
8090 }
8191
82 fn alloc(allocator: &Allocator, n: usize) -> %[]u8 {
92 fn alloc(allocator: &Allocator, n: usize, alignment: usize) -> %[]u8 {
8393 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;
8599 if (new_end_index > self.bytes.len) {
86100 return error.NoMem;
87101 }
88 const result = self.bytes[self.end_index..new_end_index];
102 const result = self.bytes[adjusted_index .. new_end_index];
89103 self.end_index = new_end_index;
90104 return result;
91105 }
92106
93 fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize) -> %[]u8 {
94 const result = %return alloc(allocator, new_size);
107 fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
108 const result = %return alloc(allocator, new_size, alignment);
95109 copy(u8, result, old_mem);
96110 return result;
97111 }
98112
99 fn free(allocator: &Allocator, bytes: []u8) {
113 fn free(allocator: &Allocator, bytes: &u8) {
100114 // Do nothing. That's the point of an incrementing allocator.
101115 }
102116};
test/cases/align.zig+21
......@@ -62,3 +62,24 @@ fn testBytesAlign(b: u8) {
6262 const ptr = @ptrCast(&u32, &bytes[0]);
6363 assert(*ptr == 0x33333333);
6464}
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 {
277277}
278278
279279test "const slice widen cast" {
280 const bytes = []u8{0x12, 0x12, 0x12, 0x12};
280 const bytes align 4 = []u8{0x12, 0x12, 0x12, 0x12};
281281
282282 const u32_value = ([]const u32)(bytes[0..])[0];
283283 assert(u32_value == 0x12121212);
test/cases/misc.zig+1-1
......@@ -404,7 +404,7 @@ test "cast slice to u8 slice" {
404404 bytes[6] = 0;
405405 bytes[7] = 0;
406406 assert(big_thing_slice[1] == 0);
407 const big_thing_again = ([]i32)(bytes);
407 const big_thing_again = ([]align 1 i32)(bytes);
408408 assert(big_thing_again[2] == 3);
409409 big_thing_again[2] = -1;
410410 assert(bytes[8] == @maxValue(u8));
test/compile_errors.zig+17
......@@ -2022,4 +2022,21 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
20222022 ".tmp_source.zig:3:17: error: cast increases pointer alignment",
20232023 ".tmp_source.zig:3:38: note: '&u8' has alignment 1",
20242024 ".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'");
20252042}
test/debug_safety.zig+20-2
......@@ -200,8 +200,8 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
200200 \\ const x = widenSlice([]u8{1, 2, 3, 4, 5});
201201 \\ if (x.len == 0) return error.Whatever;
202202 \\}
203 \\fn widenSlice(slice: []const u8) -> []const i32 {
204 \\ ([]const i32)(slice)
203 \\fn widenSlice(slice: []align 1 const u8) -> []align 1 const i32 {
204 \\ ([]align 1 const i32)(slice)
205205 \\}
206206 );
207207
......@@ -261,4 +261,22 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
261261 \\ return error(x);
262262 \\}
263263 );
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 );
264282}