authorgravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-11-01 09:22:31-07:00
committergravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-11-10 12:22:37-07:00
logff699722da1f2df3e521c92cebe71c50910594d3
treef96c8f78322a864ade0c704dfcaf09c241bf919c
parent59af6417bbb93a2cca453d930320217a970040bd

stage2: Fix comptime array initialization

This is a follow-up to 9dc98fba, which made comptime initialization patterns for union/struct more robust, especially when storing to comptime-known pointers (and globals). Resolves #13063.

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

src/Sema.zig+51-60
...@@ -4164,6 +4164,7 @@ fn validateStructInit(...@@ -4164,6 +4164,7 @@ fn validateStructInit(
4164 // We expect to see something like this in the current block AIR:4164 // We expect to see something like this in the current block AIR:
4165 // %a = field_ptr(...)4165 // %a = field_ptr(...)
4166 // store(%a, %b)4166 // store(%a, %b)
4167 // With an optional bitcast between the store and the field_ptr.
4167 // If %b is a comptime operand, this field is comptime.4168 // If %b is a comptime operand, this field is comptime.
4168 //4169 //
4169 // However, in the case of a comptime-known pointer to a struct, the4170 // However, in the case of a comptime-known pointer to a struct, the
...@@ -4374,75 +4375,65 @@ fn zirValidateArrayInit(...@@ -4374,75 +4375,65 @@ fn zirValidateArrayInit(
43744375
4375 const elem_ptr_air_ref = sema.inst_map.get(elem_ptr).?;4376 const elem_ptr_air_ref = sema.inst_map.get(elem_ptr).?;
4376 const elem_ptr_air_inst = Air.refToIndex(elem_ptr_air_ref).?;4377 const elem_ptr_air_inst = Air.refToIndex(elem_ptr_air_ref).?;
4377 // Find the block index of the elem_ptr so that we can look at the next4378
4378 // instruction after it within the same block.4379 // We expect to see something like this in the current block AIR:
4380 // %a = elem_ptr(...)
4381 // store(%a, %b)
4382 // With an optional bitcast between the store and the elem_ptr.
4383 // If %b is a comptime operand, this element is comptime.
4384 //
4385 // However, in the case of a comptime-known pointer to an array, the
4386 // the elem_ptr instruction is missing, so we have to pattern-match
4387 // based only on the store instructions.
4388 // `first_block_index` needs to point to the `elem_ptr` if it exists;
4389 // the `store` otherwise.
4390 //
4391 // It's also possible for there to be no store instruction, in the case
4392 // of nested `coerce_result_ptr` instructions. If we see the `elem_ptr`
4393 // but we have not found a `store`, treat as a runtime-known element.
4394 //
4395 // This is nearly identical to similar logic in `validateStructInit`.
4396
4379 // Possible performance enhancement: save the `block_index` between iterations4397 // Possible performance enhancement: save the `block_index` between iterations
4380 // of the for loop.4398 // of the for loop.
4381 var block_index = block.instructions.items.len - 1;4399 var block_index = block.instructions.items.len - 1;
4382 while (block.instructions.items[block_index] != elem_ptr_air_inst) {4400 while (block_index > 0) : (block_index -= 1) {
4383 if (block_index == 0) {4401 const store_inst = block.instructions.items[block_index];
4402 if (store_inst == elem_ptr_air_inst) {
4384 array_is_comptime = false;4403 array_is_comptime = false;
4385 continue :outer;4404 continue :outer;
4386 }4405 }
4387 block_index -= 1;4406 if (air_tags[store_inst] != .store) continue;
4388 }4407 const bin_op = air_datas[store_inst].bin_op;
4389 first_block_index = @min(first_block_index, block_index);4408 var lhs = bin_op.lhs;
43904409 {
4391 // If the next instructon is a store with a comptime operand, this element4410 const lhs_index = Air.refToIndex(lhs) orelse continue;
4392 // is comptime.4411 if (air_tags[lhs_index] == .bitcast) {
4393 const next_air_inst = block.instructions.items[block_index + 1];4412 lhs = air_datas[lhs_index].ty_op.operand;
4394 switch (air_tags[next_air_inst]) {4413 block_index -= 1;
4395 .store => {
4396 const bin_op = air_datas[next_air_inst].bin_op;
4397 var lhs = bin_op.lhs;
4398 if (Air.refToIndex(lhs)) |lhs_index| {
4399 if (air_tags[lhs_index] == .bitcast) {
4400 lhs = air_datas[lhs_index].ty_op.operand;
4401 block_index -= 1;
4402 }
4403 }
4404 if (lhs != elem_ptr_air_ref) {
4405 array_is_comptime = false;
4406 continue;
4407 }
4408 if (try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(block, elem_src, bin_op.rhs, &make_runtime)) |val| {
4409 element_vals[i] = val;
4410 } else {
4411 array_is_comptime = false;
4412 }
4413 continue;
4414 },
4415 .bitcast => {
4416 // %a = bitcast(*arr_ty, %array_base)
4417 // %b = ptr_elem_ptr(%a, %index)
4418 // %c = bitcast(*elem_ty, %b)
4419 // %d = store(%c, %val)
4420 if (air_datas[next_air_inst].ty_op.operand != elem_ptr_air_ref) {
4421 array_is_comptime = false;
4422 continue;
4423 }
4424 const store_inst = block.instructions.items[block_index + 2];
4425 if (air_tags[store_inst] != .store) {
4426 array_is_comptime = false;
4427 continue;
4428 }
4429 const bin_op = air_datas[store_inst].bin_op;
4430 if (bin_op.lhs != Air.indexToRef(next_air_inst)) {
4431 array_is_comptime = false;
4432 continue;
4433 }
4434 if (try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(block, elem_src, bin_op.rhs, &make_runtime)) |val| {
4435 element_vals[i] = val;
4436 } else {
4437 array_is_comptime = false;
4438 }4414 }
4439 continue;4415 }
4440 },4416 if (lhs != elem_ptr_air_ref) continue;
4441 else => {4417 while (block_index > 0) : (block_index -= 1) {
4418 const block_inst = block.instructions.items[block_index - 1];
4419 if (air_tags[block_inst] != .dbg_stmt) break;
4420 }
4421 if (block_index > 0 and
4422 elem_ptr_air_inst == block.instructions.items[block_index - 1])
4423 {
4424 first_block_index = @min(first_block_index, block_index - 1);
4425 } else {
4426 first_block_index = @min(first_block_index, block_index);
4427 }
4428 if (try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(block, elem_src, bin_op.rhs, &make_runtime)) |val| {
4429 element_vals[i] = val;
4430 } else {
4442 array_is_comptime = false;4431 array_is_comptime = false;
4443 continue;4432 }
4444 },4433 continue :outer;
4445 }4434 }
4435 array_is_comptime = false;
4436 continue :outer;
4446 }4437 }
44474438
4448 if (array_is_comptime) {4439 if (array_is_comptime) {
test/behavior.zig+1
...@@ -210,6 +210,7 @@ test {...@@ -210,6 +210,7 @@ test {
210 builtin.zig_backend != .stage2_wasm and210 builtin.zig_backend != .stage2_wasm and
211 builtin.zig_backend != .stage2_c)211 builtin.zig_backend != .stage2_c)
212 {212 {
213 _ = @import("behavior/bugs/13063.zig");
213 _ = @import("behavior/bugs/11227.zig");214 _ = @import("behavior/bugs/11227.zig");
214 _ = @import("behavior/export.zig");215 _ = @import("behavior/export.zig");
215 }216 }
test/behavior/bugs/13063.zig created+16
...@@ -0,0 +1,16 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4var pos = [2]f32{ 0.0, 0.0 };
5test "store to global array" {
6 try expect(pos[1] == 0.0);
7 pos = [2]f32{ 0.0, 1.0 };
8 try expect(pos[1] == 1.0);
9}
10
11var vpos = @Vector(2, f32){ 0.0, 0.0 };
12test "store to global vector" {
13 try expect(vpos[1] == 0.0);
14 vpos = @Vector(2, f32){ 0.0, 1.0 };
15 try expect(vpos[1] == 1.0);
16}