| author | |
| committer | |
| log | 6b0f7de247f3c12281f47f38738e93651d6bf51b |
| tree | 1ec7419096d8dd5a7122bb8a75880382585781e2 |
| parent | fb67a7260d1e7fc8ff81fa9e3cc5340422363727 |
7 files changed, 1630 insertions(+), 450 deletions(-)
lib/std/math.zig+37| ... | @@ -986,6 +986,43 @@ pub const Order = enum { | ... | @@ -986,6 +986,43 @@ pub const Order = enum { |
| 986 | 986 | ||
| 987 | /// Greater than (`>`) | 987 | /// Greater than (`>`) |
| 988 | gt, | 988 | gt, |
| 989 | |||
| 990 | pub fn invert(self: Order) Order { | ||
| 991 | return switch (self) { | ||
| 992 | .lt => .gt, | ||
| 993 | .eq => .eq, | ||
| 994 | .gt => .gt, | ||
| 995 | }; | ||
| 996 | } | ||
| 997 | |||
| 998 | pub fn compare(self: Order, op: CompareOperator) bool { | ||
| 999 | return switch (self) { | ||
| 1000 | .lt => switch (op) { | ||
| 1001 | .lt => true, | ||
| 1002 | .lte => true, | ||
| 1003 | .eq => false, | ||
| 1004 | .gte => false, | ||
| 1005 | .gt => false, | ||
| 1006 | .neq => true, | ||
| 1007 | }, | ||
| 1008 | .eq => switch (op) { | ||
| 1009 | .lt => false, | ||
| 1010 | .lte => true, | ||
| 1011 | .eq => true, | ||
| 1012 | .gte => true, | ||
| 1013 | .gt => false, | ||
| 1014 | .neq => false, | ||
| 1015 | }, | ||
| 1016 | .gt => switch (op) { | ||
| 1017 | .lt => false, | ||
| 1018 | .lte => false, | ||
| 1019 | .eq => false, | ||
| 1020 | .gte => true, | ||
| 1021 | .gt => true, | ||
| 1022 | .neq => true, | ||
| 1023 | }, | ||
| 1024 | }; | ||
| 1025 | } | ||
| 989 | }; | 1026 | }; |
| 990 | 1027 | ||
| 991 | /// Given two numbers, this function returns the order they are with respect to each other. | 1028 | /// Given two numbers, this function returns the order they are with respect to each other. |
lib/std/math/big/int.zig+44-5| ... | @@ -60,6 +60,13 @@ pub const Int = struct { | ... | @@ -60,6 +60,13 @@ pub const Int = struct { |
| 60 | return s; | 60 | return s; |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | /// Hint: use `calcLimbLen` to figure out how big an array to allocate for `limbs`. | ||
| 64 | pub fn initSetFixed(limbs: []Limb, value: var) Int { | ||
| 65 | var s = Int.initFixed(limbs); | ||
| 66 | s.set(value) catch unreachable; | ||
| 67 | return s; | ||
| 68 | } | ||
| 69 | |||
| 63 | /// Creates a new Int with a specific capacity. If capacity < default_capacity then the | 70 | /// Creates a new Int with a specific capacity. If capacity < default_capacity then the |
| 64 | /// default capacity will be used instead. | 71 | /// default capacity will be used instead. |
| 65 | pub fn initCapacity(allocator: *Allocator, capacity: usize) !Int { | 72 | pub fn initCapacity(allocator: *Allocator, capacity: usize) !Int { |
| ... | @@ -104,12 +111,11 @@ pub const Int = struct { | ... | @@ -104,12 +111,11 @@ pub const Int = struct { |
| 104 | /// Returns an Int backed by a fixed set of limb values. | 111 | /// Returns an Int backed by a fixed set of limb values. |
| 105 | /// This is read-only and cannot be used as a result argument. If the Int tries to allocate | 112 | /// This is read-only and cannot be used as a result argument. If the Int tries to allocate |
| 106 | /// memory a runtime panic will occur. | 113 | /// memory a runtime panic will occur. |
| 107 | pub fn initFixed(limbs: []const Limb) Int { | 114 | pub fn initFixed(limbs: []Limb) Int { |
| 108 | var self = Int{ | 115 | var self = Int{ |
| 109 | .allocator = null, | 116 | .allocator = null, |
| 110 | .metadata = limbs.len, | 117 | .metadata = limbs.len, |
| 111 | // Cast away the const, invalid use to pass as a pointer argument. | 118 | .limbs = limbs, |
| 112 | .limbs = @intToPtr([*]Limb, @ptrToInt(limbs.ptr))[0..limbs.len], | ||
| 113 | }; | 119 | }; |
| 114 | 120 | ||
| 115 | self.normalize(limbs.len); | 121 | self.normalize(limbs.len); |
| ... | @@ -218,7 +224,7 @@ pub const Int = struct { | ... | @@ -218,7 +224,7 @@ pub const Int = struct { |
| 218 | /// one greater than the returned value. | 224 | /// one greater than the returned value. |
| 219 | /// | 225 | /// |
| 220 | /// e.g. -127 returns 8 as it will fit in an i8. 127 returns 7 since it fits in a u7. | 226 | /// e.g. -127 returns 8 as it will fit in an i8. 127 returns 7 since it fits in a u7. |
| 221 | fn bitCountTwosComp(self: Int) usize { | 227 | pub fn bitCountTwosComp(self: Int) usize { |
| 222 | var bits = self.bitCountAbs(); | 228 | var bits = self.bitCountAbs(); |
| 223 | 229 | ||
| 224 | // If the entire value has only one bit set (e.g. 0b100000000) then the negation in twos | 230 | // If the entire value has only one bit set (e.g. 0b100000000) then the negation in twos |
| ... | @@ -267,7 +273,6 @@ pub const Int = struct { | ... | @@ -267,7 +273,6 @@ pub const Int = struct { |
| 267 | 273 | ||
| 268 | /// Sets an Int to value. Value must be an primitive integer type. | 274 | /// Sets an Int to value. Value must be an primitive integer type. |
| 269 | pub fn set(self: *Int, value: var) Allocator.Error!void { | 275 | pub fn set(self: *Int, value: var) Allocator.Error!void { |
| 270 | self.assertWritable(); | ||
| 271 | const T = @TypeOf(value); | 276 | const T = @TypeOf(value); |
| 272 | 277 | ||
| 273 | switch (@typeInfo(T)) { | 278 | switch (@typeInfo(T)) { |
| ... | @@ -598,6 +603,13 @@ pub const Int = struct { | ... | @@ -598,6 +603,13 @@ pub const Int = struct { |
| 598 | } | 603 | } |
| 599 | } | 604 | } |
| 600 | 605 | ||
| 606 | /// Same as `cmp` but the right-hand operand is a primitive integer. | ||
| 607 | pub fn orderAgainstScalar(lhs: Int, scalar: var) math.Order { | ||
| 608 | var limbs: [calcLimbLen(scalar)]Limb = undefined; | ||
| 609 | const rhs = initSetFixed(&limbs, scalar); | ||
| 610 | return cmp(lhs, rhs); | ||
| 611 | } | ||
| 612 | |||
| 601 | /// Returns true if a == 0. | 613 | /// Returns true if a == 0. |
| 602 | pub fn eqZero(a: Int) bool { | 614 | pub fn eqZero(a: Int) bool { |
| 603 | return a.len() == 1 and a.limbs[0] == 0; | 615 | return a.len() == 1 and a.limbs[0] == 0; |
| ... | @@ -642,6 +654,33 @@ pub const Int = struct { | ... | @@ -642,6 +654,33 @@ pub const Int = struct { |
| 642 | }; | 654 | }; |
| 643 | } | 655 | } |
| 644 | 656 | ||
| 657 | /// Returns the number of limbs needed to store `scalar`, which must be a | ||
| 658 | /// primitive integer value. | ||
| 659 | pub fn calcLimbLen(scalar: var) usize { | ||
| 660 | switch (@typeInfo(@TypeOf(scalar))) { | ||
| 661 | .Int => return @sizeOf(scalar) / @sizeOf(Limb), | ||
| 662 | .ComptimeInt => { | ||
| 663 | const w_value = if (scalar < 0) -scalar else scalar; | ||
| 664 | const req_limbs = @divFloor(math.log2(w_value), Limb.bit_count) + 1; | ||
| 665 | return req_limbs; | ||
| 666 | }, | ||
| 667 | else => @compileError("parameter must be a primitive integer type"), | ||
| 668 | } | ||
| 669 | } | ||
| 670 | |||
| 671 | /// r = a + scalar | ||
| 672 | /// | ||
| 673 | /// r and a may be aliases. | ||
| 674 | /// scalar is a primitive integer type. | ||
| 675 | /// | ||
| 676 | /// Returns an error if memory could not be allocated. | ||
| 677 | pub fn addScalar(r: *Int, a: Int, scalar: var) Allocator.Error!void { | ||
| 678 | var limbs: [calcLimbLen(scalar)]Limb = undefined; | ||
| 679 | var operand = initFixed(&limbs); | ||
| 680 | operand.set(scalar) catch unreachable; | ||
| 681 | return add(r, a, operand); | ||
| 682 | } | ||
| 683 | |||
| 645 | /// r = a + b | 684 | /// r = a + b |
| 646 | /// | 685 | /// |
| 647 | /// r, a and b may be aliases. | 686 | /// r, a and b may be aliases. |
src-self-hosted/codegen.zig+8| ... | @@ -501,11 +501,19 @@ fn Reg(comptime arch: Target.Cpu.Arch) type { | ... | @@ -501,11 +501,19 @@ fn Reg(comptime arch: Target.Cpu.Arch) type { |
| 501 | bh, | 501 | bh, |
| 502 | ch, | 502 | ch, |
| 503 | dh, | 503 | dh, |
| 504 | bph, | ||
| 505 | sph, | ||
| 506 | sih, | ||
| 507 | dih, | ||
| 504 | 508 | ||
| 505 | al, | 509 | al, |
| 506 | bl, | 510 | bl, |
| 507 | cl, | 511 | cl, |
| 508 | dl, | 512 | dl, |
| 513 | bpl, | ||
| 514 | spl, | ||
| 515 | sil, | ||
| 516 | dil, | ||
| 509 | r8b, | 517 | r8b, |
| 510 | r9b, | 518 | r9b, |
| 511 | r10b, | 519 | r10b, |
src-self-hosted/ir.zig+549-125| ... | @@ -26,6 +26,10 @@ pub const Inst = struct { | ... | @@ -26,6 +26,10 @@ pub const Inst = struct { |
| 26 | assembly, | 26 | assembly, |
| 27 | ptrtoint, | 27 | ptrtoint, |
| 28 | bitcast, | 28 | bitcast, |
| 29 | cmp, | ||
| 30 | condbr, | ||
| 31 | isnull, | ||
| 32 | isnonnull, | ||
| 29 | }; | 33 | }; |
| 30 | 34 | ||
| 31 | pub fn cast(base: *Inst, comptime T: type) ?*T { | 35 | pub fn cast(base: *Inst, comptime T: type) ?*T { |
| ... | @@ -41,15 +45,11 @@ pub const Inst = struct { | ... | @@ -41,15 +45,11 @@ pub const Inst = struct { |
| 41 | 45 | ||
| 42 | /// Returns `null` if runtime-known. | 46 | /// Returns `null` if runtime-known. |
| 43 | pub fn value(base: *Inst) ?Value { | 47 | pub fn value(base: *Inst) ?Value { |
| 44 | return switch (base.tag) { | 48 | if (base.ty.onePossibleValue()) |
| 45 | .unreach => Value.initTag(.noreturn_value), | 49 | return Value.initTag(.the_one_possible_value); |
| 46 | .constant => base.cast(Constant).?.val, | 50 | |
| 47 | 51 | const inst = base.cast(Constant) orelse return null; | |
| 48 | .assembly, | 52 | return inst.val; |
| 49 | .ptrtoint, | ||
| 50 | .bitcast, | ||
| 51 | => null, | ||
| 52 | }; | ||
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | pub const Unreach = struct { | 55 | pub const Unreach = struct { |
| ... | @@ -96,6 +96,46 @@ pub const Inst = struct { | ... | @@ -96,6 +96,46 @@ pub const Inst = struct { |
| 96 | operand: *Inst, | 96 | operand: *Inst, |
| 97 | }, | 97 | }, |
| 98 | }; | 98 | }; |
| 99 | |||
| 100 | pub const Cmp = struct { | ||
| 101 | pub const base_tag = Tag.cmp; | ||
| 102 | |||
| 103 | base: Inst, | ||
| 104 | args: struct { | ||
| 105 | lhs: *Inst, | ||
| 106 | op: std.math.CompareOperator, | ||
| 107 | rhs: *Inst, | ||
| 108 | }, | ||
| 109 | }; | ||
| 110 | |||
| 111 | pub const CondBr = struct { | ||
| 112 | pub const base_tag = Tag.condbr; | ||
| 113 | |||
| 114 | base: Inst, | ||
| 115 | args: struct { | ||
| 116 | condition: *Inst, | ||
| 117 | true_body: Module.Body, | ||
| 118 | false_body: Module.Body, | ||
| 119 | }, | ||
| 120 | }; | ||
| 121 | |||
| 122 | pub const IsNull = struct { | ||
| 123 | pub const base_tag = Tag.isnull; | ||
| 124 | |||
| 125 | base: Inst, | ||
| 126 | args: struct { | ||
| 127 | operand: *Inst, | ||
| 128 | }, | ||
| 129 | }; | ||
| 130 | |||
| 131 | pub const IsNonNull = struct { | ||
| 132 | pub const base_tag = Tag.isnonnull; | ||
| 133 | |||
| 134 | base: Inst, | ||
| 135 | args: struct { | ||
| 136 | operand: *Inst, | ||
| 137 | }, | ||
| 138 | }; | ||
| 99 | }; | 139 | }; |
| 100 | 140 | ||
| 101 | pub const TypedValue = struct { | 141 | pub const TypedValue = struct { |
| ... | @@ -118,15 +158,19 @@ pub const Module = struct { | ... | @@ -118,15 +158,19 @@ pub const Module = struct { |
| 118 | 158 | ||
| 119 | pub const Fn = struct { | 159 | pub const Fn = struct { |
| 120 | analysis_status: enum { in_progress, failure, success }, | 160 | analysis_status: enum { in_progress, failure, success }, |
| 121 | body: []*Inst, | 161 | body: Body, |
| 122 | fn_type: Type, | 162 | fn_type: Type, |
| 123 | }; | 163 | }; |
| 124 | 164 | ||
| 165 | pub const Body = struct { | ||
| 166 | instructions: []*Inst, | ||
| 167 | }; | ||
| 168 | |||
| 125 | pub fn deinit(self: *Module, allocator: *Allocator) void { | 169 | pub fn deinit(self: *Module, allocator: *Allocator) void { |
| 126 | allocator.free(self.exports); | 170 | allocator.free(self.exports); |
| 127 | allocator.free(self.errors); | 171 | allocator.free(self.errors); |
| 128 | for (self.fns) |f| { | 172 | for (self.fns) |f| { |
| 129 | allocator.free(f.body); | 173 | allocator.free(f.body.instructions); |
| 130 | } | 174 | } |
| 131 | allocator.free(self.fns); | 175 | allocator.free(self.fns); |
| 132 | self.arena.deinit(); | 176 | self.arena.deinit(); |
| ... | @@ -192,10 +236,15 @@ const Analyze = struct { | ... | @@ -192,10 +236,15 @@ const Analyze = struct { |
| 192 | }; | 236 | }; |
| 193 | 237 | ||
| 194 | const Fn = struct { | 238 | const Fn = struct { |
| 195 | body: std.ArrayList(*Inst), | ||
| 196 | inst_table: std.AutoHashMap(*text.Inst, NewInst), | ||
| 197 | /// Index into Module fns array | 239 | /// Index into Module fns array |
| 198 | fn_index: usize, | 240 | fn_index: usize, |
| 241 | inner_block: Block, | ||
| 242 | inst_table: std.AutoHashMap(*text.Inst, NewInst), | ||
| 243 | }; | ||
| 244 | |||
| 245 | const Block = struct { | ||
| 246 | func: *Fn, | ||
| 247 | instructions: std.ArrayList(*Inst), | ||
| 199 | }; | 248 | }; |
| 200 | 249 | ||
| 201 | const InnerError = error{ OutOfMemory, AnalysisFail }; | 250 | const InnerError = error{ OutOfMemory, AnalysisFail }; |
| ... | @@ -208,9 +257,9 @@ const Analyze = struct { | ... | @@ -208,9 +257,9 @@ const Analyze = struct { |
| 208 | } | 257 | } |
| 209 | } | 258 | } |
| 210 | 259 | ||
| 211 | fn resolveInst(self: *Analyze, opt_func: ?*Fn, old_inst: *text.Inst) InnerError!*Inst { | 260 | fn resolveInst(self: *Analyze, opt_block: ?*Block, old_inst: *text.Inst) InnerError!*Inst { |
| 212 | if (opt_func) |func| { | 261 | if (opt_block) |block| { |
| 213 | if (func.inst_table.get(old_inst)) |kv| { | 262 | if (block.func.inst_table.get(old_inst)) |kv| { |
| 214 | return kv.value.ptr orelse return error.AnalysisFail; | 263 | return kv.value.ptr orelse return error.AnalysisFail; |
| 215 | } | 264 | } |
| 216 | } | 265 | } |
| ... | @@ -230,12 +279,12 @@ const Analyze = struct { | ... | @@ -230,12 +279,12 @@ const Analyze = struct { |
| 230 | } | 279 | } |
| 231 | } | 280 | } |
| 232 | 281 | ||
| 233 | fn requireFunctionBody(self: *Analyze, func: ?*Fn, src: usize) !*Fn { | 282 | fn requireRuntimeBlock(self: *Analyze, block: ?*Block, src: usize) !*Block { |
| 234 | return func orelse return self.fail(src, "instruction illegal outside function body", .{}); | 283 | return block orelse return self.fail(src, "instruction illegal outside function body", .{}); |
| 235 | } | 284 | } |
| 236 | 285 | ||
| 237 | fn resolveInstConst(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) InnerError!TypedValue { | 286 | fn resolveInstConst(self: *Analyze, block: ?*Block, old_inst: *text.Inst) InnerError!TypedValue { |
| 238 | const new_inst = try self.resolveInst(func, old_inst); | 287 | const new_inst = try self.resolveInst(block, old_inst); |
| 239 | const val = try self.resolveConstValue(new_inst); | 288 | const val = try self.resolveConstValue(new_inst); |
| 240 | return TypedValue{ | 289 | return TypedValue{ |
| 241 | .ty = new_inst.ty, | 290 | .ty = new_inst.ty, |
| ... | @@ -244,28 +293,39 @@ const Analyze = struct { | ... | @@ -244,28 +293,39 @@ const Analyze = struct { |
| 244 | } | 293 | } |
| 245 | 294 | ||
| 246 | fn resolveConstValue(self: *Analyze, base: *Inst) !Value { | 295 | fn resolveConstValue(self: *Analyze, base: *Inst) !Value { |
| 247 | return base.value() orelse return self.fail(base.src, "unable to resolve comptime value", .{}); | 296 | return (try self.resolveDefinedValue(base)) orelse |
| 297 | return self.fail(base.src, "unable to resolve comptime value", .{}); | ||
| 248 | } | 298 | } |
| 249 | 299 | ||
| 250 | fn resolveConstString(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) ![]u8 { | 300 | fn resolveDefinedValue(self: *Analyze, base: *Inst) !?Value { |
| 251 | const new_inst = try self.resolveInst(func, old_inst); | 301 | if (base.value()) |val| { |
| 302 | if (val.isUndef()) { | ||
| 303 | return self.fail(base.src, "use of undefined value here causes undefined behavior", .{}); | ||
| 304 | } | ||
| 305 | return val; | ||
| 306 | } | ||
| 307 | return null; | ||
| 308 | } | ||
| 309 | |||
| 310 | fn resolveConstString(self: *Analyze, block: ?*Block, old_inst: *text.Inst) ![]u8 { | ||
| 311 | const new_inst = try self.resolveInst(block, old_inst); | ||
| 252 | const wanted_type = Type.initTag(.const_slice_u8); | 312 | const wanted_type = Type.initTag(.const_slice_u8); |
| 253 | const coerced_inst = try self.coerce(func, wanted_type, new_inst); | 313 | const coerced_inst = try self.coerce(block, wanted_type, new_inst); |
| 254 | const val = try self.resolveConstValue(coerced_inst); | 314 | const val = try self.resolveConstValue(coerced_inst); |
| 255 | return val.toAllocatedBytes(&self.arena.allocator); | 315 | return val.toAllocatedBytes(&self.arena.allocator); |
| 256 | } | 316 | } |
| 257 | 317 | ||
| 258 | fn resolveType(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) !Type { | 318 | fn resolveType(self: *Analyze, block: ?*Block, old_inst: *text.Inst) !Type { |
| 259 | const new_inst = try self.resolveInst(func, old_inst); | 319 | const new_inst = try self.resolveInst(block, old_inst); |
| 260 | const wanted_type = Type.initTag(.@"type"); | 320 | const wanted_type = Type.initTag(.@"type"); |
| 261 | const coerced_inst = try self.coerce(func, wanted_type, new_inst); | 321 | const coerced_inst = try self.coerce(block, wanted_type, new_inst); |
| 262 | const val = try self.resolveConstValue(coerced_inst); | 322 | const val = try self.resolveConstValue(coerced_inst); |
| 263 | return val.toType(); | 323 | return val.toType(); |
| 264 | } | 324 | } |
| 265 | 325 | ||
| 266 | fn analyzeExport(self: *Analyze, func: ?*Fn, export_inst: *text.Inst.Export) !void { | 326 | fn analyzeExport(self: *Analyze, block: ?*Block, export_inst: *text.Inst.Export) !void { |
| 267 | const symbol_name = try self.resolveConstString(func, export_inst.positionals.symbol_name); | 327 | const symbol_name = try self.resolveConstString(block, export_inst.positionals.symbol_name); |
| 268 | const typed_value = try self.resolveInstConst(func, export_inst.positionals.value); | 328 | const typed_value = try self.resolveInstConst(block, export_inst.positionals.value); |
| 269 | 329 | ||
| 270 | switch (typed_value.ty.zigTypeTag()) { | 330 | switch (typed_value.ty.zigTypeTag()) { |
| 271 | .Fn => {}, | 331 | .Fn => {}, |
| ... | @@ -285,18 +345,18 @@ const Analyze = struct { | ... | @@ -285,18 +345,18 @@ const Analyze = struct { |
| 285 | /// TODO should not need the cast on the last parameter at the callsites | 345 | /// TODO should not need the cast on the last parameter at the callsites |
| 286 | fn addNewInstArgs( | 346 | fn addNewInstArgs( |
| 287 | self: *Analyze, | 347 | self: *Analyze, |
| 288 | func: *Fn, | 348 | block: *Block, |
| 289 | src: usize, | 349 | src: usize, |
| 290 | ty: Type, | 350 | ty: Type, |
| 291 | comptime T: type, | 351 | comptime T: type, |
| 292 | args: Inst.Args(T), | 352 | args: Inst.Args(T), |
| 293 | ) !*Inst { | 353 | ) !*Inst { |
| 294 | const inst = try self.addNewInst(func, src, ty, T); | 354 | const inst = try self.addNewInst(block, src, ty, T); |
| 295 | inst.args = args; | 355 | inst.args = args; |
| 296 | return &inst.base; | 356 | return &inst.base; |
| 297 | } | 357 | } |
| 298 | 358 | ||
| 299 | fn addNewInst(self: *Analyze, func: *Fn, src: usize, ty: Type, comptime T: type) !*T { | 359 | fn addNewInst(self: *Analyze, block: *Block, src: usize, ty: Type, comptime T: type) !*T { |
| 300 | const inst = try self.arena.allocator.create(T); | 360 | const inst = try self.arena.allocator.create(T); |
| 301 | inst.* = .{ | 361 | inst.* = .{ |
| 302 | .base = .{ | 362 | .base = .{ |
| ... | @@ -306,7 +366,7 @@ const Analyze = struct { | ... | @@ -306,7 +366,7 @@ const Analyze = struct { |
| 306 | }, | 366 | }, |
| 307 | .args = undefined, | 367 | .args = undefined, |
| 308 | }; | 368 | }; |
| 309 | try func.body.append(&inst.base); | 369 | try block.instructions.append(&inst.base); |
| 310 | return inst; | 370 | return inst; |
| 311 | } | 371 | } |
| 312 | 372 | ||
| ... | @@ -349,7 +409,21 @@ const Analyze = struct { | ... | @@ -349,7 +409,21 @@ const Analyze = struct { |
| 349 | fn constVoid(self: *Analyze, src: usize) !*Inst { | 409 | fn constVoid(self: *Analyze, src: usize) !*Inst { |
| 350 | return self.constInst(src, .{ | 410 | return self.constInst(src, .{ |
| 351 | .ty = Type.initTag(.void), | 411 | .ty = Type.initTag(.void), |
| 352 | .val = Value.initTag(.void_value), | 412 | .val = Value.initTag(.the_one_possible_value), |
| 413 | }); | ||
| 414 | } | ||
| 415 | |||
| 416 | fn constUndef(self: *Analyze, src: usize, ty: Type) !*Inst { | ||
| 417 | return self.constInst(src, .{ | ||
| 418 | .ty = ty, | ||
| 419 | .val = Value.initTag(.undef), | ||
| 420 | }); | ||
| 421 | } | ||
| 422 | |||
| 423 | fn constBool(self: *Analyze, src: usize, v: bool) !*Inst { | ||
| 424 | return self.constInst(src, .{ | ||
| 425 | .ty = Type.initTag(.bool), | ||
| 426 | .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)], | ||
| 353 | }); | 427 | }); |
| 354 | } | 428 | } |
| 355 | 429 | ||
| ... | @@ -399,7 +473,7 @@ const Analyze = struct { | ... | @@ -399,7 +473,7 @@ const Analyze = struct { |
| 399 | }); | 473 | }); |
| 400 | } | 474 | } |
| 401 | 475 | ||
| 402 | fn analyzeInst(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) InnerError!*Inst { | 476 | fn analyzeInst(self: *Analyze, block: ?*Block, old_inst: *text.Inst) InnerError!*Inst { |
| 403 | switch (old_inst.tag) { | 477 | switch (old_inst.tag) { |
| 404 | .str => { | 478 | .str => { |
| 405 | // We can use this reference because Inst.Const's Value is arena-allocated. | 479 | // We can use this reference because Inst.Const's Value is arena-allocated. |
| ... | @@ -411,35 +485,43 @@ const Analyze = struct { | ... | @@ -411,35 +485,43 @@ const Analyze = struct { |
| 411 | const big_int = old_inst.cast(text.Inst.Int).?.positionals.int; | 485 | const big_int = old_inst.cast(text.Inst.Int).?.positionals.int; |
| 412 | return self.constIntBig(old_inst.src, Type.initTag(.comptime_int), big_int); | 486 | return self.constIntBig(old_inst.src, Type.initTag(.comptime_int), big_int); |
| 413 | }, | 487 | }, |
| 414 | .ptrtoint => return self.analyzeInstPtrToInt(func, old_inst.cast(text.Inst.PtrToInt).?), | 488 | .ptrtoint => return self.analyzeInstPtrToInt(block, old_inst.cast(text.Inst.PtrToInt).?), |
| 415 | .fieldptr => return self.analyzeInstFieldPtr(func, old_inst.cast(text.Inst.FieldPtr).?), | 489 | .fieldptr => return self.analyzeInstFieldPtr(block, old_inst.cast(text.Inst.FieldPtr).?), |
| 416 | .deref => return self.analyzeInstDeref(func, old_inst.cast(text.Inst.Deref).?), | 490 | .deref => return self.analyzeInstDeref(block, old_inst.cast(text.Inst.Deref).?), |
| 417 | .as => return self.analyzeInstAs(func, old_inst.cast(text.Inst.As).?), | 491 | .as => return self.analyzeInstAs(block, old_inst.cast(text.Inst.As).?), |
| 418 | .@"asm" => return self.analyzeInstAsm(func, old_inst.cast(text.Inst.Asm).?), | 492 | .@"asm" => return self.analyzeInstAsm(block, old_inst.cast(text.Inst.Asm).?), |
| 419 | .@"unreachable" => return self.analyzeInstUnreachable(func, old_inst.cast(text.Inst.Unreachable).?), | 493 | .@"unreachable" => return self.analyzeInstUnreachable(block, old_inst.cast(text.Inst.Unreachable).?), |
| 420 | .@"fn" => return self.analyzeInstFn(func, old_inst.cast(text.Inst.Fn).?), | 494 | .@"fn" => return self.analyzeInstFn(block, old_inst.cast(text.Inst.Fn).?), |
| 421 | .@"export" => { | 495 | .@"export" => { |
| 422 | try self.analyzeExport(func, old_inst.cast(text.Inst.Export).?); | 496 | try self.analyzeExport(block, old_inst.cast(text.Inst.Export).?); |
| 423 | return self.constVoid(old_inst.src); | 497 | return self.constVoid(old_inst.src); |
| 424 | }, | 498 | }, |
| 425 | .primitive => return self.analyzeInstPrimitive(func, old_inst.cast(text.Inst.Primitive).?), | 499 | .primitive => return self.analyzeInstPrimitive(old_inst.cast(text.Inst.Primitive).?), |
| 426 | .fntype => return self.analyzeInstFnType(func, old_inst.cast(text.Inst.FnType).?), | 500 | .fntype => return self.analyzeInstFnType(block, old_inst.cast(text.Inst.FnType).?), |
| 427 | .intcast => return self.analyzeInstIntCast(func, old_inst.cast(text.Inst.IntCast).?), | 501 | .intcast => return self.analyzeInstIntCast(block, old_inst.cast(text.Inst.IntCast).?), |
| 428 | .bitcast => return self.analyzeInstBitCast(func, old_inst.cast(text.Inst.BitCast).?), | 502 | .bitcast => return self.analyzeInstBitCast(block, old_inst.cast(text.Inst.BitCast).?), |
| 429 | .elemptr => return self.analyzeInstElemPtr(func, old_inst.cast(text.Inst.ElemPtr).?), | 503 | .elemptr => return self.analyzeInstElemPtr(block, old_inst.cast(text.Inst.ElemPtr).?), |
| 430 | .add => return self.analyzeInstAdd(func, old_inst.cast(text.Inst.Add).?), | 504 | .add => return self.analyzeInstAdd(block, old_inst.cast(text.Inst.Add).?), |
| 505 | .cmp => return self.analyzeInstCmp(block, old_inst.cast(text.Inst.Cmp).?), | ||
| 506 | .condbr => return self.analyzeInstCondBr(block, old_inst.cast(text.Inst.CondBr).?), | ||
| 507 | .isnull => return self.analyzeInstIsNull(block, old_inst.cast(text.Inst.IsNull).?), | ||
| 508 | .isnonnull => return self.analyzeInstIsNonNull(block, old_inst.cast(text.Inst.IsNonNull).?), | ||
| 431 | } | 509 | } |
| 432 | } | 510 | } |
| 433 | 511 | ||
| 434 | fn analyzeInstFn(self: *Analyze, opt_func: ?*Fn, fn_inst: *text.Inst.Fn) InnerError!*Inst { | 512 | fn analyzeInstFn(self: *Analyze, block: ?*Block, fn_inst: *text.Inst.Fn) InnerError!*Inst { |
| 435 | const fn_type = try self.resolveType(opt_func, fn_inst.positionals.fn_type); | 513 | const fn_type = try self.resolveType(block, fn_inst.positionals.fn_type); |
| 436 | 514 | ||
| 437 | var new_func: Fn = .{ | 515 | var new_func: Fn = .{ |
| 438 | .body = std.ArrayList(*Inst).init(self.allocator), | ||
| 439 | .inst_table = std.AutoHashMap(*text.Inst, NewInst).init(self.allocator), | ||
| 440 | .fn_index = self.fns.items.len, | 516 | .fn_index = self.fns.items.len, |
| 517 | .inner_block = .{ | ||
| 518 | .func = undefined, | ||
| 519 | .instructions = std.ArrayList(*Inst).init(self.allocator), | ||
| 520 | }, | ||
| 521 | .inst_table = std.AutoHashMap(*text.Inst, NewInst).init(self.allocator), | ||
| 441 | }; | 522 | }; |
| 442 | defer new_func.body.deinit(); | 523 | new_func.inner_block.func = &new_func; |
| 524 | defer new_func.inner_block.instructions.deinit(); | ||
| 443 | defer new_func.inst_table.deinit(); | 525 | defer new_func.inst_table.deinit(); |
| 444 | // Don't hang on to a reference to this when analyzing body instructions, since the memory | 526 | // Don't hang on to a reference to this when analyzing body instructions, since the memory |
| 445 | // could become invalid. | 527 | // could become invalid. |
| ... | @@ -449,18 +531,11 @@ const Analyze = struct { | ... | @@ -449,18 +531,11 @@ const Analyze = struct { |
| 449 | .body = undefined, | 531 | .body = undefined, |
| 450 | }; | 532 | }; |
| 451 | 533 | ||
| 452 | for (fn_inst.positionals.body.instructions) |src_inst| { | 534 | try self.analyzeBody(&new_func.inner_block, fn_inst.positionals.body); |
| 453 | const new_inst = self.analyzeInst(&new_func, src_inst) catch |err| { | ||
| 454 | self.fns.items[new_func.fn_index].analysis_status = .failure; | ||
| 455 | try new_func.inst_table.putNoClobber(src_inst, .{ .ptr = null }); | ||
| 456 | return err; | ||
| 457 | }; | ||
| 458 | try new_func.inst_table.putNoClobber(src_inst, .{ .ptr = new_inst }); | ||
| 459 | } | ||
| 460 | 535 | ||
| 461 | const f = &self.fns.items[new_func.fn_index]; | 536 | const f = &self.fns.items[new_func.fn_index]; |
| 462 | f.analysis_status = .success; | 537 | f.analysis_status = .success; |
| 463 | f.body = new_func.body.toOwnedSlice(); | 538 | f.body = .{ .instructions = new_func.inner_block.instructions.toOwnedSlice() }; |
| 464 | 539 | ||
| 465 | const fn_payload = try self.arena.allocator.create(Value.Payload.Function); | 540 | const fn_payload = try self.arena.allocator.create(Value.Payload.Function); |
| 466 | fn_payload.* = .{ .index = new_func.fn_index }; | 541 | fn_payload.* = .{ .index = new_func.fn_index }; |
| ... | @@ -471,8 +546,8 @@ const Analyze = struct { | ... | @@ -471,8 +546,8 @@ const Analyze = struct { |
| 471 | }); | 546 | }); |
| 472 | } | 547 | } |
| 473 | 548 | ||
| 474 | fn analyzeInstFnType(self: *Analyze, func: ?*Fn, fntype: *text.Inst.FnType) InnerError!*Inst { | 549 | fn analyzeInstFnType(self: *Analyze, block: ?*Block, fntype: *text.Inst.FnType) InnerError!*Inst { |
| 475 | const return_type = try self.resolveType(func, fntype.positionals.return_type); | 550 | const return_type = try self.resolveType(block, fntype.positionals.return_type); |
| 476 | 551 | ||
| 477 | if (return_type.zigTypeTag() == .NoReturn and | 552 | if (return_type.zigTypeTag() == .NoReturn and |
| 478 | fntype.positionals.param_types.len == 0 and | 553 | fntype.positionals.param_types.len == 0 and |
| ... | @@ -484,30 +559,30 @@ const Analyze = struct { | ... | @@ -484,30 +559,30 @@ const Analyze = struct { |
| 484 | return self.fail(fntype.base.src, "TODO implement fntype instruction more", .{}); | 559 | return self.fail(fntype.base.src, "TODO implement fntype instruction more", .{}); |
| 485 | } | 560 | } |
| 486 | 561 | ||
| 487 | fn analyzeInstPrimitive(self: *Analyze, func: ?*Fn, primitive: *text.Inst.Primitive) InnerError!*Inst { | 562 | fn analyzeInstPrimitive(self: *Analyze, primitive: *text.Inst.Primitive) InnerError!*Inst { |
| 488 | return self.constType(primitive.base.src, primitive.positionals.tag.toType()); | 563 | return self.constType(primitive.base.src, primitive.positionals.tag.toType()); |
| 489 | } | 564 | } |
| 490 | 565 | ||
| 491 | fn analyzeInstAs(self: *Analyze, func: ?*Fn, as: *text.Inst.As) InnerError!*Inst { | 566 | fn analyzeInstAs(self: *Analyze, block: ?*Block, as: *text.Inst.As) InnerError!*Inst { |
| 492 | const dest_type = try self.resolveType(func, as.positionals.dest_type); | 567 | const dest_type = try self.resolveType(block, as.positionals.dest_type); |
| 493 | const new_inst = try self.resolveInst(func, as.positionals.value); | 568 | const new_inst = try self.resolveInst(block, as.positionals.value); |
| 494 | return self.coerce(func, dest_type, new_inst); | 569 | return self.coerce(block, dest_type, new_inst); |
| 495 | } | 570 | } |
| 496 | 571 | ||
| 497 | fn analyzeInstPtrToInt(self: *Analyze, func: ?*Fn, ptrtoint: *text.Inst.PtrToInt) InnerError!*Inst { | 572 | fn analyzeInstPtrToInt(self: *Analyze, block: ?*Block, ptrtoint: *text.Inst.PtrToInt) InnerError!*Inst { |
| 498 | const ptr = try self.resolveInst(func, ptrtoint.positionals.ptr); | 573 | const ptr = try self.resolveInst(block, ptrtoint.positionals.ptr); |
| 499 | if (ptr.ty.zigTypeTag() != .Pointer) { | 574 | if (ptr.ty.zigTypeTag() != .Pointer) { |
| 500 | return self.fail(ptrtoint.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty}); | 575 | return self.fail(ptrtoint.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty}); |
| 501 | } | 576 | } |
| 502 | // TODO handle known-pointer-address | 577 | // TODO handle known-pointer-address |
| 503 | const f = try self.requireFunctionBody(func, ptrtoint.base.src); | 578 | const b = try self.requireRuntimeBlock(block, ptrtoint.base.src); |
| 504 | const ty = Type.initTag(.usize); | 579 | const ty = Type.initTag(.usize); |
| 505 | return self.addNewInstArgs(f, ptrtoint.base.src, ty, Inst.PtrToInt, Inst.Args(Inst.PtrToInt){ .ptr = ptr }); | 580 | return self.addNewInstArgs(b, ptrtoint.base.src, ty, Inst.PtrToInt, Inst.Args(Inst.PtrToInt){ .ptr = ptr }); |
| 506 | } | 581 | } |
| 507 | 582 | ||
| 508 | fn analyzeInstFieldPtr(self: *Analyze, func: ?*Fn, fieldptr: *text.Inst.FieldPtr) InnerError!*Inst { | 583 | fn analyzeInstFieldPtr(self: *Analyze, block: ?*Block, fieldptr: *text.Inst.FieldPtr) InnerError!*Inst { |
| 509 | const object_ptr = try self.resolveInst(func, fieldptr.positionals.object_ptr); | 584 | const object_ptr = try self.resolveInst(block, fieldptr.positionals.object_ptr); |
| 510 | const field_name = try self.resolveConstString(func, fieldptr.positionals.field_name); | 585 | const field_name = try self.resolveConstString(block, fieldptr.positionals.field_name); |
| 511 | 586 | ||
| 512 | const elem_ty = switch (object_ptr.ty.zigTypeTag()) { | 587 | const elem_ty = switch (object_ptr.ty.zigTypeTag()) { |
| 513 | .Pointer => object_ptr.ty.elemType(), | 588 | .Pointer => object_ptr.ty.elemType(), |
| ... | @@ -538,9 +613,9 @@ const Analyze = struct { | ... | @@ -538,9 +613,9 @@ const Analyze = struct { |
| 538 | } | 613 | } |
| 539 | } | 614 | } |
| 540 | 615 | ||
| 541 | fn analyzeInstIntCast(self: *Analyze, func: ?*Fn, intcast: *text.Inst.IntCast) InnerError!*Inst { | 616 | fn analyzeInstIntCast(self: *Analyze, block: ?*Block, intcast: *text.Inst.IntCast) InnerError!*Inst { |
| 542 | const dest_type = try self.resolveType(func, intcast.positionals.dest_type); | 617 | const dest_type = try self.resolveType(block, intcast.positionals.dest_type); |
| 543 | const new_inst = try self.resolveInst(func, intcast.positionals.value); | 618 | const new_inst = try self.resolveInst(block, intcast.positionals.value); |
| 544 | 619 | ||
| 545 | const dest_is_comptime_int = switch (dest_type.zigTypeTag()) { | 620 | const dest_is_comptime_int = switch (dest_type.zigTypeTag()) { |
| 546 | .ComptimeInt => true, | 621 | .ComptimeInt => true, |
| ... | @@ -564,22 +639,22 @@ const Analyze = struct { | ... | @@ -564,22 +639,22 @@ const Analyze = struct { |
| 564 | } | 639 | } |
| 565 | 640 | ||
| 566 | if (dest_is_comptime_int or new_inst.value() != null) { | 641 | if (dest_is_comptime_int or new_inst.value() != null) { |
| 567 | return self.coerce(func, dest_type, new_inst); | 642 | return self.coerce(block, dest_type, new_inst); |
| 568 | } | 643 | } |
| 569 | 644 | ||
| 570 | return self.fail(intcast.base.src, "TODO implement analyze widen or shorten int", .{}); | 645 | return self.fail(intcast.base.src, "TODO implement analyze widen or shorten int", .{}); |
| 571 | } | 646 | } |
| 572 | 647 | ||
| 573 | fn analyzeInstBitCast(self: *Analyze, func: ?*Fn, inst: *text.Inst.BitCast) InnerError!*Inst { | 648 | fn analyzeInstBitCast(self: *Analyze, block: ?*Block, inst: *text.Inst.BitCast) InnerError!*Inst { |
| 574 | const dest_type = try self.resolveType(func, inst.positionals.dest_type); | 649 | const dest_type = try self.resolveType(block, inst.positionals.dest_type); |
| 575 | const operand = try self.resolveInst(func, inst.positionals.operand); | 650 | const operand = try self.resolveInst(block, inst.positionals.operand); |
| 576 | return self.bitcast(func, dest_type, operand); | 651 | return self.bitcast(block, dest_type, operand); |
| 577 | } | 652 | } |
| 578 | 653 | ||
| 579 | fn analyzeInstElemPtr(self: *Analyze, func: ?*Fn, inst: *text.Inst.ElemPtr) InnerError!*Inst { | 654 | fn analyzeInstElemPtr(self: *Analyze, block: ?*Block, inst: *text.Inst.ElemPtr) InnerError!*Inst { |
| 580 | const array_ptr = try self.resolveInst(func, inst.positionals.array_ptr); | 655 | const array_ptr = try self.resolveInst(block, inst.positionals.array_ptr); |
| 581 | const uncasted_index = try self.resolveInst(func, inst.positionals.index); | 656 | const uncasted_index = try self.resolveInst(block, inst.positionals.index); |
| 582 | const elem_index = try self.coerce(func, Type.initTag(.usize), uncasted_index); | 657 | const elem_index = try self.coerce(block, Type.initTag(.usize), uncasted_index); |
| 583 | 658 | ||
| 584 | if (array_ptr.ty.isSinglePointer() and array_ptr.ty.elemType().zigTypeTag() == .Array) { | 659 | if (array_ptr.ty.isSinglePointer() and array_ptr.ty.elemType().zigTypeTag() == .Array) { |
| 585 | if (array_ptr.value()) |array_ptr_val| { | 660 | if (array_ptr.value()) |array_ptr_val| { |
| ... | @@ -607,15 +682,19 @@ const Analyze = struct { | ... | @@ -607,15 +682,19 @@ const Analyze = struct { |
| 607 | return self.fail(inst.base.src, "TODO implement more analyze elemptr", .{}); | 682 | return self.fail(inst.base.src, "TODO implement more analyze elemptr", .{}); |
| 608 | } | 683 | } |
| 609 | 684 | ||
| 610 | fn analyzeInstAdd(self: *Analyze, func: ?*Fn, inst: *text.Inst.Add) InnerError!*Inst { | 685 | fn analyzeInstAdd(self: *Analyze, block: ?*Block, inst: *text.Inst.Add) InnerError!*Inst { |
| 611 | const lhs = try self.resolveInst(func, inst.positionals.lhs); | 686 | const lhs = try self.resolveInst(block, inst.positionals.lhs); |
| 612 | const rhs = try self.resolveInst(func, inst.positionals.rhs); | 687 | const rhs = try self.resolveInst(block, inst.positionals.rhs); |
| 613 | 688 | ||
| 614 | if (lhs.ty.zigTypeTag() == .Int and rhs.ty.zigTypeTag() == .Int) { | 689 | if (lhs.ty.zigTypeTag() == .Int and rhs.ty.zigTypeTag() == .Int) { |
| 615 | if (lhs.value()) |lhs_val| { | 690 | if (lhs.value()) |lhs_val| { |
| 616 | if (rhs.value()) |rhs_val| { | 691 | if (rhs.value()) |rhs_val| { |
| 617 | const lhs_bigint = try lhs_val.toBigInt(&self.arena.allocator); | 692 | // TODO is this a performance issue? maybe we should try the operation without |
| 618 | const rhs_bigint = try rhs_val.toBigInt(&self.arena.allocator); | 693 | // resorting to BigInt first. |
| 694 | var lhs_space: Value.BigIntSpace = undefined; | ||
| 695 | var rhs_space: Value.BigIntSpace = undefined; | ||
| 696 | const lhs_bigint = lhs_val.toBigInt(&lhs_space); | ||
| 697 | const rhs_bigint = rhs_val.toBigInt(&rhs_space); | ||
| 619 | var result_bigint = try BigInt.init(&self.arena.allocator); | 698 | var result_bigint = try BigInt.init(&self.arena.allocator); |
| 620 | try BigInt.add(&result_bigint, lhs_bigint, rhs_bigint); | 699 | try BigInt.add(&result_bigint, lhs_bigint, rhs_bigint); |
| 621 | 700 | ||
| ... | @@ -637,8 +716,8 @@ const Analyze = struct { | ... | @@ -637,8 +716,8 @@ const Analyze = struct { |
| 637 | return self.fail(inst.base.src, "TODO implement more analyze add", .{}); | 716 | return self.fail(inst.base.src, "TODO implement more analyze add", .{}); |
| 638 | } | 717 | } |
| 639 | 718 | ||
| 640 | fn analyzeInstDeref(self: *Analyze, func: ?*Fn, deref: *text.Inst.Deref) InnerError!*Inst { | 719 | fn analyzeInstDeref(self: *Analyze, block: ?*Block, deref: *text.Inst.Deref) InnerError!*Inst { |
| 641 | const ptr = try self.resolveInst(func, deref.positionals.ptr); | 720 | const ptr = try self.resolveInst(block, deref.positionals.ptr); |
| 642 | const elem_ty = switch (ptr.ty.zigTypeTag()) { | 721 | const elem_ty = switch (ptr.ty.zigTypeTag()) { |
| 643 | .Pointer => ptr.ty.elemType(), | 722 | .Pointer => ptr.ty.elemType(), |
| 644 | else => return self.fail(deref.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty}), | 723 | else => return self.fail(deref.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty}), |
| ... | @@ -653,28 +732,28 @@ const Analyze = struct { | ... | @@ -653,28 +732,28 @@ const Analyze = struct { |
| 653 | return self.fail(deref.base.src, "TODO implement runtime deref", .{}); | 732 | return self.fail(deref.base.src, "TODO implement runtime deref", .{}); |
| 654 | } | 733 | } |
| 655 | 734 | ||
| 656 | fn analyzeInstAsm(self: *Analyze, func: ?*Fn, assembly: *text.Inst.Asm) InnerError!*Inst { | 735 | fn analyzeInstAsm(self: *Analyze, block: ?*Block, assembly: *text.Inst.Asm) InnerError!*Inst { |
| 657 | const return_type = try self.resolveType(func, assembly.positionals.return_type); | 736 | const return_type = try self.resolveType(block, assembly.positionals.return_type); |
| 658 | const asm_source = try self.resolveConstString(func, assembly.positionals.asm_source); | 737 | const asm_source = try self.resolveConstString(block, assembly.positionals.asm_source); |
| 659 | const output = if (assembly.kw_args.output) |o| try self.resolveConstString(func, o) else null; | 738 | const output = if (assembly.kw_args.output) |o| try self.resolveConstString(block, o) else null; |
| 660 | 739 | ||
| 661 | const inputs = try self.arena.allocator.alloc([]const u8, assembly.kw_args.inputs.len); | 740 | const inputs = try self.arena.allocator.alloc([]const u8, assembly.kw_args.inputs.len); |
| 662 | const clobbers = try self.arena.allocator.alloc([]const u8, assembly.kw_args.clobbers.len); | 741 | const clobbers = try self.arena.allocator.alloc([]const u8, assembly.kw_args.clobbers.len); |
| 663 | const args = try self.arena.allocator.alloc(*Inst, assembly.kw_args.args.len); | 742 | const args = try self.arena.allocator.alloc(*Inst, assembly.kw_args.args.len); |
| 664 | 743 | ||
| 665 | for (inputs) |*elem, i| { | 744 | for (inputs) |*elem, i| { |
| 666 | elem.* = try self.resolveConstString(func, assembly.kw_args.inputs[i]); | 745 | elem.* = try self.resolveConstString(block, assembly.kw_args.inputs[i]); |
| 667 | } | 746 | } |
| 668 | for (clobbers) |*elem, i| { | 747 | for (clobbers) |*elem, i| { |
| 669 | elem.* = try self.resolveConstString(func, assembly.kw_args.clobbers[i]); | 748 | elem.* = try self.resolveConstString(block, assembly.kw_args.clobbers[i]); |
| 670 | } | 749 | } |
| 671 | for (args) |*elem, i| { | 750 | for (args) |*elem, i| { |
| 672 | const arg = try self.resolveInst(func, assembly.kw_args.args[i]); | 751 | const arg = try self.resolveInst(block, assembly.kw_args.args[i]); |
| 673 | elem.* = try self.coerce(func, Type.initTag(.usize), arg); | 752 | elem.* = try self.coerce(block, Type.initTag(.usize), arg); |
| 674 | } | 753 | } |
| 675 | 754 | ||
| 676 | const f = try self.requireFunctionBody(func, assembly.base.src); | 755 | const b = try self.requireRuntimeBlock(block, assembly.base.src); |
| 677 | return self.addNewInstArgs(f, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){ | 756 | return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){ |
| 678 | .asm_source = asm_source, | 757 | .asm_source = asm_source, |
| 679 | .is_volatile = assembly.kw_args.@"volatile", | 758 | .is_volatile = assembly.kw_args.@"volatile", |
| 680 | .output = output, | 759 | .output = output, |
| ... | @@ -684,19 +763,350 @@ const Analyze = struct { | ... | @@ -684,19 +763,350 @@ const Analyze = struct { |
| 684 | }); | 763 | }); |
| 685 | } | 764 | } |
| 686 | 765 | ||
| 687 | fn analyzeInstUnreachable(self: *Analyze, func: ?*Fn, unreach: *text.Inst.Unreachable) InnerError!*Inst { | 766 | fn analyzeInstCmp(self: *Analyze, block: ?*Block, inst: *text.Inst.Cmp) InnerError!*Inst { |
| 688 | const f = try self.requireFunctionBody(func, unreach.base.src); | 767 | const lhs = try self.resolveInst(block, inst.positionals.lhs); |
| 689 | return self.addNewInstArgs(f, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {}); | 768 | const rhs = try self.resolveInst(block, inst.positionals.rhs); |
| 769 | const op = inst.positionals.op; | ||
| 770 | |||
| 771 | const is_equality_cmp = switch (op) { | ||
| 772 | .eq, .neq => true, | ||
| 773 | else => false, | ||
| 774 | }; | ||
| 775 | const lhs_ty_tag = lhs.ty.zigTypeTag(); | ||
| 776 | const rhs_ty_tag = rhs.ty.zigTypeTag(); | ||
| 777 | if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) { | ||
| 778 | // null == null, null != null | ||
| 779 | return self.constBool(inst.base.src, op == .eq); | ||
| 780 | } else if (is_equality_cmp and | ||
| 781 | ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or | ||
| 782 | rhs_ty_tag == .Null and lhs_ty_tag == .Optional)) | ||
| 783 | { | ||
| 784 | // comparing null with optionals | ||
| 785 | const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs; | ||
| 786 | if (opt_operand.value()) |opt_val| { | ||
| 787 | const is_null = opt_val.isNull(); | ||
| 788 | return self.constBool(inst.base.src, if (op == .eq) is_null else !is_null); | ||
| 789 | } | ||
| 790 | const b = try self.requireRuntimeBlock(block, inst.base.src); | ||
| 791 | switch (op) { | ||
| 792 | .eq => return self.addNewInstArgs( | ||
| 793 | b, | ||
| 794 | inst.base.src, | ||
| 795 | Type.initTag(.bool), | ||
| 796 | Inst.IsNull, | ||
| 797 | Inst.Args(Inst.IsNull){ .operand = opt_operand }, | ||
| 798 | ), | ||
| 799 | .neq => return self.addNewInstArgs( | ||
| 800 | b, | ||
| 801 | inst.base.src, | ||
| 802 | Type.initTag(.bool), | ||
| 803 | Inst.IsNonNull, | ||
| 804 | Inst.Args(Inst.IsNonNull){ .operand = opt_operand }, | ||
| 805 | ), | ||
| 806 | else => unreachable, | ||
| 807 | } | ||
| 808 | } else if (is_equality_cmp and | ||
| 809 | ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr()))) | ||
| 810 | { | ||
| 811 | return self.fail(inst.base.src, "TODO implement C pointer cmp", .{}); | ||
| 812 | } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) { | ||
| 813 | const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty; | ||
| 814 | return self.fail(inst.base.src, "comparison of '{}' with null", .{non_null_type}); | ||
| 815 | } else if (is_equality_cmp and | ||
| 816 | ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or | ||
| 817 | (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union))) | ||
| 818 | { | ||
| 819 | return self.fail(inst.base.src, "TODO implement equality comparison between a union's tag value and an enum literal", .{}); | ||
| 820 | } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) { | ||
| 821 | if (!is_equality_cmp) { | ||
| 822 | return self.fail(inst.base.src, "{} operator not allowed for errors", .{@tagName(op)}); | ||
| 823 | } | ||
| 824 | return self.fail(inst.base.src, "TODO implement equality comparison between errors", .{}); | ||
| 825 | } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) { | ||
| 826 | // This operation allows any combination of integer and float types, regardless of the | ||
| 827 | // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for | ||
| 828 | // numeric types. | ||
| 829 | return self.cmpNumeric(block, inst.base.src, lhs, rhs, op); | ||
| 830 | } | ||
| 831 | return self.fail(inst.base.src, "TODO implement more cmp analysis", .{}); | ||
| 832 | } | ||
| 833 | |||
| 834 | fn analyzeInstIsNull(self: *Analyze, block: ?*Block, inst: *text.Inst.IsNull) InnerError!*Inst { | ||
| 835 | const operand = try self.resolveInst(block, inst.positionals.operand); | ||
| 836 | return self.analyzeIsNull(block, inst.base.src, operand, true); | ||
| 837 | } | ||
| 838 | |||
| 839 | fn analyzeInstIsNonNull(self: *Analyze, block: ?*Block, inst: *text.Inst.IsNonNull) InnerError!*Inst { | ||
| 840 | const operand = try self.resolveInst(block, inst.positionals.operand); | ||
| 841 | return self.analyzeIsNull(block, inst.base.src, operand, false); | ||
| 842 | } | ||
| 843 | |||
| 844 | fn analyzeInstCondBr(self: *Analyze, block: ?*Block, inst: *text.Inst.CondBr) InnerError!*Inst { | ||
| 845 | const uncasted_cond = try self.resolveInst(block, inst.positionals.condition); | ||
| 846 | const cond = try self.coerce(block, Type.initTag(.bool), uncasted_cond); | ||
| 847 | |||
| 848 | if (try self.resolveDefinedValue(cond)) |cond_val| { | ||
| 849 | const body = if (cond_val.toBool()) &inst.positionals.true_body else &inst.positionals.false_body; | ||
| 850 | try self.analyzeBody(block, body.*); | ||
| 851 | return self.constVoid(inst.base.src); | ||
| 852 | } | ||
| 853 | |||
| 854 | const parent_block = try self.requireRuntimeBlock(block, inst.base.src); | ||
| 855 | |||
| 856 | var true_block: Block = .{ | ||
| 857 | .func = parent_block.func, | ||
| 858 | .instructions = std.ArrayList(*Inst).init(self.allocator), | ||
| 859 | }; | ||
| 860 | defer true_block.instructions.deinit(); | ||
| 861 | try self.analyzeBody(&true_block, inst.positionals.true_body); | ||
| 862 | |||
| 863 | var false_block: Block = .{ | ||
| 864 | .func = parent_block.func, | ||
| 865 | .instructions = std.ArrayList(*Inst).init(self.allocator), | ||
| 866 | }; | ||
| 867 | defer false_block.instructions.deinit(); | ||
| 868 | try self.analyzeBody(&false_block, inst.positionals.false_body); | ||
| 869 | |||
| 870 | // Copy the instruction pointers to the arena memory | ||
| 871 | const true_instructions = try self.arena.allocator.alloc(*Inst, true_block.instructions.items.len); | ||
| 872 | const false_instructions = try self.arena.allocator.alloc(*Inst, false_block.instructions.items.len); | ||
| 873 | |||
| 874 | mem.copy(*Inst, true_instructions, true_block.instructions.items); | ||
| 875 | mem.copy(*Inst, false_instructions, false_block.instructions.items); | ||
| 876 | |||
| 877 | return self.addNewInstArgs(parent_block, inst.base.src, Type.initTag(.void), Inst.CondBr, Inst.Args(Inst.CondBr){ | ||
| 878 | .condition = cond, | ||
| 879 | .true_body = .{ .instructions = true_instructions }, | ||
| 880 | .false_body = .{ .instructions = false_instructions }, | ||
| 881 | }); | ||
| 882 | } | ||
| 883 | |||
| 884 | fn analyzeInstUnreachable(self: *Analyze, block: ?*Block, unreach: *text.Inst.Unreachable) InnerError!*Inst { | ||
| 885 | const b = try self.requireRuntimeBlock(block, unreach.base.src); | ||
| 886 | return self.addNewInstArgs(b, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {}); | ||
| 887 | } | ||
| 888 | |||
| 889 | fn analyzeBody(self: *Analyze, block: ?*Block, body: text.Module.Body) !void { | ||
| 890 | for (body.instructions) |src_inst| { | ||
| 891 | const new_inst = self.analyzeInst(block, src_inst) catch |err| { | ||
| 892 | if (block) |b| { | ||
| 893 | self.fns.items[b.func.fn_index].analysis_status = .failure; | ||
| 894 | try b.func.inst_table.putNoClobber(src_inst, .{ .ptr = null }); | ||
| 895 | } | ||
| 896 | return err; | ||
| 897 | }; | ||
| 898 | if (block) |b| try b.func.inst_table.putNoClobber(src_inst, .{ .ptr = new_inst }); | ||
| 899 | } | ||
| 900 | } | ||
| 901 | |||
| 902 | fn analyzeIsNull( | ||
| 903 | self: *Analyze, | ||
| 904 | block: ?*Block, | ||
| 905 | src: usize, | ||
| 906 | operand: *Inst, | ||
| 907 | invert_logic: bool, | ||
| 908 | ) InnerError!*Inst { | ||
| 909 | return self.fail(src, "TODO implement analysis of isnull and isnotnull", .{}); | ||
| 910 | } | ||
| 911 | |||
| 912 | /// Asserts that lhs and rhs types are both numeric. | ||
| 913 | fn cmpNumeric( | ||
| 914 | self: *Analyze, | ||
| 915 | block: ?*Block, | ||
| 916 | src: usize, | ||
| 917 | lhs: *Inst, | ||
| 918 | rhs: *Inst, | ||
| 919 | op: std.math.CompareOperator, | ||
| 920 | ) !*Inst { | ||
| 921 | assert(lhs.ty.isNumeric()); | ||
| 922 | assert(rhs.ty.isNumeric()); | ||
| 923 | |||
| 924 | const lhs_ty_tag = lhs.ty.zigTypeTag(); | ||
| 925 | const rhs_ty_tag = rhs.ty.zigTypeTag(); | ||
| 926 | |||
| 927 | if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) { | ||
| 928 | if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { | ||
| 929 | return self.fail(src, "vector length mismatch: {} and {}", .{ | ||
| 930 | lhs.ty.arrayLen(), | ||
| 931 | rhs.ty.arrayLen(), | ||
| 932 | }); | ||
| 933 | } | ||
| 934 | return self.fail(src, "TODO implement support for vectors in cmpNumeric", .{}); | ||
| 935 | } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) { | ||
| 936 | return self.fail(src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{ | ||
| 937 | lhs.ty, | ||
| 938 | rhs.ty, | ||
| 939 | }); | ||
| 940 | } | ||
| 941 | |||
| 942 | if (lhs.value()) |lhs_val| { | ||
| 943 | if (rhs.value()) |rhs_val| { | ||
| 944 | return self.constBool(src, Value.compare(lhs_val, op, rhs_val)); | ||
| 945 | } | ||
| 946 | } | ||
| 947 | |||
| 948 | // TODO handle comparisons against lazy zero values | ||
| 949 | // Some values can be compared against zero without being runtime known or without forcing | ||
| 950 | // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to | ||
| 951 | // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout | ||
| 952 | // of this function if we don't need to. | ||
| 953 | |||
| 954 | // It must be a runtime comparison. | ||
| 955 | const b = try self.requireRuntimeBlock(block, src); | ||
| 956 | // For floats, emit a float comparison instruction. | ||
| 957 | const lhs_is_float = switch (lhs_ty_tag) { | ||
| 958 | .Float, .ComptimeFloat => true, | ||
| 959 | else => false, | ||
| 960 | }; | ||
| 961 | const rhs_is_float = switch (rhs_ty_tag) { | ||
| 962 | .Float, .ComptimeFloat => true, | ||
| 963 | else => false, | ||
| 964 | }; | ||
| 965 | if (lhs_is_float and rhs_is_float) { | ||
| 966 | // Implicit cast the smaller one to the larger one. | ||
| 967 | const dest_type = x: { | ||
| 968 | if (lhs_ty_tag == .ComptimeFloat) { | ||
| 969 | break :x rhs.ty; | ||
| 970 | } else if (rhs_ty_tag == .ComptimeFloat) { | ||
| 971 | break :x lhs.ty; | ||
| 972 | } | ||
| 973 | if (lhs.ty.floatBits(self.target) >= rhs.ty.floatBits(self.target)) { | ||
| 974 | break :x lhs.ty; | ||
| 975 | } else { | ||
| 976 | break :x rhs.ty; | ||
| 977 | } | ||
| 978 | }; | ||
| 979 | const casted_lhs = try self.coerce(block, dest_type, lhs); | ||
| 980 | const casted_rhs = try self.coerce(block, dest_type, rhs); | ||
| 981 | return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){ | ||
| 982 | .lhs = casted_lhs, | ||
| 983 | .rhs = casted_rhs, | ||
| 984 | .op = op, | ||
| 985 | }); | ||
| 986 | } | ||
| 987 | // For mixed unsigned integer sizes, implicit cast both operands to the larger integer. | ||
| 988 | // For mixed signed and unsigned integers, implicit cast both operands to a signed | ||
| 989 | // integer with + 1 bit. | ||
| 990 | // For mixed floats and integers, extract the integer part from the float, cast that to | ||
| 991 | // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float, | ||
| 992 | // add/subtract 1. | ||
| 993 | const lhs_is_signed = if (lhs.value()) |lhs_val| | ||
| 994 | lhs_val.compareWithZero(.lt) | ||
| 995 | else | ||
| 996 | (lhs.ty.isFloat() or lhs.ty.isSignedInt()); | ||
| 997 | const rhs_is_signed = if (rhs.value()) |rhs_val| | ||
| 998 | rhs_val.compareWithZero(.lt) | ||
| 999 | else | ||
| 1000 | (rhs.ty.isFloat() or rhs.ty.isSignedInt()); | ||
| 1001 | const dest_int_is_signed = lhs_is_signed or rhs_is_signed; | ||
| 1002 | |||
| 1003 | var dest_float_type: ?Type = null; | ||
| 1004 | |||
| 1005 | var lhs_bits: usize = undefined; | ||
| 1006 | if (lhs.value()) |lhs_val| { | ||
| 1007 | if (lhs_val.isUndef()) | ||
| 1008 | return self.constUndef(src, Type.initTag(.bool)); | ||
| 1009 | const is_unsigned = if (lhs_is_float) x: { | ||
| 1010 | var bigint_space: Value.BigIntSpace = undefined; | ||
| 1011 | var bigint = lhs_val.toBigInt(&bigint_space); | ||
| 1012 | const zcmp = lhs_val.orderAgainstZero(); | ||
| 1013 | if (lhs_val.floatHasFraction()) { | ||
| 1014 | switch (op) { | ||
| 1015 | .eq => return self.constBool(src, false), | ||
| 1016 | .neq => return self.constBool(src, true), | ||
| 1017 | else => {}, | ||
| 1018 | } | ||
| 1019 | if (zcmp == .lt) { | ||
| 1020 | try bigint.addScalar(bigint, -1); | ||
| 1021 | } else { | ||
| 1022 | try bigint.addScalar(bigint, 1); | ||
| 1023 | } | ||
| 1024 | } | ||
| 1025 | lhs_bits = bigint.bitCountTwosComp(); | ||
| 1026 | break :x (zcmp != .lt); | ||
| 1027 | } else x: { | ||
| 1028 | lhs_bits = lhs_val.intBitCountTwosComp(); | ||
| 1029 | break :x (lhs_val.orderAgainstZero() != .lt); | ||
| 1030 | }; | ||
| 1031 | lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed); | ||
| 1032 | } else if (lhs_is_float) { | ||
| 1033 | dest_float_type = lhs.ty; | ||
| 1034 | } else { | ||
| 1035 | const int_info = lhs.ty.intInfo(self.target); | ||
| 1036 | lhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed); | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | var rhs_bits: usize = undefined; | ||
| 1040 | if (rhs.value()) |rhs_val| { | ||
| 1041 | if (rhs_val.isUndef()) | ||
| 1042 | return self.constUndef(src, Type.initTag(.bool)); | ||
| 1043 | const is_unsigned = if (rhs_is_float) x: { | ||
| 1044 | var bigint_space: Value.BigIntSpace = undefined; | ||
| 1045 | var bigint = rhs_val.toBigInt(&bigint_space); | ||
| 1046 | const zcmp = rhs_val.orderAgainstZero(); | ||
| 1047 | if (rhs_val.floatHasFraction()) { | ||
| 1048 | switch (op) { | ||
| 1049 | .eq => return self.constBool(src, false), | ||
| 1050 | .neq => return self.constBool(src, true), | ||
| 1051 | else => {}, | ||
| 1052 | } | ||
| 1053 | if (zcmp == .lt) { | ||
| 1054 | try bigint.addScalar(bigint, -1); | ||
| 1055 | } else { | ||
| 1056 | try bigint.addScalar(bigint, 1); | ||
| 1057 | } | ||
| 1058 | } | ||
| 1059 | rhs_bits = bigint.bitCountTwosComp(); | ||
| 1060 | break :x (zcmp != .lt); | ||
| 1061 | } else x: { | ||
| 1062 | rhs_bits = rhs_val.intBitCountTwosComp(); | ||
| 1063 | break :x (rhs_val.orderAgainstZero() != .lt); | ||
| 1064 | }; | ||
| 1065 | rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed); | ||
| 1066 | } else if (rhs_is_float) { | ||
| 1067 | dest_float_type = rhs.ty; | ||
| 1068 | } else { | ||
| 1069 | const int_info = rhs.ty.intInfo(self.target); | ||
| 1070 | rhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed); | ||
| 1071 | } | ||
| 1072 | |||
| 1073 | const dest_type = if (dest_float_type) |ft| ft else blk: { | ||
| 1074 | const max_bits = std.math.max(lhs_bits, rhs_bits); | ||
| 1075 | const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) { | ||
| 1076 | error.Overflow => return self.fail(src, "{} exceeds maximum integer bit count", .{max_bits}), | ||
| 1077 | }; | ||
| 1078 | break :blk try self.makeIntType(dest_int_is_signed, casted_bits); | ||
| 1079 | }; | ||
| 1080 | const casted_lhs = try self.coerce(block, dest_type, lhs); | ||
| 1081 | const casted_rhs = try self.coerce(block, dest_type, lhs); | ||
| 1082 | |||
| 1083 | return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){ | ||
| 1084 | .lhs = casted_lhs, | ||
| 1085 | .rhs = casted_rhs, | ||
| 1086 | .op = op, | ||
| 1087 | }); | ||
| 1088 | } | ||
| 1089 | |||
| 1090 | fn makeIntType(self: *Analyze, signed: bool, bits: u16) !Type { | ||
| 1091 | if (signed) { | ||
| 1092 | const int_payload = try self.arena.allocator.create(Type.Payload.IntSigned); | ||
| 1093 | int_payload.* = .{ .bits = bits }; | ||
| 1094 | return Type.initPayload(&int_payload.base); | ||
| 1095 | } else { | ||
| 1096 | const int_payload = try self.arena.allocator.create(Type.Payload.IntUnsigned); | ||
| 1097 | int_payload.* = .{ .bits = bits }; | ||
| 1098 | return Type.initPayload(&int_payload.base); | ||
| 1099 | } | ||
| 690 | } | 1100 | } |
| 691 | 1101 | ||
| 692 | fn coerce(self: *Analyze, func: ?*Fn, dest_type: Type, inst: *Inst) !*Inst { | 1102 | fn coerce(self: *Analyze, block: ?*Block, dest_type: Type, inst: *Inst) !*Inst { |
| 693 | // If the types are the same, we can return the operand. | 1103 | // If the types are the same, we can return the operand. |
| 694 | if (dest_type.eql(inst.ty)) | 1104 | if (dest_type.eql(inst.ty)) |
| 695 | return inst; | 1105 | return inst; |
| 696 | 1106 | ||
| 697 | const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty); | 1107 | const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty); |
| 698 | if (in_memory_result == .ok) { | 1108 | if (in_memory_result == .ok) { |
| 699 | return self.bitcast(func, dest_type, inst); | 1109 | return self.bitcast(block, dest_type, inst); |
| 700 | } | 1110 | } |
| 701 | 1111 | ||
| 702 | // *[N]T to []T | 1112 | // *[N]T to []T |
| ... | @@ -740,14 +1150,14 @@ const Analyze = struct { | ... | @@ -740,14 +1150,14 @@ const Analyze = struct { |
| 740 | return self.fail(inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type }); | 1150 | return self.fail(inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type }); |
| 741 | } | 1151 | } |
| 742 | 1152 | ||
| 743 | fn bitcast(self: *Analyze, func: ?*Fn, dest_type: Type, inst: *Inst) !*Inst { | 1153 | fn bitcast(self: *Analyze, block: ?*Block, dest_type: Type, inst: *Inst) !*Inst { |
| 744 | if (inst.value()) |val| { | 1154 | if (inst.value()) |val| { |
| 745 | // Keep the comptime Value representation; take the new type. | 1155 | // Keep the comptime Value representation; take the new type. |
| 746 | return self.constInst(inst.src, .{ .ty = dest_type, .val = val }); | 1156 | return self.constInst(inst.src, .{ .ty = dest_type, .val = val }); |
| 747 | } | 1157 | } |
| 748 | // TODO validate the type size and other compile errors | 1158 | // TODO validate the type size and other compile errors |
| 749 | const f = try self.requireFunctionBody(func, inst.src); | 1159 | const b = try self.requireRuntimeBlock(block, inst.src); |
| 750 | return self.addNewInstArgs(f, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst }); | 1160 | return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst }); |
| 751 | } | 1161 | } |
| 752 | 1162 | ||
| 753 | fn coerceArrayPtrToSlice(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst { | 1163 | fn coerceArrayPtrToSlice(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst { |
| ... | @@ -831,17 +1241,31 @@ pub fn main() anyerror!void { | ... | @@ -831,17 +1241,31 @@ pub fn main() anyerror!void { |
| 831 | try bos.flush(); | 1241 | try bos.flush(); |
| 832 | } | 1242 | } |
| 833 | 1243 | ||
| 1244 | // executable | ||
| 1245 | //const link = @import("link.zig"); | ||
| 1246 | //var result = try link.updateExecutableFilePath(allocator, analyzed_module, std.fs.cwd(), "a.out"); | ||
| 1247 | //defer result.deinit(allocator); | ||
| 1248 | //if (result.errors.len != 0) { | ||
| 1249 | // for (result.errors) |err_msg| { | ||
| 1250 | // const loc = std.zig.findLineColumn(source, err_msg.byte_offset); | ||
| 1251 | // std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg }); | ||
| 1252 | // } | ||
| 1253 | // if (debug_error_trace) return error.LinkFailure; | ||
| 1254 | // std.process.exit(1); | ||
| 1255 | //} | ||
| 1256 | |||
| 1257 | // object file | ||
| 834 | const link = @import("link.zig"); | 1258 | const link = @import("link.zig"); |
| 835 | var result = try link.updateExecutableFilePath(allocator, analyzed_module, std.fs.cwd(), "a.out"); | 1259 | //var result = try link.updateExecutableFilePath(allocator, analyzed_module, std.fs.cwd(), "a.out"); |
| 836 | defer result.deinit(allocator); | 1260 | //defer result.deinit(allocator); |
| 837 | if (result.errors.len != 0) { | 1261 | //if (result.errors.len != 0) { |
| 838 | for (result.errors) |err_msg| { | 1262 | // for (result.errors) |err_msg| { |
| 839 | const loc = std.zig.findLineColumn(source, err_msg.byte_offset); | 1263 | // const loc = std.zig.findLineColumn(source, err_msg.byte_offset); |
| 840 | std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg }); | 1264 | // std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg }); |
| 841 | } | 1265 | // } |
| 842 | if (debug_error_trace) return error.LinkFailure; | 1266 | // if (debug_error_trace) return error.LinkFailure; |
| 843 | std.process.exit(1); | 1267 | // std.process.exit(1); |
| 844 | } | 1268 | //} |
| 845 | } | 1269 | } |
| 846 | 1270 | ||
| 847 | // Performance optimization ideas: | 1271 | // Performance optimization ideas: |
src-self-hosted/ir/text.zig+218-90| ... | @@ -34,6 +34,10 @@ pub const Inst = struct { | ... | @@ -34,6 +34,10 @@ pub const Inst = struct { |
| 34 | bitcast, | 34 | bitcast, |
| 35 | elemptr, | 35 | elemptr, |
| 36 | add, | 36 | add, |
| 37 | cmp, | ||
| 38 | condbr, | ||
| 39 | isnull, | ||
| 40 | isnonnull, | ||
| 37 | }; | 41 | }; |
| 38 | 42 | ||
| 39 | pub fn TagToType(tag: Tag) type { | 43 | pub fn TagToType(tag: Tag) type { |
| ... | @@ -54,6 +58,10 @@ pub const Inst = struct { | ... | @@ -54,6 +58,10 @@ pub const Inst = struct { |
| 54 | .bitcast => BitCast, | 58 | .bitcast => BitCast, |
| 55 | .elemptr => ElemPtr, | 59 | .elemptr => ElemPtr, |
| 56 | .add => Add, | 60 | .add => Add, |
| 61 | .cmp => Cmp, | ||
| 62 | .condbr => CondBr, | ||
| 63 | .isnull => IsNull, | ||
| 64 | .isnonnull => IsNonNull, | ||
| 57 | }; | 65 | }; |
| 58 | } | 66 | } |
| 59 | 67 | ||
| ... | @@ -157,13 +165,9 @@ pub const Inst = struct { | ... | @@ -157,13 +165,9 @@ pub const Inst = struct { |
| 157 | 165 | ||
| 158 | positionals: struct { | 166 | positionals: struct { |
| 159 | fn_type: *Inst, | 167 | fn_type: *Inst, |
| 160 | body: Body, | 168 | body: Module.Body, |
| 161 | }, | 169 | }, |
| 162 | kw_args: struct {}, | 170 | kw_args: struct {}, |
| 163 | |||
| 164 | pub const Body = struct { | ||
| 165 | instructions: []*Inst, | ||
| 166 | }; | ||
| 167 | }; | 171 | }; |
| 168 | 172 | ||
| 169 | pub const Export = struct { | 173 | pub const Export = struct { |
| ... | @@ -297,6 +301,50 @@ pub const Inst = struct { | ... | @@ -297,6 +301,50 @@ pub const Inst = struct { |
| 297 | }, | 301 | }, |
| 298 | kw_args: struct {}, | 302 | kw_args: struct {}, |
| 299 | }; | 303 | }; |
| 304 | |||
| 305 | pub const Cmp = struct { | ||
| 306 | pub const base_tag = Tag.cmp; | ||
| 307 | base: Inst, | ||
| 308 | |||
| 309 | positionals: struct { | ||
| 310 | lhs: *Inst, | ||
| 311 | op: std.math.CompareOperator, | ||
| 312 | rhs: *Inst, | ||
| 313 | }, | ||
| 314 | kw_args: struct {}, | ||
| 315 | }; | ||
| 316 | |||
| 317 | pub const CondBr = struct { | ||
| 318 | pub const base_tag = Tag.condbr; | ||
| 319 | base: Inst, | ||
| 320 | |||
| 321 | positionals: struct { | ||
| 322 | condition: *Inst, | ||
| 323 | true_body: Module.Body, | ||
| 324 | false_body: Module.Body, | ||
| 325 | }, | ||
| 326 | kw_args: struct {}, | ||
| 327 | }; | ||
| 328 | |||
| 329 | pub const IsNull = struct { | ||
| 330 | pub const base_tag = Tag.isnull; | ||
| 331 | base: Inst, | ||
| 332 | |||
| 333 | positionals: struct { | ||
| 334 | operand: *Inst, | ||
| 335 | }, | ||
| 336 | kw_args: struct {}, | ||
| 337 | }; | ||
| 338 | |||
| 339 | pub const IsNonNull = struct { | ||
| 340 | pub const base_tag = Tag.isnonnull; | ||
| 341 | base: Inst, | ||
| 342 | |||
| 343 | positionals: struct { | ||
| 344 | operand: *Inst, | ||
| 345 | }, | ||
| 346 | kw_args: struct {}, | ||
| 347 | }; | ||
| 300 | }; | 348 | }; |
| 301 | 349 | ||
| 302 | pub const ErrorMsg = struct { | 350 | pub const ErrorMsg = struct { |
| ... | @@ -309,6 +357,10 @@ pub const Module = struct { | ... | @@ -309,6 +357,10 @@ pub const Module = struct { |
| 309 | errors: []ErrorMsg, | 357 | errors: []ErrorMsg, |
| 310 | arena: std.heap.ArenaAllocator, | 358 | arena: std.heap.ArenaAllocator, |
| 311 | 359 | ||
| 360 | pub const Body = struct { | ||
| 361 | instructions: []*Inst, | ||
| 362 | }; | ||
| 363 | |||
| 312 | pub fn deinit(self: *Module, allocator: *Allocator) void { | 364 | pub fn deinit(self: *Module, allocator: *Allocator) void { |
| 313 | allocator.free(self.decls); | 365 | allocator.free(self.decls); |
| 314 | allocator.free(self.errors); | 366 | allocator.free(self.errors); |
| ... | @@ -321,7 +373,7 @@ pub const Module = struct { | ... | @@ -321,7 +373,7 @@ pub const Module = struct { |
| 321 | self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {}; | 373 | self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {}; |
| 322 | } | 374 | } |
| 323 | 375 | ||
| 324 | const InstPtrTable = std.AutoHashMap(*Inst, struct { index: usize, fn_body: ?*Inst.Fn.Body }); | 376 | const InstPtrTable = std.AutoHashMap(*Inst, struct { index: usize, fn_body: ?*Module.Body }); |
| 325 | 377 | ||
| 326 | /// The allocator is used for temporary storage, but this function always returns | 378 | /// The allocator is used for temporary storage, but this function always returns |
| 327 | /// with no resources allocated. | 379 | /// with no resources allocated. |
| ... | @@ -373,6 +425,10 @@ pub const Module = struct { | ... | @@ -373,6 +425,10 @@ pub const Module = struct { |
| 373 | .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, decl, inst_table), | 425 | .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, decl, inst_table), |
| 374 | .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, decl, inst_table), | 426 | .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, decl, inst_table), |
| 375 | .add => return self.writeInstToStreamGeneric(stream, .add, decl, inst_table), | 427 | .add => return self.writeInstToStreamGeneric(stream, .add, decl, inst_table), |
| 428 | .cmp => return self.writeInstToStreamGeneric(stream, .cmp, decl, inst_table), | ||
| 429 | .condbr => return self.writeInstToStreamGeneric(stream, .condbr, decl, inst_table), | ||
| 430 | .isnull => return self.writeInstToStreamGeneric(stream, .isnull, decl, inst_table), | ||
| 431 | .isnonnull => return self.writeInstToStreamGeneric(stream, .isnonnull, decl, inst_table), | ||
| 376 | } | 432 | } |
| 377 | } | 433 | } |
| 378 | 434 | ||
| ... | @@ -432,7 +488,7 @@ pub const Module = struct { | ... | @@ -432,7 +488,7 @@ pub const Module = struct { |
| 432 | } | 488 | } |
| 433 | try stream.writeByte(']'); | 489 | try stream.writeByte(']'); |
| 434 | }, | 490 | }, |
| 435 | Inst.Fn.Body => { | 491 | Module.Body => { |
| 436 | try stream.writeAll("{\n"); | 492 | try stream.writeAll("{\n"); |
| 437 | for (param.instructions) |inst, i| { | 493 | for (param.instructions) |inst, i| { |
| 438 | try stream.print(" %{} ", .{i}); | 494 | try stream.print(" %{} ", .{i}); |
| ... | @@ -497,7 +553,7 @@ const Parser = struct { | ... | @@ -497,7 +553,7 @@ const Parser = struct { |
| 497 | name_map: std.StringHashMap(usize), | 553 | name_map: std.StringHashMap(usize), |
| 498 | }; | 554 | }; |
| 499 | 555 | ||
| 500 | fn parseBody(self: *Parser) !Inst.Fn.Body { | 556 | fn parseBody(self: *Parser) !Module.Body { |
| 501 | var body_context = Body{ | 557 | var body_context = Body{ |
| 502 | .instructions = std.ArrayList(*Inst).init(self.allocator), | 558 | .instructions = std.ArrayList(*Inst).init(self.allocator), |
| 503 | .name_map = std.StringHashMap(usize).init(self.allocator), | 559 | .name_map = std.StringHashMap(usize).init(self.allocator), |
| ... | @@ -535,7 +591,7 @@ const Parser = struct { | ... | @@ -535,7 +591,7 @@ const Parser = struct { |
| 535 | // Move the instructions to the arena | 591 | // Move the instructions to the arena |
| 536 | const instrs = try self.arena.allocator.alloc(*Inst, body_context.instructions.items.len); | 592 | const instrs = try self.arena.allocator.alloc(*Inst, body_context.instructions.items.len); |
| 537 | mem.copy(*Inst, instrs, body_context.instructions.items); | 593 | mem.copy(*Inst, instrs, body_context.instructions.items); |
| 538 | return Inst.Fn.Body{ .instructions = instrs }; | 594 | return Module.Body{ .instructions = instrs }; |
| 539 | } | 595 | } |
| 540 | 596 | ||
| 541 | fn parseStringLiteral(self: *Parser) ![]u8 { | 597 | fn parseStringLiteral(self: *Parser) ![]u8 { |
| ... | @@ -754,7 +810,7 @@ const Parser = struct { | ... | @@ -754,7 +810,7 @@ const Parser = struct { |
| 754 | }; | 810 | }; |
| 755 | } | 811 | } |
| 756 | switch (T) { | 812 | switch (T) { |
| 757 | Inst.Fn.Body => return parseBody(self), | 813 | Module.Body => return parseBody(self), |
| 758 | bool => { | 814 | bool => { |
| 759 | const bool_value = switch (self.source[self.i]) { | 815 | const bool_value = switch (self.source[self.i]) { |
| 760 | '0' => false, | 816 | '0' => false, |
| ... | @@ -880,11 +936,12 @@ const EmitZIR = struct { | ... | @@ -880,11 +936,12 @@ const EmitZIR = struct { |
| 880 | } | 936 | } |
| 881 | 937 | ||
| 882 | fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst { | 938 | fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst { |
| 939 | const big_int_space = try self.arena.allocator.create(Value.BigIntSpace); | ||
| 883 | const int_inst = try self.arena.allocator.create(Inst.Int); | 940 | const int_inst = try self.arena.allocator.create(Inst.Int); |
| 884 | int_inst.* = .{ | 941 | int_inst.* = .{ |
| 885 | .base = .{ .src = src, .tag = Inst.Int.base_tag }, | 942 | .base = .{ .src = src, .tag = Inst.Int.base_tag }, |
| 886 | .positionals = .{ | 943 | .positionals = .{ |
| 887 | .int = try val.toBigInt(&self.arena.allocator), | 944 | .int = val.toBigInt(big_int_space), |
| 888 | }, | 945 | }, |
| 889 | .kw_args = .{}, | 946 | .kw_args = .{}, |
| 890 | }; | 947 | }; |
| ... | @@ -939,85 +996,7 @@ const EmitZIR = struct { | ... | @@ -939,85 +996,7 @@ const EmitZIR = struct { |
| 939 | var instructions = std.ArrayList(*Inst).init(self.allocator); | 996 | var instructions = std.ArrayList(*Inst).init(self.allocator); |
| 940 | defer instructions.deinit(); | 997 | defer instructions.deinit(); |
| 941 | 998 | ||
| 942 | for (module_fn.body) |inst| { | 999 | try self.emitBody(module_fn.body, &inst_table, &instructions); |
| 943 | const new_inst = switch (inst.tag) { | ||
| 944 | .unreach => blk: { | ||
| 945 | const unreach_inst = try self.arena.allocator.create(Inst.Unreachable); | ||
| 946 | unreach_inst.* = .{ | ||
| 947 | .base = .{ .src = inst.src, .tag = Inst.Unreachable.base_tag }, | ||
| 948 | .positionals = .{}, | ||
| 949 | .kw_args = .{}, | ||
| 950 | }; | ||
| 951 | break :blk &unreach_inst.base; | ||
| 952 | }, | ||
| 953 | .constant => unreachable, // excluded from function bodies | ||
| 954 | .assembly => blk: { | ||
| 955 | const old_inst = inst.cast(ir.Inst.Assembly).?; | ||
| 956 | const new_inst = try self.arena.allocator.create(Inst.Asm); | ||
| 957 | |||
| 958 | const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len); | ||
| 959 | for (inputs) |*elem, i| { | ||
| 960 | elem.* = try self.emitStringLiteral(inst.src, old_inst.args.inputs[i]); | ||
| 961 | } | ||
| 962 | |||
| 963 | const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len); | ||
| 964 | for (clobbers) |*elem, i| { | ||
| 965 | elem.* = try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i]); | ||
| 966 | } | ||
| 967 | |||
| 968 | const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len); | ||
| 969 | for (args) |*elem, i| { | ||
| 970 | elem.* = try self.resolveInst(&inst_table, old_inst.args.args[i]); | ||
| 971 | } | ||
| 972 | |||
| 973 | new_inst.* = .{ | ||
| 974 | .base = .{ .src = inst.src, .tag = Inst.Asm.base_tag }, | ||
| 975 | .positionals = .{ | ||
| 976 | .asm_source = try self.emitStringLiteral(inst.src, old_inst.args.asm_source), | ||
| 977 | .return_type = try self.emitType(inst.src, inst.ty), | ||
| 978 | }, | ||
| 979 | .kw_args = .{ | ||
| 980 | .@"volatile" = old_inst.args.is_volatile, | ||
| 981 | .output = if (old_inst.args.output) |o| | ||
| 982 | try self.emitStringLiteral(inst.src, o) | ||
| 983 | else | ||
| 984 | null, | ||
| 985 | .inputs = inputs, | ||
| 986 | .clobbers = clobbers, | ||
| 987 | .args = args, | ||
| 988 | }, | ||
| 989 | }; | ||
| 990 | break :blk &new_inst.base; | ||
| 991 | }, | ||
| 992 | .ptrtoint => blk: { | ||
| 993 | const old_inst = inst.cast(ir.Inst.PtrToInt).?; | ||
| 994 | const new_inst = try self.arena.allocator.create(Inst.PtrToInt); | ||
| 995 | new_inst.* = .{ | ||
| 996 | .base = .{ .src = inst.src, .tag = Inst.PtrToInt.base_tag }, | ||
| 997 | .positionals = .{ | ||
| 998 | .ptr = try self.resolveInst(&inst_table, old_inst.args.ptr), | ||
| 999 | }, | ||
| 1000 | .kw_args = .{}, | ||
| 1001 | }; | ||
| 1002 | break :blk &new_inst.base; | ||
| 1003 | }, | ||
| 1004 | .bitcast => blk: { | ||
| 1005 | const old_inst = inst.cast(ir.Inst.BitCast).?; | ||
| 1006 | const new_inst = try self.arena.allocator.create(Inst.BitCast); | ||
| 1007 | new_inst.* = .{ | ||
| 1008 | .base = .{ .src = inst.src, .tag = Inst.BitCast.base_tag }, | ||
| 1009 | .positionals = .{ | ||
| 1010 | .dest_type = try self.emitType(inst.src, inst.ty), | ||
| 1011 | .operand = try self.resolveInst(&inst_table, old_inst.args.operand), | ||
| 1012 | }, | ||
| 1013 | .kw_args = .{}, | ||
| 1014 | }; | ||
| 1015 | break :blk &new_inst.base; | ||
| 1016 | }, | ||
| 1017 | }; | ||
| 1018 | try instructions.append(new_inst); | ||
| 1019 | try inst_table.putNoClobber(inst, new_inst); | ||
| 1020 | } | ||
| 1021 | 1000 | ||
| 1022 | const fn_type = try self.emitType(src, module_fn.fn_type); | 1001 | const fn_type = try self.emitType(src, module_fn.fn_type); |
| 1023 | 1002 | ||
| ... | @@ -1039,6 +1018,155 @@ const EmitZIR = struct { | ... | @@ -1039,6 +1018,155 @@ const EmitZIR = struct { |
| 1039 | } | 1018 | } |
| 1040 | } | 1019 | } |
| 1041 | 1020 | ||
| 1021 | fn emitBody( | ||
| 1022 | self: *EmitZIR, | ||
| 1023 | body: ir.Module.Body, | ||
| 1024 | inst_table: *std.AutoHashMap(*ir.Inst, *Inst), | ||
| 1025 | instructions: *std.ArrayList(*Inst), | ||
| 1026 | ) Allocator.Error!void { | ||
| 1027 | for (body.instructions) |inst| { | ||
| 1028 | const new_inst = switch (inst.tag) { | ||
| 1029 | .unreach => blk: { | ||
| 1030 | const unreach_inst = try self.arena.allocator.create(Inst.Unreachable); | ||
| 1031 | unreach_inst.* = .{ | ||
| 1032 | .base = .{ .src = inst.src, .tag = Inst.Unreachable.base_tag }, | ||
| 1033 | .positionals = .{}, | ||
| 1034 | .kw_args = .{}, | ||
| 1035 | }; | ||
| 1036 | break :blk &unreach_inst.base; | ||
| 1037 | }, | ||
| 1038 | .constant => unreachable, // excluded from function bodies | ||
| 1039 | .assembly => blk: { | ||
| 1040 | const old_inst = inst.cast(ir.Inst.Assembly).?; | ||
| 1041 | const new_inst = try self.arena.allocator.create(Inst.Asm); | ||
| 1042 | |||
| 1043 | const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len); | ||
| 1044 | for (inputs) |*elem, i| { | ||
| 1045 | elem.* = try self.emitStringLiteral(inst.src, old_inst.args.inputs[i]); | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len); | ||
| 1049 | for (clobbers) |*elem, i| { | ||
| 1050 | elem.* = try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i]); | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len); | ||
| 1054 | for (args) |*elem, i| { | ||
| 1055 | elem.* = try self.resolveInst(inst_table, old_inst.args.args[i]); | ||
| 1056 | } | ||
| 1057 | |||
| 1058 | new_inst.* = .{ | ||
| 1059 | .base = .{ .src = inst.src, .tag = Inst.Asm.base_tag }, | ||
| 1060 | .positionals = .{ | ||
| 1061 | .asm_source = try self.emitStringLiteral(inst.src, old_inst.args.asm_source), | ||
| 1062 | .return_type = try self.emitType(inst.src, inst.ty), | ||
| 1063 | }, | ||
| 1064 | .kw_args = .{ | ||
| 1065 | .@"volatile" = old_inst.args.is_volatile, | ||
| 1066 | .output = if (old_inst.args.output) |o| | ||
| 1067 | try self.emitStringLiteral(inst.src, o) | ||
| 1068 | else | ||
| 1069 | null, | ||
| 1070 | .inputs = inputs, | ||
| 1071 | .clobbers = clobbers, | ||
| 1072 | .args = args, | ||
| 1073 | }, | ||
| 1074 | }; | ||
| 1075 | break :blk &new_inst.base; | ||
| 1076 | }, | ||
| 1077 | .ptrtoint => blk: { | ||
| 1078 | const old_inst = inst.cast(ir.Inst.PtrToInt).?; | ||
| 1079 | const new_inst = try self.arena.allocator.create(Inst.PtrToInt); | ||
| 1080 | new_inst.* = .{ | ||
| 1081 | .base = .{ .src = inst.src, .tag = Inst.PtrToInt.base_tag }, | ||
| 1082 | .positionals = .{ | ||
| 1083 | .ptr = try self.resolveInst(inst_table, old_inst.args.ptr), | ||
| 1084 | }, | ||
| 1085 | .kw_args = .{}, | ||
| 1086 | }; | ||
| 1087 | break :blk &new_inst.base; | ||
| 1088 | }, | ||
| 1089 | .bitcast => blk: { | ||
| 1090 | const old_inst = inst.cast(ir.Inst.BitCast).?; | ||
| 1091 | const new_inst = try self.arena.allocator.create(Inst.BitCast); | ||
| 1092 | new_inst.* = .{ | ||
| 1093 | .base = .{ .src = inst.src, .tag = Inst.BitCast.base_tag }, | ||
| 1094 | .positionals = .{ | ||
| 1095 | .dest_type = try self.emitType(inst.src, inst.ty), | ||
| 1096 | .operand = try self.resolveInst(inst_table, old_inst.args.operand), | ||
| 1097 | }, | ||
| 1098 | .kw_args = .{}, | ||
| 1099 | }; | ||
| 1100 | break :blk &new_inst.base; | ||
| 1101 | }, | ||
| 1102 | .cmp => blk: { | ||
| 1103 | const old_inst = inst.cast(ir.Inst.Cmp).?; | ||
| 1104 | const new_inst = try self.arena.allocator.create(Inst.Cmp); | ||
| 1105 | new_inst.* = .{ | ||
| 1106 | .base = .{ .src = inst.src, .tag = Inst.Cmp.base_tag }, | ||
| 1107 | .positionals = .{ | ||
| 1108 | .lhs = try self.resolveInst(inst_table, old_inst.args.lhs), | ||
| 1109 | .rhs = try self.resolveInst(inst_table, old_inst.args.rhs), | ||
| 1110 | .op = old_inst.args.op, | ||
| 1111 | }, | ||
| 1112 | .kw_args = .{}, | ||
| 1113 | }; | ||
| 1114 | break :blk &new_inst.base; | ||
| 1115 | }, | ||
| 1116 | .condbr => blk: { | ||
| 1117 | const old_inst = inst.cast(ir.Inst.CondBr).?; | ||
| 1118 | |||
| 1119 | var true_body = std.ArrayList(*Inst).init(self.allocator); | ||
| 1120 | var false_body = std.ArrayList(*Inst).init(self.allocator); | ||
| 1121 | |||
| 1122 | defer true_body.deinit(); | ||
| 1123 | defer false_body.deinit(); | ||
| 1124 | |||
| 1125 | try self.emitBody(old_inst.args.true_body, inst_table, &true_body); | ||
| 1126 | try self.emitBody(old_inst.args.false_body, inst_table, &false_body); | ||
| 1127 | |||
| 1128 | const new_inst = try self.arena.allocator.create(Inst.CondBr); | ||
| 1129 | new_inst.* = .{ | ||
| 1130 | .base = .{ .src = inst.src, .tag = Inst.CondBr.base_tag }, | ||
| 1131 | .positionals = .{ | ||
| 1132 | .condition = try self.resolveInst(inst_table, old_inst.args.condition), | ||
| 1133 | .true_body = .{ .instructions = true_body.toOwnedSlice() }, | ||
| 1134 | .false_body = .{ .instructions = false_body.toOwnedSlice() }, | ||
| 1135 | }, | ||
| 1136 | .kw_args = .{}, | ||
| 1137 | }; | ||
| 1138 | break :blk &new_inst.base; | ||
| 1139 | }, | ||
| 1140 | .isnull => blk: { | ||
| 1141 | const old_inst = inst.cast(ir.Inst.IsNull).?; | ||
| 1142 | const new_inst = try self.arena.allocator.create(Inst.IsNull); | ||
| 1143 | new_inst.* = .{ | ||
| 1144 | .base = .{ .src = inst.src, .tag = Inst.IsNull.base_tag }, | ||
| 1145 | .positionals = .{ | ||
| 1146 | .operand = try self.resolveInst(inst_table, old_inst.args.operand), | ||
| 1147 | }, | ||
| 1148 | .kw_args = .{}, | ||
| 1149 | }; | ||
| 1150 | break :blk &new_inst.base; | ||
| 1151 | }, | ||
| 1152 | .isnonnull => blk: { | ||
| 1153 | const old_inst = inst.cast(ir.Inst.IsNonNull).?; | ||
| 1154 | const new_inst = try self.arena.allocator.create(Inst.IsNonNull); | ||
| 1155 | new_inst.* = .{ | ||
| 1156 | .base = .{ .src = inst.src, .tag = Inst.IsNonNull.base_tag }, | ||
| 1157 | .positionals = .{ | ||
| 1158 | .operand = try self.resolveInst(inst_table, old_inst.args.operand), | ||
| 1159 | }, | ||
| 1160 | .kw_args = .{}, | ||
| 1161 | }; | ||
| 1162 | break :blk &new_inst.base; | ||
| 1163 | }, | ||
| 1164 | }; | ||
| 1165 | try instructions.append(new_inst); | ||
| 1166 | try inst_table.putNoClobber(inst, new_inst); | ||
| 1167 | } | ||
| 1168 | } | ||
| 1169 | |||
| 1042 | fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst { | 1170 | fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst { |
| 1043 | switch (ty.tag()) { | 1171 | switch (ty.tag()) { |
| 1044 | .isize => return self.emitPrimitiveType(src, .isize), | 1172 | .isize => return self.emitPrimitiveType(src, .isize), |
src-self-hosted/type.zig+455-204| ... | @@ -20,35 +20,37 @@ pub const Type = extern union { | ... | @@ -20,35 +20,37 @@ pub const Type = extern union { |
| 20 | 20 | ||
| 21 | pub fn zigTypeTag(self: Type) std.builtin.TypeId { | 21 | pub fn zigTypeTag(self: Type) std.builtin.TypeId { |
| 22 | switch (self.tag()) { | 22 | switch (self.tag()) { |
| 23 | .@"u8", | 23 | .u8, |
| 24 | .@"i8", | 24 | .i8, |
| 25 | .@"isize", | 25 | .isize, |
| 26 | .@"usize", | 26 | .usize, |
| 27 | .@"c_short", | 27 | .c_short, |
| 28 | .@"c_ushort", | 28 | .c_ushort, |
| 29 | .@"c_int", | 29 | .c_int, |
| 30 | .@"c_uint", | 30 | .c_uint, |
| 31 | .@"c_long", | 31 | .c_long, |
| 32 | .@"c_ulong", | 32 | .c_ulong, |
| 33 | .@"c_longlong", | 33 | .c_longlong, |
| 34 | .@"c_ulonglong", | 34 | .c_ulonglong, |
| 35 | .@"c_longdouble", | 35 | .c_longdouble, |
| 36 | .int_signed, | ||
| 37 | .int_unsigned, | ||
| 36 | => return .Int, | 38 | => return .Int, |
| 37 | 39 | ||
| 38 | .@"f16", | 40 | .f16, |
| 39 | .@"f32", | 41 | .f32, |
| 40 | .@"f64", | 42 | .f64, |
| 41 | .@"f128", | 43 | .f128, |
| 42 | => return .Float, | 44 | => return .Float, |
| 43 | 45 | ||
| 44 | .@"c_void" => return .Opaque, | 46 | .c_void => return .Opaque, |
| 45 | .@"bool" => return .Bool, | 47 | .bool => return .Bool, |
| 46 | .@"void" => return .Void, | 48 | .void => return .Void, |
| 47 | .@"type" => return .Type, | 49 | .type => return .Type, |
| 48 | .@"anyerror" => return .ErrorSet, | 50 | .anyerror => return .ErrorSet, |
| 49 | .@"comptime_int" => return .ComptimeInt, | 51 | .comptime_int => return .ComptimeInt, |
| 50 | .@"comptime_float" => return .ComptimeFloat, | 52 | .comptime_float => return .ComptimeFloat, |
| 51 | .@"noreturn" => return .NoReturn, | 53 | .noreturn => return .NoReturn, |
| 52 | 54 | ||
| 53 | .fn_naked_noreturn_no_args => return .Fn, | 55 | .fn_naked_noreturn_no_args => return .Fn, |
| 54 | 56 | ||
| ... | @@ -153,31 +155,31 @@ pub const Type = extern union { | ... | @@ -153,31 +155,31 @@ pub const Type = extern union { |
| 153 | while (true) { | 155 | while (true) { |
| 154 | const t = ty.tag(); | 156 | const t = ty.tag(); |
| 155 | switch (t) { | 157 | switch (t) { |
| 156 | .@"u8", | 158 | .u8, |
| 157 | .@"i8", | 159 | .i8, |
| 158 | .@"isize", | 160 | .isize, |
| 159 | .@"usize", | 161 | .usize, |
| 160 | .@"c_short", | 162 | .c_short, |
| 161 | .@"c_ushort", | 163 | .c_ushort, |
| 162 | .@"c_int", | 164 | .c_int, |
| 163 | .@"c_uint", | 165 | .c_uint, |
| 164 | .@"c_long", | 166 | .c_long, |
| 165 | .@"c_ulong", | 167 | .c_ulong, |
| 166 | .@"c_longlong", | 168 | .c_longlong, |
| 167 | .@"c_ulonglong", | 169 | .c_ulonglong, |
| 168 | .@"c_longdouble", | 170 | .c_longdouble, |
| 169 | .@"c_void", | 171 | .c_void, |
| 170 | .@"f16", | 172 | .f16, |
| 171 | .@"f32", | 173 | .f32, |
| 172 | .@"f64", | 174 | .f64, |
| 173 | .@"f128", | 175 | .f128, |
| 174 | .@"bool", | 176 | .bool, |
| 175 | .@"void", | 177 | .void, |
| 176 | .@"type", | 178 | .type, |
| 177 | .@"anyerror", | 179 | .anyerror, |
| 178 | .@"comptime_int", | 180 | .comptime_int, |
| 179 | .@"comptime_float", | 181 | .comptime_float, |
| 180 | .@"noreturn", | 182 | .noreturn, |
| 181 | => return out_stream.writeAll(@tagName(t)), | 183 | => return out_stream.writeAll(@tagName(t)), |
| 182 | 184 | ||
| 183 | .const_slice_u8 => return out_stream.writeAll("[]const u8"), | 185 | .const_slice_u8 => return out_stream.writeAll("[]const u8"), |
| ... | @@ -200,6 +202,14 @@ pub const Type = extern union { | ... | @@ -200,6 +202,14 @@ pub const Type = extern union { |
| 200 | ty = payload.pointee_type; | 202 | ty = payload.pointee_type; |
| 201 | continue; | 203 | continue; |
| 202 | }, | 204 | }, |
| 205 | .int_signed => { | ||
| 206 | const payload = @fieldParentPtr(Payload.IntSigned, "base", ty.ptr_otherwise); | ||
| 207 | return out_stream.print("i{}", .{payload.bits}); | ||
| 208 | }, | ||
| 209 | .int_unsigned => { | ||
| 210 | const payload = @fieldParentPtr(Payload.IntUnsigned, "base", ty.ptr_otherwise); | ||
| 211 | return out_stream.print("u{}", .{payload.bits}); | ||
| 212 | }, | ||
| 203 | } | 213 | } |
| 204 | unreachable; | 214 | unreachable; |
| 205 | } | 215 | } |
| ... | @@ -207,31 +217,31 @@ pub const Type = extern union { | ... | @@ -207,31 +217,31 @@ pub const Type = extern union { |
| 207 | 217 | ||
| 208 | pub fn toValue(self: Type, allocator: *Allocator) Allocator.Error!Value { | 218 | pub fn toValue(self: Type, allocator: *Allocator) Allocator.Error!Value { |
| 209 | switch (self.tag()) { | 219 | switch (self.tag()) { |
| 210 | .@"u8" => return Value.initTag(.u8_type), | 220 | .u8 => return Value.initTag(.u8_type), |
| 211 | .@"i8" => return Value.initTag(.i8_type), | 221 | .i8 => return Value.initTag(.i8_type), |
| 212 | .@"isize" => return Value.initTag(.isize_type), | 222 | .isize => return Value.initTag(.isize_type), |
| 213 | .@"usize" => return Value.initTag(.usize_type), | 223 | .usize => return Value.initTag(.usize_type), |
| 214 | .@"c_short" => return Value.initTag(.c_short_type), | 224 | .c_short => return Value.initTag(.c_short_type), |
| 215 | .@"c_ushort" => return Value.initTag(.c_ushort_type), | 225 | .c_ushort => return Value.initTag(.c_ushort_type), |
| 216 | .@"c_int" => return Value.initTag(.c_int_type), | 226 | .c_int => return Value.initTag(.c_int_type), |
| 217 | .@"c_uint" => return Value.initTag(.c_uint_type), | 227 | .c_uint => return Value.initTag(.c_uint_type), |
| 218 | .@"c_long" => return Value.initTag(.c_long_type), | 228 | .c_long => return Value.initTag(.c_long_type), |
| 219 | .@"c_ulong" => return Value.initTag(.c_ulong_type), | 229 | .c_ulong => return Value.initTag(.c_ulong_type), |
| 220 | .@"c_longlong" => return Value.initTag(.c_longlong_type), | 230 | .c_longlong => return Value.initTag(.c_longlong_type), |
| 221 | .@"c_ulonglong" => return Value.initTag(.c_ulonglong_type), | 231 | .c_ulonglong => return Value.initTag(.c_ulonglong_type), |
| 222 | .@"c_longdouble" => return Value.initTag(.c_longdouble_type), | 232 | .c_longdouble => return Value.initTag(.c_longdouble_type), |
| 223 | .@"c_void" => return Value.initTag(.c_void_type), | 233 | .c_void => return Value.initTag(.c_void_type), |
| 224 | .@"f16" => return Value.initTag(.f16_type), | 234 | .f16 => return Value.initTag(.f16_type), |
| 225 | .@"f32" => return Value.initTag(.f32_type), | 235 | .f32 => return Value.initTag(.f32_type), |
| 226 | .@"f64" => return Value.initTag(.f64_type), | 236 | .f64 => return Value.initTag(.f64_type), |
| 227 | .@"f128" => return Value.initTag(.f128_type), | 237 | .f128 => return Value.initTag(.f128_type), |
| 228 | .@"bool" => return Value.initTag(.bool_type), | 238 | .bool => return Value.initTag(.bool_type), |
| 229 | .@"void" => return Value.initTag(.void_type), | 239 | .void => return Value.initTag(.void_type), |
| 230 | .@"type" => return Value.initTag(.type_type), | 240 | .type => return Value.initTag(.type_type), |
| 231 | .@"anyerror" => return Value.initTag(.anyerror_type), | 241 | .anyerror => return Value.initTag(.anyerror_type), |
| 232 | .@"comptime_int" => return Value.initTag(.comptime_int_type), | 242 | .comptime_int => return Value.initTag(.comptime_int_type), |
| 233 | .@"comptime_float" => return Value.initTag(.comptime_float_type), | 243 | .comptime_float => return Value.initTag(.comptime_float_type), |
| 234 | .@"noreturn" => return Value.initTag(.noreturn_type), | 244 | .noreturn => return Value.initTag(.noreturn_type), |
| 235 | .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type), | 245 | .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type), |
| 236 | .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type), | 246 | .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type), |
| 237 | .const_slice_u8 => return Value.initTag(.const_slice_u8_type), | 247 | .const_slice_u8 => return Value.initTag(.const_slice_u8_type), |
| ... | @@ -245,35 +255,37 @@ pub const Type = extern union { | ... | @@ -245,35 +255,37 @@ pub const Type = extern union { |
| 245 | 255 | ||
| 246 | pub fn isSinglePointer(self: Type) bool { | 256 | pub fn isSinglePointer(self: Type) bool { |
| 247 | return switch (self.tag()) { | 257 | return switch (self.tag()) { |
| 248 | .@"u8", | 258 | .u8, |
| 249 | .@"i8", | 259 | .i8, |
| 250 | .@"isize", | 260 | .isize, |
| 251 | .@"usize", | 261 | .usize, |
| 252 | .@"c_short", | 262 | .c_short, |
| 253 | .@"c_ushort", | 263 | .c_ushort, |
| 254 | .@"c_int", | 264 | .c_int, |
| 255 | .@"c_uint", | 265 | .c_uint, |
| 256 | .@"c_long", | 266 | .c_long, |
| 257 | .@"c_ulong", | 267 | .c_ulong, |
| 258 | .@"c_longlong", | 268 | .c_longlong, |
| 259 | .@"c_ulonglong", | 269 | .c_ulonglong, |
| 260 | .@"c_longdouble", | 270 | .c_longdouble, |
| 261 | .@"f16", | 271 | .f16, |
| 262 | .@"f32", | 272 | .f32, |
| 263 | .@"f64", | 273 | .f64, |
| 264 | .@"f128", | 274 | .f128, |
| 265 | .@"c_void", | 275 | .c_void, |
| 266 | .@"bool", | 276 | .bool, |
| 267 | .@"void", | 277 | .void, |
| 268 | .@"type", | 278 | .type, |
| 269 | .@"anyerror", | 279 | .anyerror, |
| 270 | .@"comptime_int", | 280 | .comptime_int, |
| 271 | .@"comptime_float", | 281 | .comptime_float, |
| 272 | .@"noreturn", | 282 | .noreturn, |
| 273 | .array, | 283 | .array, |
| 274 | .array_u8_sentinel_0, | 284 | .array_u8_sentinel_0, |
| 275 | .const_slice_u8, | 285 | .const_slice_u8, |
| 276 | .fn_naked_noreturn_no_args, | 286 | .fn_naked_noreturn_no_args, |
| 287 | .int_unsigned, | ||
| 288 | .int_signed, | ||
| 277 | => false, | 289 | => false, |
| 278 | 290 | ||
| 279 | .single_const_pointer, | 291 | .single_const_pointer, |
| ... | @@ -284,36 +296,38 @@ pub const Type = extern union { | ... | @@ -284,36 +296,38 @@ pub const Type = extern union { |
| 284 | 296 | ||
| 285 | pub fn isSlice(self: Type) bool { | 297 | pub fn isSlice(self: Type) bool { |
| 286 | return switch (self.tag()) { | 298 | return switch (self.tag()) { |
| 287 | .@"u8", | 299 | .u8, |
| 288 | .@"i8", | 300 | .i8, |
| 289 | .@"isize", | 301 | .isize, |
| 290 | .@"usize", | 302 | .usize, |
| 291 | .@"c_short", | 303 | .c_short, |
| 292 | .@"c_ushort", | 304 | .c_ushort, |
| 293 | .@"c_int", | 305 | .c_int, |
| 294 | .@"c_uint", | 306 | .c_uint, |
| 295 | .@"c_long", | 307 | .c_long, |
| 296 | .@"c_ulong", | 308 | .c_ulong, |
| 297 | .@"c_longlong", | 309 | .c_longlong, |
| 298 | .@"c_ulonglong", | 310 | .c_ulonglong, |
| 299 | .@"c_longdouble", | 311 | .c_longdouble, |
| 300 | .@"f16", | 312 | .f16, |
| 301 | .@"f32", | 313 | .f32, |
| 302 | .@"f64", | 314 | .f64, |
| 303 | .@"f128", | 315 | .f128, |
| 304 | .@"c_void", | 316 | .c_void, |
| 305 | .@"bool", | 317 | .bool, |
| 306 | .@"void", | 318 | .void, |
| 307 | .@"type", | 319 | .type, |
| 308 | .@"anyerror", | 320 | .anyerror, |
| 309 | .@"comptime_int", | 321 | .comptime_int, |
| 310 | .@"comptime_float", | 322 | .comptime_float, |
| 311 | .@"noreturn", | 323 | .noreturn, |
| 312 | .array, | 324 | .array, |
| 313 | .array_u8_sentinel_0, | 325 | .array_u8_sentinel_0, |
| 314 | .single_const_pointer, | 326 | .single_const_pointer, |
| 315 | .single_const_pointer_to_comptime_int, | 327 | .single_const_pointer_to_comptime_int, |
| 316 | .fn_naked_noreturn_no_args, | 328 | .fn_naked_noreturn_no_args, |
| 329 | .int_unsigned, | ||
| 330 | .int_signed, | ||
| 317 | => false, | 331 | => false, |
| 318 | 332 | ||
| 319 | .const_slice_u8 => true, | 333 | .const_slice_u8 => true, |
| ... | @@ -323,34 +337,36 @@ pub const Type = extern union { | ... | @@ -323,34 +337,36 @@ pub const Type = extern union { |
| 323 | /// Asserts the type is a pointer type. | 337 | /// Asserts the type is a pointer type. |
| 324 | pub fn pointerIsConst(self: Type) bool { | 338 | pub fn pointerIsConst(self: Type) bool { |
| 325 | return switch (self.tag()) { | 339 | return switch (self.tag()) { |
| 326 | .@"u8", | 340 | .u8, |
| 327 | .@"i8", | 341 | .i8, |
| 328 | .@"isize", | 342 | .isize, |
| 329 | .@"usize", | 343 | .usize, |
| 330 | .@"c_short", | 344 | .c_short, |
| 331 | .@"c_ushort", | 345 | .c_ushort, |
| 332 | .@"c_int", | 346 | .c_int, |
| 333 | .@"c_uint", | 347 | .c_uint, |
| 334 | .@"c_long", | 348 | .c_long, |
| 335 | .@"c_ulong", | 349 | .c_ulong, |
| 336 | .@"c_longlong", | 350 | .c_longlong, |
| 337 | .@"c_ulonglong", | 351 | .c_ulonglong, |
| 338 | .@"c_longdouble", | 352 | .c_longdouble, |
| 339 | .@"f16", | 353 | .f16, |
| 340 | .@"f32", | 354 | .f32, |
| 341 | .@"f64", | 355 | .f64, |
| 342 | .@"f128", | 356 | .f128, |
| 343 | .@"c_void", | 357 | .c_void, |
| 344 | .@"bool", | 358 | .bool, |
| 345 | .@"void", | 359 | .void, |
| 346 | .@"type", | 360 | .type, |
| 347 | .@"anyerror", | 361 | .anyerror, |
| 348 | .@"comptime_int", | 362 | .comptime_int, |
| 349 | .@"comptime_float", | 363 | .comptime_float, |
| 350 | .@"noreturn", | 364 | .noreturn, |
| 351 | .array, | 365 | .array, |
| 352 | .array_u8_sentinel_0, | 366 | .array_u8_sentinel_0, |
| 353 | .fn_naked_noreturn_no_args, | 367 | .fn_naked_noreturn_no_args, |
| 368 | .int_unsigned, | ||
| 369 | .int_signed, | ||
| 354 | => unreachable, | 370 | => unreachable, |
| 355 | 371 | ||
| 356 | .single_const_pointer, | 372 | .single_const_pointer, |
| ... | @@ -363,32 +379,34 @@ pub const Type = extern union { | ... | @@ -363,32 +379,34 @@ pub const Type = extern union { |
| 363 | /// Asserts the type is a pointer or array type. | 379 | /// Asserts the type is a pointer or array type. |
| 364 | pub fn elemType(self: Type) Type { | 380 | pub fn elemType(self: Type) Type { |
| 365 | return switch (self.tag()) { | 381 | return switch (self.tag()) { |
| 366 | .@"u8", | 382 | .u8, |
| 367 | .@"i8", | 383 | .i8, |
| 368 | .@"isize", | 384 | .isize, |
| 369 | .@"usize", | 385 | .usize, |
| 370 | .@"c_short", | 386 | .c_short, |
| 371 | .@"c_ushort", | 387 | .c_ushort, |
| 372 | .@"c_int", | 388 | .c_int, |
| 373 | .@"c_uint", | 389 | .c_uint, |
| 374 | .@"c_long", | 390 | .c_long, |
| 375 | .@"c_ulong", | 391 | .c_ulong, |
| 376 | .@"c_longlong", | 392 | .c_longlong, |
| 377 | .@"c_ulonglong", | 393 | .c_ulonglong, |
| 378 | .@"c_longdouble", | 394 | .c_longdouble, |
| 379 | .@"f16", | 395 | .f16, |
| 380 | .@"f32", | 396 | .f32, |
| 381 | .@"f64", | 397 | .f64, |
| 382 | .@"f128", | 398 | .f128, |
| 383 | .@"c_void", | 399 | .c_void, |
| 384 | .@"bool", | 400 | .bool, |
| 385 | .@"void", | 401 | .void, |
| 386 | .@"type", | 402 | .type, |
| 387 | .@"anyerror", | 403 | .anyerror, |
| 388 | .@"comptime_int", | 404 | .comptime_int, |
| 389 | .@"comptime_float", | 405 | .comptime_float, |
| 390 | .@"noreturn", | 406 | .noreturn, |
| 391 | .fn_naked_noreturn_no_args, | 407 | .fn_naked_noreturn_no_args, |
| 408 | .int_unsigned, | ||
| 409 | .int_signed, | ||
| 392 | => unreachable, | 410 | => unreachable, |
| 393 | 411 | ||
| 394 | .array => self.cast(Payload.Array).?.elem_type, | 412 | .array => self.cast(Payload.Array).?.elem_type, |
| ... | @@ -398,7 +416,7 @@ pub const Type = extern union { | ... | @@ -398,7 +416,7 @@ pub const Type = extern union { |
| 398 | }; | 416 | }; |
| 399 | } | 417 | } |
| 400 | 418 | ||
| 401 | /// Asserts the type is an array. | 419 | /// Asserts the type is an array or vector. |
| 402 | pub fn arrayLen(self: Type) u64 { | 420 | pub fn arrayLen(self: Type) u64 { |
| 403 | return switch (self.tag()) { | 421 | return switch (self.tag()) { |
| 404 | .u8, | 422 | .u8, |
| ... | @@ -430,6 +448,8 @@ pub const Type = extern union { | ... | @@ -430,6 +448,8 @@ pub const Type = extern union { |
| 430 | .single_const_pointer, | 448 | .single_const_pointer, |
| 431 | .single_const_pointer_to_comptime_int, | 449 | .single_const_pointer_to_comptime_int, |
| 432 | .const_slice_u8, | 450 | .const_slice_u8, |
| 451 | .int_unsigned, | ||
| 452 | .int_signed, | ||
| 433 | => unreachable, | 453 | => unreachable, |
| 434 | 454 | ||
| 435 | .array => self.cast(Payload.Array).?.len, | 455 | .array => self.cast(Payload.Array).?.len, |
| ... | @@ -437,22 +457,64 @@ pub const Type = extern union { | ... | @@ -437,22 +457,64 @@ pub const Type = extern union { |
| 437 | }; | 457 | }; |
| 438 | } | 458 | } |
| 439 | 459 | ||
| 460 | /// Returns true if and only if the type is a fixed-width, signed integer. | ||
| 461 | pub fn isSignedInt(self: Type) bool { | ||
| 462 | return switch (self.tag()) { | ||
| 463 | .f16, | ||
| 464 | .f32, | ||
| 465 | .f64, | ||
| 466 | .f128, | ||
| 467 | .c_longdouble, | ||
| 468 | .c_void, | ||
| 469 | .bool, | ||
| 470 | .void, | ||
| 471 | .type, | ||
| 472 | .anyerror, | ||
| 473 | .comptime_int, | ||
| 474 | .comptime_float, | ||
| 475 | .noreturn, | ||
| 476 | .fn_naked_noreturn_no_args, | ||
| 477 | .array, | ||
| 478 | .single_const_pointer, | ||
| 479 | .single_const_pointer_to_comptime_int, | ||
| 480 | .array_u8_sentinel_0, | ||
| 481 | .const_slice_u8, | ||
| 482 | .int_unsigned, | ||
| 483 | .u8, | ||
| 484 | .usize, | ||
| 485 | .c_ushort, | ||
| 486 | .c_uint, | ||
| 487 | .c_ulong, | ||
| 488 | .c_ulonglong, | ||
| 489 | => false, | ||
| 490 | |||
| 491 | .int_signed, | ||
| 492 | .i8, | ||
| 493 | .isize, | ||
| 494 | .c_short, | ||
| 495 | .c_int, | ||
| 496 | .c_long, | ||
| 497 | .c_longlong, | ||
| 498 | => true, | ||
| 499 | }; | ||
| 500 | } | ||
| 501 | |||
| 440 | /// Asserts the type is a fixed-width integer. | 502 | /// Asserts the type is a fixed-width integer. |
| 441 | pub fn intInfo(self: Type, target: Target) struct { signed: bool, bits: u16 } { | 503 | pub fn intInfo(self: Type, target: Target) struct { signed: bool, bits: u16 } { |
| 442 | return switch (self.tag()) { | 504 | return switch (self.tag()) { |
| 443 | .@"f16", | 505 | .f16, |
| 444 | .@"f32", | 506 | .f32, |
| 445 | .@"f64", | 507 | .f64, |
| 446 | .@"f128", | 508 | .f128, |
| 447 | .@"c_longdouble", | 509 | .c_longdouble, |
| 448 | .@"c_void", | 510 | .c_void, |
| 449 | .@"bool", | 511 | .bool, |
| 450 | .@"void", | 512 | .void, |
| 451 | .@"type", | 513 | .type, |
| 452 | .@"anyerror", | 514 | .anyerror, |
| 453 | .@"comptime_int", | 515 | .comptime_int, |
| 454 | .@"comptime_float", | 516 | .comptime_float, |
| 455 | .@"noreturn", | 517 | .noreturn, |
| 456 | .fn_naked_noreturn_no_args, | 518 | .fn_naked_noreturn_no_args, |
| 457 | .array, | 519 | .array, |
| 458 | .single_const_pointer, | 520 | .single_const_pointer, |
| ... | @@ -461,18 +523,46 @@ pub const Type = extern union { | ... | @@ -461,18 +523,46 @@ pub const Type = extern union { |
| 461 | .const_slice_u8, | 523 | .const_slice_u8, |
| 462 | => unreachable, | 524 | => unreachable, |
| 463 | 525 | ||
| 464 | .@"u8" => .{ .signed = false, .bits = 8 }, | 526 | .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits }, |
| 465 | .@"i8" => .{ .signed = true, .bits = 8 }, | 527 | .int_signed => .{ .signed = true, .bits = self.cast(Payload.IntSigned).?.bits }, |
| 466 | .@"usize" => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() }, | 528 | .u8 => .{ .signed = false, .bits = 8 }, |
| 467 | .@"isize" => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() }, | 529 | .i8 => .{ .signed = true, .bits = 8 }, |
| 468 | .@"c_short" => .{ .signed = true, .bits = CInteger.short.sizeInBits(target) }, | 530 | .usize => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() }, |
| 469 | .@"c_ushort" => .{ .signed = false, .bits = CInteger.ushort.sizeInBits(target) }, | 531 | .isize => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() }, |
| 470 | .@"c_int" => .{ .signed = true, .bits = CInteger.int.sizeInBits(target) }, | 532 | .c_short => .{ .signed = true, .bits = CType.short.sizeInBits(target) }, |
| 471 | .@"c_uint" => .{ .signed = false, .bits = CInteger.uint.sizeInBits(target) }, | 533 | .c_ushort => .{ .signed = false, .bits = CType.ushort.sizeInBits(target) }, |
| 472 | .@"c_long" => .{ .signed = true, .bits = CInteger.long.sizeInBits(target) }, | 534 | .c_int => .{ .signed = true, .bits = CType.int.sizeInBits(target) }, |
| 473 | .@"c_ulong" => .{ .signed = false, .bits = CInteger.ulong.sizeInBits(target) }, | 535 | .c_uint => .{ .signed = false, .bits = CType.uint.sizeInBits(target) }, |
| 474 | .@"c_longlong" => .{ .signed = true, .bits = CInteger.longlong.sizeInBits(target) }, | 536 | .c_long => .{ .signed = true, .bits = CType.long.sizeInBits(target) }, |
| 475 | .@"c_ulonglong" => .{ .signed = false, .bits = CInteger.ulonglong.sizeInBits(target) }, | 537 | .c_ulong => .{ .signed = false, .bits = CType.ulong.sizeInBits(target) }, |
| 538 | .c_longlong => .{ .signed = true, .bits = CType.longlong.sizeInBits(target) }, | ||
| 539 | .c_ulonglong => .{ .signed = false, .bits = CType.ulonglong.sizeInBits(target) }, | ||
| 540 | }; | ||
| 541 | } | ||
| 542 | |||
| 543 | pub fn isFloat(self: Type) bool { | ||
| 544 | return switch (self.tag()) { | ||
| 545 | .f16, | ||
| 546 | .f32, | ||
| 547 | .f64, | ||
| 548 | .f128, | ||
| 549 | .c_longdouble, | ||
| 550 | => true, | ||
| 551 | |||
| 552 | else => false, | ||
| 553 | }; | ||
| 554 | } | ||
| 555 | |||
| 556 | /// Asserts the type is a fixed-size float. | ||
| 557 | pub fn floatBits(self: Type, target: Target) u16 { | ||
| 558 | return switch (self.tag()) { | ||
| 559 | .f16 => 16, | ||
| 560 | .f32 => 32, | ||
| 561 | .f64 => 64, | ||
| 562 | .f128 => 128, | ||
| 563 | .c_longdouble => CType.longdouble.sizeInBits(target), | ||
| 564 | |||
| 565 | else => unreachable, | ||
| 476 | }; | 566 | }; |
| 477 | } | 567 | } |
| 478 | 568 | ||
| ... | @@ -511,6 +601,8 @@ pub const Type = extern union { | ... | @@ -511,6 +601,8 @@ pub const Type = extern union { |
| 511 | .c_ulong, | 601 | .c_ulong, |
| 512 | .c_longlong, | 602 | .c_longlong, |
| 513 | .c_ulonglong, | 603 | .c_ulonglong, |
| 604 | .int_unsigned, | ||
| 605 | .int_signed, | ||
| 514 | => unreachable, | 606 | => unreachable, |
| 515 | }; | 607 | }; |
| 516 | } | 608 | } |
| ... | @@ -551,6 +643,8 @@ pub const Type = extern union { | ... | @@ -551,6 +643,8 @@ pub const Type = extern union { |
| 551 | .c_ulong, | 643 | .c_ulong, |
| 552 | .c_longlong, | 644 | .c_longlong, |
| 553 | .c_ulonglong, | 645 | .c_ulonglong, |
| 646 | .int_unsigned, | ||
| 647 | .int_signed, | ||
| 554 | => unreachable, | 648 | => unreachable, |
| 555 | } | 649 | } |
| 556 | } | 650 | } |
| ... | @@ -590,6 +684,8 @@ pub const Type = extern union { | ... | @@ -590,6 +684,8 @@ pub const Type = extern union { |
| 590 | .c_ulong, | 684 | .c_ulong, |
| 591 | .c_longlong, | 685 | .c_longlong, |
| 592 | .c_ulonglong, | 686 | .c_ulonglong, |
| 687 | .int_unsigned, | ||
| 688 | .int_signed, | ||
| 593 | => unreachable, | 689 | => unreachable, |
| 594 | }; | 690 | }; |
| 595 | } | 691 | } |
| ... | @@ -629,10 +725,145 @@ pub const Type = extern union { | ... | @@ -629,10 +725,145 @@ pub const Type = extern union { |
| 629 | .c_ulong, | 725 | .c_ulong, |
| 630 | .c_longlong, | 726 | .c_longlong, |
| 631 | .c_ulonglong, | 727 | .c_ulonglong, |
| 728 | .int_unsigned, | ||
| 729 | .int_signed, | ||
| 632 | => unreachable, | 730 | => unreachable, |
| 633 | }; | 731 | }; |
| 634 | } | 732 | } |
| 635 | 733 | ||
| 734 | pub fn isNumeric(self: Type) bool { | ||
| 735 | return switch (self.tag()) { | ||
| 736 | .f16, | ||
| 737 | .f32, | ||
| 738 | .f64, | ||
| 739 | .f128, | ||
| 740 | .c_longdouble, | ||
| 741 | .comptime_int, | ||
| 742 | .comptime_float, | ||
| 743 | .u8, | ||
| 744 | .i8, | ||
| 745 | .usize, | ||
| 746 | .isize, | ||
| 747 | .c_short, | ||
| 748 | .c_ushort, | ||
| 749 | .c_int, | ||
| 750 | .c_uint, | ||
| 751 | .c_long, | ||
| 752 | .c_ulong, | ||
| 753 | .c_longlong, | ||
| 754 | .c_ulonglong, | ||
| 755 | .int_unsigned, | ||
| 756 | .int_signed, | ||
| 757 | => true, | ||
| 758 | |||
| 759 | .c_void, | ||
| 760 | .bool, | ||
| 761 | .void, | ||
| 762 | .type, | ||
| 763 | .anyerror, | ||
| 764 | .noreturn, | ||
| 765 | .fn_naked_noreturn_no_args, | ||
| 766 | .array, | ||
| 767 | .single_const_pointer, | ||
| 768 | .single_const_pointer_to_comptime_int, | ||
| 769 | .array_u8_sentinel_0, | ||
| 770 | .const_slice_u8, | ||
| 771 | => false, | ||
| 772 | }; | ||
| 773 | } | ||
| 774 | |||
| 775 | pub fn onePossibleValue(self: Type) bool { | ||
| 776 | var ty = self; | ||
| 777 | while (true) switch (ty.tag()) { | ||
| 778 | .f16, | ||
| 779 | .f32, | ||
| 780 | .f64, | ||
| 781 | .f128, | ||
| 782 | .c_longdouble, | ||
| 783 | .comptime_int, | ||
| 784 | .comptime_float, | ||
| 785 | .u8, | ||
| 786 | .i8, | ||
| 787 | .usize, | ||
| 788 | .isize, | ||
| 789 | .c_short, | ||
| 790 | .c_ushort, | ||
| 791 | .c_int, | ||
| 792 | .c_uint, | ||
| 793 | .c_long, | ||
| 794 | .c_ulong, | ||
| 795 | .c_longlong, | ||
| 796 | .c_ulonglong, | ||
| 797 | .bool, | ||
| 798 | .type, | ||
| 799 | .anyerror, | ||
| 800 | .fn_naked_noreturn_no_args, | ||
| 801 | .single_const_pointer_to_comptime_int, | ||
| 802 | .array_u8_sentinel_0, | ||
| 803 | .const_slice_u8, | ||
| 804 | => return false, | ||
| 805 | |||
| 806 | .c_void, | ||
| 807 | .void, | ||
| 808 | .noreturn, | ||
| 809 | => return true, | ||
| 810 | |||
| 811 | .int_unsigned => return ty.cast(Payload.IntUnsigned).?.bits == 0, | ||
| 812 | .int_signed => return ty.cast(Payload.IntSigned).?.bits == 0, | ||
| 813 | .array => { | ||
| 814 | const array = ty.cast(Payload.Array).?; | ||
| 815 | if (array.len == 0) | ||
| 816 | return true; | ||
| 817 | ty = array.elem_type; | ||
| 818 | continue; | ||
| 819 | }, | ||
| 820 | .single_const_pointer => { | ||
| 821 | const ptr = ty.cast(Payload.SingleConstPointer).?; | ||
| 822 | ty = ptr.pointee_type; | ||
| 823 | continue; | ||
| 824 | }, | ||
| 825 | }; | ||
| 826 | } | ||
| 827 | |||
| 828 | pub fn isCPtr(self: Type) bool { | ||
| 829 | return switch (self.tag()) { | ||
| 830 | .f16, | ||
| 831 | .f32, | ||
| 832 | .f64, | ||
| 833 | .f128, | ||
| 834 | .c_longdouble, | ||
| 835 | .comptime_int, | ||
| 836 | .comptime_float, | ||
| 837 | .u8, | ||
| 838 | .i8, | ||
| 839 | .usize, | ||
| 840 | .isize, | ||
| 841 | .c_short, | ||
| 842 | .c_ushort, | ||
| 843 | .c_int, | ||
| 844 | .c_uint, | ||
| 845 | .c_long, | ||
| 846 | .c_ulong, | ||
| 847 | .c_longlong, | ||
| 848 | .c_ulonglong, | ||
| 849 | .bool, | ||
| 850 | .type, | ||
| 851 | .anyerror, | ||
| 852 | .fn_naked_noreturn_no_args, | ||
| 853 | .single_const_pointer_to_comptime_int, | ||
| 854 | .array_u8_sentinel_0, | ||
| 855 | .const_slice_u8, | ||
| 856 | .c_void, | ||
| 857 | .void, | ||
| 858 | .noreturn, | ||
| 859 | .int_unsigned, | ||
| 860 | .int_signed, | ||
| 861 | .array, | ||
| 862 | .single_const_pointer, | ||
| 863 | => return false, | ||
| 864 | }; | ||
| 865 | } | ||
| 866 | |||
| 636 | /// This enum does not directly correspond to `std.builtin.TypeId` because | 867 | /// This enum does not directly correspond to `std.builtin.TypeId` because |
| 637 | /// it has extra enum tags in it, as a way of using less memory. For example, | 868 | /// it has extra enum tags in it, as a way of using less memory. For example, |
| 638 | /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types | 869 | /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types |
| ... | @@ -674,6 +905,8 @@ pub const Type = extern union { | ... | @@ -674,6 +905,8 @@ pub const Type = extern union { |
| 674 | array_u8_sentinel_0, | 905 | array_u8_sentinel_0, |
| 675 | array, | 906 | array, |
| 676 | single_const_pointer, | 907 | single_const_pointer, |
| 908 | int_signed, | ||
| 909 | int_unsigned, | ||
| 677 | 910 | ||
| 678 | pub const last_no_payload_tag = Tag.const_slice_u8; | 911 | pub const last_no_payload_tag = Tag.const_slice_u8; |
| 679 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; | 912 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| ... | @@ -700,10 +933,22 @@ pub const Type = extern union { | ... | @@ -700,10 +933,22 @@ pub const Type = extern union { |
| 700 | 933 | ||
| 701 | pointee_type: Type, | 934 | pointee_type: Type, |
| 702 | }; | 935 | }; |
| 936 | |||
| 937 | pub const IntSigned = struct { | ||
| 938 | base: Payload = Payload{ .tag = .int_signed }, | ||
| 939 | |||
| 940 | bits: u16, | ||
| 941 | }; | ||
| 942 | |||
| 943 | pub const IntUnsigned = struct { | ||
| 944 | base: Payload = Payload{ .tag = .int_unsigned }, | ||
| 945 | |||
| 946 | bits: u16, | ||
| 947 | }; | ||
| 703 | }; | 948 | }; |
| 704 | }; | 949 | }; |
| 705 | 950 | ||
| 706 | pub const CInteger = enum { | 951 | pub const CType = enum { |
| 707 | short, | 952 | short, |
| 708 | ushort, | 953 | ushort, |
| 709 | int, | 954 | int, |
| ... | @@ -712,8 +957,9 @@ pub const CInteger = enum { | ... | @@ -712,8 +957,9 @@ pub const CInteger = enum { |
| 712 | ulong, | 957 | ulong, |
| 713 | longlong, | 958 | longlong, |
| 714 | ulonglong, | 959 | ulonglong, |
| 960 | longdouble, | ||
| 715 | 961 | ||
| 716 | pub fn sizeInBits(self: CInteger, target: Target) u16 { | 962 | pub fn sizeInBits(self: CType, target: Target) u16 { |
| 717 | const arch = target.cpu.arch; | 963 | const arch = target.cpu.arch; |
| 718 | switch (target.os.tag) { | 964 | switch (target.os.tag) { |
| 719 | .freestanding, .other => switch (target.cpu.arch) { | 965 | .freestanding, .other => switch (target.cpu.arch) { |
| ... | @@ -729,6 +975,7 @@ pub const CInteger = enum { | ... | @@ -729,6 +975,7 @@ pub const CInteger = enum { |
| 729 | .longlong, | 975 | .longlong, |
| 730 | .ulonglong, | 976 | .ulonglong, |
| 731 | => return 64, | 977 | => return 64, |
| 978 | .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"), | ||
| 732 | }, | 979 | }, |
| 733 | else => switch (self) { | 980 | else => switch (self) { |
| 734 | .short, | 981 | .short, |
| ... | @@ -743,6 +990,7 @@ pub const CInteger = enum { | ... | @@ -743,6 +990,7 @@ pub const CInteger = enum { |
| 743 | .longlong, | 990 | .longlong, |
| 744 | .ulonglong, | 991 | .ulonglong, |
| 745 | => return 64, | 992 | => return 64, |
| 993 | .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"), | ||
| 746 | }, | 994 | }, |
| 747 | }, | 995 | }, |
| 748 | 996 | ||
| ... | @@ -767,6 +1015,7 @@ pub const CInteger = enum { | ... | @@ -767,6 +1015,7 @@ pub const CInteger = enum { |
| 767 | .longlong, | 1015 | .longlong, |
| 768 | .ulonglong, | 1016 | .ulonglong, |
| 769 | => return 64, | 1017 | => return 64, |
| 1018 | .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"), | ||
| 770 | }, | 1019 | }, |
| 771 | 1020 | ||
| 772 | .windows, .uefi => switch (self) { | 1021 | .windows, .uefi => switch (self) { |
| ... | @@ -781,6 +1030,7 @@ pub const CInteger = enum { | ... | @@ -781,6 +1030,7 @@ pub const CInteger = enum { |
| 781 | .longlong, | 1030 | .longlong, |
| 782 | .ulonglong, | 1031 | .ulonglong, |
| 783 | => return 64, | 1032 | => return 64, |
| 1033 | .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"), | ||
| 784 | }, | 1034 | }, |
| 785 | 1035 | ||
| 786 | .ios => switch (self) { | 1036 | .ios => switch (self) { |
| ... | @@ -795,6 +1045,7 @@ pub const CInteger = enum { | ... | @@ -795,6 +1045,7 @@ pub const CInteger = enum { |
| 795 | .longlong, | 1045 | .longlong, |
| 796 | .ulonglong, | 1046 | .ulonglong, |
| 797 | => return 64, | 1047 | => return 64, |
| 1048 | .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"), | ||
| 798 | }, | 1049 | }, |
| 799 | 1050 | ||
| 800 | .ananas, | 1051 | .ananas, |
| ... | @@ -821,7 +1072,7 @@ pub const CInteger = enum { | ... | @@ -821,7 +1072,7 @@ pub const CInteger = enum { |
| 821 | .amdpal, | 1072 | .amdpal, |
| 822 | .hermit, | 1073 | .hermit, |
| 823 | .hurd, | 1074 | .hurd, |
| 824 | => @panic("TODO specify the C integer type sizes for this OS"), | 1075 | => @panic("TODO specify the C integer and float type sizes for this OS"), |
| 825 | } | 1076 | } |
| 826 | } | 1077 | } |
| 827 | }; | 1078 | }; |
src-self-hosted/value.zig+319-26| ... | @@ -48,9 +48,10 @@ pub const Value = extern union { | ... | @@ -48,9 +48,10 @@ pub const Value = extern union { |
| 48 | single_const_pointer_to_comptime_int_type, | 48 | single_const_pointer_to_comptime_int_type, |
| 49 | const_slice_u8_type, | 49 | const_slice_u8_type, |
| 50 | 50 | ||
| 51 | undef, | ||
| 51 | zero, | 52 | zero, |
| 52 | void_value, | 53 | the_one_possible_value, // when the type only has one possible value |
| 53 | noreturn_value, | 54 | null_value, |
| 54 | bool_true, | 55 | bool_true, |
| 55 | bool_false, // See last_no_payload_tag below. | 56 | bool_false, // See last_no_payload_tag below. |
| 56 | // After this, the tag requires a payload. | 57 | // After this, the tag requires a payload. |
| ... | @@ -63,6 +64,7 @@ pub const Value = extern union { | ... | @@ -63,6 +64,7 @@ pub const Value = extern union { |
| 63 | ref, | 64 | ref, |
| 64 | ref_val, | 65 | ref_val, |
| 65 | bytes, | 66 | bytes, |
| 67 | repeated, // the value is a value repeated some number of times | ||
| 66 | 68 | ||
| 67 | pub const last_no_payload_tag = Tag.bool_false; | 69 | pub const last_no_payload_tag = Tag.bool_false; |
| 68 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; | 70 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| ... | @@ -135,9 +137,10 @@ pub const Value = extern union { | ... | @@ -135,9 +137,10 @@ pub const Value = extern union { |
| 135 | .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"), | 137 | .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"), |
| 136 | .const_slice_u8_type => return out_stream.writeAll("[]const u8"), | 138 | .const_slice_u8_type => return out_stream.writeAll("[]const u8"), |
| 137 | 139 | ||
| 140 | .null_value => return out_stream.writeAll("null"), | ||
| 141 | .undef => return out_stream.writeAll("undefined"), | ||
| 138 | .zero => return out_stream.writeAll("0"), | 142 | .zero => return out_stream.writeAll("0"), |
| 139 | .void_value => return out_stream.writeAll("{}"), | 143 | .the_one_possible_value => return out_stream.writeAll("(one possible value)"), |
| 140 | .noreturn_value => return out_stream.writeAll("unreachable"), | ||
| 141 | .bool_true => return out_stream.writeAll("true"), | 144 | .bool_true => return out_stream.writeAll("true"), |
| 142 | .bool_false => return out_stream.writeAll("false"), | 145 | .bool_false => return out_stream.writeAll("false"), |
| 143 | .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream), | 146 | .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream), |
| ... | @@ -152,6 +155,10 @@ pub const Value = extern union { | ... | @@ -152,6 +155,10 @@ pub const Value = extern union { |
| 152 | continue; | 155 | continue; |
| 153 | }, | 156 | }, |
| 154 | .bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream), | 157 | .bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream), |
| 158 | .repeated => { | ||
| 159 | try out_stream.writeAll("(repeated) "); | ||
| 160 | val = val.cast(Payload.Repeated).?.val; | ||
| 161 | }, | ||
| 155 | }; | 162 | }; |
| 156 | } | 163 | } |
| 157 | 164 | ||
| ... | @@ -198,11 +205,12 @@ pub const Value = extern union { | ... | @@ -198,11 +205,12 @@ pub const Value = extern union { |
| 198 | .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int), | 205 | .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int), |
| 199 | .const_slice_u8_type => Type.initTag(.const_slice_u8), | 206 | .const_slice_u8_type => Type.initTag(.const_slice_u8), |
| 200 | 207 | ||
| 208 | .undef, | ||
| 201 | .zero, | 209 | .zero, |
| 202 | .void_value, | 210 | .the_one_possible_value, |
| 203 | .noreturn_value, | ||
| 204 | .bool_true, | 211 | .bool_true, |
| 205 | .bool_false, | 212 | .bool_false, |
| 213 | .null_value, | ||
| 206 | .int_u64, | 214 | .int_u64, |
| 207 | .int_i64, | 215 | .int_i64, |
| 208 | .int_big, | 216 | .int_big, |
| ... | @@ -210,12 +218,13 @@ pub const Value = extern union { | ... | @@ -210,12 +218,13 @@ pub const Value = extern union { |
| 210 | .ref, | 218 | .ref, |
| 211 | .ref_val, | 219 | .ref_val, |
| 212 | .bytes, | 220 | .bytes, |
| 221 | .repeated, | ||
| 213 | => unreachable, | 222 | => unreachable, |
| 214 | }; | 223 | }; |
| 215 | } | 224 | } |
| 216 | 225 | ||
| 217 | /// Asserts the value is an integer. | 226 | /// Asserts the value is an integer. |
| 218 | pub fn toBigInt(self: Value, allocator: *Allocator) Allocator.Error!BigInt { | 227 | pub fn toBigInt(self: Value, space: *BigIntSpace) BigInt { |
| 219 | switch (self.tag()) { | 228 | switch (self.tag()) { |
| 220 | .ty, | 229 | .ty, |
| 221 | .u8_type, | 230 | .u8_type, |
| ... | @@ -246,20 +255,23 @@ pub const Value = extern union { | ... | @@ -246,20 +255,23 @@ pub const Value = extern union { |
| 246 | .fn_naked_noreturn_no_args_type, | 255 | .fn_naked_noreturn_no_args_type, |
| 247 | .single_const_pointer_to_comptime_int_type, | 256 | .single_const_pointer_to_comptime_int_type, |
| 248 | .const_slice_u8_type, | 257 | .const_slice_u8_type, |
| 249 | .void_value, | ||
| 250 | .noreturn_value, | ||
| 251 | .bool_true, | 258 | .bool_true, |
| 252 | .bool_false, | 259 | .bool_false, |
| 260 | .null_value, | ||
| 253 | .function, | 261 | .function, |
| 254 | .ref, | 262 | .ref, |
| 255 | .ref_val, | 263 | .ref_val, |
| 256 | .bytes, | 264 | .bytes, |
| 265 | .undef, | ||
| 266 | .repeated, | ||
| 257 | => unreachable, | 267 | => unreachable, |
| 258 | 268 | ||
| 259 | .zero => return BigInt.initSet(allocator, 0), | 269 | .the_one_possible_value, // An integer with one possible value is always zero. |
| 270 | .zero, | ||
| 271 | => return BigInt.initSetFixed(&space.limbs, 0), | ||
| 260 | 272 | ||
| 261 | .int_u64 => return BigInt.initSet(allocator, self.cast(Payload.Int_u64).?.int), | 273 | .int_u64 => return BigInt.initSetFixed(&space.limbs, self.cast(Payload.Int_u64).?.int), |
| 262 | .int_i64 => return BigInt.initSet(allocator, self.cast(Payload.Int_i64).?.int), | 274 | .int_i64 => return BigInt.initSetFixed(&space.limbs, self.cast(Payload.Int_i64).?.int), |
| 263 | .int_big => return self.cast(Payload.IntBig).?.big_int, | 275 | .int_big => return self.cast(Payload.IntBig).?.big_int, |
| 264 | } | 276 | } |
| 265 | } | 277 | } |
| ... | @@ -296,17 +308,20 @@ pub const Value = extern union { | ... | @@ -296,17 +308,20 @@ pub const Value = extern union { |
| 296 | .fn_naked_noreturn_no_args_type, | 308 | .fn_naked_noreturn_no_args_type, |
| 297 | .single_const_pointer_to_comptime_int_type, | 309 | .single_const_pointer_to_comptime_int_type, |
| 298 | .const_slice_u8_type, | 310 | .const_slice_u8_type, |
| 299 | .void_value, | ||
| 300 | .noreturn_value, | ||
| 301 | .bool_true, | 311 | .bool_true, |
| 302 | .bool_false, | 312 | .bool_false, |
| 313 | .null_value, | ||
| 303 | .function, | 314 | .function, |
| 304 | .ref, | 315 | .ref, |
| 305 | .ref_val, | 316 | .ref_val, |
| 306 | .bytes, | 317 | .bytes, |
| 318 | .undef, | ||
| 319 | .repeated, | ||
| 307 | => unreachable, | 320 | => unreachable, |
| 308 | 321 | ||
| 309 | .zero => return 0, | 322 | .zero, |
| 323 | .the_one_possible_value, // an integer with one possible value is always zero | ||
| 324 | => return 0, | ||
| 310 | 325 | ||
| 311 | .int_u64 => return self.cast(Payload.Int_u64).?.int, | 326 | .int_u64 => return self.cast(Payload.Int_u64).?.int, |
| 312 | .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int), | 327 | .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int), |
| ... | @@ -314,6 +329,66 @@ pub const Value = extern union { | ... | @@ -314,6 +329,66 @@ pub const Value = extern union { |
| 314 | } | 329 | } |
| 315 | } | 330 | } |
| 316 | 331 | ||
| 332 | /// Asserts the value is an integer and not undefined. | ||
| 333 | /// Returns the number of bits the value requires to represent stored in twos complement form. | ||
| 334 | pub fn intBitCountTwosComp(self: Value) usize { | ||
| 335 | switch (self.tag()) { | ||
| 336 | .ty, | ||
| 337 | .u8_type, | ||
| 338 | .i8_type, | ||
| 339 | .isize_type, | ||
| 340 | .usize_type, | ||
| 341 | .c_short_type, | ||
| 342 | .c_ushort_type, | ||
| 343 | .c_int_type, | ||
| 344 | .c_uint_type, | ||
| 345 | .c_long_type, | ||
| 346 | .c_ulong_type, | ||
| 347 | .c_longlong_type, | ||
| 348 | .c_ulonglong_type, | ||
| 349 | .c_longdouble_type, | ||
| 350 | .f16_type, | ||
| 351 | .f32_type, | ||
| 352 | .f64_type, | ||
| 353 | .f128_type, | ||
| 354 | .c_void_type, | ||
| 355 | .bool_type, | ||
| 356 | .void_type, | ||
| 357 | .type_type, | ||
| 358 | .anyerror_type, | ||
| 359 | .comptime_int_type, | ||
| 360 | .comptime_float_type, | ||
| 361 | .noreturn_type, | ||
| 362 | .fn_naked_noreturn_no_args_type, | ||
| 363 | .single_const_pointer_to_comptime_int_type, | ||
| 364 | .const_slice_u8_type, | ||
| 365 | .bool_true, | ||
| 366 | .bool_false, | ||
| 367 | .null_value, | ||
| 368 | .function, | ||
| 369 | .ref, | ||
| 370 | .ref_val, | ||
| 371 | .bytes, | ||
| 372 | .undef, | ||
| 373 | .repeated, | ||
| 374 | => unreachable, | ||
| 375 | |||
| 376 | .the_one_possible_value, // an integer with one possible value is always zero | ||
| 377 | .zero, | ||
| 378 | => return 0, | ||
| 379 | |||
| 380 | .int_u64 => { | ||
| 381 | const x = self.cast(Payload.Int_u64).?.int; | ||
| 382 | if (x == 0) return 0; | ||
| 383 | return std.math.log2(x) + 1; | ||
| 384 | }, | ||
| 385 | .int_i64 => { | ||
| 386 | @panic("TODO implement i64 intBitCountTwosComp"); | ||
| 387 | }, | ||
| 388 | .int_big => return self.cast(Payload.IntBig).?.big_int.bitCountTwosComp(), | ||
| 389 | } | ||
| 390 | } | ||
| 391 | |||
| 317 | /// Asserts the value is an integer, and the destination type is ComptimeInt or Int. | 392 | /// Asserts the value is an integer, and the destination type is ComptimeInt or Int. |
| 318 | pub fn intFitsInType(self: Value, ty: Type, target: Target) bool { | 393 | pub fn intFitsInType(self: Value, ty: Type, target: Target) bool { |
| 319 | switch (self.tag()) { | 394 | switch (self.tag()) { |
| ... | @@ -346,17 +421,20 @@ pub const Value = extern union { | ... | @@ -346,17 +421,20 @@ pub const Value = extern union { |
| 346 | .fn_naked_noreturn_no_args_type, | 421 | .fn_naked_noreturn_no_args_type, |
| 347 | .single_const_pointer_to_comptime_int_type, | 422 | .single_const_pointer_to_comptime_int_type, |
| 348 | .const_slice_u8_type, | 423 | .const_slice_u8_type, |
| 349 | .void_value, | ||
| 350 | .noreturn_value, | ||
| 351 | .bool_true, | 424 | .bool_true, |
| 352 | .bool_false, | 425 | .bool_false, |
| 426 | .null_value, | ||
| 353 | .function, | 427 | .function, |
| 354 | .ref, | 428 | .ref, |
| 355 | .ref_val, | 429 | .ref_val, |
| 356 | .bytes, | 430 | .bytes, |
| 431 | .repeated, | ||
| 357 | => unreachable, | 432 | => unreachable, |
| 358 | 433 | ||
| 359 | .zero => return true, | 434 | .zero, |
| 435 | .undef, | ||
| 436 | .the_one_possible_value, // an integer with one possible value is always zero | ||
| 437 | => return true, | ||
| 360 | 438 | ||
| 361 | .int_u64 => switch (ty.zigTypeTag()) { | 439 | .int_u64 => switch (ty.zigTypeTag()) { |
| 362 | .Int => { | 440 | .Int => { |
| ... | @@ -392,9 +470,148 @@ pub const Value = extern union { | ... | @@ -392,9 +470,148 @@ pub const Value = extern union { |
| 392 | } | 470 | } |
| 393 | } | 471 | } |
| 394 | 472 | ||
| 473 | /// Asserts the value is a float | ||
| 474 | pub fn floatHasFraction(self: Value) bool { | ||
| 475 | return switch (self.tag()) { | ||
| 476 | .ty, | ||
| 477 | .u8_type, | ||
| 478 | .i8_type, | ||
| 479 | .isize_type, | ||
| 480 | .usize_type, | ||
| 481 | .c_short_type, | ||
| 482 | .c_ushort_type, | ||
| 483 | .c_int_type, | ||
| 484 | .c_uint_type, | ||
| 485 | .c_long_type, | ||
| 486 | .c_ulong_type, | ||
| 487 | .c_longlong_type, | ||
| 488 | .c_ulonglong_type, | ||
| 489 | .c_longdouble_type, | ||
| 490 | .f16_type, | ||
| 491 | .f32_type, | ||
| 492 | .f64_type, | ||
| 493 | .f128_type, | ||
| 494 | .c_void_type, | ||
| 495 | .bool_type, | ||
| 496 | .void_type, | ||
| 497 | .type_type, | ||
| 498 | .anyerror_type, | ||
| 499 | .comptime_int_type, | ||
| 500 | .comptime_float_type, | ||
| 501 | .noreturn_type, | ||
| 502 | .fn_naked_noreturn_no_args_type, | ||
| 503 | .single_const_pointer_to_comptime_int_type, | ||
| 504 | .const_slice_u8_type, | ||
| 505 | .bool_true, | ||
| 506 | .bool_false, | ||
| 507 | .null_value, | ||
| 508 | .function, | ||
| 509 | .ref, | ||
| 510 | .ref_val, | ||
| 511 | .bytes, | ||
| 512 | .repeated, | ||
| 513 | .undef, | ||
| 514 | .int_u64, | ||
| 515 | .int_i64, | ||
| 516 | .int_big, | ||
| 517 | .the_one_possible_value, | ||
| 518 | => unreachable, | ||
| 519 | |||
| 520 | .zero => false, | ||
| 521 | }; | ||
| 522 | } | ||
| 523 | |||
| 524 | pub fn orderAgainstZero(lhs: Value) std.math.Order { | ||
| 525 | switch (lhs.tag()) { | ||
| 526 | .ty, | ||
| 527 | .u8_type, | ||
| 528 | .i8_type, | ||
| 529 | .isize_type, | ||
| 530 | .usize_type, | ||
| 531 | .c_short_type, | ||
| 532 | .c_ushort_type, | ||
| 533 | .c_int_type, | ||
| 534 | .c_uint_type, | ||
| 535 | .c_long_type, | ||
| 536 | .c_ulong_type, | ||
| 537 | .c_longlong_type, | ||
| 538 | .c_ulonglong_type, | ||
| 539 | .c_longdouble_type, | ||
| 540 | .f16_type, | ||
| 541 | .f32_type, | ||
| 542 | .f64_type, | ||
| 543 | .f128_type, | ||
| 544 | .c_void_type, | ||
| 545 | .bool_type, | ||
| 546 | .void_type, | ||
| 547 | .type_type, | ||
| 548 | .anyerror_type, | ||
| 549 | .comptime_int_type, | ||
| 550 | .comptime_float_type, | ||
| 551 | .noreturn_type, | ||
| 552 | .fn_naked_noreturn_no_args_type, | ||
| 553 | .single_const_pointer_to_comptime_int_type, | ||
| 554 | .const_slice_u8_type, | ||
| 555 | .bool_true, | ||
| 556 | .bool_false, | ||
| 557 | .null_value, | ||
| 558 | .function, | ||
| 559 | .ref, | ||
| 560 | .ref_val, | ||
| 561 | .bytes, | ||
| 562 | .repeated, | ||
| 563 | .undef, | ||
| 564 | => unreachable, | ||
| 565 | |||
| 566 | .zero, | ||
| 567 | .the_one_possible_value, // an integer with one possible value is always zero | ||
| 568 | => return .eq, | ||
| 569 | |||
| 570 | .int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0), | ||
| 571 | .int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0), | ||
| 572 | .int_big => return lhs.cast(Payload.IntBig).?.big_int.orderAgainstScalar(0), | ||
| 573 | } | ||
| 574 | } | ||
| 575 | |||
| 576 | /// Asserts the value is comparable. | ||
| 577 | pub fn order(lhs: Value, rhs: Value) std.math.Order { | ||
| 578 | const lhs_tag = lhs.tag(); | ||
| 579 | const rhs_tag = lhs.tag(); | ||
| 580 | const lhs_is_zero = lhs_tag == .zero or lhs_tag == .the_one_possible_value; | ||
| 581 | const rhs_is_zero = rhs_tag == .zero or rhs_tag == .the_one_possible_value; | ||
| 582 | if (lhs_is_zero) return rhs.orderAgainstZero().invert(); | ||
| 583 | if (rhs_is_zero) return lhs.orderAgainstZero(); | ||
| 584 | |||
| 585 | // TODO floats | ||
| 586 | |||
| 587 | var lhs_bigint_space: BigIntSpace = undefined; | ||
| 588 | var rhs_bigint_space: BigIntSpace = undefined; | ||
| 589 | const lhs_bigint = lhs.toBigInt(&lhs_bigint_space); | ||
| 590 | const rhs_bigint = rhs.toBigInt(&rhs_bigint_space); | ||
| 591 | return BigInt.cmp(lhs_bigint, rhs_bigint); | ||
| 592 | } | ||
| 593 | |||
| 594 | /// Asserts the value is comparable. | ||
| 595 | pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value) bool { | ||
| 596 | return order(lhs, rhs).compare(op); | ||
| 597 | } | ||
| 598 | |||
| 599 | /// Asserts the value is comparable. | ||
| 600 | pub fn compareWithZero(lhs: Value, op: std.math.CompareOperator) bool { | ||
| 601 | return orderAgainstZero(lhs).compare(op); | ||
| 602 | } | ||
| 603 | |||
| 604 | pub fn toBool(self: Value) bool { | ||
| 605 | return switch (self.tag()) { | ||
| 606 | .bool_true => true, | ||
| 607 | .bool_false => false, | ||
| 608 | else => unreachable, | ||
| 609 | }; | ||
| 610 | } | ||
| 611 | |||
| 395 | /// Asserts the value is a pointer and dereferences it. | 612 | /// Asserts the value is a pointer and dereferences it. |
| 396 | pub fn pointerDeref(self: Value) Value { | 613 | pub fn pointerDeref(self: Value) Value { |
| 397 | switch (self.tag()) { | 614 | return switch (self.tag()) { |
| 398 | .ty, | 615 | .ty, |
| 399 | .u8_type, | 616 | .u8_type, |
| 400 | .i8_type, | 617 | .i8_type, |
| ... | @@ -425,20 +642,22 @@ pub const Value = extern union { | ... | @@ -425,20 +642,22 @@ pub const Value = extern union { |
| 425 | .single_const_pointer_to_comptime_int_type, | 642 | .single_const_pointer_to_comptime_int_type, |
| 426 | .const_slice_u8_type, | 643 | .const_slice_u8_type, |
| 427 | .zero, | 644 | .zero, |
| 428 | .void_value, | ||
| 429 | .noreturn_value, | ||
| 430 | .bool_true, | 645 | .bool_true, |
| 431 | .bool_false, | 646 | .bool_false, |
| 647 | .null_value, | ||
| 432 | .function, | 648 | .function, |
| 433 | .int_u64, | 649 | .int_u64, |
| 434 | .int_i64, | 650 | .int_i64, |
| 435 | .int_big, | 651 | .int_big, |
| 436 | .bytes, | 652 | .bytes, |
| 653 | .undef, | ||
| 654 | .repeated, | ||
| 437 | => unreachable, | 655 | => unreachable, |
| 438 | 656 | ||
| 439 | .ref => return self.cast(Payload.Ref).?.cell.contents, | 657 | .the_one_possible_value => Value.initTag(.the_one_possible_value), |
| 440 | .ref_val => return self.cast(Payload.RefVal).?.val, | 658 | .ref => self.cast(Payload.Ref).?.cell.contents, |
| 441 | } | 659 | .ref_val => self.cast(Payload.RefVal).?.val, |
| 660 | }; | ||
| 442 | } | 661 | } |
| 443 | 662 | ||
| 444 | /// Asserts the value is a single-item pointer to an array, or an array, | 663 | /// Asserts the value is a single-item pointer to an array, or an array, |
| ... | @@ -475,14 +694,15 @@ pub const Value = extern union { | ... | @@ -475,14 +694,15 @@ pub const Value = extern union { |
| 475 | .single_const_pointer_to_comptime_int_type, | 694 | .single_const_pointer_to_comptime_int_type, |
| 476 | .const_slice_u8_type, | 695 | .const_slice_u8_type, |
| 477 | .zero, | 696 | .zero, |
| 478 | .void_value, | 697 | .the_one_possible_value, |
| 479 | .noreturn_value, | ||
| 480 | .bool_true, | 698 | .bool_true, |
| 481 | .bool_false, | 699 | .bool_false, |
| 700 | .null_value, | ||
| 482 | .function, | 701 | .function, |
| 483 | .int_u64, | 702 | .int_u64, |
| 484 | .int_i64, | 703 | .int_i64, |
| 485 | .int_big, | 704 | .int_big, |
| 705 | .undef, | ||
| 486 | => unreachable, | 706 | => unreachable, |
| 487 | 707 | ||
| 488 | .ref => @panic("TODO figure out how MemoryCell works"), | 708 | .ref => @panic("TODO figure out how MemoryCell works"), |
| ... | @@ -493,9 +713,68 @@ pub const Value = extern union { | ... | @@ -493,9 +713,68 @@ pub const Value = extern union { |
| 493 | int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] }; | 713 | int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] }; |
| 494 | return Value.initPayload(&int_payload.base); | 714 | return Value.initPayload(&int_payload.base); |
| 495 | }, | 715 | }, |
| 716 | |||
| 717 | // No matter the index; all the elements are the same! | ||
| 718 | .repeated => return self.cast(Payload.Repeated).?.val, | ||
| 496 | } | 719 | } |
| 497 | } | 720 | } |
| 498 | 721 | ||
| 722 | pub fn isUndef(self: Value) bool { | ||
| 723 | return self.tag() == .undef; | ||
| 724 | } | ||
| 725 | |||
| 726 | /// Valid for all types. Asserts the value is not undefined. | ||
| 727 | /// `.the_one_possible_value` is reported as not null. | ||
| 728 | pub fn isNull(self: Value) bool { | ||
| 729 | return switch (self.tag()) { | ||
| 730 | .ty, | ||
| 731 | .u8_type, | ||
| 732 | .i8_type, | ||
| 733 | .isize_type, | ||
| 734 | .usize_type, | ||
| 735 | .c_short_type, | ||
| 736 | .c_ushort_type, | ||
| 737 | .c_int_type, | ||
| 738 | .c_uint_type, | ||
| 739 | .c_long_type, | ||
| 740 | .c_ulong_type, | ||
| 741 | .c_longlong_type, | ||
| 742 | .c_ulonglong_type, | ||
| 743 | .c_longdouble_type, | ||
| 744 | .f16_type, | ||
| 745 | .f32_type, | ||
| 746 | .f64_type, | ||
| 747 | .f128_type, | ||
| 748 | .c_void_type, | ||
| 749 | .bool_type, | ||
| 750 | .void_type, | ||
| 751 | .type_type, | ||
| 752 | .anyerror_type, | ||
| 753 | .comptime_int_type, | ||
| 754 | .comptime_float_type, | ||
| 755 | .noreturn_type, | ||
| 756 | .fn_naked_noreturn_no_args_type, | ||
| 757 | .single_const_pointer_to_comptime_int_type, | ||
| 758 | .const_slice_u8_type, | ||
| 759 | .zero, | ||
| 760 | .the_one_possible_value, | ||
| 761 | .bool_true, | ||
| 762 | .bool_false, | ||
| 763 | .function, | ||
| 764 | .int_u64, | ||
| 765 | .int_i64, | ||
| 766 | .int_big, | ||
| 767 | .ref, | ||
| 768 | .ref_val, | ||
| 769 | .bytes, | ||
| 770 | .repeated, | ||
| 771 | => false, | ||
| 772 | |||
| 773 | .undef => unreachable, | ||
| 774 | .null_value => true, | ||
| 775 | }; | ||
| 776 | } | ||
| 777 | |||
| 499 | /// This type is not copyable since it may contain pointers to its inner data. | 778 | /// This type is not copyable since it may contain pointers to its inner data. |
| 500 | pub const Payload = struct { | 779 | pub const Payload = struct { |
| 501 | tag: Tag, | 780 | tag: Tag, |
| ... | @@ -550,6 +829,20 @@ pub const Value = extern union { | ... | @@ -550,6 +829,20 @@ pub const Value = extern union { |
| 550 | base: Payload = Payload{ .tag = .ty }, | 829 | base: Payload = Payload{ .tag = .ty }, |
| 551 | ty: Type, | 830 | ty: Type, |
| 552 | }; | 831 | }; |
| 832 | |||
| 833 | pub const Repeated = struct { | ||
| 834 | base: Payload = Payload{ .tag = .ty }, | ||
| 835 | /// This value is repeated some number of times. The amount of times to repeat | ||
| 836 | /// is stored externally. | ||
| 837 | val: Value, | ||
| 838 | }; | ||
| 839 | }; | ||
| 840 | |||
| 841 | /// Big enough to fit any non-BigInt value | ||
| 842 | pub const BigIntSpace = struct { | ||
| 843 | /// The +1 is headroom so that operations such as incrementing once or decrementing once | ||
| 844 | /// are possible without using an allocator. | ||
| 845 | limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb, | ||
| 553 | }; | 846 | }; |
| 554 | }; | 847 | }; |
| 555 | 848 |