stage2: fully implement Type.eql for pointers

Also fixed abiAlignment - for pointers it was returning the abi
alignment inside the type, rather than of the pointer itself. There is
now `ptrAlignment` for getting the alignment inside the type of
pointers.
This commit is contained in:
Andrew Kelley 2021-03-27 23:55:19 -07:00
parent 95cc457d97
commit 68f4eb0f67
3 changed files with 204 additions and 53 deletions

View File

@ -1,22 +1,14 @@
this is my WIP branch scratch pad, to be deleted before merging into master this is my WIP branch scratch pad, to be deleted before merging into master
Merge TODO list: Merge TODO list:
* don't have an explicit dbg_stmt zir instruction - instead merge it with * uncomment the commented out stage2 tests
var decl and assignment instructions, etc.
- make it set sema.src where appropriate
* remove the LazySrcLoc.todo tag * remove the LazySrcLoc.todo tag
* update astgen.zig * update astgen.zig
* finish updating Sema.zig * finish updating Sema.zig
* finish implementing SrcLoc byteOffset function * finish implementing SrcLoc byteOffset function
* audit Module.zig for use of token_starts - it should only be when
resolving LazySrcLoc
* audit astgen.zig for use of token_starts - I think there should be no uses
* audit all the .unneeded src locations * audit all the .unneeded src locations
* audit the calls in codegen toSrcLocWithDecl specifically if there is inlined function * audit the calls in codegen toSrcLocWithDecl specifically if there is inlined function
calls from other files. calls from other files.
* uncomment the commented out stage2 tests
* memory leaks on --watch update
* memory leaks on test-stage2
Performance optimizations to look into: Performance optimizations to look into:
* astgen: pass *GenZir as the first arg, not *Module * astgen: pass *GenZir as the first arg, not *Module
@ -41,3 +33,7 @@ Performance optimizations to look into:
* in astgen, if a decl_val would be to a const variable or to a function, there could be * in astgen, if a decl_val would be to a const variable or to a function, there could be
a special zir.Inst.Ref form that means to refer to a decl as the operand. This a special zir.Inst.Ref form that means to refer to a decl as the operand. This
would elide all the decl_val instructions in the ZIR. would elide all the decl_val instructions in the ZIR.
* don't have an explicit dbg_stmt zir instruction - instead merge it with
var decl and assignment instructions, etc.
- make it set sema.src where appropriate
* look into not emitting redundant dbg stmts to TZIR

View File

