authorgravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-11-10 11:58:34-07:00
committergravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-11-10 12:22:40-07:00
log8f3880074fb76871d9a4f35d1f72d0304ac5b404
treef9d2cce5de2ef4080766064f853a9f0793ab5696
parentff699722da1f2df3e521c92cebe71c50910594d3

stage2: Be more strict about eliding loads

This change makes any of the `*_val` instructions check whether it's safe to elide copies for by-ref types rather than performing this elision blindly. AIR instructions fixed: - .array_elem_val - .struct_field_val - .unwrap_errunion_payload - .try - .optional_payload These now all respect value semantics, as expected. P.S. Thanks to Andrew for the new way to approach this. Many of the lines here are from his recommended change, which comes with the significant advantage that loads are now as small as the intervening memory access allows. Co-authored by: Andrew Kelley <andrew@ziglang.org>

5 files changed, 177 insertions(+), 75 deletions(-)

src/codegen/llvm.zig+118-75
......@@ -4568,14 +4568,14 @@ pub const FuncGen = struct {
45684568 .ret_addr => try self.airRetAddr(inst),
45694569 .frame_addr => try self.airFrameAddress(inst),
45704570 .cond_br => try self.airCondBr(inst),
4571 .@"try" => try self.airTry(inst),
4571 .@"try" => try self.airTry(body[i..]),
45724572 .try_ptr => try self.airTryPtr(inst),
45734573 .intcast => try self.airIntCast(inst),
45744574 .trunc => try self.airTrunc(inst),
45754575 .fptrunc => try self.airFptrunc(inst),
45764576 .fpext => try self.airFpext(inst),
45774577 .ptrtoint => try self.airPtrToInt(inst),
4578 .load => try self.airLoad(inst, body, i + 1),
4578 .load => try self.airLoad(body[i..]),
45794579 .loop => try self.airLoop(inst),
45804580 .not => try self.airNot(inst),
45814581 .ret => try self.airRet(inst),
......@@ -4634,7 +4634,7 @@ pub const FuncGen = struct {
46344634 .atomic_store_seq_cst => try self.airAtomicStore(inst, .SequentiallyConsistent),
46354635
46364636 .struct_field_ptr => try self.airStructFieldPtr(inst),
4637 .struct_field_val => try self.airStructFieldVal(inst),
4637 .struct_field_val => try self.airStructFieldVal(body[i..]),
46384638
46394639 .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0),
46404640 .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1),
......@@ -4643,18 +4643,18 @@ pub const FuncGen = struct {
46434643
46444644 .field_parent_ptr => try self.airFieldParentPtr(inst),
46454645
4646 .array_elem_val => try self.airArrayElemVal(inst),
4647 .slice_elem_val => try self.airSliceElemVal(inst),
4646 .array_elem_val => try self.airArrayElemVal(body[i..]),
4647 .slice_elem_val => try self.airSliceElemVal(body[i..]),
46484648 .slice_elem_ptr => try self.airSliceElemPtr(inst),
4649 .ptr_elem_val => try self.airPtrElemVal(inst),
4649 .ptr_elem_val => try self.airPtrElemVal(body[i..]),
46504650 .ptr_elem_ptr => try self.airPtrElemPtr(inst),
46514651
4652 .optional_payload => try self.airOptionalPayload(inst),
4652 .optional_payload => try self.airOptionalPayload(body[i..]),
46534653 .optional_payload_ptr => try self.airOptionalPayloadPtr(inst),
46544654 .optional_payload_ptr_set => try self.airOptionalPayloadPtrSet(inst),
46554655
4656 .unwrap_errunion_payload => try self.airErrUnionPayload(inst, false),
4657 .unwrap_errunion_payload_ptr => try self.airErrUnionPayload(inst, true),
4656 .unwrap_errunion_payload => try self.airErrUnionPayload(body[i..], false),
4657 .unwrap_errunion_payload_ptr => try self.airErrUnionPayload(body[i..], true),
46584658 .unwrap_errunion_err => try self.airErrUnionErr(inst, false),
46594659 .unwrap_errunion_err_ptr => try self.airErrUnionErr(inst, true),
46604660 .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),
......@@ -5159,8 +5159,8 @@ pub const FuncGen = struct {
51595159 _ = self.builder.buildBr(end_block);
51605160
51615161 self.builder.positionBuilderAtEnd(both_pl_block);
5162 const lhs_payload = try self.optPayloadHandle(opt_llvm_ty, lhs, scalar_ty);
5163 const rhs_payload = try self.optPayloadHandle(opt_llvm_ty, rhs, scalar_ty);
5162 const lhs_payload = try self.optPayloadHandle(opt_llvm_ty, lhs, scalar_ty, true);
5163 const rhs_payload = try self.optPayloadHandle(opt_llvm_ty, rhs, scalar_ty, true);
51645164 const payload_cmp = try self.cmp(lhs_payload, rhs_payload, payload_ty, op);
51655165 _ = self.builder.buildBr(end_block);
51665166 const both_pl_block_end = self.builder.getInsertBlock();
......@@ -5305,14 +5305,16 @@ pub const FuncGen = struct {
53055305 return null;
53065306 }
53075307
5308 fn airTry(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5308 fn airTry(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value {
5309 const inst = body_tail[0];
53095310 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
53105311 const err_union = try self.resolveInst(pl_op.operand);
53115312 const extra = self.air.extraData(Air.Try, pl_op.payload);
53125313 const body = self.air.extra[extra.end..][0..extra.data.body_len];
53135314 const err_union_ty = self.air.typeOf(pl_op.operand);
5314 const result_ty = self.air.typeOfIndex(inst);
5315 return lowerTry(self, err_union, body, err_union_ty, false, result_ty);
5315 const payload_ty = self.air.typeOfIndex(inst);
5316 const can_elide_load = if (isByRef(payload_ty)) self.canElideLoad(body_tail) else false;
5317 return lowerTry(self, err_union, body, err_union_ty, false, can_elide_load, payload_ty);
53165318 }
53175319
53185320 fn airTryPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
......@@ -5321,8 +5323,8 @@ pub const FuncGen = struct {
53215323 const err_union_ptr = try self.resolveInst(extra.data.ptr);
53225324 const body = self.air.extra[extra.end..][0..extra.data.body_len];
53235325 const err_union_ty = self.air.typeOf(extra.data.ptr).childType();
5324 const result_ty = self.air.typeOfIndex(inst);
5325 return lowerTry(self, err_union_ptr, body, err_union_ty, true, result_ty);
5326 const payload_ty = self.air.typeOfIndex(inst);
5327 return lowerTry(self, err_union_ptr, body, err_union_ty, true, true, payload_ty);
53265328 }
53275329
53285330 fn lowerTry(
......@@ -5331,6 +5333,7 @@ pub const FuncGen = struct {
53315333 body: []const Air.Inst.Index,
53325334 err_union_ty: Type,
53335335 operand_is_ptr: bool,
5336 can_elide_load: bool,
53345337 result_ty: Type,
53355338 ) !?*llvm.Value {
53365339 const payload_ty = err_union_ty.errorUnionPayload();
......@@ -5379,12 +5382,15 @@ pub const FuncGen = struct {
53795382 return fg.builder.buildBitCast(err_union, res_ptr_ty, "");
53805383 }
53815384 const offset = errUnionPayloadOffset(payload_ty, target);
5382 if (operand_is_ptr or isByRef(payload_ty)) {
5385 if (operand_is_ptr) {
53835386 return fg.builder.buildStructGEP(err_union_llvm_ty, err_union, offset, "");
53845387 } else if (isByRef(err_union_ty)) {
53855388 const payload_ptr = fg.builder.buildStructGEP(err_union_llvm_ty, err_union, offset, "");
53865389 if (isByRef(payload_ty)) {
5387 return payload_ptr;
5390 if (can_elide_load)
5391 return payload_ptr;
5392
5393 return fg.loadByRef(payload_ptr, payload_ty, payload_ty.abiAlignment(target), false);
53885394 }
53895395 const load_inst = fg.builder.buildLoad(payload_ptr.getGEPResultElementType(), payload_ptr, "");
53905396 load_inst.setAlignment(payload_ty.abiAlignment(target));
......@@ -5625,14 +5631,16 @@ pub const FuncGen = struct {
56255631 return self.builder.buildStructGEP(slice_llvm_ty, slice_ptr, index, "");
56265632 }
56275633
5628 fn airSliceElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5634 fn airSliceElemVal(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value {
5635 const inst = body_tail[0];
56295636 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
56305637 const slice_ty = self.air.typeOf(bin_op.lhs);
56315638 if (!slice_ty.isVolatilePtr() and self.liveness.isUnused(inst)) return null;
56325639
56335640 const slice = try self.resolveInst(bin_op.lhs);
56345641 const index = try self.resolveInst(bin_op.rhs);
5635 const llvm_elem_ty = try self.dg.lowerPtrElemTy(slice_ty.childType());
5642 const elem_ty = slice_ty.childType();
5643 const llvm_elem_ty = try self.dg.lowerPtrElemTy(elem_ty);
56365644 const base_ptr = self.builder.buildExtractValue(slice, 0, "");
56375645 const indices: [1]*llvm.Value = .{index};
56385646 const ptr = self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");
......@@ -5653,7 +5661,8 @@ pub const FuncGen = struct {
56535661 return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");
56545662 }
56555663
5656 fn airArrayElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5664 fn airArrayElemVal(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value {
5665 const inst = body_tail[0];
56575666 if (self.liveness.isUnused(inst)) return null;
56585667
56595668 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
......@@ -5666,7 +5675,11 @@ pub const FuncGen = struct {
56665675 const elem_ptr = self.builder.buildInBoundsGEP(array_llvm_ty, array_llvm_val, &indices, indices.len, "");
56675676 const elem_ty = array_ty.childType();
56685677 if (isByRef(elem_ty)) {
5669 return elem_ptr;
5678 if (canElideLoad(self, body_tail))
5679 return elem_ptr;
5680
5681 const target = self.dg.module.getTarget();
5682 return self.loadByRef(elem_ptr, elem_ty, elem_ty.abiAlignment(target), false);
56705683 } else {
56715684 const elem_llvm_ty = try self.dg.lowerType(elem_ty);
56725685 return self.builder.buildLoad(elem_llvm_ty, elem_ptr, "");
......@@ -5677,12 +5690,14 @@ pub const FuncGen = struct {
56775690 return self.builder.buildExtractElement(array_llvm_val, rhs, "");
56785691 }
56795692
5680 fn airPtrElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5693 fn airPtrElemVal(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value {
5694 const inst = body_tail[0];
56815695 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
56825696 const ptr_ty = self.air.typeOf(bin_op.lhs);
56835697 if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst)) return null;
56845698
5685 const llvm_elem_ty = try self.dg.lowerPtrElemTy(ptr_ty.childType());
5699 const elem_ty = ptr_ty.childType();
5700 const llvm_elem_ty = try self.dg.lowerPtrElemTy(elem_ty);
56865701 const base_ptr = try self.resolveInst(bin_op.lhs);
56875702 const rhs = try self.resolveInst(bin_op.rhs);
56885703 // TODO: when we go fully opaque pointers in LLVM 16 we can remove this branch
......@@ -5743,7 +5758,8 @@ pub const FuncGen = struct {
57435758 return self.fieldPtr(inst, struct_ptr, struct_ptr_ty, field_index);
57445759 }
57455760
5746 fn airStructFieldVal(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
5761 fn airStructFieldVal(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value {
5762 const inst = body_tail[0];
57475763 if (self.liveness.isUnused(inst)) return null;
57485764
57495765 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
......@@ -5826,7 +5842,10 @@ pub const FuncGen = struct {
58265842 const llvm_field_ty = try self.dg.lowerType(field_ty);
58275843 const field_ptr = self.builder.buildBitCast(union_field_ptr, llvm_field_ty.pointerType(0), "");
58285844 if (isByRef(field_ty)) {
5829 return field_ptr;
5845 if (canElideLoad(self, body_tail))
5846 return field_ptr;
5847
5848 return self.loadByRef(field_ptr, field_ty, layout.payload_align, false);
58305849 } else {
58315850 return self.builder.buildLoad(llvm_field_ty, field_ptr, "");
58325851 }
......@@ -6516,7 +6535,8 @@ pub const FuncGen = struct {
65166535 return self.builder.buildStructGEP(optional_llvm_ty, operand, 0, "");
65176536 }
65186537
6519 fn airOptionalPayload(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
6538 fn airOptionalPayload(self: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value {
6539 const inst = body_tail[0];
65206540 if (self.liveness.isUnused(inst)) return null;
65216541
65226542 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
......@@ -6531,14 +6551,16 @@ pub const FuncGen = struct {
65316551 }
65326552
65336553 const opt_llvm_ty = try self.dg.lowerType(optional_ty);
6534 return self.optPayloadHandle(opt_llvm_ty, operand, optional_ty);
6554 const can_elide_load = if (isByRef(payload_ty)) self.canElideLoad(body_tail) else false;
6555 return self.optPayloadHandle(opt_llvm_ty, operand, optional_ty, can_elide_load);
65356556 }
65366557
65376558 fn airErrUnionPayload(
65386559 self: *FuncGen,
6539 inst: Air.Inst.Index,
6560 body_tail: []const Air.Inst.Index,
65406561 operand_is_ptr: bool,
65416562 ) !?*llvm.Value {
6563 const inst = body_tail[0];
65426564 if (self.liveness.isUnused(inst)) return null;
65436565
65446566 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
......@@ -6558,12 +6580,15 @@ pub const FuncGen = struct {
65586580 }
65596581 const offset = errUnionPayloadOffset(payload_ty, target);
65606582 const err_union_llvm_ty = try self.dg.lowerType(err_union_ty);
6561 if (operand_is_ptr or isByRef(payload_ty)) {
6583 if (operand_is_ptr) {
65626584 return self.builder.buildStructGEP(err_union_llvm_ty, operand, offset, "");
65636585 } else if (isByRef(err_union_ty)) {
65646586 const payload_ptr = self.builder.buildStructGEP(err_union_llvm_ty, operand, offset, "");
65656587 if (isByRef(payload_ty)) {
6566 return payload_ptr;
6588 if (self.canElideLoad(body_tail))
6589 return payload_ptr;
6590
6591 return self.loadByRef(payload_ptr, payload_ty, payload_ty.abiAlignment(target), false);
65676592 }
65686593 const load_inst = self.builder.buildLoad(payload_ptr.getGEPResultElementType(), payload_ptr, "");
65696594 load_inst.setAlignment(payload_ty.abiAlignment(target));
......@@ -8064,35 +8089,37 @@ pub const FuncGen = struct {
80648089 return null;
80658090 }
80668091
8067 fn airLoad(
8068 self: *FuncGen,
8069 inst: Air.Inst.Index,
8070 body: []const Air.Inst.Index,
8071 body_i: usize,
8072 ) !?*llvm.Value {
8073 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
8074 const ptr_ty = self.air.typeOf(ty_op.operand);
8092 /// As an optimization, we want to avoid unnecessary copies of isByRef=true
8093 /// types. Here, we scan forward in the current block, looking to see if
8094 /// this load dies before any side effects occur. In such case, we can
8095 /// safely return the operand without making a copy.
8096 ///
8097 /// The first instruction of `body_tail` is the one whose copy we want to elide.
8098 fn canElideLoad(fg: *FuncGen, body_tail: []const Air.Inst.Index) bool {
8099 for (body_tail[1..]) |body_inst| {
8100 switch (fg.liveness.categorizeOperand(fg.air, body_inst, body_tail[0])) {
8101 .none => continue,
8102 .write, .noret, .complex => return false,
8103 .tomb => return true,
8104 }
8105 } else unreachable;
8106 }
8107
8108 fn airLoad(fg: *FuncGen, body_tail: []const Air.Inst.Index) !?*llvm.Value {
8109 const inst = body_tail[0];
8110 const ty_op = fg.air.instructions.items(.data)[inst].ty_op;
8111 const ptr_ty = fg.air.typeOf(ty_op.operand);
8112 const ptr_info = ptr_ty.ptrInfo().data;
8113 const ptr = try fg.resolveInst(ty_op.operand);
8114
80758115 elide: {
8076 const ptr_info = ptr_ty.ptrInfo().data;
80778116 if (ptr_info.@"volatile") break :elide;
8078 if (self.liveness.isUnused(inst)) return null;
8117 if (fg.liveness.isUnused(inst)) return null;
80798118 if (!isByRef(ptr_info.pointee_type)) break :elide;
8080
8081 // It would be valid to fall back to the code below here that simply calls
8082 // load(). However, as an optimization, we want to avoid unnecessary copies
8083 // of isByRef=true types. Here, we scan forward in the current block,
8084 // looking to see if this load dies before any side effects occur.
8085 // In such case, we can safely return the operand without making a copy.
8086 for (body[body_i..]) |body_inst| {
8087 switch (self.liveness.categorizeOperand(self.air, body_inst, inst)) {
8088 .none => continue,
8089 .write, .noret, .complex => break :elide,
8090 .tomb => return try self.resolveInst(ty_op.operand),
8091 }
8092 } else unreachable;
8119 if (!canElideLoad(fg, body_tail)) break :elide;
8120 return ptr;
80938121 }
8094 const ptr = try self.resolveInst(ty_op.operand);
8095 return self.load(ptr, ptr_ty);
8122 return fg.load(ptr, ptr_ty);
80968123 }
80978124
80988125 fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
......@@ -9412,6 +9439,7 @@ pub const FuncGen = struct {
94129439 opt_llvm_ty: *llvm.Type,
94139440 opt_handle: *llvm.Value,
94149441 opt_ty: Type,
9442 can_elide_load: bool,
94159443 ) !*llvm.Value {
94169444 var buf: Type.Payload.ElemType = undefined;
94179445 const payload_ty = opt_ty.optionalChild(&buf);
......@@ -9420,11 +9448,14 @@ pub const FuncGen = struct {
94209448 // We have a pointer and we need to return a pointer to the first field.
94219449 const payload_ptr = fg.builder.buildStructGEP(opt_llvm_ty, opt_handle, 0, "");
94229450
9423 if (isByRef(payload_ty)) {
9424 return payload_ptr;
9425 }
94269451 const target = fg.dg.module.getTarget();
94279452 const payload_alignment = payload_ty.abiAlignment(target);
9453 if (isByRef(payload_ty)) {
9454 if (can_elide_load)
9455 return payload_ptr;
9456
9457 return fg.loadByRef(payload_ptr, payload_ty, payload_alignment, false);
9458 }
94289459 const payload_llvm_ty = try fg.dg.lowerType(payload_ty);
94299460 const load_inst = fg.builder.buildLoad(payload_llvm_ty, payload_ptr, "");
94309461 load_inst.setAlignment(payload_alignment);
......@@ -9559,6 +9590,32 @@ pub const FuncGen = struct {
95599590 return self.llvmModule().getIntrinsicDeclaration(id, types.ptr, types.len);
95609591 }
95619592
9593 /// Load a by-ref type by constructing a new alloca and performing a memcpy.
9594 fn loadByRef(
9595 fg: *FuncGen,
9596 ptr: *llvm.Value,
9597 pointee_type: Type,
9598 ptr_alignment: u32,
9599 is_volatile: bool,
9600 ) !*llvm.Value {
9601 const pointee_llvm_ty = try fg.dg.lowerType(pointee_type);
9602 const target = fg.dg.module.getTarget();
9603 const result_align = @max(ptr_alignment, pointee_type.abiAlignment(target));
9604 const result_ptr = fg.buildAlloca(pointee_llvm_ty, result_align);
9605 const llvm_ptr_u8 = fg.context.intType(8).pointerType(0);
9606 const llvm_usize = fg.context.intType(Type.usize.intInfo(target).bits);
9607 const size_bytes = pointee_type.abiSize(target);
9608 _ = fg.builder.buildMemCpy(
9609 fg.builder.buildBitCast(result_ptr, llvm_ptr_u8, ""),
9610 result_align,
9611 fg.builder.buildBitCast(ptr, llvm_ptr_u8, ""),
9612 ptr_alignment,
9613 llvm_usize.constInt(size_bytes, .False),
9614 is_volatile,
9615 );
9616 return result_ptr;
9617 }
9618
95629619 /// This function always performs a copy. For isByRef=true types, it creates a new
95639620 /// alloca and copies the value into it, then returns the alloca instruction.
95649621 /// For isByRef=false types, it creates a load instruction and returns it.
......@@ -9570,24 +9627,10 @@ pub const FuncGen = struct {
95709627 const ptr_alignment = info.alignment(target);
95719628 const ptr_volatile = llvm.Bool.fromBool(ptr_ty.isVolatilePtr());
95729629 if (info.host_size == 0) {
9573 const elem_llvm_ty = try self.dg.lowerType(info.pointee_type);
95749630 if (isByRef(info.pointee_type)) {
9575 const result_align = info.pointee_type.abiAlignment(target);
9576 const max_align = @max(result_align, ptr_alignment);
9577 const result_ptr = self.buildAlloca(elem_llvm_ty, max_align);
9578 const llvm_ptr_u8 = self.context.intType(8).pointerType(0);
9579 const llvm_usize = self.context.intType(Type.usize.intInfo(target).bits);
9580 const size_bytes = info.pointee_type.abiSize(target);
9581 _ = self.builder.buildMemCpy(
9582 self.builder.buildBitCast(result_ptr, llvm_ptr_u8, ""),
9583 max_align,
9584 self.builder.buildBitCast(ptr, llvm_ptr_u8, ""),
9585 max_align,
9586 llvm_usize.constInt(size_bytes, .False),
9587 info.@"volatile",
9588 );
9589 return result_ptr;
9631 return self.loadByRef(ptr, info.pointee_type, ptr_alignment, info.@"volatile");
95909632 }
9633 const elem_llvm_ty = try self.dg.lowerType(info.pointee_type);
95919634 const llvm_inst = self.builder.buildLoad(elem_llvm_ty, ptr, "");
95929635 llvm_inst.setAlignment(ptr_alignment);
95939636 llvm_inst.setVolatile(ptr_volatile);
test/behavior.zig+3
......@@ -104,7 +104,10 @@ test {
104104 _ = @import("behavior/bugs/12945.zig");
105105 _ = @import("behavior/bugs/12972.zig");
106106 _ = @import("behavior/bugs/12984.zig");
107 _ = @import("behavior/bugs/13064.zig");
108 _ = @import("behavior/bugs/13065.zig");
107109 _ = @import("behavior/bugs/13068.zig");
110 _ = @import("behavior/bugs/13069.zig");
108111 _ = @import("behavior/bugs/13112.zig");
109112 _ = @import("behavior/bugs/13128.zig");
110113 _ = @import("behavior/bugs/13164.zig");
test/behavior/bugs/13064.zig created+17
......@@ -0,0 +1,17 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const expect = std.testing.expect;
4
5test {
6 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
7 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
8 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
9
10 var x: [10][10]u32 = undefined;
11
12 x[0][1] = 0;
13 const a = x[0];
14 x[0][1] = 15;
15
16 try expect(a[1] == 0);
17}
test/behavior/bugs/13065.zig created+22
......@@ -0,0 +1,22 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const expect = std.testing.expect;
4
5const U = union(enum) {
6 array: [10]u32,
7 other: u32,
8};
9
10test {
11 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
12 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
13 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
14
15 var x = U{ .array = undefined };
16
17 x.array[1] = 0;
18 const a = x.array;
19 x.array[1] = 15;
20
21 try expect(a[1] == 0);
22}
test/behavior/bugs/13069.zig created+17
......@@ -0,0 +1,17 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const expect = std.testing.expect;
4
5test {
6 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
7 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
8 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
10
11 var opt_x: ?[3]f32 = [_]f32{0.0} ** 3;
12
13 const x = opt_x.?;
14 opt_x.?[0] = 15.0;
15
16 try expect(x[0] == 0.0);
17}