authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-02-18 15:34:00-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-02-18 19:20:19-07:00
log74db8c2e8348cb7e9bf264294f73d26be956ea61
tree19e4cf466b86153493adfb8eeded5a16f0db323f
parent7abeb52abc9e9e38d8af4e17e25e89083cdec397

omit safety checks for element access in for loops

One of the main points of for loops is that you can safety check the length once, before entering the loop, and then safely assume that every element inside the loop is in bounds. In master branch, the safety checks are incorrectly intact even inside for loops. This commit fixes it. It's especially nice with multi-object loops because the number of elided checks is N * M where N is how many iterations and M is how many objects.

3 files changed, 60 insertions(+), 51 deletions(-)

src/Sema.zig+52-49
......@@ -4544,7 +4544,7 @@ fn zirValidateArrayInit(
45444544 // any ZIR instructions at comptime; we need to do that here.
45454545 if (array_ty.sentinel()) |sentinel_val| {
45464546 const array_len_ref = try sema.addIntUnsigned(Type.usize, array_len);
4547 const sentinel_ptr = try sema.elemPtrArray(block, init_src, init_src, array_ptr, init_src, array_len_ref, true);
4547 const sentinel_ptr = try sema.elemPtrArray(block, init_src, init_src, array_ptr, init_src, array_len_ref, true, true);
45484548 const sentinel = try sema.addConstant(array_ty.childType(), sentinel_val);
45494549 try sema.storePtr2(block, init_src, sentinel_ptr, init_src, sentinel, init_src, .store);
45504550 }
......@@ -9691,7 +9691,7 @@ fn zirElemVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
96919691 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
96929692 const array = try sema.resolveInst(extra.lhs);
96939693 const elem_index = try sema.resolveInst(extra.rhs);
9694 return sema.elemVal(block, src, array, elem_index, src);
9694 return sema.elemVal(block, src, array, elem_index, src, false);
96959695}
96969696
96979697fn zirElemValNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -9704,7 +9704,7 @@ fn zirElemValNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
97049704 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
97059705 const array = try sema.resolveInst(extra.lhs);
97069706 const elem_index = try sema.resolveInst(extra.rhs);
9707 return sema.elemVal(block, src, array, elem_index, elem_index_src);
9707 return sema.elemVal(block, src, array, elem_index, elem_index_src, true);
97089708}
97099709
97109710fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -9731,7 +9731,7 @@ fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
97319731 };
97329732 return sema.failWithOwnedErrorMsg(msg);
97339733 }
9734 return sema.elemPtrOneLayerOnly(block, src, array_ptr, elem_index, src, false);
9734 return sema.elemPtrOneLayerOnly(block, src, array_ptr, elem_index, src, false, false);
97359735}
97369736
97379737fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -9744,7 +9744,7 @@ fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
97449744 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
97459745 const array_ptr = try sema.resolveInst(extra.lhs);
97469746 const elem_index = try sema.resolveInst(extra.rhs);
9747 return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src, false);
9747 return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src, false, true);
97489748}
97499749
97509750fn zirElemPtrImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -9756,7 +9756,7 @@ fn zirElemPtrImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
97569756 const extra = sema.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data;
97579757 const array_ptr = try sema.resolveInst(extra.ptr);
97589758 const elem_index = try sema.addIntUnsigned(Type.usize, extra.index);
9759 return sema.elemPtr(block, src, array_ptr, elem_index, src, true);
9759 return sema.elemPtr(block, src, array_ptr, elem_index, src, true, true);
97609760}
97619761
97629762fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -12521,14 +12521,14 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1252112521 while (elem_i < lhs_len) : (elem_i += 1) {
1252212522 const elem_index = try sema.addIntUnsigned(Type.usize, elem_i);
1252312523 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
12524 const init = try sema.elemVal(block, lhs_src, lhs, elem_index, src);
12524 const init = try sema.elemVal(block, lhs_src, lhs, elem_index, src, true);
1252512525 try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
1252612526 }
1252712527 while (elem_i < result_len) : (elem_i += 1) {
1252812528 const elem_index = try sema.addIntUnsigned(Type.usize, elem_i);
1252912529 const rhs_index = try sema.addIntUnsigned(Type.usize, elem_i - lhs_len);
1253012530 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
12531 const init = try sema.elemVal(block, rhs_src, rhs, rhs_index, src);
12531 const init = try sema.elemVal(block, rhs_src, rhs, rhs_index, src, true);
1253212532 try sema.storePtr2(block, src, elem_ptr, src, init, rhs_src, .store);
1253312533 }
1253412534 if (res_sent_val) |sent_val| {
......@@ -12546,12 +12546,12 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1254612546 var elem_i: usize = 0;
1254712547 while (elem_i < lhs_len) : (elem_i += 1) {
1254812548 const index = try sema.addIntUnsigned(Type.usize, elem_i);
12549 const init = try sema.elemVal(block, lhs_src, lhs, index, src);
12549 const init = try sema.elemVal(block, lhs_src, lhs, index, src, true);
1255012550 element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, lhs_src);
1255112551 }
1255212552 while (elem_i < result_len) : (elem_i += 1) {
1255312553 const index = try sema.addIntUnsigned(Type.usize, elem_i - lhs_len);
12554 const init = try sema.elemVal(block, rhs_src, rhs, index, src);
12554 const init = try sema.elemVal(block, rhs_src, rhs, index, src, true);
1255512555 element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, rhs_src);
1255612556 }
1255712557 }
......@@ -12771,7 +12771,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1277112771 elem_i += 1;
1277212772 const lhs_index = try sema.addIntUnsigned(Type.usize, lhs_i);
1277312773 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
12774 const init = try sema.elemVal(block, lhs_src, lhs, lhs_index, src);
12774 const init = try sema.elemVal(block, lhs_src, lhs, lhs_index, src, true);
1277512775 try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
1277612776 }
1277712777 }
......@@ -12791,7 +12791,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1279112791 var lhs_i: usize = 0;
1279212792 while (lhs_i < lhs_len) : (lhs_i += 1) {
1279312793 const lhs_index = try sema.addIntUnsigned(Type.usize, lhs_i);
12794 const init = try sema.elemVal(block, lhs_src, lhs, lhs_index, src);
12794 const init = try sema.elemVal(block, lhs_src, lhs, lhs_index, src, true);
1279512795 element_refs[elem_i] = init;
1279612796 elem_i += 1;
1279712797 }
......@@ -24145,6 +24145,7 @@ fn elemPtr(
2414524145 elem_index: Air.Inst.Ref,
2414624146 elem_index_src: LazySrcLoc,
2414724147 init: bool,
24148 oob_safety: bool,
2414824149) CompileError!Air.Inst.Ref {
2414924150 const indexable_ptr_src = src; // TODO better source location
2415024151 const indexable_ptr_ty = sema.typeOf(indexable_ptr);
......@@ -24154,7 +24155,7 @@ fn elemPtr(
2415424155 else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty.fmt(sema.mod)}),
2415524156 };
2415624157 switch (indexable_ty.zigTypeTag()) {
24157 .Array, .Vector => return sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init),
24158 .Array, .Vector => return sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety),
2415824159 .Struct => {
2415924160 // Tuple field access.
2416024161 const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime-known");
......@@ -24163,11 +24164,12 @@ fn elemPtr(
2416324164 },
2416424165 else => {
2416524166 const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);
24166 return elemPtrOneLayerOnly(sema, block, src, indexable, elem_index, elem_index_src, init);
24167 return elemPtrOneLayerOnly(sema, block, src, indexable, elem_index, elem_index_src, init, oob_safety);
2416724168 },
2416824169 }
2416924170}
2417024171
24172/// Asserts that the type of indexable is pointer.
2417124173fn elemPtrOneLayerOnly(
2417224174 sema: *Sema,
2417324175 block: *Block,
......@@ -24176,6 +24178,7 @@ fn elemPtrOneLayerOnly(
2417624178 elem_index: Air.Inst.Ref,
2417724179 elem_index_src: LazySrcLoc,
2417824180 init: bool,
24181 oob_safety: bool,
2417924182) CompileError!Air.Inst.Ref {
2418024183 const indexable_src = src; // TODO better source location
2418124184 const indexable_ty = sema.typeOf(indexable);
......@@ -24184,33 +24187,28 @@ fn elemPtrOneLayerOnly(
2418424187 }
2418524188 const target = sema.mod.getTarget();
2418624189
24187 switch (indexable_ty.zigTypeTag()) {
24188 .Pointer => {
24189 switch (indexable_ty.ptrSize()) {
24190 .Slice => return sema.elemPtrSlice(block, src, indexable_src, indexable, elem_index_src, elem_index),
24191 .Many, .C => {
24192 const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
24193 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
24194 const runtime_src = rs: {
24195 const ptr_val = maybe_ptr_val orelse break :rs indexable_src;
24196 const index_val = maybe_index_val orelse break :rs elem_index_src;
24197 const index = @intCast(usize, index_val.toUnsignedInt(target));
24198 const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index, sema.mod);
24199 const result_ty = try sema.elemPtrType(indexable_ty, index);
24200 return sema.addConstant(result_ty, elem_ptr);
24201 };
24202 const result_ty = try sema.elemPtrType(indexable_ty, null);
24190 switch (indexable_ty.ptrSize()) {
24191 .Slice => return sema.elemPtrSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
24192 .Many, .C => {
24193 const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
24194 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
24195 const runtime_src = rs: {
24196 const ptr_val = maybe_ptr_val orelse break :rs indexable_src;
24197 const index_val = maybe_index_val orelse break :rs elem_index_src;
24198 const index = @intCast(usize, index_val.toUnsignedInt(target));
24199 const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index, sema.mod);
24200 const result_ty = try sema.elemPtrType(indexable_ty, index);
24201 return sema.addConstant(result_ty, elem_ptr);
24202 };
24203 const result_ty = try sema.elemPtrType(indexable_ty, null);
2420324204
24204 try sema.requireRuntimeBlock(block, src, runtime_src);
24205 return block.addPtrElemPtr(indexable, elem_index, result_ty);
24206 },
24207 .One => {
24208 assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable
24209 return sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init);
24210 },
24211 }
24205 try sema.requireRuntimeBlock(block, src, runtime_src);
24206 return block.addPtrElemPtr(indexable, elem_index, result_ty);
24207 },
24208 .One => {
24209 assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable
24210 return sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety);
2421224211 },
24213 else => unreachable,
2421424212 }
2421524213}
2421624214
......@@ -24221,6 +24219,7 @@ fn elemVal(
2422124219 indexable: Air.Inst.Ref,
2422224220 elem_index_uncasted: Air.Inst.Ref,
2422324221 elem_index_src: LazySrcLoc,
24222 oob_safety: bool,
2422424223) CompileError!Air.Inst.Ref {
2422524224 const indexable_src = src; // TODO better source location
2422624225 const indexable_ty = sema.typeOf(indexable);
......@@ -24236,7 +24235,7 @@ fn elemVal(
2423624235
2423724236 switch (indexable_ty.zigTypeTag()) {
2423824237 .Pointer => switch (indexable_ty.ptrSize()) {
24239 .Slice => return sema.elemValSlice(block, src, indexable_src, indexable, elem_index_src, elem_index),
24238 .Slice => return sema.elemValSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
2424024239 .Many, .C => {
2424124240 const maybe_indexable_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
2424224241 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
......@@ -24257,14 +24256,14 @@ fn elemVal(
2425724256 },
2425824257 .One => {
2425924258 assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable
24260 const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false);
24259 const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false, oob_safety);
2426124260 return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src);
2426224261 },
2426324262 },
24264 .Array => return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index),
24263 .Array => return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
2426524264 .Vector => {
2426624265 // TODO: If the index is a vector, the result should be a vector.
24267 return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index);
24266 return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety);
2426824267 },
2426924268 .Struct => {
2427024269 // Tuple field access.
......@@ -24409,6 +24408,7 @@ fn elemValArray(
2440924408 array: Air.Inst.Ref,
2441024409 elem_index_src: LazySrcLoc,
2441124410 elem_index: Air.Inst.Ref,
24411 oob_safety: bool,
2441224412) CompileError!Air.Inst.Ref {
2441324413 const array_ty = sema.typeOf(array);
2441424414 const array_sent = array_ty.sentinel();
......@@ -24452,7 +24452,7 @@ fn elemValArray(
2445224452
2445324453 const runtime_src = if (maybe_undef_array_val != null) elem_index_src else array_src;
2445424454 try sema.requireRuntimeBlock(block, src, runtime_src);
24455 if (block.wantSafety()) {
24455 if (oob_safety and block.wantSafety()) {
2445624456 // Runtime check is only needed if unable to comptime check
2445724457 if (maybe_index_val == null) {
2445824458 const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
......@@ -24472,6 +24472,7 @@ fn elemPtrArray(
2447224472 elem_index_src: LazySrcLoc,
2447324473 elem_index: Air.Inst.Ref,
2447424474 init: bool,
24475 oob_safety: bool,
2447524476) CompileError!Air.Inst.Ref {
2447624477 const target = sema.mod.getTarget();
2447724478 const array_ptr_ty = sema.typeOf(array_ptr);
......@@ -24515,7 +24516,7 @@ fn elemPtrArray(
2451524516 try sema.requireRuntimeBlock(block, src, runtime_src);
2451624517
2451724518 // Runtime check is only needed if unable to comptime check.
24518 if (block.wantSafety() and offset == null) {
24519 if (oob_safety and block.wantSafety() and offset == null) {
2451924520 const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
2452024521 const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
2452124522 try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
......@@ -24532,6 +24533,7 @@ fn elemValSlice(
2453224533 slice: Air.Inst.Ref,
2453324534 elem_index_src: LazySrcLoc,
2453424535 elem_index: Air.Inst.Ref,
24536 oob_safety: bool,
2453524537) CompileError!Air.Inst.Ref {
2453624538 const slice_ty = sema.typeOf(slice);
2453724539 const slice_sent = slice_ty.sentinel() != null;
......@@ -24568,7 +24570,7 @@ fn elemValSlice(
2456824570 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, slice_ty, slice_src);
2456924571
2457024572 try sema.requireRuntimeBlock(block, src, runtime_src);
24571 if (block.wantSafety()) {
24573 if (oob_safety and block.wantSafety()) {
2457224574 const len_inst = if (maybe_slice_val) |slice_val|
2457324575 try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(sema.mod))
2457424576 else
......@@ -24588,6 +24590,7 @@ fn elemPtrSlice(
2458824590 slice: Air.Inst.Ref,
2458924591 elem_index_src: LazySrcLoc,
2459024592 elem_index: Air.Inst.Ref,
24593 oob_safety: bool,
2459124594) CompileError!Air.Inst.Ref {
2459224595 const target = sema.mod.getTarget();
2459324596 const slice_ty = sema.typeOf(slice);
......@@ -24625,7 +24628,7 @@ fn elemPtrSlice(
2462524628
2462624629 const runtime_src = if (maybe_undef_slice_val != null) elem_index_src else slice_src;
2462724630 try sema.requireRuntimeBlock(block, src, runtime_src);
24628 if (block.wantSafety()) {
24631 if (oob_safety and block.wantSafety()) {
2462924632 const len_inst = len: {
2463024633 if (maybe_undef_slice_val) |slice_val|
2463124634 if (!slice_val.isUndef())
......@@ -26330,7 +26333,7 @@ fn storePtr2(
2633026333 const elem_src = operand_src; // TODO better source location
2633126334 const elem = try sema.tupleField(block, operand_src, uncasted_operand, elem_src, i);
2633226335 const elem_index = try sema.addIntUnsigned(Type.usize, i);
26333 const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src, false);
26336 const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src, false, true);
2633426337 try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store);
2633526338 }
2633626339 return;
......@@ -27782,7 +27785,7 @@ fn coerceArrayLike(
2778227785 );
2778327786 const src = inst_src; // TODO better source location
2778427787 const elem_src = inst_src; // TODO better source location
27785 const elem_ref = try sema.elemValArray(block, src, inst_src, inst, elem_src, index_ref);
27788 const elem_ref = try sema.elemValArray(block, src, inst_src, inst, elem_src, index_ref, true);
2778627789 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
2778727790 element_refs[i] = coerced;
2778827791 if (runtime_src == null) {
src/Zir.zig+6-2
......@@ -382,7 +382,9 @@ pub const Inst = struct {
382382 /// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`.
383383 elem_ptr_node,
384384 /// Same as `elem_ptr_node` but used only for for loop.
385 /// Uses the `pl_node` union field. AST node is the condition of a for loop. Payload is `Bin`.
385 /// Uses the `pl_node` union field. AST node is the condition of a for loop.
386 /// Payload is `Bin`.
387 /// No OOB safety check is emitted.
386388 elem_ptr,
387389 /// Same as `elem_ptr_node` except the index is stored immediately rather than
388390 /// as a reference to another ZIR instruction.
......@@ -395,7 +397,9 @@ pub const Inst = struct {
395397 /// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`.
396398 elem_val_node,
397399 /// Same as `elem_val_node` but used only for for loop.
398 /// Uses the `pl_node` union field. AST node is the condition of a for loop. Payload is `Bin`.
400 /// Uses the `pl_node` union field. AST node is the condition of a for loop.
401 /// Payload is `Bin`.
402 /// No OOB safety check is emitted.
399403 elem_val,
400404 /// Emits a compile error if the operand is not `void`.
401405 /// Uses the `un_node` field.
test/behavior/for.zig+2
......@@ -314,6 +314,7 @@ test "slice and two counters, one is offset and one is runtime" {
314314 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
315315 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
316316 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
317 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
317318
318319 const slice: []const u8 = "blah";
319320 var start: usize = 0;
......@@ -342,6 +343,7 @@ test "two slices, one captured by-ref" {
342343 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
343344 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
344345 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
346 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
345347
346348 var buf: [10]u8 = undefined;
347349 const slice1: []const u8 = "blah";