@ -169,6 +169,125 @@ pub const Type = extern union {
}; };
} }
pub fn ptrInfo(self: Type) Payload.Pointer {
switch (self.tag()) {
.single_const_pointer_to_comptime_int => return .{ .data = .{
.pointee_type = Type.initTag(.comptime_int),
.sentinel = null,
.@"align" = 0,
.bit_offset = 0,
.host_size = 0,
.@"allowzero" = false,
.mutable = false,
.@"volatile" = false,
.size = .One,
} },
.const_slice_u8 => return .{ .data = .{
.pointee_type = Type.initTag(.u8),
.sentinel = null,
.@"align" = 0,
.bit_offset = 0,
.host_size = 0,
.@"allowzero" = false,
.mutable = false,
.@"volatile" = false,
.size = .Slice,
} },
.single_const_pointer => return .{ .data = .{
.pointee_type = self.castPointer().?.data,
.sentinel = null,
.@"align" = 0,
.bit_offset = 0,
.host_size = 0,
.@"allowzero" = false,
.mutable = false,
.@"volatile" = false,
.size = .One,
} },
.single_mut_pointer => return .{ .data = .{
.pointee_type = self.castPointer().?.data,
.sentinel = null,
.@"align" = 0,
.bit_offset = 0,
.host_size = 0,
.@"allowzero" = false,
.mutable = true,
.@"volatile" = false,
.size = .One,
} },
.many_const_pointer => return .{ .data = .{
.pointee_type = self.castPointer().?.data,
.sentinel = null,
.@"align" = 0,
.bit_offset = 0,
.host_size = 0,
.@"allowzero" = false,
.mutable = false,
.@"volatile" = false,
.size = .Many,
} },
.many_mut_pointer => return .{ .data = .{
.pointee_type = self.castPointer().?.data,
.sentinel = null,
.@"align" = 0,
.bit_offset = 0,
.host_size = 0,
.@"allowzero" = false,
.mutable = true,
.@"volatile" = false,
.size = .Many,
} },
.c_const_pointer => return .{ .data = .{
.pointee_type = self.castPointer().?.data,
.sentinel = null,
.@"align" = 0,
.bit_offset = 0,
.host_size = 0,
.@"allowzero" = false,
.mutable = false,
.@"volatile" = false,
.size = .C,
} },
.c_mut_pointer => return .{ .data = .{
.pointee_type = self.castPointer().?.data,
.sentinel = null,
.@"align" = 0,
.bit_offset = 0,
.host_size = 0,
.@"allowzero" = false,
.mutable = true,
.@"volatile" = false,
.size = .C,
} },
.const_slice => return .{ .data = .{
.pointee_type = self.castPointer().?.data,
.sentinel = null,
.@"align" = 0,
.bit_offset = 0,
.host_size = 0,
.@"allowzero" = false,
.mutable = false,
.@"volatile" = false,
.size = .Slice,
} },
.mut_slice => return .{ .data = .{
.pointee_type = self.castPointer().?.data,
.sentinel = null,
.@"align" = 0,
.bit_offset = 0,
.host_size = 0,
.@"allowzero" = false,
.mutable = true,
.@"volatile" = false,
.size = .Slice,
} },
.pointer => return self.castTag(.pointer).?.*,
else => unreachable,
}
}
pub fn eql(a: Type, b: Type) bool { pub fn eql(a: Type, b: Type) bool {
// As a shortcut, if the small tags / addresses match, we're done. // As a shortcut, if the small tags / addresses match, we're done.
if (a.tag_if_small_enough == b.tag_if_small_enough) if (a.tag_if_small_enough == b.tag_if_small_enough)
@ -191,25 +310,38 @@ pub const Type = extern union {
return a.elemType().eql(b.elemType()); return a.elemType().eql(b.elemType());
}, },
.Pointer => { .Pointer => {
// Hot path for common case: const info_a = a.ptrInfo().data;
if (a.castPointer()) |a_payload| { const info_b = b.ptrInfo().data;
if (b.castPointer()) |b_payload| { if (!info_a.pointee_type.eql(info_b.pointee_type))
return a.tag() == b.tag() and eql(a_payload.data, b_payload.data); return false;
if (info_a.size != info_b.size)
return false;
if (info_a.mutable != info_b.mutable)
return false;
if (info_a.@"volatile" != info_b.@"volatile")
return false;
if (info_a.@"allowzero" != info_b.@"allowzero")
return false;
if (info_a.bit_offset != info_b.bit_offset)
return false;
if (info_a.host_size != info_b.host_size)
return false;
const sentinel_a = info_a.sentinel;
const sentinel_b = info_b.sentinel;
if (sentinel_a) |sa| {
if (sentinel_b) |sb| {
if (!sa.eql(sb))
return false;
} else {
return false;
} }
} else {
if (sentinel_b != null)
return false;
} }
const is_slice_a = isSlice(a);
const is_slice_b = isSlice(b);
if (is_slice_a != is_slice_b)
return false;
const ptr_size_a = ptrSize(a); return true;
const ptr_size_b = ptrSize(b);
if (ptr_size_a != ptr_size_b)
return false;
std.debug.panic("TODO implement more pointer Type equality comparison: {} and {}", .{
a, b,
});
}, },
.Int => { .Int => {
// Detect that e.g. u64 != usize, even if the bits match on a particular target. // Detect that e.g. u64 != usize, even if the bits match on a particular target.
@ -844,6 +976,35 @@ pub const Type = extern union {
return fast_result; return fast_result;
} }
pub fn ptrAlignment(self: Type, target: Target) u32 {
switch (self.tag()) {
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
.optional_single_const_pointer,
.optional_single_mut_pointer,
=> return self.cast(Payload.ElemType).?.data.abiAlignment(target),
.const_slice_u8 => return 1,
.pointer => {
const ptr_info = self.castTag(.pointer).?.data;
if (ptr_info.@"align" != 0) {
return ptr_info.@"align";
} else {
return ptr_info.pointee_type.abiAlignment();
}
},
else => unreachable,
}
}
/// Asserts that hasCodeGenBits() is true. /// Asserts that hasCodeGenBits() is true.
pub fn abiAlignment(self: Type, target: Target) u32 { pub fn abiAlignment(self: Type, target: Target) u32 {
return switch (self.tag()) { return switch (self.tag()) {
@ -885,15 +1046,9 @@ pub const Type = extern union {
.mut_slice, .mut_slice,
.optional_single_const_pointer, .optional_single_const_pointer,
.optional_single_mut_pointer, .optional_single_mut_pointer,
.pointer,
=> return @divExact(target.cpu.arch.ptrBitWidth(), 8), => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
.pointer => {
const payload = self.castTag(.pointer).?.data;
if (payload.@"align" != 0) return payload.@"align";
return @divExact(target.cpu.arch.ptrBitWidth(), 8);
},
.c_short => return @divExact(CType.short.sizeInBits(target), 8), .c_short => return @divExact(CType.short.sizeInBits(target), 8),
.c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8), .c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8),
.c_int => return @divExact(CType.int.sizeInBits(target), 8), .c_int => return @divExact(CType.int.sizeInBits(target), 8),

View File

@ -1502,27 +1502,27 @@ pub fn addCases(ctx: *TestContext) !void {
"", "",
); );
//case.addCompareOutput( case.addCompareOutput(
// \\export fn _start() noreturn { \\export fn _start() noreturn {
// \\ const a: anyerror!comptime_int = 42; \\ const a: anyerror!comptime_int = 42;
// \\ const b: *const comptime_int = &(a catch unreachable); \\ const b: *const comptime_int = &(a catch unreachable);
// \\ assert(b.* == 42); \\ assert(b.* == 42);
// \\ \\
// \\ exit(); \\ exit();
// \\} \\}
// \\fn assert(b: bool) void { \\fn assert(b: bool) void {
// \\ if (!b) unreachable; // assertion failure \\ if (!b) unreachable; // assertion failure
// \\} \\}
// \\fn exit() noreturn { \\fn exit() noreturn {
// \\ asm volatile ("syscall" \\ asm volatile ("syscall"
// \\ : \\ :
// \\ : [number] "{rax}" (231), \\ : [number] "{rax}" (231),
// \\ [arg1] "{rdi}" (0) \\ [arg1] "{rdi}" (0)
// \\ : "rcx", "r11", "memory" \\ : "rcx", "r11", "memory"
// \\ ); \\ );
// \\ unreachable; \\ unreachable;
// \\} \\}
//, ""); , "");
case.addCompareOutput( case.addCompareOutput(
\\export fn _start() noreturn { \\export fn _start() noreturn {