authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-06-01 15:43:21-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-06-01 15:43:21-07:00
logb82cccc9e9b2230097f81fecec12ac0fdae97518
treece1cf7a47b20f4dc2fc1c2d9f1033b396027a555
parentb095aa6986badc3b8c2255ad2c824ca4ea9959d9

Sema: fix alignment of element ptr result type


3 files changed, 81 insertions(+), 52 deletions(-)

src/Sema.zig+66-23
...@@ -10938,6 +10938,7 @@ fn analyzePtrArithmetic(...@@ -10938,6 +10938,7 @@ fn analyzePtrArithmetic(
1093810938
10939 const new_ptr_ty = t: {10939 const new_ptr_ty = t: {
10940 // Calculate the new pointer alignment.10940 // Calculate the new pointer alignment.
10941 // This code is duplicated in `elemPtrType`.
10941 if (ptr_info.@"align" == 0) {10942 if (ptr_info.@"align" == 0) {
10942 // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness.10943 // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness.
10943 break :t ptr_ty;10944 break :t ptr_ty;
...@@ -18617,20 +18618,20 @@ fn elemPtr(...@@ -18617,20 +18618,20 @@ fn elemPtr(
18617 .Pointer => {18618 .Pointer => {
18618 // In all below cases, we have to deref the ptr operand to get the actual indexable pointer.18619 // In all below cases, we have to deref the ptr operand to get the actual indexable pointer.
18619 const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);18620 const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);
18620 const result_ty = try indexable_ty.elemPtrType(sema.arena, sema.mod);
18621 switch (indexable_ty.ptrSize()) {18621 switch (indexable_ty.ptrSize()) {
18622 .Slice => return sema.elemPtrSlice(block, indexable_ptr_src, indexable, elem_index_src, elem_index),18622 .Slice => return sema.elemPtrSlice(block, indexable_ptr_src, indexable, elem_index_src, elem_index),
18623 .Many, .C => {18623 .Many, .C => {
18624 const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_ptr_src, indexable);18624 const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_ptr_src, indexable);
18625 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);18625 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
18626
18627 const runtime_src = rs: {18626 const runtime_src = rs: {
18628 const ptr_val = maybe_ptr_val orelse break :rs indexable_ptr_src;18627 const ptr_val = maybe_ptr_val orelse break :rs indexable_ptr_src;
18629 const index_val = maybe_index_val orelse break :rs elem_index_src;18628 const index_val = maybe_index_val orelse break :rs elem_index_src;
18630 const index = @intCast(usize, index_val.toUnsignedInt(target));18629 const index = @intCast(usize, index_val.toUnsignedInt(target));
18631 const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index, sema.mod);18630 const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index, sema.mod);
18631 const result_ty = try sema.elemPtrType(indexable_ty, index);
18632 return sema.addConstant(result_ty, elem_ptr);18632 return sema.addConstant(result_ty, elem_ptr);
18633 };18633 };
18634 const result_ty = try sema.elemPtrType(indexable_ty, null);
1863418635
18635 try sema.requireRuntimeBlock(block, runtime_src);18636 try sema.requireRuntimeBlock(block, runtime_src);
18636 return block.addPtrElemPtr(indexable, elem_index, result_ty);18637 return block.addPtrElemPtr(indexable, elem_index, result_ty);
...@@ -18883,29 +18884,29 @@ fn elemPtrArray(...@@ -18883,29 +18884,29 @@ fn elemPtrArray(
18883 const array_sent = array_ty.sentinel() != null;18884 const array_sent = array_ty.sentinel() != null;
18884 const array_len = array_ty.arrayLen();18885 const array_len = array_ty.arrayLen();
18885 const array_len_s = array_len + @boolToInt(array_sent);18886 const array_len_s = array_len + @boolToInt(array_sent);
18886 const elem_ptr_ty = try array_ptr_ty.elemPtrType(sema.arena, sema.mod);
1888718887
18888 if (array_len_s == 0) {18888 if (array_len_s == 0) {
18889 return sema.fail(block, elem_index_src, "indexing into empty array", .{});18889 return sema.fail(block, elem_index_src, "indexing into empty array", .{});
18890 }18890 }
1889118891
18892 const maybe_undef_array_ptr_val = try sema.resolveMaybeUndefVal(block, array_ptr_src, array_ptr);18892 const maybe_undef_array_ptr_val = try sema.resolveMaybeUndefVal(block, array_ptr_src, array_ptr);
18893 // index must be defined since it can index out of bounds18893 // The index must not be undefined since it can be out of bounds.
18894 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);18894 const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
1889518895 const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(target));
18896 if (maybe_index_val) |index_val| {
18897 const index = @intCast(usize, index_val.toUnsignedInt(target));
18898 if (index >= array_len_s) {18896 if (index >= array_len_s) {
18899 const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else "";18897 const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else "";
18900 return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });18898 return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
18901 }18899 }
18902 }18900 break :o index;
18901 } else null;
18902
18903 const elem_ptr_ty = try sema.elemPtrType(array_ptr_ty, offset);
18904
18903 if (maybe_undef_array_ptr_val) |array_ptr_val| {18905 if (maybe_undef_array_ptr_val) |array_ptr_val| {
18904 if (array_ptr_val.isUndef()) {18906 if (array_ptr_val.isUndef()) {
18905 return sema.addConstUndef(elem_ptr_ty);18907 return sema.addConstUndef(elem_ptr_ty);
18906 }18908 }
18907 if (maybe_index_val) |index_val| {18909 if (offset) |index| {
18908 const index = @intCast(usize, index_val.toUnsignedInt(target));
18909 const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, index, sema.mod);18910 const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, index, sema.mod);
18910 return sema.addConstant(elem_ptr_ty, elem_ptr);18911 return sema.addConstant(elem_ptr_ty, elem_ptr);
18911 }18912 }
...@@ -18932,14 +18933,14 @@ fn elemPtrArray(...@@ -18932,14 +18933,14 @@ fn elemPtrArray(
1893218933
18933 const runtime_src = if (maybe_undef_array_ptr_val != null) elem_index_src else array_ptr_src;18934 const runtime_src = if (maybe_undef_array_ptr_val != null) elem_index_src else array_ptr_src;
18934 try sema.requireRuntimeBlock(block, runtime_src);18935 try sema.requireRuntimeBlock(block, runtime_src);
18935 if (block.wantSafety()) {18936
18936 // Runtime check is only needed if unable to comptime check18937 // Runtime check is only needed if unable to comptime check.
18937 if (maybe_index_val == null) {18938 if (block.wantSafety() and offset == null) {
18938 const len_inst = try sema.addIntUnsigned(Type.usize, array_len);18939 const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
18939 const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;18940 const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
18940 try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);18941 try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
18941 }
18942 }18942 }
18943
18943 return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty);18944 return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty);
18944}18945}
1894518946
...@@ -19007,11 +19008,15 @@ fn elemPtrSlice(...@@ -19007,11 +19008,15 @@ fn elemPtrSlice(
19007 const target = sema.mod.getTarget();19008 const target = sema.mod.getTarget();
19008 const slice_ty = sema.typeOf(slice);19009 const slice_ty = sema.typeOf(slice);
19009 const slice_sent = slice_ty.sentinel() != null;19010 const slice_sent = slice_ty.sentinel() != null;
19010 const elem_ptr_ty = try slice_ty.elemPtrType(sema.arena, sema.mod);
1901119011
19012 const maybe_undef_slice_val = try sema.resolveMaybeUndefVal(block, slice_src, slice);19012 const maybe_undef_slice_val = try sema.resolveMaybeUndefVal(block, slice_src, slice);
19013 // index must be defined since it can index out of bounds19013 // The index must not be undefined since it can be out of bounds.
19014 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);19014 const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
19015 const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(target));
19016 break :o index;
19017 } else null;
19018
19019 const elem_ptr_ty = try sema.elemPtrType(slice_ty, null);
1901519020
19016 if (maybe_undef_slice_val) |slice_val| {19021 if (maybe_undef_slice_val) |slice_val| {
19017 if (slice_val.isUndef()) {19022 if (slice_val.isUndef()) {
...@@ -19022,8 +19027,7 @@ fn elemPtrSlice(...@@ -19022,8 +19027,7 @@ fn elemPtrSlice(
19022 if (slice_len_s == 0) {19027 if (slice_len_s == 0) {
19023 return sema.fail(block, elem_index_src, "indexing into empty slice", .{});19028 return sema.fail(block, elem_index_src, "indexing into empty slice", .{});
19024 }19029 }
19025 if (maybe_index_val) |index_val| {19030 if (offset) |index| {
19026 const index = @intCast(usize, index_val.toUnsignedInt(target));
19027 if (index >= slice_len_s) {19031 if (index >= slice_len_s) {
19028 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";19032 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
19029 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });19033 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
...@@ -25364,3 +25368,42 @@ fn compareVector(...@@ -25364,3 +25368,42 @@ fn compareVector(
25364 }25368 }
25365 return Value.Tag.aggregate.create(sema.arena, result_data);25369 return Value.Tag.aggregate.create(sema.arena, result_data);
25366}25370}
25371
25372/// Returns the type of a pointer to an element.
25373/// Asserts that the type is a pointer, and that the element type is indexable.
25374/// For *[N]T, return *T
25375/// For [*]T, returns *T
25376/// For []T, returns *T
25377/// Handles const-ness and address spaces in particular.
25378/// This code is duplicated in `analyzePtrArithmetic`.
25379fn elemPtrType(sema: *Sema, ptr_ty: Type, offset: ?usize) !Type {
25380 const ptr_info = ptr_ty.ptrInfo().data;
25381 const elem_ty = ptr_ty.elemType2();
25382 const allow_zero = ptr_info.@"allowzero" and (offset orelse 0) == 0;
25383 const alignment: u32 = a: {
25384 // Calculate the new pointer alignment.
25385 if (ptr_info.@"align" == 0) {
25386 // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness.
25387 break :a 0;
25388 }
25389 // If the addend is not a comptime-known value we can still count on
25390 // it being a multiple of the type size.
25391 const target = sema.mod.getTarget();
25392 const elem_size = elem_ty.abiSize(target);
25393 const addend = if (offset) |off| elem_size * off else elem_size;
25394
25395 // The resulting pointer is aligned to the lcd between the offset (an
25396 // arbitrary number) and the alignment factor (always a power of two,
25397 // non zero).
25398 const new_align = @as(u32, 1) << @intCast(u5, @ctz(u64, addend | ptr_info.@"align"));
25399 break :a new_align;
25400 };
25401 return try Type.ptr(sema.arena, sema.mod, .{
25402 .pointee_type = elem_ty,
25403 .mutable = ptr_info.mutable,
25404 .@"addrspace" = ptr_info.@"addrspace",
25405 .@"allowzero" = allow_zero,
25406 .@"volatile" = ptr_info.@"volatile",
25407 .@"align" = alignment,
25408 });
25409}
src/type.zig-14
...@@ -4177,20 +4177,6 @@ pub const Type = extern union {...@@ -4177,20 +4177,6 @@ pub const Type = extern union {
4177 };4177 };
4178 }4178 }
41794179
4180 /// Returns the type of a pointer to an element.
4181 /// Asserts that the type is a pointer, and that the element type is indexable.
4182 /// For *[N]T, return *T
4183 /// For [*]T, returns *T
4184 /// For []T, returns *T
4185 /// Handles const-ness and address spaces in particular.
4186 pub fn elemPtrType(ptr_ty: Type, arena: Allocator, mod: *Module) !Type {
4187 return try Type.ptr(arena, mod, .{
4188 .pointee_type = ptr_ty.elemType2(),
4189 .mutable = ptr_ty.ptrIsMutable(),
4190 .@"addrspace" = ptr_ty.ptrAddressSpace(),
4191 });
4192 }
4193
4194 fn shallowElemType(child_ty: Type) Type {4180 fn shallowElemType(child_ty: Type) Type {
4195 return switch (child_ty.zigTypeTag()) {4181 return switch (child_ty.zigTypeTag()) {
4196 .Array, .Vector => child_ty.childType(),4182 .Array, .Vector => child_ty.childType(),
test/behavior/align.zig+15-15
...@@ -2,6 +2,7 @@ const std = @import("std");...@@ -2,6 +2,7 @@ const std = @import("std");
2const expect = std.testing.expect;2const expect = std.testing.expect;
3const builtin = @import("builtin");3const builtin = @import("builtin");
4const native_arch = builtin.target.cpu.arch;4const native_arch = builtin.target.cpu.arch;
5const assert = std.debug.assert;
56
6var foo: u8 align(4) = 100;7var foo: u8 align(4) = 100;
78
...@@ -375,38 +376,37 @@ test "function callconv expression depends on generic parameter" {...@@ -375,38 +376,37 @@ test "function callconv expression depends on generic parameter" {
375}376}
376377
377test "runtime known array index has best alignment possible" {378test "runtime known array index has best alignment possible" {
378 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
379 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO379 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
380380
381 // take full advantage of over-alignment381 // take full advantage of over-alignment
382 var array align(4) = [_]u8{ 1, 2, 3, 4 };382 var array align(4) = [_]u8{ 1, 2, 3, 4 };
383 try expect(@TypeOf(&array[0]) == *align(4) u8);383 comptime assert(@TypeOf(&array[0]) == *align(4) u8);
384 try expect(@TypeOf(&array[1]) == *u8);384 comptime assert(@TypeOf(&array[1]) == *u8);
385 try expect(@TypeOf(&array[2]) == *align(2) u8);385 comptime assert(@TypeOf(&array[2]) == *align(2) u8);
386 try expect(@TypeOf(&array[3]) == *u8);386 comptime assert(@TypeOf(&array[3]) == *u8);
387387
388 // because align is too small but we still figure out to use 2388 // because align is too small but we still figure out to use 2
389 var bigger align(2) = [_]u64{ 1, 2, 3, 4 };389 var bigger align(2) = [_]u64{ 1, 2, 3, 4 };
390 try expect(@TypeOf(&bigger[0]) == *align(2) u64);390 comptime assert(@TypeOf(&bigger[0]) == *align(2) u64);
391 try expect(@TypeOf(&bigger[1]) == *align(2) u64);391 comptime assert(@TypeOf(&bigger[1]) == *align(2) u64);
392 try expect(@TypeOf(&bigger[2]) == *align(2) u64);392 comptime assert(@TypeOf(&bigger[2]) == *align(2) u64);
393 try expect(@TypeOf(&bigger[3]) == *align(2) u64);393 comptime assert(@TypeOf(&bigger[3]) == *align(2) u64);
394394
395 // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2395 // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2
396 var smaller align(2) = [_]u32{ 1, 2, 3, 4 };396 var smaller align(2) = [_]u32{ 1, 2, 3, 4 };
397 var runtime_zero: usize = 0;397 var runtime_zero: usize = 0;
398 comptime try expect(@TypeOf(smaller[runtime_zero..]) == []align(2) u32);398 comptime assert(@TypeOf(smaller[runtime_zero..]) == []align(2) u32);
399 comptime try expect(@TypeOf(smaller[runtime_zero..].ptr) == [*]align(2) u32);399 comptime assert(@TypeOf(smaller[runtime_zero..].ptr) == [*]align(2) u32);
400 try testIndex(smaller[runtime_zero..].ptr, 0, *align(2) u32);400 try testIndex(smaller[runtime_zero..].ptr, 0, *align(2) u32);
401 try testIndex(smaller[runtime_zero..].ptr, 1, *align(2) u32);401 try testIndex(smaller[runtime_zero..].ptr, 1, *align(2) u32);
402 try testIndex(smaller[runtime_zero..].ptr, 2, *align(2) u32);402 try testIndex(smaller[runtime_zero..].ptr, 2, *align(2) u32);
403 try testIndex(smaller[runtime_zero..].ptr, 3, *align(2) u32);403 try testIndex(smaller[runtime_zero..].ptr, 3, *align(2) u32);
404404
405 // has to use ABI alignment because index known at runtime only405 // has to use ABI alignment because index known at runtime only
406 try testIndex2(array[runtime_zero..].ptr, 0, *u8);406 try testIndex2(&array, 0, *u8);
407 try testIndex2(array[runtime_zero..].ptr, 1, *u8);407 try testIndex2(&array, 1, *u8);
408 try testIndex2(array[runtime_zero..].ptr, 2, *u8);408 try testIndex2(&array, 2, *u8);
409 try testIndex2(array[runtime_zero..].ptr, 3, *u8);409 try testIndex2(&array, 3, *u8);
410}410}
411fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) !void {411fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) !void {
412 comptime try expect(@TypeOf(&smaller[index]) == T);412 comptime try expect(@TypeOf(&smaller[index]) == T);