authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-28 21:04:18-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-01 06:47:20-04:00
log6b0f7de247f3c12281f47f38738e93651d6bf51b
tree1ec7419096d8dd5a7122bb8a75880382585781e2
parentfb67a7260d1e7fc8ff81fa9e3cc5340422363727

ZIR: add cmp and condbr instructions


7 files changed, 1630 insertions(+), 450 deletions(-)

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