authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-05-05 13:26:58-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-06-10 20:42:28-07:00
logac07ddadeb36019c262b4f28a4fa3884f6f50b32
tree3dc3f2ea03f2faa5b110001b6dd0d9dff567de0e
parent75cf06c187d9d0288c2ea31f34b18c3a7da4bd1d

InternPool: enhance integer values

The Key struct now has a Storage tagged union which can store a u64, i64, or big int. This is needed so that indexToKey can be implemented for integers stored compactly in the data structure.

3 files changed, 257 insertions(+), 135 deletions(-)

src/InternPool.zig+166-78
...@@ -11,6 +11,7 @@ const std = @import("std");...@@ -11,6 +11,7 @@ const std = @import("std");
11const Allocator = std.mem.Allocator;11const Allocator = std.mem.Allocator;
12const assert = std.debug.assert;12const assert = std.debug.assert;
13const BigIntConst = std.math.big.int.Const;13const BigIntConst = std.math.big.int.Const;
14const BigIntMutable = std.math.big.int.Mutable;
1415
15const InternPool = @This();16const InternPool = @This();
16const DeclIndex = enum(u32) { _ };17const DeclIndex = enum(u32) { _ };
...@@ -50,10 +51,7 @@ pub const Key = union(enum) {...@@ -50,10 +51,7 @@ pub const Key = union(enum) {
50 /// Index into the string table bytes.51 /// Index into the string table bytes.
51 lib_name: u32,52 lib_name: u32,
52 },53 },
53 int: struct {54 int: Key.Int,
54 ty: Index,
55 big_int: BigIntConst,
56 },
57 enum_tag: struct {55 enum_tag: struct {
58 ty: Index,56 ty: Index,
59 tag: BigIntConst,57 tag: BigIntConst,
...@@ -110,6 +108,32 @@ pub const Key = union(enum) {...@@ -110,6 +108,32 @@ pub const Key = union(enum) {
110 child: Index,108 child: Index,
111 };109 };
112110
111 pub const Int = struct {
112 ty: Index,
113 storage: Storage,
114
115 pub const Storage = union(enum) {
116 u64: u64,
117 i64: i64,
118 big_int: BigIntConst,
119
120 /// Big enough to fit any non-BigInt value
121 pub const BigIntSpace = struct {
122 /// The +1 is headroom so that operations such as incrementing once
123 /// or decrementing once are possible without using an allocator.
124 limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb,
125 };
126
127 pub fn toBigInt(storage: Storage, space: *BigIntSpace) BigIntConst {
128 return switch (storage) {
129 .big_int => |x| x,
130 .u64 => |x| BigIntMutable.init(&space.limbs, x).toConst(),
131 .i64 => |x| BigIntMutable.init(&space.limbs, x).toConst(),
132 };
133 }
134 };
135 };
136
113 pub fn hash32(key: Key) u32 {137 pub fn hash32(key: Key) u32 {
114 return @truncate(u32, key.hash64());138 return @truncate(u32, key.hash64());
115 }139 }
...@@ -137,9 +161,13 @@ pub const Key = union(enum) {...@@ -137,9 +161,13 @@ pub const Key = union(enum) {
137 => |info| std.hash.autoHash(hasher, info),161 => |info| std.hash.autoHash(hasher, info),
138162
139 .int => |int| {163 .int => |int| {
164 // Canonicalize all integers by converting them to BigIntConst.
165 var buffer: Key.Int.Storage.BigIntSpace = undefined;
166 const big_int = int.storage.toBigInt(&buffer);
167
140 std.hash.autoHash(hasher, int.ty);168 std.hash.autoHash(hasher, int.ty);
141 std.hash.autoHash(hasher, int.big_int.positive);169 std.hash.autoHash(hasher, big_int.positive);
142 for (int.big_int.limbs) |limb| std.hash.autoHash(hasher, limb);170 for (big_int.limbs) |limb| std.hash.autoHash(hasher, limb);
143 },171 },
144172
145 .enum_tag => |enum_tag| {173 .enum_tag => |enum_tag| {
...@@ -573,42 +601,27 @@ pub const static_keys = [_]Key{...@@ -573,42 +601,27 @@ pub const static_keys = [_]Key{
573601
574 .{ .int = .{602 .{ .int = .{
575 .ty = .comptime_int_type,603 .ty = .comptime_int_type,
576 .big_int = .{604 .storage = .{ .u64 = 0 },
577 .limbs = &.{0},
578 .positive = true,
579 },
580 } },605 } },
581606
582 .{ .int = .{607 .{ .int = .{
583 .ty = .usize_type,608 .ty = .usize_type,
584 .big_int = .{609 .storage = .{ .u64 = 0 },
585 .limbs = &.{0},
586 .positive = true,
587 },
588 } },610 } },
589611
590 .{ .int = .{612 .{ .int = .{
591 .ty = .u8_type,613 .ty = .u8_type,
592 .big_int = .{614 .storage = .{ .u64 = 0 },
593 .limbs = &.{0},
594 .positive = true,
595 },
596 } },615 } },
597616
598 .{ .int = .{617 .{ .int = .{
599 .ty = .comptime_int_type,618 .ty = .comptime_int_type,
600 .big_int = .{619 .storage = .{ .u64 = 1 },
601 .limbs = &.{1},
602 .positive = true,
603 },
604 } },620 } },
605621
606 .{ .int = .{622 .{ .int = .{
607 .ty = .usize_type,623 .ty = .usize_type,
608 .big_int = .{624 .storage = .{ .u64 = 1 },
609 .limbs = &.{1},
610 .positive = true,
611 },
612 } },625 } },
613626
614 .{ .enum_tag = .{627 .{ .enum_tag = .{
...@@ -680,19 +693,19 @@ pub const Tag = enum(u8) {...@@ -680,19 +693,19 @@ pub const Tag = enum(u8) {
680 simple_internal,693 simple_internal,
681 /// Type: u32694 /// Type: u32
682 /// data is integer value695 /// data is integer value
683 int_small_u32,696 int_u32,
684 /// Type: i32697 /// Type: i32
685 /// data is integer value bitcasted to u32.698 /// data is integer value bitcasted to u32.
686 int_small_i32,699 int_i32,
687 /// A usize that fits in 32 bits.700 /// A usize that fits in 32 bits.
688 /// data is integer value.701 /// data is integer value.
689 int_small_usize,702 int_usize,
690 /// A comptime_int that fits in a u32.703 /// A comptime_int that fits in a u32.
691 /// data is integer value.704 /// data is integer value.
692 int_small_comptime_unsigned,705 int_comptime_int_u32,
693 /// A comptime_int that fits in an i32.706 /// A comptime_int that fits in an i32.
694 /// data is integer value bitcasted to u32.707 /// data is integer value bitcasted to u32.
695 int_small_comptime_signed,708 int_comptime_int_i32,
696 /// A positive integer value.709 /// A positive integer value.
697 /// data is a limbs index to Int.710 /// data is a limbs index to Int.
698 int_positive,711 int_positive,
...@@ -932,11 +945,26 @@ pub fn indexToKey(ip: InternPool, index: Index) Key {...@@ -932,11 +945,26 @@ pub fn indexToKey(ip: InternPool, index: Index) Key {
932 .type_error_union => @panic("TODO"),945 .type_error_union => @panic("TODO"),
933 .type_enum_simple => @panic("TODO"),946 .type_enum_simple => @panic("TODO"),
934 .simple_internal => @panic("TODO"),947 .simple_internal => @panic("TODO"),
935 .int_small_u32 => @panic("TODO"),948 .int_u32 => return .{ .int = .{
936 .int_small_i32 => @panic("TODO"),949 .ty = .u32_type,
937 .int_small_usize => @panic("TODO"),950 .storage = .{ .u64 = data },
938 .int_small_comptime_unsigned => @panic("TODO"),951 } },
939 .int_small_comptime_signed => @panic("TODO"),952 .int_i32 => return .{ .int = .{
953 .ty = .i32_type,
954 .storage = .{ .i64 = @bitCast(i32, data) },
955 } },
956 .int_usize => return .{ .int = .{
957 .ty = .usize_type,
958 .storage = .{ .u64 = data },
959 } },
960 .int_comptime_int_u32 => return .{ .int = .{
961 .ty = .comptime_int_type,
962 .storage = .{ .u64 = data },
963 } },
964 .int_comptime_int_i32 => return .{ .int = .{
965 .ty = .comptime_int_type,
966 .storage = .{ .i64 = @bitCast(i32, data) },
967 } },
940 .int_positive => @panic("TODO"),968 .int_positive => @panic("TODO"),
941 .int_negative => @panic("TODO"),969 .int_negative => @panic("TODO"),
942 .enum_tag_positive => @panic("TODO"),970 .enum_tag_positive => @panic("TODO"),
...@@ -1041,54 +1069,114 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {...@@ -1041,54 +1069,114 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
10411069
1042 .int => |int| b: {1070 .int => |int| b: {
1043 switch (int.ty) {1071 switch (int.ty) {
1044 .u32_type => {1072 .u32_type => switch (int.storage) {
1045 if (int.big_int.fits(u32)) {1073 .big_int => |big_int| {
1046 ip.items.appendAssumeCapacity(.{1074 if (big_int.to(u32)) |casted| {
1047 .tag = .int_small_u32,1075 ip.items.appendAssumeCapacity(.{
1048 .data = int.big_int.to(u32) catch unreachable,1076 .tag = .int_u32,
1049 });1077 .data = casted,
1050 break :b;1078 });
1051 }1079 break :b;
1080 } else |_| {}
1081 },
1082 inline .u64, .i64 => |x| {
1083 if (std.math.cast(u32, x)) |casted| {
1084 ip.items.appendAssumeCapacity(.{
1085 .tag = .int_u32,
1086 .data = casted,
1087 });
1088 break :b;
1089 }
1090 },
1052 },1091 },
1053 .i32_type => {1092 .i32_type => switch (int.storage) {
1054 if (int.big_int.fits(i32)) {1093 .big_int => |big_int| {
1055 ip.items.appendAssumeCapacity(.{1094 if (big_int.to(i32)) |casted| {
1056 .tag = .int_small_i32,1095 ip.items.appendAssumeCapacity(.{
1057 .data = @bitCast(u32, int.big_int.to(i32) catch unreachable),1096 .tag = .int_i32,
1058 });1097 .data = @bitCast(u32, casted),
1059 break :b;1098 });
1060 }1099 break :b;
1100 } else |_| {}
1101 },
1102 inline .u64, .i64 => |x| {
1103 if (std.math.cast(i32, x)) |casted| {
1104 ip.items.appendAssumeCapacity(.{
1105 .tag = .int_u32,
1106 .data = @bitCast(u32, casted),
1107 });
1108 break :b;
1109 }
1110 },
1061 },1111 },
1062 .usize_type => {1112 .usize_type => switch (int.storage) {
1063 if (int.big_int.fits(u32)) {1113 .big_int => |big_int| {
1064 ip.items.appendAssumeCapacity(.{1114 if (big_int.to(u32)) |casted| {
1065 .tag = .int_small_usize,1115 ip.items.appendAssumeCapacity(.{
1066 .data = int.big_int.to(u32) catch unreachable,1116 .tag = .int_usize,
1067 });1117 .data = casted,
1068 break :b;1118 });
1069 }1119 break :b;
1120 } else |_| {}
1121 },
1122 inline .u64, .i64 => |x| {
1123 if (std.math.cast(u32, x)) |casted| {
1124 ip.items.appendAssumeCapacity(.{
1125 .tag = .int_usize,
1126 .data = casted,
1127 });
1128 break :b;
1129 }
1130 },
1070 },1131 },
1071 .comptime_int_type => {1132 .comptime_int_type => switch (int.storage) {
1072 if (int.big_int.fits(u32)) {1133 .big_int => |big_int| {
1073 ip.items.appendAssumeCapacity(.{1134 if (big_int.to(u32)) |casted| {
1074 .tag = .int_small_comptime_unsigned,1135 ip.items.appendAssumeCapacity(.{
1075 .data = int.big_int.to(u32) catch unreachable,1136 .tag = .int_comptime_int_u32,
1076 });1137 .data = casted,
1077 break :b;1138 });
1078 }1139 break :b;
1079 if (int.big_int.fits(i32)) {1140 } else |_| {}
1080 ip.items.appendAssumeCapacity(.{1141 if (big_int.to(i32)) |casted| {
1081 .tag = .int_small_comptime_signed,1142 ip.items.appendAssumeCapacity(.{
1082 .data = @bitCast(u32, int.big_int.to(i32) catch unreachable),1143 .tag = .int_comptime_int_i32,
1083 });1144 .data = @bitCast(u32, casted),
1084 break :b;1145 });
1085 }1146 break :b;
1147 } else |_| {}
1148 },
1149 inline .u64, .i64 => |x| {
1150 if (std.math.cast(u32, x)) |casted| {
1151 ip.items.appendAssumeCapacity(.{
1152 .tag = .int_comptime_int_u32,
1153 .data = casted,
1154 });
1155 break :b;
1156 }
1157 if (std.math.cast(i32, x)) |casted| {
1158 ip.items.appendAssumeCapacity(.{
1159 .tag = .int_comptime_int_i32,
1160 .data = @bitCast(u32, casted),
1161 });
1162 break :b;
1163 }
1164 },
1086 },1165 },
1087 else => {},1166 else => {},
1088 }1167 }
10891168 switch (int.storage) {
1090 const tag: Tag = if (int.big_int.positive) .int_positive else .int_negative;1169 .big_int => |big_int| {
1091 try addInt(ip, gpa, int.ty, tag, int.big_int.limbs);1170 const tag: Tag = if (big_int.positive) .int_positive else .int_negative;
1171 try addInt(ip, gpa, int.ty, tag, big_int.limbs);
1172 },
1173 inline .i64, .u64 => |x| {
1174 var buf: [2]usize = undefined;
1175 const big_int = BigIntMutable.init(&buf, x).toConst();
1176 const tag: Tag = if (big_int.positive) .int_positive else .int_negative;
1177 try addInt(ip, gpa, int.ty, tag, big_int.limbs);
1178 },
1179 }
1092 },1180 },
10931181
1094 .enum_tag => |enum_tag| {1182 .enum_tag => |enum_tag| {
src/Sema.zig+3-3
...@@ -1981,7 +1981,7 @@ fn resolveMaybeUndefValAllowVariablesMaybeRuntime(...@@ -1981,7 +1981,7 @@ fn resolveMaybeUndefValAllowVariablesMaybeRuntime(
1981 if (air_tags[i] == .constant) {1981 if (air_tags[i] == .constant) {
1982 const ty_pl = sema.air_instructions.items(.data)[i].ty_pl;1982 const ty_pl = sema.air_instructions.items(.data)[i].ty_pl;
1983 const val = sema.air_values.items[ty_pl.payload];1983 const val = sema.air_values.items[ty_pl.payload];
1984 if (val.tag() == .variable) return val;1984 if (val.tagIsVariable()) return val;
1985 }1985 }
1986 return opv;1986 return opv;
1987 }1987 }
...@@ -5033,7 +5033,7 @@ fn storeToInferredAllocComptime(...@@ -5033,7 +5033,7 @@ fn storeToInferredAllocComptime(
5033 // There will be only one store_to_inferred_ptr because we are running at comptime.5033 // There will be only one store_to_inferred_ptr because we are running at comptime.
5034 // The alloc will turn into a Decl.5034 // The alloc will turn into a Decl.
5035 if (try sema.resolveMaybeUndefValAllowVariables(operand)) |operand_val| store: {5035 if (try sema.resolveMaybeUndefValAllowVariables(operand)) |operand_val| store: {
5036 if (operand_val.tag() == .variable) break :store;5036 if (operand_val.tagIsVariable()) break :store;
5037 var anon_decl = try block.startAnonDecl();5037 var anon_decl = try block.startAnonDecl();
5038 defer anon_decl.deinit();5038 defer anon_decl.deinit();
5039 iac.data.decl_index = try anon_decl.finish(5039 iac.data.decl_index = try anon_decl.finish(
...@@ -28125,7 +28125,7 @@ fn beginComptimePtrLoad(...@@ -28125,7 +28125,7 @@ fn beginComptimePtrLoad(
28125 const is_mutable = ptr_val.tag() == .decl_ref_mut;28125 const is_mutable = ptr_val.tag() == .decl_ref_mut;
28126 const decl = sema.mod.declPtr(decl_index);28126 const decl = sema.mod.declPtr(decl_index);
28127 const decl_tv = try decl.typedValue();28127 const decl_tv = try decl.typedValue();
28128 if (decl_tv.val.tag() == .variable) return error.RuntimeLoad;28128 if (decl_tv.val.tagIsVariable()) return error.RuntimeLoad;
2812928129
28130 const layout_defined = decl.ty.hasWellDefinedLayout(mod);28130 const layout_defined = decl.ty.hasWellDefinedLayout(mod);
28131 break :blk ComptimePtrLoadKit{28131 break :blk ComptimePtrLoadKit{
src/value.zig+88-54
...@@ -796,17 +796,17 @@ pub const Value = struct {...@@ -796,17 +796,17 @@ pub const Value = struct {
796 mod: *const Module,796 mod: *const Module,
797 opt_sema: ?*Sema,797 opt_sema: ?*Sema,
798 ) Module.CompileError!BigIntConst {798 ) Module.CompileError!BigIntConst {
799 switch (val.ip_index) {799 return switch (val.ip_index) {
800 .bool_false => return BigIntMutable.init(&space.limbs, 0).toConst(),800 .bool_false => BigIntMutable.init(&space.limbs, 0).toConst(),
801 .bool_true => return BigIntMutable.init(&space.limbs, 1).toConst(),801 .bool_true => BigIntMutable.init(&space.limbs, 1).toConst(),
802 .undef => unreachable,802 .undef => unreachable,
803 .null_value => return BigIntMutable.init(&space.limbs, 0).toConst(),803 .null_value => BigIntMutable.init(&space.limbs, 0).toConst(),
804 .none => switch (val.tag()) {804 .none => switch (val.tag()) {
805 .zero,805 .zero,
806 .the_only_possible_value, // i0, u0806 .the_only_possible_value, // i0, u0
807 => return BigIntMutable.init(&space.limbs, 0).toConst(),807 => BigIntMutable.init(&space.limbs, 0).toConst(),
808808
809 .one => return BigIntMutable.init(&space.limbs, 1).toConst(),809 .one => BigIntMutable.init(&space.limbs, 1).toConst(),
810810
811 .enum_field_index => {811 .enum_field_index => {
812 const index = val.castTag(.enum_field_index).?.data;812 const index = val.castTag(.enum_field_index).?.data;
...@@ -816,10 +816,10 @@ pub const Value = struct {...@@ -816,10 +816,10 @@ pub const Value = struct {
816 const sub_val = val.castTag(.runtime_value).?.data;816 const sub_val = val.castTag(.runtime_value).?.data;
817 return sub_val.toBigIntAdvanced(space, mod, opt_sema);817 return sub_val.toBigIntAdvanced(space, mod, opt_sema);
818 },818 },
819 .int_u64 => return BigIntMutable.init(&space.limbs, val.castTag(.int_u64).?.data).toConst(),819 .int_u64 => BigIntMutable.init(&space.limbs, val.castTag(.int_u64).?.data).toConst(),
820 .int_i64 => return BigIntMutable.init(&space.limbs, val.castTag(.int_i64).?.data).toConst(),820 .int_i64 => BigIntMutable.init(&space.limbs, val.castTag(.int_i64).?.data).toConst(),
821 .int_big_positive => return val.castTag(.int_big_positive).?.asBigInt(),821 .int_big_positive => val.castTag(.int_big_positive).?.asBigInt(),
822 .int_big_negative => return val.castTag(.int_big_negative).?.asBigInt(),822 .int_big_negative => val.castTag(.int_big_negative).?.asBigInt(),
823823
824 .lazy_align => {824 .lazy_align => {
825 const ty = val.castTag(.lazy_align).?.data;825 const ty = val.castTag(.lazy_align).?.data;
...@@ -849,10 +849,10 @@ pub const Value = struct {...@@ -849,10 +849,10 @@ pub const Value = struct {
849 else => unreachable,849 else => unreachable,
850 },850 },
851 else => switch (mod.intern_pool.indexToKey(val.ip_index)) {851 else => switch (mod.intern_pool.indexToKey(val.ip_index)) {
852 .int => |int| return int.big_int,852 .int => |int| int.storage.toBigInt(space),
853 else => unreachable,853 else => unreachable,
854 },854 },
855 }855 };
856 }856 }
857857
858 /// If the value fits in a u64, return it, otherwise null.858 /// If the value fits in a u64, return it, otherwise null.
...@@ -900,7 +900,11 @@ pub const Value = struct {...@@ -900,7 +900,11 @@ pub const Value = struct {
900 else => return null,900 else => return null,
901 },901 },
902 else => return switch (mod.intern_pool.indexToKey(val.ip_index)) {902 else => return switch (mod.intern_pool.indexToKey(val.ip_index)) {
903 .int => |int| int.big_int.to(u64) catch null,903 .int => |int| switch (int.storage) {
904 .big_int => |big_int| big_int.to(u64) catch null,
905 .u64 => |x| x,
906 .i64 => |x| std.math.cast(u64, x),
907 },
904 else => null,908 else => null,
905 },909 },
906 }910 }
...@@ -940,18 +944,22 @@ pub const Value = struct {...@@ -940,18 +944,22 @@ pub const Value = struct {
940944
941 else => unreachable,945 else => unreachable,
942 },946 },
943 else => switch (mod.intern_pool.indexToKey(val.ip_index)) {947 else => return switch (mod.intern_pool.indexToKey(val.ip_index)) {
944 .int => |int| return int.big_int.to(i64) catch unreachable,948 .int => |int| switch (int.storage) {
949 .big_int => |big_int| big_int.to(i64) catch unreachable,
950 .i64 => |x| x,
951 .u64 => |x| @intCast(i64, x),
952 },
945 else => unreachable,953 else => unreachable,
946 },954 },
947 }955 }
948 }956 }
949957
950 pub fn toBool(val: Value, mod: *const Module) bool {958 pub fn toBool(val: Value, mod: *const Module) bool {
951 switch (val.ip_index) {959 return switch (val.ip_index) {
952 .bool_true => return true,960 .bool_true => true,
953 .bool_false => return false,961 .bool_false => false,
954 .none => return switch (val.tag()) {962 .none => switch (val.tag()) {
955 .one => true,963 .one => true,
956 .zero => false,964 .zero => false,
957965
...@@ -968,10 +976,13 @@ pub const Value = struct {...@@ -968,10 +976,13 @@ pub const Value = struct {
968 else => unreachable,976 else => unreachable,
969 },977 },
970 else => switch (mod.intern_pool.indexToKey(val.ip_index)) {978 else => switch (mod.intern_pool.indexToKey(val.ip_index)) {
971 .int => |int| return !int.big_int.eqZero(),979 .int => |int| switch (int.storage) {
980 .big_int => |big_int| !big_int.eqZero(),
981 inline .u64, .i64 => |x| x != 0,
982 },
972 else => unreachable,983 else => unreachable,
973 },984 },
974 }985 };
975 }986 }
976987
977 fn isDeclRef(val: Value) bool {988 fn isDeclRef(val: Value) bool {
...@@ -1483,12 +1494,12 @@ pub const Value = struct {...@@ -1483,12 +1494,12 @@ pub const Value = struct {
14831494
1484 pub fn clz(val: Value, ty: Type, mod: *const Module) u64 {1495 pub fn clz(val: Value, ty: Type, mod: *const Module) u64 {
1485 const ty_bits = ty.intInfo(mod).bits;1496 const ty_bits = ty.intInfo(mod).bits;
1486 switch (val.ip_index) {1497 return switch (val.ip_index) {
1487 .bool_false => return ty_bits,1498 .bool_false => ty_bits,
1488 .bool_true => return ty_bits - 1,1499 .bool_true => ty_bits - 1,
1489 .none => switch (val.tag()) {1500 .none => switch (val.tag()) {
1490 .zero => return ty_bits,1501 .zero => ty_bits,
1491 .one => return ty_bits - 1,1502 .one => ty_bits - 1,
14921503
1493 .int_u64 => {1504 .int_u64 => {
1494 const big = @clz(val.castTag(.int_u64).?.data);1505 const big = @clz(val.castTag(.int_u64).?.data);
...@@ -1519,20 +1530,24 @@ pub const Value = struct {...@@ -1519,20 +1530,24 @@ pub const Value = struct {
1519 else => unreachable,1530 else => unreachable,
1520 },1531 },
1521 else => switch (mod.intern_pool.indexToKey(val.ip_index)) {1532 else => switch (mod.intern_pool.indexToKey(val.ip_index)) {
1522 .int => |int| return int.big_int.clz(ty_bits),1533 .int => |int| switch (int.storage) {
1534 .big_int => |big_int| big_int.clz(ty_bits),
1535 .u64 => |x| @clz(x) + ty_bits - 64,
1536 .i64 => @panic("TODO implement i64 Value clz"),
1537 },
1523 else => unreachable,1538 else => unreachable,
1524 },1539 },
1525 }1540 };
1526 }1541 }
15271542
1528 pub fn ctz(val: Value, ty: Type, mod: *const Module) u64 {1543 pub fn ctz(val: Value, ty: Type, mod: *const Module) u64 {
1529 const ty_bits = ty.intInfo(mod).bits;1544 const ty_bits = ty.intInfo(mod).bits;
1530 switch (val.ip_index) {1545 return switch (val.ip_index) {
1531 .bool_false => return ty_bits,1546 .bool_false => ty_bits,
1532 .bool_true => return 0,1547 .bool_true => 0,
1533 .none => switch (val.tag()) {1548 .none => switch (val.tag()) {
1534 .zero => return ty_bits,1549 .zero => ty_bits,
1535 .one => return 0,1550 .one => 0,
15361551
1537 .int_u64 => {1552 .int_u64 => {
1538 const big = @ctz(val.castTag(.int_u64).?.data);1553 const big = @ctz(val.castTag(.int_u64).?.data);
...@@ -1563,10 +1578,17 @@ pub const Value = struct {...@@ -1563,10 +1578,17 @@ pub const Value = struct {
1563 else => unreachable,1578 else => unreachable,
1564 },1579 },
1565 else => switch (mod.intern_pool.indexToKey(val.ip_index)) {1580 else => switch (mod.intern_pool.indexToKey(val.ip_index)) {
1566 .int => |int| return int.big_int.ctz(),1581 .int => |int| switch (int.storage) {
1582 .big_int => |big_int| big_int.ctz(),
1583 .u64 => |x| {
1584 const big = @ctz(x);
1585 return if (big == 64) ty_bits else big;
1586 },
1587 .i64 => @panic("TODO implement i64 Value ctz"),
1588 },
1567 else => unreachable,1589 else => unreachable,
1568 },1590 },
1569 }1591 };
1570 }1592 }
15711593
1572 pub fn popCount(val: Value, ty: Type, mod: *const Module) u64 {1594 pub fn popCount(val: Value, ty: Type, mod: *const Module) u64 {
...@@ -1591,7 +1613,9 @@ pub const Value = struct {...@@ -1591,7 +1613,9 @@ pub const Value = struct {
1591 else => switch (mod.intern_pool.indexToKey(val.ip_index)) {1613 else => switch (mod.intern_pool.indexToKey(val.ip_index)) {
1592 .int => |int| {1614 .int => |int| {
1593 const info = ty.intInfo(mod);1615 const info = ty.intInfo(mod);
1594 return int.big_int.popCount(info.bits);1616 var buffer: Value.BigIntSpace = undefined;
1617 const big_int = int.storage.toBigInt(&buffer);
1618 return @intCast(u64, big_int.popCount(info.bits));
1595 },1619 },
1596 else => unreachable,1620 else => unreachable,
1597 },1621 },
...@@ -1641,23 +1665,23 @@ pub const Value = struct {...@@ -1641,23 +1665,23 @@ pub const Value = struct {
1641 /// Returns the number of bits the value requires to represent stored in twos complement form.1665 /// Returns the number of bits the value requires to represent stored in twos complement form.
1642 pub fn intBitCountTwosComp(self: Value, mod: *const Module) usize {1666 pub fn intBitCountTwosComp(self: Value, mod: *const Module) usize {
1643 const target = mod.getTarget();1667 const target = mod.getTarget();
1644 switch (self.ip_index) {1668 return switch (self.ip_index) {
1645 .bool_false => return 0,1669 .bool_false => 0,
1646 .bool_true => return 1,1670 .bool_true => 1,
1647 .none => switch (self.tag()) {1671 .none => switch (self.tag()) {
1648 .zero,1672 .zero,
1649 .the_only_possible_value,1673 .the_only_possible_value,
1650 => return 0,1674 => 0,
16511675
1652 .one => return 1,1676 .one => 1,
16531677
1654 .int_u64 => {1678 .int_u64 => {
1655 const x = self.castTag(.int_u64).?.data;1679 const x = self.castTag(.int_u64).?.data;
1656 if (x == 0) return 0;1680 if (x == 0) return 0;
1657 return @intCast(usize, std.math.log2(x) + 1);1681 return @intCast(usize, std.math.log2(x) + 1);
1658 },1682 },
1659 .int_big_positive => return self.castTag(.int_big_positive).?.asBigInt().bitCountTwosComp(),1683 .int_big_positive => self.castTag(.int_big_positive).?.asBigInt().bitCountTwosComp(),
1660 .int_big_negative => return self.castTag(.int_big_negative).?.asBigInt().bitCountTwosComp(),1684 .int_big_negative => self.castTag(.int_big_negative).?.asBigInt().bitCountTwosComp(),
16611685
1662 .decl_ref_mut,1686 .decl_ref_mut,
1663 .comptime_field_ptr,1687 .comptime_field_ptr,
...@@ -1667,7 +1691,7 @@ pub const Value = struct {...@@ -1667,7 +1691,7 @@ pub const Value = struct {
1667 .variable,1691 .variable,
1668 .eu_payload_ptr,1692 .eu_payload_ptr,
1669 .opt_payload_ptr,1693 .opt_payload_ptr,
1670 => return target.ptrBitWidth(),1694 => target.ptrBitWidth(),
16711695
1672 else => {1696 else => {
1673 var buffer: BigIntSpace = undefined;1697 var buffer: BigIntSpace = undefined;
...@@ -1675,10 +1699,18 @@ pub const Value = struct {...@@ -1675,10 +1699,18 @@ pub const Value = struct {
1675 },1699 },
1676 },1700 },
1677 else => switch (mod.intern_pool.indexToKey(self.ip_index)) {1701 else => switch (mod.intern_pool.indexToKey(self.ip_index)) {
1678 .int => |int| return int.big_int.bitCountTwosComp(),1702 .int => |int| switch (int.storage) {
1703 .big_int => |big_int| big_int.bitCountTwosComp(),
1704 .u64 => |x| if (x == 0) 0 else @intCast(usize, std.math.log2(x) + 1),
1705 .i64 => {
1706 var buffer: Value.BigIntSpace = undefined;
1707 const big_int = int.storage.toBigInt(&buffer);
1708 return big_int.bitCountTwosComp();
1709 },
1710 },
1679 else => unreachable,1711 else => unreachable,
1680 },1712 },
1681 }1713 };
1682 }1714 }
16831715
1684 /// Converts an integer or a float to a float. May result in a loss of information.1716 /// Converts an integer or a float to a float. May result in a loss of information.
...@@ -1798,8 +1830,11 @@ pub const Value = struct {...@@ -1798,8 +1830,11 @@ pub const Value = struct {
17981830
1799 else => unreachable,1831 else => unreachable,
1800 },1832 },
1801 else => switch (mod.intern_pool.indexToKey(lhs.ip_index)) {1833 else => return switch (mod.intern_pool.indexToKey(lhs.ip_index)) {
1802 .int => |int| return int.big_int.orderAgainstScalar(0),1834 .int => |int| switch (int.storage) {
1835 .big_int => |big_int| big_int.orderAgainstScalar(0),
1836 inline .u64, .i64 => |x| std.math.order(x, 0),
1837 },
1803 else => unreachable,1838 else => unreachable,
1804 },1839 },
1805 }1840 }
...@@ -2777,6 +2812,10 @@ pub const Value = struct {...@@ -2777,6 +2812,10 @@ pub const Value = struct {
2777 }2812 }
2778 }2813 }
27792814
2815 pub fn tagIsVariable(val: Value) bool {
2816 return val.ip_index == .none and val.tag() == .variable;
2817 }
2818
2780 /// Returns true if a Value is backed by a variable2819 /// Returns true if a Value is backed by a variable
2781 pub fn isVariable(val: Value, mod: *Module) bool {2820 pub fn isVariable(val: Value, mod: *Module) bool {
2782 return switch (val.ip_index) {2821 return switch (val.ip_index) {
...@@ -5399,12 +5438,7 @@ pub const Value = struct {...@@ -5399,12 +5438,7 @@ pub const Value = struct {
5399 };5438 };
5400 };5439 };
54015440
5402 /// Big enough to fit any non-BigInt value5441 pub const BigIntSpace = InternPool.Key.Int.Storage.BigIntSpace;
5403 pub const BigIntSpace = struct {
5404 /// The +1 is headroom so that operations such as incrementing once or decrementing once
5405 /// are possible without using an allocator.
5406 limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb,
5407 };
54085442
5409 pub const zero = initTag(.zero);5443 pub const zero = initTag(.zero);
5410 pub const one = initTag(.one);5444 pub const one = initTag(.one);