From cb616cb7972bb2f38ea4527c7ec0ae3cc0d64c7c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 5 Oct 2021 21:38:47 -0700 Subject: stage2: implement runtime `@intToEnum` * Update AIR instruction `intcast` to allow the dest type to be an enum. * LLVM backend: update `intcast` to support when the bit counts of operand and dest type are the same. This was already a requirement of the instruction previously. * Type: `intInfo` supports the case when the type is an enum, and retrieves the info for the integer tag type. This makes it pretty easy for backends to implement `intcast` without having to care explicitly that the new type is an enum. As a bonus, simple enums never have to go through the type system; their signedness and bit count are computed directly. The "int to enum" behavior test case is now passing for stage2 in the LLVM backend. --- src/type.zig | 85 +++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 49 insertions(+), 36 deletions(-) (limited to 'src/type.zig') diff --git a/src/type.zig b/src/type.zig index 317d2dacbe..d6362cf31e 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2657,38 +2657,49 @@ pub const Type = extern union { }; } - /// Asserts the type is an integer. + /// Asserts the type is an integer or enum. pub fn intInfo(self: Type, target: Target) struct { signedness: std.builtin.Signedness, bits: u16 } { - return switch (self.tag()) { - .int_unsigned => .{ + var ty = self; + while (true) switch (ty.tag()) { + .int_unsigned => return .{ .signedness = .unsigned, - .bits = self.castTag(.int_unsigned).?.data, + .bits = ty.castTag(.int_unsigned).?.data, }, - .int_signed => .{ + .int_signed => return .{ .signedness = .signed, - .bits = self.castTag(.int_signed).?.data, - }, - .u1 => .{ .signedness = .unsigned, .bits = 1 }, - .u8 => .{ .signedness = .unsigned, .bits = 8 }, - .i8 => .{ .signedness = .signed, .bits = 8 }, - .u16 => .{ .signedness = .unsigned, .bits = 16 }, - .i16 => .{ .signedness = .signed, .bits = 16 }, - .u32 => .{ .signedness = .unsigned, .bits = 32 }, - .i32 => .{ .signedness = .signed, .bits = 32 }, - .u64 => .{ .signedness = .unsigned, .bits = 64 }, - .i64 => .{ .signedness = .signed, .bits = 64 }, - .u128 => .{ .signedness = .unsigned, .bits = 128 }, - .i128 => .{ .signedness = .signed, .bits = 128 }, - .usize => .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() }, - .isize => .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() }, - .c_short => .{ .signedness = .signed, .bits = CType.short.sizeInBits(target) }, - .c_ushort => .{ .signedness = .unsigned, .bits = CType.ushort.sizeInBits(target) }, - .c_int => .{ .signedness = .signed, .bits = CType.int.sizeInBits(target) }, - .c_uint => .{ .signedness = .unsigned, .bits = CType.uint.sizeInBits(target) }, - .c_long => .{ .signedness = .signed, .bits = CType.long.sizeInBits(target) }, - .c_ulong => .{ .signedness = .unsigned, .bits = CType.ulong.sizeInBits(target) }, - .c_longlong => .{ .signedness = .signed, .bits = CType.longlong.sizeInBits(target) }, - .c_ulonglong => .{ .signedness = .unsigned, .bits = CType.ulonglong.sizeInBits(target) }, + .bits = ty.castTag(.int_signed).?.data, + }, + .u1 => return .{ .signedness = .unsigned, .bits = 1 }, + .u8 => return .{ .signedness = .unsigned, .bits = 8 }, + .i8 => return .{ .signedness = .signed, .bits = 8 }, + .u16 => return .{ .signedness = .unsigned, .bits = 16 }, + .i16 => return .{ .signedness = .signed, .bits = 16 }, + .u32 => return .{ .signedness = .unsigned, .bits = 32 }, + .i32 => return .{ .signedness = .signed, .bits = 32 }, + .u64 => return .{ .signedness = .unsigned, .bits = 64 }, + .i64 => return .{ .signedness = .signed, .bits = 64 }, + .u128 => return .{ .signedness = .unsigned, .bits = 128 }, + .i128 => return .{ .signedness = .signed, .bits = 128 }, + .usize => return .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() }, + .isize => return .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() }, + .c_short => return .{ .signedness = .signed, .bits = CType.short.sizeInBits(target) }, + .c_ushort => return .{ .signedness = .unsigned, .bits = CType.ushort.sizeInBits(target) }, + .c_int => return .{ .signedness = .signed, .bits = CType.int.sizeInBits(target) }, + .c_uint => return .{ .signedness = .unsigned, .bits = CType.uint.sizeInBits(target) }, + .c_long => return .{ .signedness = .signed, .bits = CType.long.sizeInBits(target) }, + .c_ulong => return .{ .signedness = .unsigned, .bits = CType.ulong.sizeInBits(target) }, + .c_longlong => return .{ .signedness = .signed, .bits = CType.longlong.sizeInBits(target) }, + .c_ulonglong => return .{ .signedness = .unsigned, .bits = CType.ulonglong.sizeInBits(target) }, + + .enum_full, .enum_nonexhaustive => ty = ty.cast(Payload.EnumFull).?.data.tag_ty, + .enum_numbered => ty = self.castTag(.enum_numbered).?.data.tag_ty, + .enum_simple => { + const enum_obj = self.castTag(.enum_simple).?.data; + return .{ + .signedness = .unsigned, + .bits = smallestUnsignedBits(enum_obj.fields.count()), + }; + }, else => unreachable, }; @@ -3905,20 +3916,22 @@ pub const Type = extern union { return Type.initPayload(&type_payload.base); } + pub fn smallestUnsignedBits(max: u64) u16 { + if (max == 0) return 0; + const base = std.math.log2(max); + const upper = (@as(u64, 1) << @intCast(u6, base)) - 1; + return @intCast(u16, base + @boolToInt(upper < max)); + } + pub fn smallestUnsignedInt(arena: *Allocator, max: u64) !Type { - const bits = bits: { - if (max == 0) break :bits 0; - const base = std.math.log2(max); - const upper = (@as(u64, 1) << @intCast(u6, base)) - 1; - break :bits base + @boolToInt(upper < max); - }; - return switch (@intCast(u16, bits)) { + const bits = smallestUnsignedBits(max); + return switch (bits) { 1 => initTag(.u1), 8 => initTag(.u8), 16 => initTag(.u16), 32 => initTag(.u32), 64 => initTag(.u64), - else => |b| return Tag.int_unsigned.create(arena, b), + else => return Tag.int_unsigned.create(arena, bits), }; } }; -- cgit v1.2.3