| 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 | 986 | |
| 987 | 987 | /// Greater than (`>`) |
| 988 | 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 | 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 | 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 | 70 | /// Creates a new Int with a specific capacity. If capacity < default_capacity then the |
| 64 | 71 | /// default capacity will be used instead. |
| 65 | 72 | pub fn initCapacity(allocator: *Allocator, capacity: usize) !Int { |
| ... | ... | @@ -104,12 +111,11 @@ pub const Int = struct { |
| 104 | 111 | /// Returns an Int backed by a fixed set of limb values. |
| 105 | 112 | /// This is read-only and cannot be used as a result argument. If the Int tries to allocate |
| 106 | 113 | /// memory a runtime panic will occur. |
| 107 | pub fn initFixed(limbs: []const Limb) Int { | |
| 114 | pub fn initFixed(limbs: []Limb) Int { | |
| 108 | 115 | var self = Int{ |
| 109 | 116 | .allocator = null, |
| 110 | 117 | .metadata = limbs.len, |
| 111 | // Cast away the const, invalid use to pass as a pointer argument. | |
| 112 | .limbs = @intToPtr([*]Limb, @ptrToInt(limbs.ptr))[0..limbs.len], | |
| 118 | .limbs = limbs, | |
| 113 | 119 | }; |
| 114 | 120 | |
| 115 | 121 | self.normalize(limbs.len); |
| ... | ... | @@ -218,7 +224,7 @@ pub const Int = struct { |
| 218 | 224 | /// one greater than the returned value. |
| 219 | 225 | /// |
| 220 | 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 | 228 | var bits = self.bitCountAbs(); |
| 223 | 229 | |
| 224 | 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 | 273 | |
| 268 | 274 | /// Sets an Int to value. Value must be an primitive integer type. |
| 269 | 275 | pub fn set(self: *Int, value: var) Allocator.Error!void { |
| 270 | self.assertWritable(); | |
| 271 | 276 | const T = @TypeOf(value); |
| 272 | 277 | |
| 273 | 278 | switch (@typeInfo(T)) { |
| ... | ... | @@ -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 | 613 | /// Returns true if a == 0. |
| 602 | 614 | pub fn eqZero(a: Int) bool { |
| 603 | 615 | return a.len() == 1 and a.limbs[0] == 0; |
| ... | ... | @@ -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 | 684 | /// r = a + b |
| 646 | 685 | /// |
| 647 | 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 | 501 | bh, |
| 502 | 502 | ch, |
| 503 | 503 | dh, |
| 504 | bph, | |
| 505 | sph, | |
| 506 | sih, | |
| 507 | dih, | |
| 504 | 508 | |
| 505 | 509 | al, |
| 506 | 510 | bl, |
| 507 | 511 | cl, |
| 508 | 512 | dl, |
| 513 | bpl, | |
| 514 | spl, | |
| 515 | sil, | |
| 516 | dil, | |
| 509 | 517 | r8b, |
| 510 | 518 | r9b, |
| 511 | 519 | r10b, |
src-self-hosted/ir.zig+549-125| ... | ... | @@ -26,6 +26,10 @@ pub const Inst = struct { |
| 26 | 26 | assembly, |
| 27 | 27 | ptrtoint, |
| 28 | 28 | bitcast, |
| 29 | cmp, | |
| 30 | condbr, | |
| 31 | isnull, | |
| 32 | isnonnull, | |
| 29 | 33 | }; |
| 30 | 34 | |
| 31 | 35 | pub fn cast(base: *Inst, comptime T: type) ?*T { |
| ... | ... | @@ -41,15 +45,11 @@ pub const Inst = struct { |
| 41 | 45 | |
| 42 | 46 | /// Returns `null` if runtime-known. |
| 43 | 47 | pub fn value(base: *Inst) ?Value { |
| 44 | return switch (base.tag) { | |
| 45 | .unreach => Value.initTag(.noreturn_value), | |
| 46 | .constant => base.cast(Constant).?.val, | |
| 47 | ||
| 48 | .assembly, | |
| 49 | .ptrtoint, | |
| 50 | .bitcast, | |
| 51 | => null, | |
| 52 | }; | |
| 48 | if (base.ty.onePossibleValue()) | |
| 49 | return Value.initTag(.the_one_possible_value); | |
| 50 | ||
| 51 | const inst = base.cast(Constant) orelse return null; | |
| 52 | return inst.val; | |
| 53 | 53 | } |
| 54 | 54 | |
| 55 | 55 | pub const Unreach = struct { |
| ... | ... | @@ -96,6 +96,46 @@ pub const Inst = struct { |
| 96 | 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 | 141 | pub const TypedValue = struct { |
| ... | ... | @@ -118,15 +158,19 @@ pub const Module = struct { |
| 118 | 158 | |
| 119 | 159 | pub const Fn = struct { |
| 120 | 160 | analysis_status: enum { in_progress, failure, success }, |
| 121 | body: []*Inst, | |
| 161 | body: Body, | |
| 122 | 162 | fn_type: Type, |
| 123 | 163 | }; |
| 124 | 164 | |
| 165 | pub const Body = struct { | |
| 166 | instructions: []*Inst, | |
| 167 | }; | |
| 168 | ||
| 125 | 169 | pub fn deinit(self: *Module, allocator: *Allocator) void { |
| 126 | 170 | allocator.free(self.exports); |
| 127 | 171 | allocator.free(self.errors); |
| 128 | 172 | for (self.fns) |f| { |
| 129 | allocator.free(f.body); | |
| 173 | allocator.free(f.body.instructions); | |
| 130 | 174 | } |
| 131 | 175 | allocator.free(self.fns); |
| 132 | 176 | self.arena.deinit(); |
| ... | ... | @@ -192,10 +236,15 @@ const Analyze = struct { |
| 192 | 236 | }; |
| 193 | 237 | |
| 194 | 238 | const Fn = struct { |
| 195 | body: std.ArrayList(*Inst), | |
| 196 | inst_table: std.AutoHashMap(*text.Inst, NewInst), | |
| 197 | 239 | /// Index into Module fns array |
| 198 | 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 | 250 | const InnerError = error{ OutOfMemory, AnalysisFail }; |
| ... | ... | @@ -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 { | |
| 212 | if (opt_func) |func| { | |
| 213 | if (func.inst_table.get(old_inst)) |kv| { | |
| 260 | fn resolveInst(self: *Analyze, opt_block: ?*Block, old_inst: *text.Inst) InnerError!*Inst { | |
| 261 | if (opt_block) |block| { | |
| 262 | if (block.func.inst_table.get(old_inst)) |kv| { | |
| 214 | 263 | return kv.value.ptr orelse return error.AnalysisFail; |
| 215 | 264 | } |
| 216 | 265 | } |
| ... | ... | @@ -230,12 +279,12 @@ const Analyze = struct { |
| 230 | 279 | } |
| 231 | 280 | } |
| 232 | 281 | |
| 233 | fn requireFunctionBody(self: *Analyze, func: ?*Fn, src: usize) !*Fn { | |
| 234 | return func orelse return self.fail(src, "instruction illegal outside function body", .{}); | |
| 282 | fn requireRuntimeBlock(self: *Analyze, block: ?*Block, src: usize) !*Block { | |
| 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 { | |
| 238 | const new_inst = try self.resolveInst(func, old_inst); | |
| 286 | fn resolveInstConst(self: *Analyze, block: ?*Block, old_inst: *text.Inst) InnerError!TypedValue { | |
| 287 | const new_inst = try self.resolveInst(block, old_inst); | |
| 239 | 288 | const val = try self.resolveConstValue(new_inst); |
| 240 | 289 | return TypedValue{ |
| 241 | 290 | .ty = new_inst.ty, |
| ... | ... | @@ -244,28 +293,39 @@ const Analyze = struct { |
| 244 | 293 | } |
| 245 | 294 | |
| 246 | 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 { | |
| 251 | const new_inst = try self.resolveInst(func, old_inst); | |
| 300 | fn resolveDefinedValue(self: *Analyze, base: *Inst) !?Value { | |
| 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 | 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 | 314 | const val = try self.resolveConstValue(coerced_inst); |
| 255 | 315 | return val.toAllocatedBytes(&self.arena.allocator); |
| 256 | 316 | } |
| 257 | 317 | |
| 258 | fn resolveType(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) !Type { | |
| 259 | const new_inst = try self.resolveInst(func, old_inst); | |
| 318 | fn resolveType(self: *Analyze, block: ?*Block, old_inst: *text.Inst) !Type { | |
| 319 | const new_inst = try self.resolveInst(block, old_inst); | |
| 260 | 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 | 322 | const val = try self.resolveConstValue(coerced_inst); |
| 263 | 323 | return val.toType(); |
| 264 | 324 | } |
| 265 | 325 | |
| 266 | fn analyzeExport(self: *Analyze, func: ?*Fn, export_inst: *text.Inst.Export) !void { | |
| 267 | const symbol_name = try self.resolveConstString(func, export_inst.positionals.symbol_name); | |
| 268 | const typed_value = try self.resolveInstConst(func, export_inst.positionals.value); | |
| 326 | fn analyzeExport(self: *Analyze, block: ?*Block, export_inst: *text.Inst.Export) !void { | |
| 327 | const symbol_name = try self.resolveConstString(block, export_inst.positionals.symbol_name); | |
| 328 | const typed_value = try self.resolveInstConst(block, export_inst.positionals.value); | |
| 269 | 329 | |
| 270 | 330 | switch (typed_value.ty.zigTypeTag()) { |
| 271 | 331 | .Fn => {}, |
| ... | ... | @@ -285,18 +345,18 @@ const Analyze = struct { |
| 285 | 345 | /// TODO should not need the cast on the last parameter at the callsites |
| 286 | 346 | fn addNewInstArgs( |
| 287 | 347 | self: *Analyze, |
| 288 | func: *Fn, | |
| 348 | block: *Block, | |
| 289 | 349 | src: usize, |
| 290 | 350 | ty: Type, |
| 291 | 351 | comptime T: type, |
| 292 | 352 | args: Inst.Args(T), |
| 293 | 353 | ) !*Inst { |
| 294 | const inst = try self.addNewInst(func, src, ty, T); | |
| 354 | const inst = try self.addNewInst(block, src, ty, T); | |
| 295 | 355 | inst.args = args; |
| 296 | 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 | 360 | const inst = try self.arena.allocator.create(T); |
| 301 | 361 | inst.* = .{ |
| 302 | 362 | .base = .{ |
| ... | ... | @@ -306,7 +366,7 @@ const Analyze = struct { |
| 306 | 366 | }, |
| 307 | 367 | .args = undefined, |
| 308 | 368 | }; |
| 309 | try func.body.append(&inst.base); | |
| 369 | try block.instructions.append(&inst.base); | |
| 310 | 370 | return inst; |
| 311 | 371 | } |
| 312 | 372 | |
| ... | ... | @@ -349,7 +409,21 @@ const Analyze = struct { |
| 349 | 409 | fn constVoid(self: *Analyze, src: usize) !*Inst { |
| 350 | 410 | return self.constInst(src, .{ |
| 351 | 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 | 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 | 477 | switch (old_inst.tag) { |
| 404 | 478 | .str => { |
| 405 | 479 | // We can use this reference because Inst.Const's Value is arena-allocated. |
| ... | ... | @@ -411,35 +485,43 @@ const Analyze = struct { |
| 411 | 485 | const big_int = old_inst.cast(text.Inst.Int).?.positionals.int; |
| 412 | 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).?), | |
| 415 | .fieldptr => return self.analyzeInstFieldPtr(func, old_inst.cast(text.Inst.FieldPtr).?), | |
| 416 | .deref => return self.analyzeInstDeref(func, old_inst.cast(text.Inst.Deref).?), | |
| 417 | .as => return self.analyzeInstAs(func, old_inst.cast(text.Inst.As).?), | |
| 418 | .@"asm" => return self.analyzeInstAsm(func, old_inst.cast(text.Inst.Asm).?), | |
| 419 | .@"unreachable" => return self.analyzeInstUnreachable(func, old_inst.cast(text.Inst.Unreachable).?), | |
| 420 | .@"fn" => return self.analyzeInstFn(func, old_inst.cast(text.Inst.Fn).?), | |
| 488 | .ptrtoint => return self.analyzeInstPtrToInt(block, old_inst.cast(text.Inst.PtrToInt).?), | |
| 489 | .fieldptr => return self.analyzeInstFieldPtr(block, old_inst.cast(text.Inst.FieldPtr).?), | |
| 490 | .deref => return self.analyzeInstDeref(block, old_inst.cast(text.Inst.Deref).?), | |
| 491 | .as => return self.analyzeInstAs(block, old_inst.cast(text.Inst.As).?), | |
| 492 | .@"asm" => return self.analyzeInstAsm(block, old_inst.cast(text.Inst.Asm).?), | |
| 493 | .@"unreachable" => return self.analyzeInstUnreachable(block, old_inst.cast(text.Inst.Unreachable).?), | |
| 494 | .@"fn" => return self.analyzeInstFn(block, old_inst.cast(text.Inst.Fn).?), | |
| 421 | 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 | 497 | return self.constVoid(old_inst.src); |
| 424 | 498 | }, |
| 425 | .primitive => return self.analyzeInstPrimitive(func, old_inst.cast(text.Inst.Primitive).?), | |
| 426 | .fntype => return self.analyzeInstFnType(func, old_inst.cast(text.Inst.FnType).?), | |
| 427 | .intcast => return self.analyzeInstIntCast(func, old_inst.cast(text.Inst.IntCast).?), | |
| 428 | .bitcast => return self.analyzeInstBitCast(func, old_inst.cast(text.Inst.BitCast).?), | |
| 429 | .elemptr => return self.analyzeInstElemPtr(func, old_inst.cast(text.Inst.ElemPtr).?), | |
| 430 | .add => return self.analyzeInstAdd(func, old_inst.cast(text.Inst.Add).?), | |
| 499 | .primitive => return self.analyzeInstPrimitive(old_inst.cast(text.Inst.Primitive).?), | |
| 500 | .fntype => return self.analyzeInstFnType(block, old_inst.cast(text.Inst.FnType).?), | |
| 501 | .intcast => return self.analyzeInstIntCast(block, old_inst.cast(text.Inst.IntCast).?), | |
| 502 | .bitcast => return self.analyzeInstBitCast(block, old_inst.cast(text.Inst.BitCast).?), | |
| 503 | .elemptr => return self.analyzeInstElemPtr(block, old_inst.cast(text.Inst.ElemPtr).?), | |
| 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 { | |
| 435 | const fn_type = try self.resolveType(opt_func, fn_inst.positionals.fn_type); | |
| 512 | fn analyzeInstFn(self: *Analyze, block: ?*Block, fn_inst: *text.Inst.Fn) InnerError!*Inst { | |
| 513 | const fn_type = try self.resolveType(block, fn_inst.positionals.fn_type); | |
| 436 | 514 | |
| 437 | 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 | 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 | 525 | defer new_func.inst_table.deinit(); |
| 444 | 526 | // Don't hang on to a reference to this when analyzing body instructions, since the memory |
| 445 | 527 | // could become invalid. |
| ... | ... | @@ -449,18 +531,11 @@ const Analyze = struct { |
| 449 | 531 | .body = undefined, |
| 450 | 532 | }; |
| 451 | 533 | |
| 452 | for (fn_inst.positionals.body.instructions) |src_inst| { | |
| 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 | } | |
| 534 | try self.analyzeBody(&new_func.inner_block, fn_inst.positionals.body); | |
| 460 | 535 | |
| 461 | 536 | const f = &self.fns.items[new_func.fn_index]; |
| 462 | 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 | 540 | const fn_payload = try self.arena.allocator.create(Value.Payload.Function); |
| 466 | 541 | fn_payload.* = .{ .index = new_func.fn_index }; |
| ... | ... | @@ -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 { | |
| 475 | const return_type = try self.resolveType(func, fntype.positionals.return_type); | |
| 549 | fn analyzeInstFnType(self: *Analyze, block: ?*Block, fntype: *text.Inst.FnType) InnerError!*Inst { | |
| 550 | const return_type = try self.resolveType(block, fntype.positionals.return_type); | |
| 476 | 551 | |
| 477 | 552 | if (return_type.zigTypeTag() == .NoReturn and |
| 478 | 553 | fntype.positionals.param_types.len == 0 and |
| ... | ... | @@ -484,30 +559,30 @@ const Analyze = struct { |
| 484 | 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 | 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 { | |
| 492 | const dest_type = try self.resolveType(func, as.positionals.dest_type); | |
| 493 | const new_inst = try self.resolveInst(func, as.positionals.value); | |
| 494 | return self.coerce(func, dest_type, new_inst); | |
| 566 | fn analyzeInstAs(self: *Analyze, block: ?*Block, as: *text.Inst.As) InnerError!*Inst { | |
| 567 | const dest_type = try self.resolveType(block, as.positionals.dest_type); | |
| 568 | const new_inst = try self.resolveInst(block, as.positionals.value); | |
| 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 { | |
| 498 | const ptr = try self.resolveInst(func, ptrtoint.positionals.ptr); | |
| 572 | fn analyzeInstPtrToInt(self: *Analyze, block: ?*Block, ptrtoint: *text.Inst.PtrToInt) InnerError!*Inst { | |
| 573 | const ptr = try self.resolveInst(block, ptrtoint.positionals.ptr); | |
| 499 | 574 | if (ptr.ty.zigTypeTag() != .Pointer) { |
| 500 | 575 | return self.fail(ptrtoint.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty}); |
| 501 | 576 | } |
| 502 | 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 | 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 { | |
| 509 | const object_ptr = try self.resolveInst(func, fieldptr.positionals.object_ptr); | |
| 510 | const field_name = try self.resolveConstString(func, fieldptr.positionals.field_name); | |
| 583 | fn analyzeInstFieldPtr(self: *Analyze, block: ?*Block, fieldptr: *text.Inst.FieldPtr) InnerError!*Inst { | |
| 584 | const object_ptr = try self.resolveInst(block, fieldptr.positionals.object_ptr); | |
| 585 | const field_name = try self.resolveConstString(block, fieldptr.positionals.field_name); | |
| 511 | 586 | |
| 512 | 587 | const elem_ty = switch (object_ptr.ty.zigTypeTag()) { |
| 513 | 588 | .Pointer => object_ptr.ty.elemType(), |
| ... | ... | @@ -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 { | |
| 542 | const dest_type = try self.resolveType(func, intcast.positionals.dest_type); | |
| 543 | const new_inst = try self.resolveInst(func, intcast.positionals.value); | |
| 616 | fn analyzeInstIntCast(self: *Analyze, block: ?*Block, intcast: *text.Inst.IntCast) InnerError!*Inst { | |
| 617 | const dest_type = try self.resolveType(block, intcast.positionals.dest_type); | |
| 618 | const new_inst = try self.resolveInst(block, intcast.positionals.value); | |
| 544 | 619 | |
| 545 | 620 | const dest_is_comptime_int = switch (dest_type.zigTypeTag()) { |
| 546 | 621 | .ComptimeInt => true, |
| ... | ... | @@ -564,22 +639,22 @@ const Analyze = struct { |
| 564 | 639 | } |
| 565 | 640 | |
| 566 | 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 | 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 { | |
| 574 | const dest_type = try self.resolveType(func, inst.positionals.dest_type); | |
| 575 | const operand = try self.resolveInst(func, inst.positionals.operand); | |
| 576 | return self.bitcast(func, dest_type, operand); | |
| 648 | fn analyzeInstBitCast(self: *Analyze, block: ?*Block, inst: *text.Inst.BitCast) InnerError!*Inst { | |
| 649 | const dest_type = try self.resolveType(block, inst.positionals.dest_type); | |
| 650 | const operand = try self.resolveInst(block, inst.positionals.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 { | |
| 580 | const array_ptr = try self.resolveInst(func, inst.positionals.array_ptr); | |
| 581 | const uncasted_index = try self.resolveInst(func, inst.positionals.index); | |
| 582 | const elem_index = try self.coerce(func, Type.initTag(.usize), uncasted_index); | |
| 654 | fn analyzeInstElemPtr(self: *Analyze, block: ?*Block, inst: *text.Inst.ElemPtr) InnerError!*Inst { | |
| 655 | const array_ptr = try self.resolveInst(block, inst.positionals.array_ptr); | |
| 656 | const uncasted_index = try self.resolveInst(block, inst.positionals.index); | |
| 657 | const elem_index = try self.coerce(block, Type.initTag(.usize), uncasted_index); | |
| 583 | 658 | |
| 584 | 659 | if (array_ptr.ty.isSinglePointer() and array_ptr.ty.elemType().zigTypeTag() == .Array) { |
| 585 | 660 | if (array_ptr.value()) |array_ptr_val| { |
| ... | ... | @@ -607,15 +682,19 @@ const Analyze = struct { |
| 607 | 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 { | |
| 611 | const lhs = try self.resolveInst(func, inst.positionals.lhs); | |
| 612 | const rhs = try self.resolveInst(func, inst.positionals.rhs); | |
| 685 | fn analyzeInstAdd(self: *Analyze, block: ?*Block, inst: *text.Inst.Add) InnerError!*Inst { | |
| 686 | const lhs = try self.resolveInst(block, inst.positionals.lhs); | |
| 687 | const rhs = try self.resolveInst(block, inst.positionals.rhs); | |
| 613 | 688 | |
| 614 | 689 | if (lhs.ty.zigTypeTag() == .Int and rhs.ty.zigTypeTag() == .Int) { |
| 615 | 690 | if (lhs.value()) |lhs_val| { |
| 616 | 691 | if (rhs.value()) |rhs_val| { |
| 617 | const lhs_bigint = try lhs_val.toBigInt(&self.arena.allocator); | |
| 618 | const rhs_bigint = try rhs_val.toBigInt(&self.arena.allocator); | |
| 692 | // TODO is this a performance issue? maybe we should try the operation without | |
| 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 | 698 | var result_bigint = try BigInt.init(&self.arena.allocator); |
| 620 | 699 | try BigInt.add(&result_bigint, lhs_bigint, rhs_bigint); |
| 621 | 700 | |
| ... | ... | @@ -637,8 +716,8 @@ const Analyze = struct { |
| 637 | 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 { | |
| 641 | const ptr = try self.resolveInst(func, deref.positionals.ptr); | |
| 719 | fn analyzeInstDeref(self: *Analyze, block: ?*Block, deref: *text.Inst.Deref) InnerError!*Inst { | |
| 720 | const ptr = try self.resolveInst(block, deref.positionals.ptr); | |
| 642 | 721 | const elem_ty = switch (ptr.ty.zigTypeTag()) { |
| 643 | 722 | .Pointer => ptr.ty.elemType(), |
| 644 | 723 | else => return self.fail(deref.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty}), |
| ... | ... | @@ -653,28 +732,28 @@ const Analyze = struct { |
| 653 | 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 { | |
| 657 | const return_type = try self.resolveType(func, assembly.positionals.return_type); | |
| 658 | const asm_source = try self.resolveConstString(func, assembly.positionals.asm_source); | |
| 659 | const output = if (assembly.kw_args.output) |o| try self.resolveConstString(func, o) else null; | |
| 735 | fn analyzeInstAsm(self: *Analyze, block: ?*Block, assembly: *text.Inst.Asm) InnerError!*Inst { | |
| 736 | const return_type = try self.resolveType(block, assembly.positionals.return_type); | |
| 737 | const asm_source = try self.resolveConstString(block, assembly.positionals.asm_source); | |
| 738 | const output = if (assembly.kw_args.output) |o| try self.resolveConstString(block, o) else null; | |
| 660 | 739 | |
| 661 | 740 | const inputs = try self.arena.allocator.alloc([]const u8, assembly.kw_args.inputs.len); |
| 662 | 741 | const clobbers = try self.arena.allocator.alloc([]const u8, assembly.kw_args.clobbers.len); |
| 663 | 742 | const args = try self.arena.allocator.alloc(*Inst, assembly.kw_args.args.len); |
| 664 | 743 | |
| 665 | 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 | 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 | 750 | for (args) |*elem, i| { |
| 672 | const arg = try self.resolveInst(func, assembly.kw_args.args[i]); | |
| 673 | elem.* = try self.coerce(func, Type.initTag(.usize), arg); | |
| 751 | const arg = try self.resolveInst(block, assembly.kw_args.args[i]); | |
| 752 | elem.* = try self.coerce(block, Type.initTag(.usize), arg); | |
| 674 | 753 | } |
| 675 | 754 | |
| 676 | const f = try self.requireFunctionBody(func, assembly.base.src); | |
| 677 | return self.addNewInstArgs(f, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){ | |
| 755 | const b = try self.requireRuntimeBlock(block, assembly.base.src); | |
| 756 | return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){ | |
| 678 | 757 | .asm_source = asm_source, |
| 679 | 758 | .is_volatile = assembly.kw_args.@"volatile", |
| 680 | 759 | .output = output, |
| ... | ... | @@ -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 { | |
| 688 | const f = try self.requireFunctionBody(func, unreach.base.src); | |
| 689 | return self.addNewInstArgs(f, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {}); | |
| 766 | fn analyzeInstCmp(self: *Analyze, block: ?*Block, inst: *text.Inst.Cmp) InnerError!*Inst { | |
| 767 | const lhs = try self.resolveInst(block, inst.positionals.lhs); | |
| 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 | 1103 | // If the types are the same, we can return the operand. |
| 694 | 1104 | if (dest_type.eql(inst.ty)) |
| 695 | 1105 | return inst; |
| 696 | 1106 | |
| 697 | 1107 | const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty); |
| 698 | 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 | 1112 | // *[N]T to []T |
| ... | ... | @@ -740,14 +1150,14 @@ const Analyze = struct { |
| 740 | 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 | 1154 | if (inst.value()) |val| { |
| 745 | 1155 | // Keep the comptime Value representation; take the new type. |
| 746 | 1156 | return self.constInst(inst.src, .{ .ty = dest_type, .val = val }); |
| 747 | 1157 | } |
| 748 | 1158 | // TODO validate the type size and other compile errors |
| 749 | const f = try self.requireFunctionBody(func, inst.src); | |
| 750 | return self.addNewInstArgs(f, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst }); | |
| 1159 | const b = try self.requireRuntimeBlock(block, inst.src); | |
| 1160 | return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst }); | |
| 751 | 1161 | } |
| 752 | 1162 | |
| 753 | 1163 | fn coerceArrayPtrToSlice(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst { |
| ... | ... | @@ -831,17 +1241,31 @@ pub fn main() anyerror!void { |
| 831 | 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 | 1258 | const link = @import("link.zig"); |
| 835 | var result = try link.updateExecutableFilePath(allocator, analyzed_module, std.fs.cwd(), "a.out"); | |
| 836 | defer result.deinit(allocator); | |
| 837 | if (result.errors.len != 0) { | |
| 838 | for (result.errors) |err_msg| { | |
| 839 | 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 }); | |
| 841 | } | |
| 842 | if (debug_error_trace) return error.LinkFailure; | |
| 843 | std.process.exit(1); | |
| 844 | } | |
| 1259 | //var result = try link.updateExecutableFilePath(allocator, analyzed_module, std.fs.cwd(), "a.out"); | |
| 1260 | //defer result.deinit(allocator); | |
| 1261 | //if (result.errors.len != 0) { | |
| 1262 | // for (result.errors) |err_msg| { | |
| 1263 | // const loc = std.zig.findLineColumn(source, err_msg.byte_offset); | |
| 1264 | // std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg }); | |
| 1265 | // } | |
| 1266 | // if (debug_error_trace) return error.LinkFailure; | |
| 1267 | // std.process.exit(1); | |
| 1268 | //} | |
| 845 | 1269 | } |
| 846 | 1270 | |
| 847 | 1271 | // Performance optimization ideas: |
src-self-hosted/ir/text.zig+218-90| ... | ... | @@ -34,6 +34,10 @@ pub const Inst = struct { |
| 34 | 34 | bitcast, |
| 35 | 35 | elemptr, |
| 36 | 36 | add, |
| 37 | cmp, | |
| 38 | condbr, | |
| 39 | isnull, | |
| 40 | isnonnull, | |
| 37 | 41 | }; |
| 38 | 42 | |
| 39 | 43 | pub fn TagToType(tag: Tag) type { |
| ... | ... | @@ -54,6 +58,10 @@ pub const Inst = struct { |
| 54 | 58 | .bitcast => BitCast, |
| 55 | 59 | .elemptr => ElemPtr, |
| 56 | 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 | 165 | |
| 158 | 166 | positionals: struct { |
| 159 | 167 | fn_type: *Inst, |
| 160 | body: Body, | |
| 168 | body: Module.Body, | |
| 161 | 169 | }, |
| 162 | 170 | kw_args: struct {}, |
| 163 | ||
| 164 | pub const Body = struct { | |
| 165 | instructions: []*Inst, | |
| 166 | }; | |
| 167 | 171 | }; |
| 168 | 172 | |
| 169 | 173 | pub const Export = struct { |
| ... | ... | @@ -297,6 +301,50 @@ pub const Inst = struct { |
| 297 | 301 | }, |
| 298 | 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 | 350 | pub const ErrorMsg = struct { |
| ... | ... | @@ -309,6 +357,10 @@ pub const Module = struct { |
| 309 | 357 | errors: []ErrorMsg, |
| 310 | 358 | arena: std.heap.ArenaAllocator, |
| 311 | 359 | |
| 360 | pub const Body = struct { | |
| 361 | instructions: []*Inst, | |
| 362 | }; | |
| 363 | ||
| 312 | 364 | pub fn deinit(self: *Module, allocator: *Allocator) void { |
| 313 | 365 | allocator.free(self.decls); |
| 314 | 366 | allocator.free(self.errors); |
| ... | ... | @@ -321,7 +373,7 @@ pub const Module = struct { |
| 321 | 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 | 378 | /// The allocator is used for temporary storage, but this function always returns |
| 327 | 379 | /// with no resources allocated. |
| ... | ... | @@ -373,6 +425,10 @@ pub const Module = struct { |
| 373 | 425 | .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, decl, inst_table), |
| 374 | 426 | .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, decl, inst_table), |
| 375 | 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 | 488 | } |
| 433 | 489 | try stream.writeByte(']'); |
| 434 | 490 | }, |
| 435 | Inst.Fn.Body => { | |
| 491 | Module.Body => { | |
| 436 | 492 | try stream.writeAll("{\n"); |
| 437 | 493 | for (param.instructions) |inst, i| { |
| 438 | 494 | try stream.print(" %{} ", .{i}); |
| ... | ... | @@ -497,7 +553,7 @@ const Parser = struct { |
| 497 | 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 | 557 | var body_context = Body{ |
| 502 | 558 | .instructions = std.ArrayList(*Inst).init(self.allocator), |
| 503 | 559 | .name_map = std.StringHashMap(usize).init(self.allocator), |
| ... | ... | @@ -535,7 +591,7 @@ const Parser = struct { |
| 535 | 591 | // Move the instructions to the arena |
| 536 | 592 | const instrs = try self.arena.allocator.alloc(*Inst, body_context.instructions.items.len); |
| 537 | 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 | 597 | fn parseStringLiteral(self: *Parser) ![]u8 { |
| ... | ... | @@ -754,7 +810,7 @@ const Parser = struct { |
| 754 | 810 | }; |
| 755 | 811 | } |
| 756 | 812 | switch (T) { |
| 757 | Inst.Fn.Body => return parseBody(self), | |
| 813 | Module.Body => return parseBody(self), | |
| 758 | 814 | bool => { |
| 759 | 815 | const bool_value = switch (self.source[self.i]) { |
| 760 | 816 | '0' => false, |
| ... | ... | @@ -880,11 +936,12 @@ const EmitZIR = struct { |
| 880 | 936 | } |
| 881 | 937 | |
| 882 | 938 | fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst { |
| 939 | const big_int_space = try self.arena.allocator.create(Value.BigIntSpace); | |
| 883 | 940 | const int_inst = try self.arena.allocator.create(Inst.Int); |
| 884 | 941 | int_inst.* = .{ |
| 885 | 942 | .base = .{ .src = src, .tag = Inst.Int.base_tag }, |
| 886 | 943 | .positionals = .{ |
| 887 | .int = try val.toBigInt(&self.arena.allocator), | |
| 944 | .int = val.toBigInt(big_int_space), | |
| 888 | 945 | }, |
| 889 | 946 | .kw_args = .{}, |
| 890 | 947 | }; |
| ... | ... | @@ -939,85 +996,7 @@ const EmitZIR = struct { |
| 939 | 996 | var instructions = std.ArrayList(*Inst).init(self.allocator); |
| 940 | 997 | defer instructions.deinit(); |
| 941 | 998 | |
| 942 | for (module_fn.body) |inst| { | |
| 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 | } | |
| 999 | try self.emitBody(module_fn.body, &inst_table, &instructions); | |
| 1021 | 1000 | |
| 1022 | 1001 | const fn_type = try self.emitType(src, module_fn.fn_type); |
| 1023 | 1002 | |
| ... | ... | @@ -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 | 1170 | fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst { |
| 1043 | 1171 | switch (ty.tag()) { |
| 1044 | 1172 | .isize => return self.emitPrimitiveType(src, .isize), |
src-self-hosted/type.zig+455-204| ... | ... | @@ -20,35 +20,37 @@ pub const Type = extern union { |
| 20 | 20 | |
| 21 | 21 | pub fn zigTypeTag(self: Type) std.builtin.TypeId { |
| 22 | 22 | switch (self.tag()) { |
| 23 | .@"u8", | |
| 24 | .@"i8", | |
| 25 | .@"isize", | |
| 26 | .@"usize", | |
| 27 | .@"c_short", | |
| 28 | .@"c_ushort", | |
| 29 | .@"c_int", | |
| 30 | .@"c_uint", | |
| 31 | .@"c_long", | |
| 32 | .@"c_ulong", | |
| 33 | .@"c_longlong", | |
| 34 | .@"c_ulonglong", | |
| 35 | .@"c_longdouble", | |
| 23 | .u8, | |
| 24 | .i8, | |
| 25 | .isize, | |
| 26 | .usize, | |
| 27 | .c_short, | |
| 28 | .c_ushort, | |
| 29 | .c_int, | |
| 30 | .c_uint, | |
| 31 | .c_long, | |
| 32 | .c_ulong, | |
| 33 | .c_longlong, | |
| 34 | .c_ulonglong, | |
| 35 | .c_longdouble, | |
| 36 | .int_signed, | |
| 37 | .int_unsigned, | |
| 36 | 38 | => return .Int, |
| 37 | 39 | |
| 38 | .@"f16", | |
| 39 | .@"f32", | |
| 40 | .@"f64", | |
| 41 | .@"f128", | |
| 40 | .f16, | |
| 41 | .f32, | |
| 42 | .f64, | |
| 43 | .f128, | |
| 42 | 44 | => return .Float, |
| 43 | 45 | |
| 44 | .@"c_void" => return .Opaque, | |
| 45 | .@"bool" => return .Bool, | |
| 46 | .@"void" => return .Void, | |
| 47 | .@"type" => return .Type, | |
| 48 | .@"anyerror" => return .ErrorSet, | |
| 49 | .@"comptime_int" => return .ComptimeInt, | |
| 50 | .@"comptime_float" => return .ComptimeFloat, | |
| 51 | .@"noreturn" => return .NoReturn, | |
| 46 | .c_void => return .Opaque, | |
| 47 | .bool => return .Bool, | |
| 48 | .void => return .Void, | |
| 49 | .type => return .Type, | |
| 50 | .anyerror => return .ErrorSet, | |
| 51 | .comptime_int => return .ComptimeInt, | |
| 52 | .comptime_float => return .ComptimeFloat, | |
| 53 | .noreturn => return .NoReturn, | |
| 52 | 54 | |
| 53 | 55 | .fn_naked_noreturn_no_args => return .Fn, |
| 54 | 56 | |
| ... | ... | @@ -153,31 +155,31 @@ pub const Type = extern union { |
| 153 | 155 | while (true) { |
| 154 | 156 | const t = ty.tag(); |
| 155 | 157 | switch (t) { |
| 156 | .@"u8", | |
| 157 | .@"i8", | |
| 158 | .@"isize", | |
| 159 | .@"usize", | |
| 160 | .@"c_short", | |
| 161 | .@"c_ushort", | |
| 162 | .@"c_int", | |
| 163 | .@"c_uint", | |
| 164 | .@"c_long", | |
| 165 | .@"c_ulong", | |
| 166 | .@"c_longlong", | |
| 167 | .@"c_ulonglong", | |
| 168 | .@"c_longdouble", | |
| 169 | .@"c_void", | |
| 170 | .@"f16", | |
| 171 | .@"f32", | |
| 172 | .@"f64", | |
| 173 | .@"f128", | |
| 174 | .@"bool", | |
| 175 | .@"void", | |
| 176 | .@"type", | |
| 177 | .@"anyerror", | |
| 178 | .@"comptime_int", | |
| 179 | .@"comptime_float", | |
| 180 | .@"noreturn", | |
| 158 | .u8, | |
| 159 | .i8, | |
| 160 | .isize, | |
| 161 | .usize, | |
| 162 | .c_short, | |
| 163 | .c_ushort, | |
| 164 | .c_int, | |
| 165 | .c_uint, | |
| 166 | .c_long, | |
| 167 | .c_ulong, | |
| 168 | .c_longlong, | |
| 169 | .c_ulonglong, | |
| 170 | .c_longdouble, | |
| 171 | .c_void, | |
| 172 | .f16, | |
| 173 | .f32, | |
| 174 | .f64, | |
| 175 | .f128, | |
| 176 | .bool, | |
| 177 | .void, | |
| 178 | .type, | |
| 179 | .anyerror, | |
| 180 | .comptime_int, | |
| 181 | .comptime_float, | |
| 182 | .noreturn, | |
| 181 | 183 | => return out_stream.writeAll(@tagName(t)), |
| 182 | 184 | |
| 183 | 185 | .const_slice_u8 => return out_stream.writeAll("[]const u8"), |
| ... | ... | @@ -200,6 +202,14 @@ pub const Type = extern union { |
| 200 | 202 | ty = payload.pointee_type; |
| 201 | 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 | 214 | unreachable; |
| 205 | 215 | } |
| ... | ... | @@ -207,31 +217,31 @@ pub const Type = extern union { |
| 207 | 217 | |
| 208 | 218 | pub fn toValue(self: Type, allocator: *Allocator) Allocator.Error!Value { |
| 209 | 219 | switch (self.tag()) { |
| 210 | .@"u8" => return Value.initTag(.u8_type), | |
| 211 | .@"i8" => return Value.initTag(.i8_type), | |
| 212 | .@"isize" => return Value.initTag(.isize_type), | |
| 213 | .@"usize" => return Value.initTag(.usize_type), | |
| 214 | .@"c_short" => return Value.initTag(.c_short_type), | |
| 215 | .@"c_ushort" => return Value.initTag(.c_ushort_type), | |
| 216 | .@"c_int" => return Value.initTag(.c_int_type), | |
| 217 | .@"c_uint" => return Value.initTag(.c_uint_type), | |
| 218 | .@"c_long" => return Value.initTag(.c_long_type), | |
| 219 | .@"c_ulong" => return Value.initTag(.c_ulong_type), | |
| 220 | .@"c_longlong" => return Value.initTag(.c_longlong_type), | |
| 221 | .@"c_ulonglong" => return Value.initTag(.c_ulonglong_type), | |
| 222 | .@"c_longdouble" => return Value.initTag(.c_longdouble_type), | |
| 223 | .@"c_void" => return Value.initTag(.c_void_type), | |
| 224 | .@"f16" => return Value.initTag(.f16_type), | |
| 225 | .@"f32" => return Value.initTag(.f32_type), | |
| 226 | .@"f64" => return Value.initTag(.f64_type), | |
| 227 | .@"f128" => return Value.initTag(.f128_type), | |
| 228 | .@"bool" => return Value.initTag(.bool_type), | |
| 229 | .@"void" => return Value.initTag(.void_type), | |
| 230 | .@"type" => return Value.initTag(.type_type), | |
| 231 | .@"anyerror" => return Value.initTag(.anyerror_type), | |
| 232 | .@"comptime_int" => return Value.initTag(.comptime_int_type), | |
| 233 | .@"comptime_float" => return Value.initTag(.comptime_float_type), | |
| 234 | .@"noreturn" => return Value.initTag(.noreturn_type), | |
| 220 | .u8 => return Value.initTag(.u8_type), | |
| 221 | .i8 => return Value.initTag(.i8_type), | |
| 222 | .isize => return Value.initTag(.isize_type), | |
| 223 | .usize => return Value.initTag(.usize_type), | |
| 224 | .c_short => return Value.initTag(.c_short_type), | |
| 225 | .c_ushort => return Value.initTag(.c_ushort_type), | |
| 226 | .c_int => return Value.initTag(.c_int_type), | |
| 227 | .c_uint => return Value.initTag(.c_uint_type), | |
| 228 | .c_long => return Value.initTag(.c_long_type), | |
| 229 | .c_ulong => return Value.initTag(.c_ulong_type), | |
| 230 | .c_longlong => return Value.initTag(.c_longlong_type), | |
| 231 | .c_ulonglong => return Value.initTag(.c_ulonglong_type), | |
| 232 | .c_longdouble => return Value.initTag(.c_longdouble_type), | |
| 233 | .c_void => return Value.initTag(.c_void_type), | |
| 234 | .f16 => return Value.initTag(.f16_type), | |
| 235 | .f32 => return Value.initTag(.f32_type), | |
| 236 | .f64 => return Value.initTag(.f64_type), | |
| 237 | .f128 => return Value.initTag(.f128_type), | |
| 238 | .bool => return Value.initTag(.bool_type), | |
| 239 | .void => return Value.initTag(.void_type), | |
| 240 | .type => return Value.initTag(.type_type), | |
| 241 | .anyerror => return Value.initTag(.anyerror_type), | |
| 242 | .comptime_int => return Value.initTag(.comptime_int_type), | |
| 243 | .comptime_float => return Value.initTag(.comptime_float_type), | |
| 244 | .noreturn => return Value.initTag(.noreturn_type), | |
| 235 | 245 | .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type), |
| 236 | 246 | .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type), |
| 237 | 247 | .const_slice_u8 => return Value.initTag(.const_slice_u8_type), |
| ... | ... | @@ -245,35 +255,37 @@ pub const Type = extern union { |
| 245 | 255 | |
| 246 | 256 | pub fn isSinglePointer(self: Type) bool { |
| 247 | 257 | return switch (self.tag()) { |
| 248 | .@"u8", | |
| 249 | .@"i8", | |
| 250 | .@"isize", | |
| 251 | .@"usize", | |
| 252 | .@"c_short", | |
| 253 | .@"c_ushort", | |
| 254 | .@"c_int", | |
| 255 | .@"c_uint", | |
| 256 | .@"c_long", | |
| 257 | .@"c_ulong", | |
| 258 | .@"c_longlong", | |
| 259 | .@"c_ulonglong", | |
| 260 | .@"c_longdouble", | |
| 261 | .@"f16", | |
| 262 | .@"f32", | |
| 263 | .@"f64", | |
| 264 | .@"f128", | |
| 265 | .@"c_void", | |
| 266 | .@"bool", | |
| 267 | .@"void", | |
| 268 | .@"type", | |
| 269 | .@"anyerror", | |
| 270 | .@"comptime_int", | |
| 271 | .@"comptime_float", | |
| 272 | .@"noreturn", | |
| 258 | .u8, | |
| 259 | .i8, | |
| 260 | .isize, | |
| 261 | .usize, | |
| 262 | .c_short, | |
| 263 | .c_ushort, | |
| 264 | .c_int, | |
| 265 | .c_uint, | |
| 266 | .c_long, | |
| 267 | .c_ulong, | |
| 268 | .c_longlong, | |
| 269 | .c_ulonglong, | |
| 270 | .c_longdouble, | |
| 271 | .f16, | |
| 272 | .f32, | |
| 273 | .f64, | |
| 274 | .f128, | |
| 275 | .c_void, | |
| 276 | .bool, | |
| 277 | .void, | |
| 278 | .type, | |
| 279 | .anyerror, | |
| 280 | .comptime_int, | |
| 281 | .comptime_float, | |
| 282 | .noreturn, | |
| 273 | 283 | .array, |
| 274 | 284 | .array_u8_sentinel_0, |
| 275 | 285 | .const_slice_u8, |
| 276 | 286 | .fn_naked_noreturn_no_args, |
| 287 | .int_unsigned, | |
| 288 | .int_signed, | |
| 277 | 289 | => false, |
| 278 | 290 | |
| 279 | 291 | .single_const_pointer, |
| ... | ... | @@ -284,36 +296,38 @@ pub const Type = extern union { |
| 284 | 296 | |
| 285 | 297 | pub fn isSlice(self: Type) bool { |
| 286 | 298 | return switch (self.tag()) { |
| 287 | .@"u8", | |
| 288 | .@"i8", | |
| 289 | .@"isize", | |
| 290 | .@"usize", | |
| 291 | .@"c_short", | |
| 292 | .@"c_ushort", | |
| 293 | .@"c_int", | |
| 294 | .@"c_uint", | |
| 295 | .@"c_long", | |
| 296 | .@"c_ulong", | |
| 297 | .@"c_longlong", | |
| 298 | .@"c_ulonglong", | |
| 299 | .@"c_longdouble", | |
| 300 | .@"f16", | |
| 301 | .@"f32", | |
| 302 | .@"f64", | |
| 303 | .@"f128", | |
| 304 | .@"c_void", | |
| 305 | .@"bool", | |
| 306 | .@"void", | |
| 307 | .@"type", | |
| 308 | .@"anyerror", | |
| 309 | .@"comptime_int", | |
| 310 | .@"comptime_float", | |
| 311 | .@"noreturn", | |
| 299 | .u8, | |
| 300 | .i8, | |
| 301 | .isize, | |
| 302 | .usize, | |
| 303 | .c_short, | |
| 304 | .c_ushort, | |
| 305 | .c_int, | |
| 306 | .c_uint, | |
| 307 | .c_long, | |
| 308 | .c_ulong, | |
| 309 | .c_longlong, | |
| 310 | .c_ulonglong, | |
| 311 | .c_longdouble, | |
| 312 | .f16, | |
| 313 | .f32, | |
| 314 | .f64, | |
| 315 | .f128, | |
| 316 | .c_void, | |
| 317 | .bool, | |
| 318 | .void, | |
| 319 | .type, | |
| 320 | .anyerror, | |
| 321 | .comptime_int, | |
| 322 | .comptime_float, | |
| 323 | .noreturn, | |
| 312 | 324 | .array, |
| 313 | 325 | .array_u8_sentinel_0, |
| 314 | 326 | .single_const_pointer, |
| 315 | 327 | .single_const_pointer_to_comptime_int, |
| 316 | 328 | .fn_naked_noreturn_no_args, |
| 329 | .int_unsigned, | |
| 330 | .int_signed, | |
| 317 | 331 | => false, |
| 318 | 332 | |
| 319 | 333 | .const_slice_u8 => true, |
| ... | ... | @@ -323,34 +337,36 @@ pub const Type = extern union { |
| 323 | 337 | /// Asserts the type is a pointer type. |
| 324 | 338 | pub fn pointerIsConst(self: Type) bool { |
| 325 | 339 | return switch (self.tag()) { |
| 326 | .@"u8", | |
| 327 | .@"i8", | |
| 328 | .@"isize", | |
| 329 | .@"usize", | |
| 330 | .@"c_short", | |
| 331 | .@"c_ushort", | |
| 332 | .@"c_int", | |
| 333 | .@"c_uint", | |
| 334 | .@"c_long", | |
| 335 | .@"c_ulong", | |
| 336 | .@"c_longlong", | |
| 337 | .@"c_ulonglong", | |
| 338 | .@"c_longdouble", | |
| 339 | .@"f16", | |
| 340 | .@"f32", | |
| 341 | .@"f64", | |
| 342 | .@"f128", | |
| 343 | .@"c_void", | |
| 344 | .@"bool", | |
| 345 | .@"void", | |
| 346 | .@"type", | |
| 347 | .@"anyerror", | |
| 348 | .@"comptime_int", | |
| 349 | .@"comptime_float", | |
| 350 | .@"noreturn", | |
| 340 | .u8, | |
| 341 | .i8, | |
| 342 | .isize, | |
| 343 | .usize, | |
| 344 | .c_short, | |
| 345 | .c_ushort, | |
| 346 | .c_int, | |
| 347 | .c_uint, | |
| 348 | .c_long, | |
| 349 | .c_ulong, | |
| 350 | .c_longlong, | |
| 351 | .c_ulonglong, | |
| 352 | .c_longdouble, | |
| 353 | .f16, | |
| 354 | .f32, | |
| 355 | .f64, | |
| 356 | .f128, | |
| 357 | .c_void, | |
| 358 | .bool, | |
| 359 | .void, | |
| 360 | .type, | |
| 361 | .anyerror, | |
| 362 | .comptime_int, | |
| 363 | .comptime_float, | |
| 364 | .noreturn, | |
| 351 | 365 | .array, |
| 352 | 366 | .array_u8_sentinel_0, |
| 353 | 367 | .fn_naked_noreturn_no_args, |
| 368 | .int_unsigned, | |
| 369 | .int_signed, | |
| 354 | 370 | => unreachable, |
| 355 | 371 | |
| 356 | 372 | .single_const_pointer, |
| ... | ... | @@ -363,32 +379,34 @@ pub const Type = extern union { |
| 363 | 379 | /// Asserts the type is a pointer or array type. |
| 364 | 380 | pub fn elemType(self: Type) Type { |
| 365 | 381 | return switch (self.tag()) { |
| 366 | .@"u8", | |
| 367 | .@"i8", | |
| 368 | .@"isize", | |
| 369 | .@"usize", | |
| 370 | .@"c_short", | |
| 371 | .@"c_ushort", | |
| 372 | .@"c_int", | |
| 373 | .@"c_uint", | |
| 374 | .@"c_long", | |
| 375 | .@"c_ulong", | |
| 376 | .@"c_longlong", | |
| 377 | .@"c_ulonglong", | |
| 378 | .@"c_longdouble", | |
| 379 | .@"f16", | |
| 380 | .@"f32", | |
| 381 | .@"f64", | |
| 382 | .@"f128", | |
| 383 | .@"c_void", | |
| 384 | .@"bool", | |
| 385 | .@"void", | |
| 386 | .@"type", | |
| 387 | .@"anyerror", | |
| 388 | .@"comptime_int", | |
| 389 | .@"comptime_float", | |
| 390 | .@"noreturn", | |
| 382 | .u8, | |
| 383 | .i8, | |
| 384 | .isize, | |
| 385 | .usize, | |
| 386 | .c_short, | |
| 387 | .c_ushort, | |
| 388 | .c_int, | |
| 389 | .c_uint, | |
| 390 | .c_long, | |
| 391 | .c_ulong, | |
| 392 | .c_longlong, | |
| 393 | .c_ulonglong, | |
| 394 | .c_longdouble, | |
| 395 | .f16, | |
| 396 | .f32, | |
| 397 | .f64, | |
| 398 | .f128, | |
| 399 | .c_void, | |
| 400 | .bool, | |
| 401 | .void, | |
| 402 | .type, | |
| 403 | .anyerror, | |
| 404 | .comptime_int, | |
| 405 | .comptime_float, | |
| 406 | .noreturn, | |
| 391 | 407 | .fn_naked_noreturn_no_args, |
| 408 | .int_unsigned, | |
| 409 | .int_signed, | |
| 392 | 410 | => unreachable, |
| 393 | 411 | |
| 394 | 412 | .array => self.cast(Payload.Array).?.elem_type, |
| ... | ... | @@ -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 | 420 | pub fn arrayLen(self: Type) u64 { |
| 403 | 421 | return switch (self.tag()) { |
| 404 | 422 | .u8, |
| ... | ... | @@ -430,6 +448,8 @@ pub const Type = extern union { |
| 430 | 448 | .single_const_pointer, |
| 431 | 449 | .single_const_pointer_to_comptime_int, |
| 432 | 450 | .const_slice_u8, |
| 451 | .int_unsigned, | |
| 452 | .int_signed, | |
| 433 | 453 | => unreachable, |
| 434 | 454 | |
| 435 | 455 | .array => self.cast(Payload.Array).?.len, |
| ... | ... | @@ -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 | 502 | /// Asserts the type is a fixed-width integer. |
| 441 | 503 | pub fn intInfo(self: Type, target: Target) struct { signed: bool, bits: u16 } { |
| 442 | 504 | return switch (self.tag()) { |
| 443 | .@"f16", | |
| 444 | .@"f32", | |
| 445 | .@"f64", | |
| 446 | .@"f128", | |
| 447 | .@"c_longdouble", | |
| 448 | .@"c_void", | |
| 449 | .@"bool", | |
| 450 | .@"void", | |
| 451 | .@"type", | |
| 452 | .@"anyerror", | |
| 453 | .@"comptime_int", | |
| 454 | .@"comptime_float", | |
| 455 | .@"noreturn", | |
| 505 | .f16, | |
| 506 | .f32, | |
| 507 | .f64, | |
| 508 | .f128, | |
| 509 | .c_longdouble, | |
| 510 | .c_void, | |
| 511 | .bool, | |
| 512 | .void, | |
| 513 | .type, | |
| 514 | .anyerror, | |
| 515 | .comptime_int, | |
| 516 | .comptime_float, | |
| 517 | .noreturn, | |
| 456 | 518 | .fn_naked_noreturn_no_args, |
| 457 | 519 | .array, |
| 458 | 520 | .single_const_pointer, |
| ... | ... | @@ -461,18 +523,46 @@ pub const Type = extern union { |
| 461 | 523 | .const_slice_u8, |
| 462 | 524 | => unreachable, |
| 463 | 525 | |
| 464 | .@"u8" => .{ .signed = false, .bits = 8 }, | |
| 465 | .@"i8" => .{ .signed = true, .bits = 8 }, | |
| 466 | .@"usize" => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() }, | |
| 467 | .@"isize" => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() }, | |
| 468 | .@"c_short" => .{ .signed = true, .bits = CInteger.short.sizeInBits(target) }, | |
| 469 | .@"c_ushort" => .{ .signed = false, .bits = CInteger.ushort.sizeInBits(target) }, | |
| 470 | .@"c_int" => .{ .signed = true, .bits = CInteger.int.sizeInBits(target) }, | |
| 471 | .@"c_uint" => .{ .signed = false, .bits = CInteger.uint.sizeInBits(target) }, | |
| 472 | .@"c_long" => .{ .signed = true, .bits = CInteger.long.sizeInBits(target) }, | |
| 473 | .@"c_ulong" => .{ .signed = false, .bits = CInteger.ulong.sizeInBits(target) }, | |
| 474 | .@"c_longlong" => .{ .signed = true, .bits = CInteger.longlong.sizeInBits(target) }, | |
| 475 | .@"c_ulonglong" => .{ .signed = false, .bits = CInteger.ulonglong.sizeInBits(target) }, | |
| 526 | .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits }, | |
| 527 | .int_signed => .{ .signed = true, .bits = self.cast(Payload.IntSigned).?.bits }, | |
| 528 | .u8 => .{ .signed = false, .bits = 8 }, | |
| 529 | .i8 => .{ .signed = true, .bits = 8 }, | |
| 530 | .usize => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() }, | |
| 531 | .isize => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() }, | |
| 532 | .c_short => .{ .signed = true, .bits = CType.short.sizeInBits(target) }, | |
| 533 | .c_ushort => .{ .signed = false, .bits = CType.ushort.sizeInBits(target) }, | |
| 534 | .c_int => .{ .signed = true, .bits = CType.int.sizeInBits(target) }, | |
| 535 | .c_uint => .{ .signed = false, .bits = CType.uint.sizeInBits(target) }, | |
| 536 | .c_long => .{ .signed = true, .bits = CType.long.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 | 601 | .c_ulong, |
| 512 | 602 | .c_longlong, |
| 513 | 603 | .c_ulonglong, |
| 604 | .int_unsigned, | |
| 605 | .int_signed, | |
| 514 | 606 | => unreachable, |
| 515 | 607 | }; |
| 516 | 608 | } |
| ... | ... | @@ -551,6 +643,8 @@ pub const Type = extern union { |
| 551 | 643 | .c_ulong, |
| 552 | 644 | .c_longlong, |
| 553 | 645 | .c_ulonglong, |
| 646 | .int_unsigned, | |
| 647 | .int_signed, | |
| 554 | 648 | => unreachable, |
| 555 | 649 | } |
| 556 | 650 | } |
| ... | ... | @@ -590,6 +684,8 @@ pub const Type = extern union { |
| 590 | 684 | .c_ulong, |
| 591 | 685 | .c_longlong, |
| 592 | 686 | .c_ulonglong, |
| 687 | .int_unsigned, | |
| 688 | .int_signed, | |
| 593 | 689 | => unreachable, |
| 594 | 690 | }; |
| 595 | 691 | } |
| ... | ... | @@ -629,10 +725,145 @@ pub const Type = extern union { |
| 629 | 725 | .c_ulong, |
| 630 | 726 | .c_longlong, |
| 631 | 727 | .c_ulonglong, |
| 728 | .int_unsigned, | |
| 729 | .int_signed, | |
| 632 | 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 | 867 | /// This enum does not directly correspond to `std.builtin.TypeId` because |
| 637 | 868 | /// it has extra enum tags in it, as a way of using less memory. For example, |
| 638 | 869 | /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types |
| ... | ... | @@ -674,6 +905,8 @@ pub const Type = extern union { |
| 674 | 905 | array_u8_sentinel_0, |
| 675 | 906 | array, |
| 676 | 907 | single_const_pointer, |
| 908 | int_signed, | |
| 909 | int_unsigned, | |
| 677 | 910 | |
| 678 | 911 | pub const last_no_payload_tag = Tag.const_slice_u8; |
| 679 | 912 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| ... | ... | @@ -700,10 +933,22 @@ pub const Type = extern union { |
| 700 | 933 | |
| 701 | 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 | 952 | short, |
| 708 | 953 | ushort, |
| 709 | 954 | int, |
| ... | ... | @@ -712,8 +957,9 @@ pub const CInteger = enum { |
| 712 | 957 | ulong, |
| 713 | 958 | longlong, |
| 714 | 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 | 963 | const arch = target.cpu.arch; |
| 718 | 964 | switch (target.os.tag) { |
| 719 | 965 | .freestanding, .other => switch (target.cpu.arch) { |
| ... | ... | @@ -729,6 +975,7 @@ pub const CInteger = enum { |
| 729 | 975 | .longlong, |
| 730 | 976 | .ulonglong, |
| 731 | 977 | => return 64, |
| 978 | .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"), | |
| 732 | 979 | }, |
| 733 | 980 | else => switch (self) { |
| 734 | 981 | .short, |
| ... | ... | @@ -743,6 +990,7 @@ pub const CInteger = enum { |
| 743 | 990 | .longlong, |
| 744 | 991 | .ulonglong, |
| 745 | 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 | 1015 | .longlong, |
| 768 | 1016 | .ulonglong, |
| 769 | 1017 | => return 64, |
| 1018 | .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"), | |
| 770 | 1019 | }, |
| 771 | 1020 | |
| 772 | 1021 | .windows, .uefi => switch (self) { |
| ... | ... | @@ -781,6 +1030,7 @@ pub const CInteger = enum { |
| 781 | 1030 | .longlong, |
| 782 | 1031 | .ulonglong, |
| 783 | 1032 | => return 64, |
| 1033 | .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"), | |
| 784 | 1034 | }, |
| 785 | 1035 | |
| 786 | 1036 | .ios => switch (self) { |
| ... | ... | @@ -795,6 +1045,7 @@ pub const CInteger = enum { |
| 795 | 1045 | .longlong, |
| 796 | 1046 | .ulonglong, |
| 797 | 1047 | => return 64, |
| 1048 | .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"), | |
| 798 | 1049 | }, |
| 799 | 1050 | |
| 800 | 1051 | .ananas, |
| ... | ... | @@ -821,7 +1072,7 @@ pub const CInteger = enum { |
| 821 | 1072 | .amdpal, |
| 822 | 1073 | .hermit, |
| 823 | 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 | 48 | single_const_pointer_to_comptime_int_type, |
| 49 | 49 | const_slice_u8_type, |
| 50 | 50 | |
| 51 | undef, | |
| 51 | 52 | zero, |
| 52 | void_value, | |
| 53 | noreturn_value, | |
| 53 | the_one_possible_value, // when the type only has one possible value | |
| 54 | null_value, | |
| 54 | 55 | bool_true, |
| 55 | 56 | bool_false, // See last_no_payload_tag below. |
| 56 | 57 | // After this, the tag requires a payload. |
| ... | ... | @@ -63,6 +64,7 @@ pub const Value = extern union { |
| 63 | 64 | ref, |
| 64 | 65 | ref_val, |
| 65 | 66 | bytes, |
| 67 | repeated, // the value is a value repeated some number of times | |
| 66 | 68 | |
| 67 | 69 | pub const last_no_payload_tag = Tag.bool_false; |
| 68 | 70 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| ... | ... | @@ -135,9 +137,10 @@ pub const Value = extern union { |
| 135 | 137 | .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"), |
| 136 | 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 | 142 | .zero => return out_stream.writeAll("0"), |
| 139 | .void_value => return out_stream.writeAll("{}"), | |
| 140 | .noreturn_value => return out_stream.writeAll("unreachable"), | |
| 143 | .the_one_possible_value => return out_stream.writeAll("(one possible value)"), | |
| 141 | 144 | .bool_true => return out_stream.writeAll("true"), |
| 142 | 145 | .bool_false => return out_stream.writeAll("false"), |
| 143 | 146 | .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream), |
| ... | ... | @@ -152,6 +155,10 @@ pub const Value = extern union { |
| 152 | 155 | continue; |
| 153 | 156 | }, |
| 154 | 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 | 205 | .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int), |
| 199 | 206 | .const_slice_u8_type => Type.initTag(.const_slice_u8), |
| 200 | 207 | |
| 208 | .undef, | |
| 201 | 209 | .zero, |
| 202 | .void_value, | |
| 203 | .noreturn_value, | |
| 210 | .the_one_possible_value, | |
| 204 | 211 | .bool_true, |
| 205 | 212 | .bool_false, |
| 213 | .null_value, | |
| 206 | 214 | .int_u64, |
| 207 | 215 | .int_i64, |
| 208 | 216 | .int_big, |
| ... | ... | @@ -210,12 +218,13 @@ pub const Value = extern union { |
| 210 | 218 | .ref, |
| 211 | 219 | .ref_val, |
| 212 | 220 | .bytes, |
| 221 | .repeated, | |
| 213 | 222 | => unreachable, |
| 214 | 223 | }; |
| 215 | 224 | } |
| 216 | 225 | |
| 217 | 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 | 228 | switch (self.tag()) { |
| 220 | 229 | .ty, |
| 221 | 230 | .u8_type, |
| ... | ... | @@ -246,20 +255,23 @@ pub const Value = extern union { |
| 246 | 255 | .fn_naked_noreturn_no_args_type, |
| 247 | 256 | .single_const_pointer_to_comptime_int_type, |
| 248 | 257 | .const_slice_u8_type, |
| 249 | .void_value, | |
| 250 | .noreturn_value, | |
| 251 | 258 | .bool_true, |
| 252 | 259 | .bool_false, |
| 260 | .null_value, | |
| 253 | 261 | .function, |
| 254 | 262 | .ref, |
| 255 | 263 | .ref_val, |
| 256 | 264 | .bytes, |
| 265 | .undef, | |
| 266 | .repeated, | |
| 257 | 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), | |
| 262 | .int_i64 => return BigInt.initSet(allocator, self.cast(Payload.Int_i64).?.int), | |
| 273 | .int_u64 => return BigInt.initSetFixed(&space.limbs, self.cast(Payload.Int_u64).?.int), | |
| 274 | .int_i64 => return BigInt.initSetFixed(&space.limbs, self.cast(Payload.Int_i64).?.int), | |
| 263 | 275 | .int_big => return self.cast(Payload.IntBig).?.big_int, |
| 264 | 276 | } |
| 265 | 277 | } |
| ... | ... | @@ -296,17 +308,20 @@ pub const Value = extern union { |
| 296 | 308 | .fn_naked_noreturn_no_args_type, |
| 297 | 309 | .single_const_pointer_to_comptime_int_type, |
| 298 | 310 | .const_slice_u8_type, |
| 299 | .void_value, | |
| 300 | .noreturn_value, | |
| 301 | 311 | .bool_true, |
| 302 | 312 | .bool_false, |
| 313 | .null_value, | |
| 303 | 314 | .function, |
| 304 | 315 | .ref, |
| 305 | 316 | .ref_val, |
| 306 | 317 | .bytes, |
| 318 | .undef, | |
| 319 | .repeated, | |
| 307 | 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 | 326 | .int_u64 => return self.cast(Payload.Int_u64).?.int, |
| 312 | 327 | .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int), |
| ... | ... | @@ -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 | 392 | /// Asserts the value is an integer, and the destination type is ComptimeInt or Int. |
| 318 | 393 | pub fn intFitsInType(self: Value, ty: Type, target: Target) bool { |
| 319 | 394 | switch (self.tag()) { |
| ... | ... | @@ -346,17 +421,20 @@ pub const Value = extern union { |
| 346 | 421 | .fn_naked_noreturn_no_args_type, |
| 347 | 422 | .single_const_pointer_to_comptime_int_type, |
| 348 | 423 | .const_slice_u8_type, |
| 349 | .void_value, | |
| 350 | .noreturn_value, | |
| 351 | 424 | .bool_true, |
| 352 | 425 | .bool_false, |
| 426 | .null_value, | |
| 353 | 427 | .function, |
| 354 | 428 | .ref, |
| 355 | 429 | .ref_val, |
| 356 | 430 | .bytes, |
| 431 | .repeated, | |
| 357 | 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 | 439 | .int_u64 => switch (ty.zigTypeTag()) { |
| 362 | 440 | .Int => { |
| ... | ... | @@ -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 | 612 | /// Asserts the value is a pointer and dereferences it. |
| 396 | 613 | pub fn pointerDeref(self: Value) Value { |
| 397 | switch (self.tag()) { | |
| 614 | return switch (self.tag()) { | |
| 398 | 615 | .ty, |
| 399 | 616 | .u8_type, |
| 400 | 617 | .i8_type, |
| ... | ... | @@ -425,20 +642,22 @@ pub const Value = extern union { |
| 425 | 642 | .single_const_pointer_to_comptime_int_type, |
| 426 | 643 | .const_slice_u8_type, |
| 427 | 644 | .zero, |
| 428 | .void_value, | |
| 429 | .noreturn_value, | |
| 430 | 645 | .bool_true, |
| 431 | 646 | .bool_false, |
| 647 | .null_value, | |
| 432 | 648 | .function, |
| 433 | 649 | .int_u64, |
| 434 | 650 | .int_i64, |
| 435 | 651 | .int_big, |
| 436 | 652 | .bytes, |
| 653 | .undef, | |
| 654 | .repeated, | |
| 437 | 655 | => unreachable, |
| 438 | 656 | |
| 439 | .ref => return self.cast(Payload.Ref).?.cell.contents, | |
| 440 | .ref_val => return self.cast(Payload.RefVal).?.val, | |
| 441 | } | |
| 657 | .the_one_possible_value => Value.initTag(.the_one_possible_value), | |
| 658 | .ref => self.cast(Payload.Ref).?.cell.contents, | |
| 659 | .ref_val => self.cast(Payload.RefVal).?.val, | |
| 660 | }; | |
| 442 | 661 | } |
| 443 | 662 | |
| 444 | 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 | 694 | .single_const_pointer_to_comptime_int_type, |
| 476 | 695 | .const_slice_u8_type, |
| 477 | 696 | .zero, |
| 478 | .void_value, | |
| 479 | .noreturn_value, | |
| 697 | .the_one_possible_value, | |
| 480 | 698 | .bool_true, |
| 481 | 699 | .bool_false, |
| 700 | .null_value, | |
| 482 | 701 | .function, |
| 483 | 702 | .int_u64, |
| 484 | 703 | .int_i64, |
| 485 | 704 | .int_big, |
| 705 | .undef, | |
| 486 | 706 | => unreachable, |
| 487 | 707 | |
| 488 | 708 | .ref => @panic("TODO figure out how MemoryCell works"), |
| ... | ... | @@ -493,9 +713,68 @@ pub const Value = extern union { |
| 493 | 713 | int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] }; |
| 494 | 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 | 778 | /// This type is not copyable since it may contain pointers to its inner data. |
| 500 | 779 | pub const Payload = struct { |
| 501 | 780 | tag: Tag, |
| ... | ... | @@ -550,6 +829,20 @@ pub const Value = extern union { |
| 550 | 829 | base: Payload = Payload{ .tag = .ty }, |
| 551 | 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 |