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(
1093810938
1093910939 const new_ptr_ty = t: {
1094010940 // Calculate the new pointer alignment.
10941 // This code is duplicated in `elemPtrType`.
1094110942 if (ptr_info.@"align" == 0) {
1094210943 // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness.
1094310944 break :t ptr_ty;
......@@ -18617,20 +18618,20 @@ fn elemPtr(
1861718618 .Pointer => {
1861818619 // In all below cases, we have to deref the ptr operand to get the actual indexable pointer.
1861918620 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);
1862118621 switch (indexable_ty.ptrSize()) {
1862218622 .Slice => return sema.elemPtrSlice(block, indexable_ptr_src, indexable, elem_index_src, elem_index),
1862318623 .Many, .C => {
1862418624 const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_ptr_src, indexable);
1862518625 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
18626
1862718626 const runtime_src = rs: {
1862818627 const ptr_val = maybe_ptr_val orelse break :rs indexable_ptr_src;
1862918628 const index_val = maybe_index_val orelse break :rs elem_index_src;
1863018629 const index = @intCast(usize, index_val.toUnsignedInt(target));
1863118630 const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index, sema.mod);
18631 const result_ty = try sema.elemPtrType(indexable_ty, index);
1863218632 return sema.addConstant(result_ty, elem_ptr);
1863318633 };
18634 const result_ty = try sema.elemPtrType(indexable_ty, null);
1863418635
1863518636 try sema.requireRuntimeBlock(block, runtime_src);
1863618637 return block.addPtrElemPtr(indexable, elem_index, result_ty);
......@@ -18883,29 +18884,29 @@ fn elemPtrArray(
1888318884 const array_sent = array_ty.sentinel() != null;
1888418885 const array_len = array_ty.arrayLen();
1888518886 const array_len_s = array_len + @boolToInt(array_sent);
18886 const elem_ptr_ty = try array_ptr_ty.elemPtrType(sema.arena, sema.mod);
1888718887
1888818888 if (array_len_s == 0) {
1888918889 return sema.fail(block, elem_index_src, "indexing into empty array", .{});
1889018890 }
1889118891
1889218892 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 bounds
18894 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
18895
18896 if (maybe_index_val) |index_val| {
18897 const index = @intCast(usize, index_val.toUnsignedInt(target));
18893 // The index must not be undefined since it can be out of bounds.
18894 const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
18895 const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(target));
1889818896 if (index >= array_len_s) {
1889918897 const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else "";
1890018898 return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
1890118899 }
18902 }
18900 break :o index;
18901 } else null;
18902
18903 const elem_ptr_ty = try sema.elemPtrType(array_ptr_ty, offset);
18904
1890318905 if (maybe_undef_array_ptr_val) |array_ptr_val| {
1890418906 if (array_ptr_val.isUndef()) {
1890518907 return sema.addConstUndef(elem_ptr_ty);
1890618908 }
18907 if (maybe_index_val) |index_val| {
18908 const index = @intCast(usize, index_val.toUnsignedInt(target));
18909 if (offset) |index| {
1890918910 const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, index, sema.mod);
1891018911 return sema.addConstant(elem_ptr_ty, elem_ptr);
1891118912 }
......@@ -18932,14 +18933,14 @@ fn elemPtrArray(
1893218933
1893318934 const runtime_src = if (maybe_undef_array_ptr_val != null) elem_index_src else array_ptr_src;
1893418935 try sema.requireRuntimeBlock(block, runtime_src);
18935 if (block.wantSafety()) {
18936 // Runtime check is only needed if unable to comptime check
18937 if (maybe_index_val == null) {
18938 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 try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
18941 }
18936
18937 // Runtime check is only needed if unable to comptime check.
18938 if (block.wantSafety() and offset == null) {
18939 const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
18940 const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
18941 try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
1894218942 }
18943
1894318944 return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty);
1894418945}
1894518946
......@@ -19007,11 +19008,15 @@ fn elemPtrSlice(
1900719008 const target = sema.mod.getTarget();
1900819009 const slice_ty = sema.typeOf(slice);
1900919010 const slice_sent = slice_ty.sentinel() != null;
19010 const elem_ptr_ty = try slice_ty.elemPtrType(sema.arena, sema.mod);
1901119011
1901219012 const maybe_undef_slice_val = try sema.resolveMaybeUndefVal(block, slice_src, slice);
19013 // index must be defined since it can index out of bounds
19014 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
19013 // The index must not be undefined since it can be out of bounds.
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
1901619021 if (maybe_undef_slice_val) |slice_val| {
1901719022 if (slice_val.isUndef()) {
......@@ -19022,8 +19027,7 @@ fn elemPtrSlice(
1902219027 if (slice_len_s == 0) {
1902319028 return sema.fail(block, elem_index_src, "indexing into empty slice", .{});
1902419029 }
19025 if (maybe_index_val) |index_val| {
19026 const index = @intCast(usize, index_val.toUnsignedInt(target));
19030 if (offset) |index| {
1902719031 if (index >= slice_len_s) {
1902819032 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
1902919033 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(
2536425368 }
2536525369 return Value.Tag.aggregate.create(sema.arena, result_data);
2536625370}
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 {
41774177 };
41784178 }
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
41944180 fn shallowElemType(child_ty: Type) Type {
41954181 return switch (child_ty.zigTypeTag()) {
41964182 .Array, .Vector => child_ty.childType(),
test/behavior/align.zig+15-15
......@@ -2,6 +2,7 @@ const std = @import("std");
22const expect = std.testing.expect;
33const builtin = @import("builtin");
44const native_arch = builtin.target.cpu.arch;
5const assert = std.debug.assert;
56
67var foo: u8 align(4) = 100;
78
......@@ -375,38 +376,37 @@ test "function callconv expression depends on generic parameter" {
375376}
376377
377378test "runtime known array index has best alignment possible" {
378 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
379379 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
380380
381381 // take full advantage of over-alignment
382382 var array align(4) = [_]u8{ 1, 2, 3, 4 };
383 try expect(@TypeOf(&array[0]) == *align(4) u8);
384 try expect(@TypeOf(&array[1]) == *u8);
385 try expect(@TypeOf(&array[2]) == *align(2) u8);
386 try expect(@TypeOf(&array[3]) == *u8);
383 comptime assert(@TypeOf(&array[0]) == *align(4) u8);
384 comptime assert(@TypeOf(&array[1]) == *u8);
385 comptime assert(@TypeOf(&array[2]) == *align(2) u8);
386 comptime assert(@TypeOf(&array[3]) == *u8);
387387
388388 // because align is too small but we still figure out to use 2
389389 var bigger align(2) = [_]u64{ 1, 2, 3, 4 };
390 try expect(@TypeOf(&bigger[0]) == *align(2) u64);
391 try expect(@TypeOf(&bigger[1]) == *align(2) u64);
392 try expect(@TypeOf(&bigger[2]) == *align(2) u64);
393 try expect(@TypeOf(&bigger[3]) == *align(2) u64);
390 comptime assert(@TypeOf(&bigger[0]) == *align(2) u64);
391 comptime assert(@TypeOf(&bigger[1]) == *align(2) u64);
392 comptime assert(@TypeOf(&bigger[2]) == *align(2) u64);
393 comptime assert(@TypeOf(&bigger[3]) == *align(2) u64);
394394
395395 // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2
396396 var smaller align(2) = [_]u32{ 1, 2, 3, 4 };
397397 var runtime_zero: usize = 0;
398 comptime try expect(@TypeOf(smaller[runtime_zero..]) == []align(2) u32);
399 comptime try expect(@TypeOf(smaller[runtime_zero..].ptr) == [*]align(2) u32);
398 comptime assert(@TypeOf(smaller[runtime_zero..]) == []align(2) u32);
399 comptime assert(@TypeOf(smaller[runtime_zero..].ptr) == [*]align(2) u32);
400400 try testIndex(smaller[runtime_zero..].ptr, 0, *align(2) u32);
401401 try testIndex(smaller[runtime_zero..].ptr, 1, *align(2) u32);
402402 try testIndex(smaller[runtime_zero..].ptr, 2, *align(2) u32);
403403 try testIndex(smaller[runtime_zero..].ptr, 3, *align(2) u32);
404404
405405 // has to use ABI alignment because index known at runtime only
406 try testIndex2(array[runtime_zero..].ptr, 0, *u8);
407 try testIndex2(array[runtime_zero..].ptr, 1, *u8);
408 try testIndex2(array[runtime_zero..].ptr, 2, *u8);
409 try testIndex2(array[runtime_zero..].ptr, 3, *u8);
406 try testIndex2(&array, 0, *u8);
407 try testIndex2(&array, 1, *u8);
408 try testIndex2(&array, 2, *u8);
409 try testIndex2(&array, 3, *u8);
410410}
411411fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) !void {
412412 comptime try expect(@TypeOf(&smaller[index]) == T);