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 {
986986
987987 /// Greater than (`>`)
988988 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 }
9891026};
9901027
9911028/// 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 {
6060 return s;
6161 }
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
6370 /// Creates a new Int with a specific capacity. If capacity < default_capacity then the
6471 /// default capacity will be used instead.
6572 pub fn initCapacity(allocator: *Allocator, capacity: usize) !Int {
......@@ -104,12 +111,11 @@ pub const Int = struct {
104111 /// Returns an Int backed by a fixed set of limb values.
105112 /// This is read-only and cannot be used as a result argument. If the Int tries to allocate
106113 /// memory a runtime panic will occur.
107 pub fn initFixed(limbs: []const Limb) Int {
114 pub fn initFixed(limbs: []Limb) Int {
108115 var self = Int{
109116 .allocator = null,
110117 .metadata = limbs.len,
111 // Cast away the const, invalid use to pass as a pointer argument.
112 .limbs = @intToPtr([*]Limb, @ptrToInt(limbs.ptr))[0..limbs.len],
118 .limbs = limbs,
113119 };
114120
115121 self.normalize(limbs.len);
......@@ -218,7 +224,7 @@ pub const Int = struct {
218224 /// one greater than the returned value.
219225 ///
220226 /// 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 {
222228 var bits = self.bitCountAbs();
223229
224230 // 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 {
267273
268274 /// Sets an Int to value. Value must be an primitive integer type.
269275 pub fn set(self: *Int, value: var) Allocator.Error!void {
270 self.assertWritable();
271276 const T = @TypeOf(value);
272277
273278 switch (@typeInfo(T)) {
......@@ -598,6 +603,13 @@ pub const Int = struct {
598603 }
599604 }
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
601613 /// Returns true if a == 0.
602614 pub fn eqZero(a: Int) bool {
603615 return a.len() == 1 and a.limbs[0] == 0;
......@@ -642,6 +654,33 @@ pub const Int = struct {
642654 };
643655 }
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
645684 /// r = a + b
646685 ///
647686 /// 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 {
501501 bh,
502502 ch,
503503 dh,
504 bph,
505 sph,
506 sih,
507 dih,
504508
505509 al,
506510 bl,
507511 cl,
508512 dl,
513 bpl,
514 spl,
515 sil,
516 dil,
509517 r8b,
510518 r9b,
511519 r10b,
src-self-hosted/ir.zig+549-125
......@@ -26,6 +26,10 @@ pub const Inst = struct {
2626 assembly,
2727 ptrtoint,
2828 bitcast,
29 cmp,
30 condbr,
31 isnull,
32 isnonnull,
2933 };
3034
3135 pub fn cast(base: *Inst, comptime T: type) ?*T {
......@@ -41,15 +45,11 @@ pub const Inst = struct {
4145
4246 /// Returns `null` if runtime-known.
4347 pub fn value(base: *Inst) ?Value {
44 return switch (base.tag) {
45 .unreach => Value.initTag(.noreturn_value),
46 .constant => base.cast(Constant).?.val,
47
48 .assembly,
49 .ptrtoint,
50 .bitcast,
51 => null,
52 };
48 if (base.ty.onePossibleValue())
49 return Value.initTag(.the_one_possible_value);
50
51 const inst = base.cast(Constant) orelse return null;
52 return inst.val;
5353 }
5454
5555 pub const Unreach = struct {
......@@ -96,6 +96,46 @@ pub const Inst = struct {
9696 operand: *Inst,
9797 },
9898 };
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 };
99139};
100140
101141pub const TypedValue = struct {
......@@ -118,15 +158,19 @@ pub const Module = struct {
118158
119159 pub const Fn = struct {
120160 analysis_status: enum { in_progress, failure, success },
121 body: []*Inst,
161 body: Body,
122162 fn_type: Type,
123163 };
124164
165 pub const Body = struct {
166 instructions: []*Inst,
167 };
168
125169 pub fn deinit(self: *Module, allocator: *Allocator) void {
126170 allocator.free(self.exports);
127171 allocator.free(self.errors);
128172 for (self.fns) |f| {
129 allocator.free(f.body);
173 allocator.free(f.body.instructions);
130174 }
131175 allocator.free(self.fns);
132176 self.arena.deinit();
......@@ -192,10 +236,15 @@ const Analyze = struct {
192236 };
193237
194238 const Fn = struct {
195 body: std.ArrayList(*Inst),
196 inst_table: std.AutoHashMap(*text.Inst, NewInst),
197239 /// Index into Module fns array
198240 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),
199248 };
200249
201250 const InnerError = error{ OutOfMemory, AnalysisFail };
......@@ -208,9 +257,9 @@ const Analyze = struct {
208257 }
209258 }
210259
211 fn resolveInst(self: *Analyze, opt_func: ?*Fn, old_inst: *text.Inst) InnerError!*Inst {
212 if (opt_func) |func| {
213 if (func.inst_table.get(old_inst)) |kv| {
260 fn resolveInst(self: *Analyze, opt_block: ?*Block, old_inst: *text.Inst) InnerError!*Inst {
261 if (opt_block) |block| {
262 if (block.func.inst_table.get(old_inst)) |kv| {
214263 return kv.value.ptr orelse return error.AnalysisFail;
215264 }
216265 }
......@@ -230,12 +279,12 @@ const Analyze = struct {
230279 }
231280 }
232281
233 fn requireFunctionBody(self: *Analyze, func: ?*Fn, src: usize) !*Fn {
234 return func orelse return self.fail(src, "instruction illegal outside function body", .{});
282 fn requireRuntimeBlock(self: *Analyze, block: ?*Block, src: usize) !*Block {
283 return block orelse return self.fail(src, "instruction illegal outside function body", .{});
235284 }
236285
237 fn resolveInstConst(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) InnerError!TypedValue {
238 const new_inst = try self.resolveInst(func, old_inst);
286 fn resolveInstConst(self: *Analyze, block: ?*Block, old_inst: *text.Inst) InnerError!TypedValue {
287 const new_inst = try self.resolveInst(block, old_inst);
239288 const val = try self.resolveConstValue(new_inst);
240289 return TypedValue{
241290 .ty = new_inst.ty,
......@@ -244,28 +293,39 @@ const Analyze = struct {
244293 }
245294
246295 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", .{});
248298 }
249299
250 fn resolveConstString(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) ![]u8 {
251 const new_inst = try self.resolveInst(func, old_inst);
300 fn resolveDefinedValue(self: *Analyze, base: *Inst) !?Value {
301 if (base.value()) |val| {
302 if (val.isUndef()) {
303 return self.fail(base.src, "use of undefined value here causes undefined behavior", .{});
304 }
305 return val;
306 }
307 return null;
308 }
309
310 fn resolveConstString(self: *Analyze, block: ?*Block, old_inst: *text.Inst) ![]u8 {
311 const new_inst = try self.resolveInst(block, old_inst);
252312 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);
254314 const val = try self.resolveConstValue(coerced_inst);
255315 return val.toAllocatedBytes(&self.arena.allocator);
256316 }
257317
258 fn resolveType(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) !Type {
259 const new_inst = try self.resolveInst(func, old_inst);
318 fn resolveType(self: *Analyze, block: ?*Block, old_inst: *text.Inst) !Type {
319 const new_inst = try self.resolveInst(block, old_inst);
260320 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);
262322 const val = try self.resolveConstValue(coerced_inst);
263323 return val.toType();
264324 }
265325
266 fn analyzeExport(self: *Analyze, func: ?*Fn, export_inst: *text.Inst.Export) !void {
267 const symbol_name = try self.resolveConstString(func, export_inst.positionals.symbol_name);
268 const typed_value = try self.resolveInstConst(func, export_inst.positionals.value);
326 fn analyzeExport(self: *Analyze, block: ?*Block, export_inst: *text.Inst.Export) !void {
327 const symbol_name = try self.resolveConstString(block, export_inst.positionals.symbol_name);
328 const typed_value = try self.resolveInstConst(block, export_inst.positionals.value);
269329
270330 switch (typed_value.ty.zigTypeTag()) {
271331 .Fn => {},
......@@ -285,18 +345,18 @@ const Analyze = struct {
285345 /// TODO should not need the cast on the last parameter at the callsites
286346 fn addNewInstArgs(
287347 self: *Analyze,
288 func: *Fn,
348 block: *Block,
289349 src: usize,
290350 ty: Type,
291351 comptime T: type,
292352 args: Inst.Args(T),
293353 ) !*Inst {
294 const inst = try self.addNewInst(func, src, ty, T);
354 const inst = try self.addNewInst(block, src, ty, T);
295355 inst.args = args;
296356 return &inst.base;
297357 }
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 {
300360 const inst = try self.arena.allocator.create(T);
301361 inst.* = .{
302362 .base = .{
......@@ -306,7 +366,7 @@ const Analyze = struct {
306366 },
307367 .args = undefined,
308368 };
309 try func.body.append(&inst.base);
369 try block.instructions.append(&inst.base);
310370 return inst;
311371 }
312372
......@@ -349,7 +409,21 @@ const Analyze = struct {
349409 fn constVoid(self: *Analyze, src: usize) !*Inst {
350410 return self.constInst(src, .{
351411 .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)],
353427 });
354428 }
355429
......@@ -399,7 +473,7 @@ const Analyze = struct {
399473 });
400474 }
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 {
403477 switch (old_inst.tag) {
404478 .str => {
405479 // We can use this reference because Inst.Const's Value is arena-allocated.
......@@ -411,35 +485,43 @@ const Analyze = struct {
411485 const big_int = old_inst.cast(text.Inst.Int).?.positionals.int;
412486 return self.constIntBig(old_inst.src, Type.initTag(.comptime_int), big_int);
413487 },
414 .ptrtoint => return self.analyzeInstPtrToInt(func, old_inst.cast(text.Inst.PtrToInt).?),
415 .fieldptr => return self.analyzeInstFieldPtr(func, old_inst.cast(text.Inst.FieldPtr).?),
416 .deref => return self.analyzeInstDeref(func, old_inst.cast(text.Inst.Deref).?),
417 .as => return self.analyzeInstAs(func, old_inst.cast(text.Inst.As).?),
418 .@"asm" => return self.analyzeInstAsm(func, old_inst.cast(text.Inst.Asm).?),
419 .@"unreachable" => return self.analyzeInstUnreachable(func, old_inst.cast(text.Inst.Unreachable).?),
420 .@"fn" => return self.analyzeInstFn(func, old_inst.cast(text.Inst.Fn).?),
488 .ptrtoint => return self.analyzeInstPtrToInt(block, old_inst.cast(text.Inst.PtrToInt).?),
489 .fieldptr => return self.analyzeInstFieldPtr(block, old_inst.cast(text.Inst.FieldPtr).?),
490 .deref => return self.analyzeInstDeref(block, old_inst.cast(text.Inst.Deref).?),
491 .as => return self.analyzeInstAs(block, old_inst.cast(text.Inst.As).?),
492 .@"asm" => return self.analyzeInstAsm(block, old_inst.cast(text.Inst.Asm).?),
493 .@"unreachable" => return self.analyzeInstUnreachable(block, old_inst.cast(text.Inst.Unreachable).?),
494 .@"fn" => return self.analyzeInstFn(block, old_inst.cast(text.Inst.Fn).?),
421495 .@"export" => {
422 try self.analyzeExport(func, old_inst.cast(text.Inst.Export).?);
496 try self.analyzeExport(block, old_inst.cast(text.Inst.Export).?);
423497 return self.constVoid(old_inst.src);
424498 },
425 .primitive => return self.analyzeInstPrimitive(func, old_inst.cast(text.Inst.Primitive).?),
426 .fntype => return self.analyzeInstFnType(func, old_inst.cast(text.Inst.FnType).?),
427 .intcast => return self.analyzeInstIntCast(func, old_inst.cast(text.Inst.IntCast).?),
428 .bitcast => return self.analyzeInstBitCast(func, old_inst.cast(text.Inst.BitCast).?),
429 .elemptr => return self.analyzeInstElemPtr(func, old_inst.cast(text.Inst.ElemPtr).?),
430 .add => return self.analyzeInstAdd(func, old_inst.cast(text.Inst.Add).?),
499 .primitive => return self.analyzeInstPrimitive(old_inst.cast(text.Inst.Primitive).?),
500 .fntype => return self.analyzeInstFnType(block, old_inst.cast(text.Inst.FnType).?),
501 .intcast => return self.analyzeInstIntCast(block, old_inst.cast(text.Inst.IntCast).?),
502 .bitcast => return self.analyzeInstBitCast(block, old_inst.cast(text.Inst.BitCast).?),
503 .elemptr => return self.analyzeInstElemPtr(block, old_inst.cast(text.Inst.ElemPtr).?),
504 .add => return self.analyzeInstAdd(block, old_inst.cast(text.Inst.Add).?),
505 .cmp => return self.analyzeInstCmp(block, old_inst.cast(text.Inst.Cmp).?),
506 .condbr => return self.analyzeInstCondBr(block, old_inst.cast(text.Inst.CondBr).?),
507 .isnull => return self.analyzeInstIsNull(block, old_inst.cast(text.Inst.IsNull).?),
508 .isnonnull => return self.analyzeInstIsNonNull(block, old_inst.cast(text.Inst.IsNonNull).?),
431509 }
432510 }
433511
434 fn analyzeInstFn(self: *Analyze, opt_func: ?*Fn, fn_inst: *text.Inst.Fn) InnerError!*Inst {
435 const fn_type = try self.resolveType(opt_func, fn_inst.positionals.fn_type);
512 fn analyzeInstFn(self: *Analyze, block: ?*Block, fn_inst: *text.Inst.Fn) InnerError!*Inst {
513 const fn_type = try self.resolveType(block, fn_inst.positionals.fn_type);
436514
437515 var new_func: Fn = .{
438 .body = std.ArrayList(*Inst).init(self.allocator),
439 .inst_table = std.AutoHashMap(*text.Inst, NewInst).init(self.allocator),
440516 .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),
441522 };
442 defer new_func.body.deinit();
523 new_func.inner_block.func = &new_func;
524 defer new_func.inner_block.instructions.deinit();
443525 defer new_func.inst_table.deinit();
444526 // Don't hang on to a reference to this when analyzing body instructions, since the memory
445527 // could become invalid.
......@@ -449,18 +531,11 @@ const Analyze = struct {
449531 .body = undefined,
450532 };
451533
452 for (fn_inst.positionals.body.instructions) |src_inst| {
453 const new_inst = self.analyzeInst(&new_func, src_inst) catch |err| {
454 self.fns.items[new_func.fn_index].analysis_status = .failure;
455 try new_func.inst_table.putNoClobber(src_inst, .{ .ptr = null });
456 return err;
457 };
458 try new_func.inst_table.putNoClobber(src_inst, .{ .ptr = new_inst });
459 }
534 try self.analyzeBody(&new_func.inner_block, fn_inst.positionals.body);
460535
461536 const f = &self.fns.items[new_func.fn_index];
462537 f.analysis_status = .success;
463 f.body = new_func.body.toOwnedSlice();
538 f.body = .{ .instructions = new_func.inner_block.instructions.toOwnedSlice() };
464539
465540 const fn_payload = try self.arena.allocator.create(Value.Payload.Function);
466541 fn_payload.* = .{ .index = new_func.fn_index };
......@@ -471,8 +546,8 @@ const Analyze = struct {
471546 });
472547 }
473548
474 fn analyzeInstFnType(self: *Analyze, func: ?*Fn, fntype: *text.Inst.FnType) InnerError!*Inst {
475 const return_type = try self.resolveType(func, fntype.positionals.return_type);
549 fn analyzeInstFnType(self: *Analyze, block: ?*Block, fntype: *text.Inst.FnType) InnerError!*Inst {
550 const return_type = try self.resolveType(block, fntype.positionals.return_type);
476551
477552 if (return_type.zigTypeTag() == .NoReturn and
478553 fntype.positionals.param_types.len == 0 and
......@@ -484,30 +559,30 @@ const Analyze = struct {
484559 return self.fail(fntype.base.src, "TODO implement fntype instruction more", .{});
485560 }
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 {
488563 return self.constType(primitive.base.src, primitive.positionals.tag.toType());
489564 }
490565
491 fn analyzeInstAs(self: *Analyze, func: ?*Fn, as: *text.Inst.As) InnerError!*Inst {
492 const dest_type = try self.resolveType(func, as.positionals.dest_type);
493 const new_inst = try self.resolveInst(func, as.positionals.value);
494 return self.coerce(func, dest_type, new_inst);
566 fn analyzeInstAs(self: *Analyze, block: ?*Block, as: *text.Inst.As) InnerError!*Inst {
567 const dest_type = try self.resolveType(block, as.positionals.dest_type);
568 const new_inst = try self.resolveInst(block, as.positionals.value);
569 return self.coerce(block, dest_type, new_inst);
495570 }
496571
497 fn analyzeInstPtrToInt(self: *Analyze, func: ?*Fn, ptrtoint: *text.Inst.PtrToInt) InnerError!*Inst {
498 const ptr = try self.resolveInst(func, ptrtoint.positionals.ptr);
572 fn analyzeInstPtrToInt(self: *Analyze, block: ?*Block, ptrtoint: *text.Inst.PtrToInt) InnerError!*Inst {
573 const ptr = try self.resolveInst(block, ptrtoint.positionals.ptr);
499574 if (ptr.ty.zigTypeTag() != .Pointer) {
500575 return self.fail(ptrtoint.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty});
501576 }
502577 // 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);
504579 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 });
506581 }
507582
508 fn analyzeInstFieldPtr(self: *Analyze, func: ?*Fn, fieldptr: *text.Inst.FieldPtr) InnerError!*Inst {
509 const object_ptr = try self.resolveInst(func, fieldptr.positionals.object_ptr);
510 const field_name = try self.resolveConstString(func, fieldptr.positionals.field_name);
583 fn analyzeInstFieldPtr(self: *Analyze, block: ?*Block, fieldptr: *text.Inst.FieldPtr) InnerError!*Inst {
584 const object_ptr = try self.resolveInst(block, fieldptr.positionals.object_ptr);
585 const field_name = try self.resolveConstString(block, fieldptr.positionals.field_name);
511586
512587 const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
513588 .Pointer => object_ptr.ty.elemType(),
......@@ -538,9 +613,9 @@ const Analyze = struct {
538613 }
539614 }
540615
541 fn analyzeInstIntCast(self: *Analyze, func: ?*Fn, intcast: *text.Inst.IntCast) InnerError!*Inst {
542 const dest_type = try self.resolveType(func, intcast.positionals.dest_type);
543 const new_inst = try self.resolveInst(func, intcast.positionals.value);
616 fn analyzeInstIntCast(self: *Analyze, block: ?*Block, intcast: *text.Inst.IntCast) InnerError!*Inst {
617 const dest_type = try self.resolveType(block, intcast.positionals.dest_type);
618 const new_inst = try self.resolveInst(block, intcast.positionals.value);
544619
545620 const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
546621 .ComptimeInt => true,
......@@ -564,22 +639,22 @@ const Analyze = struct {
564639 }
565640
566641 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);
568643 }
569644
570645 return self.fail(intcast.base.src, "TODO implement analyze widen or shorten int", .{});
571646 }
572647
573 fn analyzeInstBitCast(self: *Analyze, func: ?*Fn, inst: *text.Inst.BitCast) InnerError!*Inst {
574 const dest_type = try self.resolveType(func, inst.positionals.dest_type);
575 const operand = try self.resolveInst(func, inst.positionals.operand);
576 return self.bitcast(func, dest_type, operand);
648 fn analyzeInstBitCast(self: *Analyze, block: ?*Block, inst: *text.Inst.BitCast) InnerError!*Inst {
649 const dest_type = try self.resolveType(block, inst.positionals.dest_type);
650 const operand = try self.resolveInst(block, inst.positionals.operand);
651 return self.bitcast(block, dest_type, operand);
577652 }
578653
579 fn analyzeInstElemPtr(self: *Analyze, func: ?*Fn, inst: *text.Inst.ElemPtr) InnerError!*Inst {
580 const array_ptr = try self.resolveInst(func, inst.positionals.array_ptr);
581 const uncasted_index = try self.resolveInst(func, inst.positionals.index);
582 const elem_index = try self.coerce(func, Type.initTag(.usize), uncasted_index);
654 fn analyzeInstElemPtr(self: *Analyze, block: ?*Block, inst: *text.Inst.ElemPtr) InnerError!*Inst {
655 const array_ptr = try self.resolveInst(block, inst.positionals.array_ptr);
656 const uncasted_index = try self.resolveInst(block, inst.positionals.index);
657 const elem_index = try self.coerce(block, Type.initTag(.usize), uncasted_index);
583658
584659 if (array_ptr.ty.isSinglePointer() and array_ptr.ty.elemType().zigTypeTag() == .Array) {
585660 if (array_ptr.value()) |array_ptr_val| {
......@@ -607,15 +682,19 @@ const Analyze = struct {
607682 return self.fail(inst.base.src, "TODO implement more analyze elemptr", .{});
608683 }
609684
610 fn analyzeInstAdd(self: *Analyze, func: ?*Fn, inst: *text.Inst.Add) InnerError!*Inst {
611 const lhs = try self.resolveInst(func, inst.positionals.lhs);
612 const rhs = try self.resolveInst(func, inst.positionals.rhs);
685 fn analyzeInstAdd(self: *Analyze, block: ?*Block, inst: *text.Inst.Add) InnerError!*Inst {
686 const lhs = try self.resolveInst(block, inst.positionals.lhs);
687 const rhs = try self.resolveInst(block, inst.positionals.rhs);
613688
614689 if (lhs.ty.zigTypeTag() == .Int and rhs.ty.zigTypeTag() == .Int) {
615690 if (lhs.value()) |lhs_val| {
616691 if (rhs.value()) |rhs_val| {
617 const lhs_bigint = try lhs_val.toBigInt(&self.arena.allocator);
618 const rhs_bigint = try rhs_val.toBigInt(&self.arena.allocator);
692 // TODO is this a performance issue? maybe we should try the operation without
693 // resorting to BigInt first.
694 var lhs_space: Value.BigIntSpace = undefined;
695 var rhs_space: Value.BigIntSpace = undefined;
696 const lhs_bigint = lhs_val.toBigInt(&lhs_space);
697 const rhs_bigint = rhs_val.toBigInt(&rhs_space);
619698 var result_bigint = try BigInt.init(&self.arena.allocator);
620699 try BigInt.add(&result_bigint, lhs_bigint, rhs_bigint);
621700
......@@ -637,8 +716,8 @@ const Analyze = struct {
637716 return self.fail(inst.base.src, "TODO implement more analyze add", .{});
638717 }
639718
640 fn analyzeInstDeref(self: *Analyze, func: ?*Fn, deref: *text.Inst.Deref) InnerError!*Inst {
641 const ptr = try self.resolveInst(func, deref.positionals.ptr);
719 fn analyzeInstDeref(self: *Analyze, block: ?*Block, deref: *text.Inst.Deref) InnerError!*Inst {
720 const ptr = try self.resolveInst(block, deref.positionals.ptr);
642721 const elem_ty = switch (ptr.ty.zigTypeTag()) {
643722 .Pointer => ptr.ty.elemType(),
644723 else => return self.fail(deref.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty}),
......@@ -653,28 +732,28 @@ const Analyze = struct {
653732 return self.fail(deref.base.src, "TODO implement runtime deref", .{});
654733 }
655734
656 fn analyzeInstAsm(self: *Analyze, func: ?*Fn, assembly: *text.Inst.Asm) InnerError!*Inst {
657 const return_type = try self.resolveType(func, assembly.positionals.return_type);
658 const asm_source = try self.resolveConstString(func, assembly.positionals.asm_source);
659 const output = if (assembly.kw_args.output) |o| try self.resolveConstString(func, o) else null;
735 fn analyzeInstAsm(self: *Analyze, block: ?*Block, assembly: *text.Inst.Asm) InnerError!*Inst {
736 const return_type = try self.resolveType(block, assembly.positionals.return_type);
737 const asm_source = try self.resolveConstString(block, assembly.positionals.asm_source);
738 const output = if (assembly.kw_args.output) |o| try self.resolveConstString(block, o) else null;
660739
661740 const inputs = try self.arena.allocator.alloc([]const u8, assembly.kw_args.inputs.len);
662741 const clobbers = try self.arena.allocator.alloc([]const u8, assembly.kw_args.clobbers.len);
663742 const args = try self.arena.allocator.alloc(*Inst, assembly.kw_args.args.len);
664743
665744 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]);
667746 }
668747 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]);
670749 }
671750 for (args) |*elem, i| {
672 const arg = try self.resolveInst(func, assembly.kw_args.args[i]);
673 elem.* = try self.coerce(func, Type.initTag(.usize), arg);
751 const arg = try self.resolveInst(block, assembly.kw_args.args[i]);
752 elem.* = try self.coerce(block, Type.initTag(.usize), arg);
674753 }
675754
676 const f = try self.requireFunctionBody(func, assembly.base.src);
677 return self.addNewInstArgs(f, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){
755 const b = try self.requireRuntimeBlock(block, assembly.base.src);
756 return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){
678757 .asm_source = asm_source,
679758 .is_volatile = assembly.kw_args.@"volatile",
680759 .output = output,
......@@ -684,19 +763,350 @@ const Analyze = struct {
684763 });
685764 }
686765
687 fn analyzeInstUnreachable(self: *Analyze, func: ?*Fn, unreach: *text.Inst.Unreachable) InnerError!*Inst {
688 const f = try self.requireFunctionBody(func, unreach.base.src);
689 return self.addNewInstArgs(f, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {});
766 fn analyzeInstCmp(self: *Analyze, block: ?*Block, inst: *text.Inst.Cmp) InnerError!*Inst {
767 const lhs = try self.resolveInst(block, inst.positionals.lhs);
768 const rhs = try self.resolveInst(block, inst.positionals.rhs);
769 const op = inst.positionals.op;
770
771 const is_equality_cmp = switch (op) {
772 .eq, .neq => true,
773 else => false,
774 };
775 const lhs_ty_tag = lhs.ty.zigTypeTag();
776 const rhs_ty_tag = rhs.ty.zigTypeTag();
777 if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) {
778 // null == null, null != null
779 return self.constBool(inst.base.src, op == .eq);
780 } else if (is_equality_cmp and
781 ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or
782 rhs_ty_tag == .Null and lhs_ty_tag == .Optional))
783 {
784 // comparing null with optionals
785 const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs;
786 if (opt_operand.value()) |opt_val| {
787 const is_null = opt_val.isNull();
788 return self.constBool(inst.base.src, if (op == .eq) is_null else !is_null);
789 }
790 const b = try self.requireRuntimeBlock(block, inst.base.src);
791 switch (op) {
792 .eq => return self.addNewInstArgs(
793 b,
794 inst.base.src,
795 Type.initTag(.bool),
796 Inst.IsNull,
797 Inst.Args(Inst.IsNull){ .operand = opt_operand },
798 ),
799 .neq => return self.addNewInstArgs(
800 b,
801 inst.base.src,
802 Type.initTag(.bool),
803 Inst.IsNonNull,
804 Inst.Args(Inst.IsNonNull){ .operand = opt_operand },
805 ),
806 else => unreachable,
807 }
808 } else if (is_equality_cmp and
809 ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr())))
810 {
811 return self.fail(inst.base.src, "TODO implement C pointer cmp", .{});
812 } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) {
813 const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty;
814 return self.fail(inst.base.src, "comparison of '{}' with null", .{non_null_type});
815 } else if (is_equality_cmp and
816 ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or
817 (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union)))
818 {
819 return self.fail(inst.base.src, "TODO implement equality comparison between a union's tag value and an enum literal", .{});
820 } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) {
821 if (!is_equality_cmp) {
822 return self.fail(inst.base.src, "{} operator not allowed for errors", .{@tagName(op)});
823 }
824 return self.fail(inst.base.src, "TODO implement equality comparison between errors", .{});
825 } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) {
826 // This operation allows any combination of integer and float types, regardless of the
827 // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
828 // numeric types.
829 return self.cmpNumeric(block, inst.base.src, lhs, rhs, op);
830 }
831 return self.fail(inst.base.src, "TODO implement more cmp analysis", .{});
832 }
833
834 fn analyzeInstIsNull(self: *Analyze, block: ?*Block, inst: *text.Inst.IsNull) InnerError!*Inst {
835 const operand = try self.resolveInst(block, inst.positionals.operand);
836 return self.analyzeIsNull(block, inst.base.src, operand, true);
837 }
838
839 fn analyzeInstIsNonNull(self: *Analyze, block: ?*Block, inst: *text.Inst.IsNonNull) InnerError!*Inst {
840 const operand = try self.resolveInst(block, inst.positionals.operand);
841 return self.analyzeIsNull(block, inst.base.src, operand, false);
842 }
843
844 fn analyzeInstCondBr(self: *Analyze, block: ?*Block, inst: *text.Inst.CondBr) InnerError!*Inst {
845 const uncasted_cond = try self.resolveInst(block, inst.positionals.condition);
846 const cond = try self.coerce(block, Type.initTag(.bool), uncasted_cond);
847
848 if (try self.resolveDefinedValue(cond)) |cond_val| {
849 const body = if (cond_val.toBool()) &inst.positionals.true_body else &inst.positionals.false_body;
850 try self.analyzeBody(block, body.*);
851 return self.constVoid(inst.base.src);
852 }
853
854 const parent_block = try self.requireRuntimeBlock(block, inst.base.src);
855
856 var true_block: Block = .{
857 .func = parent_block.func,
858 .instructions = std.ArrayList(*Inst).init(self.allocator),
859 };
860 defer true_block.instructions.deinit();
861 try self.analyzeBody(&true_block, inst.positionals.true_body);
862
863 var false_block: Block = .{
864 .func = parent_block.func,
865 .instructions = std.ArrayList(*Inst).init(self.allocator),
866 };
867 defer false_block.instructions.deinit();
868 try self.analyzeBody(&false_block, inst.positionals.false_body);
869
870 // Copy the instruction pointers to the arena memory
871 const true_instructions = try self.arena.allocator.alloc(*Inst, true_block.instructions.items.len);
872 const false_instructions = try self.arena.allocator.alloc(*Inst, false_block.instructions.items.len);
873
874 mem.copy(*Inst, true_instructions, true_block.instructions.items);
875 mem.copy(*Inst, false_instructions, false_block.instructions.items);
876
877 return self.addNewInstArgs(parent_block, inst.base.src, Type.initTag(.void), Inst.CondBr, Inst.Args(Inst.CondBr){
878 .condition = cond,
879 .true_body = .{ .instructions = true_instructions },
880 .false_body = .{ .instructions = false_instructions },
881 });
882 }
883
884 fn analyzeInstUnreachable(self: *Analyze, block: ?*Block, unreach: *text.Inst.Unreachable) InnerError!*Inst {
885 const b = try self.requireRuntimeBlock(block, unreach.base.src);
886 return self.addNewInstArgs(b, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {});
887 }
888
889 fn analyzeBody(self: *Analyze, block: ?*Block, body: text.Module.Body) !void {
890 for (body.instructions) |src_inst| {
891 const new_inst = self.analyzeInst(block, src_inst) catch |err| {
892 if (block) |b| {
893 self.fns.items[b.func.fn_index].analysis_status = .failure;
894 try b.func.inst_table.putNoClobber(src_inst, .{ .ptr = null });
895 }
896 return err;
897 };
898 if (block) |b| try b.func.inst_table.putNoClobber(src_inst, .{ .ptr = new_inst });
899 }
900 }
901
902 fn analyzeIsNull(
903 self: *Analyze,
904 block: ?*Block,
905 src: usize,
906 operand: *Inst,
907 invert_logic: bool,
908 ) InnerError!*Inst {
909 return self.fail(src, "TODO implement analysis of isnull and isnotnull", .{});
910 }
911
912 /// Asserts that lhs and rhs types are both numeric.
913 fn cmpNumeric(
914 self: *Analyze,
915 block: ?*Block,
916 src: usize,
917 lhs: *Inst,
918 rhs: *Inst,
919 op: std.math.CompareOperator,
920 ) !*Inst {
921 assert(lhs.ty.isNumeric());
922 assert(rhs.ty.isNumeric());
923
924 const lhs_ty_tag = lhs.ty.zigTypeTag();
925 const rhs_ty_tag = rhs.ty.zigTypeTag();
926
927 if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) {
928 if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) {
929 return self.fail(src, "vector length mismatch: {} and {}", .{
930 lhs.ty.arrayLen(),
931 rhs.ty.arrayLen(),
932 });
933 }
934 return self.fail(src, "TODO implement support for vectors in cmpNumeric", .{});
935 } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) {
936 return self.fail(src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
937 lhs.ty,
938 rhs.ty,
939 });
940 }
941
942 if (lhs.value()) |lhs_val| {
943 if (rhs.value()) |rhs_val| {
944 return self.constBool(src, Value.compare(lhs_val, op, rhs_val));
945 }
946 }
947
948 // TODO handle comparisons against lazy zero values
949 // Some values can be compared against zero without being runtime known or without forcing
950 // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to
951 // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout
952 // of this function if we don't need to.
953
954 // It must be a runtime comparison.
955 const b = try self.requireRuntimeBlock(block, src);
956 // For floats, emit a float comparison instruction.
957 const lhs_is_float = switch (lhs_ty_tag) {
958 .Float, .ComptimeFloat => true,
959 else => false,
960 };
961 const rhs_is_float = switch (rhs_ty_tag) {
962 .Float, .ComptimeFloat => true,
963 else => false,
964 };
965 if (lhs_is_float and rhs_is_float) {
966 // Implicit cast the smaller one to the larger one.
967 const dest_type = x: {
968 if (lhs_ty_tag == .ComptimeFloat) {
969 break :x rhs.ty;
970 } else if (rhs_ty_tag == .ComptimeFloat) {
971 break :x lhs.ty;
972 }
973 if (lhs.ty.floatBits(self.target) >= rhs.ty.floatBits(self.target)) {
974 break :x lhs.ty;
975 } else {
976 break :x rhs.ty;
977 }
978 };
979 const casted_lhs = try self.coerce(block, dest_type, lhs);
980 const casted_rhs = try self.coerce(block, dest_type, rhs);
981 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){
982 .lhs = casted_lhs,
983 .rhs = casted_rhs,
984 .op = op,
985 });
986 }
987 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
988 // For mixed signed and unsigned integers, implicit cast both operands to a signed
989 // integer with + 1 bit.
990 // For mixed floats and integers, extract the integer part from the float, cast that to
991 // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
992 // add/subtract 1.
993 const lhs_is_signed = if (lhs.value()) |lhs_val|
994 lhs_val.compareWithZero(.lt)
995 else
996 (lhs.ty.isFloat() or lhs.ty.isSignedInt());
997 const rhs_is_signed = if (rhs.value()) |rhs_val|
998 rhs_val.compareWithZero(.lt)
999 else
1000 (rhs.ty.isFloat() or rhs.ty.isSignedInt());
1001 const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
1002
1003 var dest_float_type: ?Type = null;
1004
1005 var lhs_bits: usize = undefined;
1006 if (lhs.value()) |lhs_val| {
1007 if (lhs_val.isUndef())
1008 return self.constUndef(src, Type.initTag(.bool));
1009 const is_unsigned = if (lhs_is_float) x: {
1010 var bigint_space: Value.BigIntSpace = undefined;
1011 var bigint = lhs_val.toBigInt(&bigint_space);
1012 const zcmp = lhs_val.orderAgainstZero();
1013 if (lhs_val.floatHasFraction()) {
1014 switch (op) {
1015 .eq => return self.constBool(src, false),
1016 .neq => return self.constBool(src, true),
1017 else => {},
1018 }
1019 if (zcmp == .lt) {
1020 try bigint.addScalar(bigint, -1);
1021 } else {
1022 try bigint.addScalar(bigint, 1);
1023 }
1024 }
1025 lhs_bits = bigint.bitCountTwosComp();
1026 break :x (zcmp != .lt);
1027 } else x: {
1028 lhs_bits = lhs_val.intBitCountTwosComp();
1029 break :x (lhs_val.orderAgainstZero() != .lt);
1030 };
1031 lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
1032 } else if (lhs_is_float) {
1033 dest_float_type = lhs.ty;
1034 } else {
1035 const int_info = lhs.ty.intInfo(self.target);
1036 lhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
1037 }
1038
1039 var rhs_bits: usize = undefined;
1040 if (rhs.value()) |rhs_val| {
1041 if (rhs_val.isUndef())
1042 return self.constUndef(src, Type.initTag(.bool));
1043 const is_unsigned = if (rhs_is_float) x: {
1044 var bigint_space: Value.BigIntSpace = undefined;
1045 var bigint = rhs_val.toBigInt(&bigint_space);
1046 const zcmp = rhs_val.orderAgainstZero();
1047 if (rhs_val.floatHasFraction()) {
1048 switch (op) {
1049 .eq => return self.constBool(src, false),
1050 .neq => return self.constBool(src, true),
1051 else => {},
1052 }
1053 if (zcmp == .lt) {
1054 try bigint.addScalar(bigint, -1);
1055 } else {
1056 try bigint.addScalar(bigint, 1);
1057 }
1058 }
1059 rhs_bits = bigint.bitCountTwosComp();
1060 break :x (zcmp != .lt);
1061 } else x: {
1062 rhs_bits = rhs_val.intBitCountTwosComp();
1063 break :x (rhs_val.orderAgainstZero() != .lt);
1064 };
1065 rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
1066 } else if (rhs_is_float) {
1067 dest_float_type = rhs.ty;
1068 } else {
1069 const int_info = rhs.ty.intInfo(self.target);
1070 rhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed);
1071 }
1072
1073 const dest_type = if (dest_float_type) |ft| ft else blk: {
1074 const max_bits = std.math.max(lhs_bits, rhs_bits);
1075 const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) {
1076 error.Overflow => return self.fail(src, "{} exceeds maximum integer bit count", .{max_bits}),
1077 };
1078 break :blk try self.makeIntType(dest_int_is_signed, casted_bits);
1079 };
1080 const casted_lhs = try self.coerce(block, dest_type, lhs);
1081 const casted_rhs = try self.coerce(block, dest_type, lhs);
1082
1083 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){
1084 .lhs = casted_lhs,
1085 .rhs = casted_rhs,
1086 .op = op,
1087 });
1088 }
1089
1090 fn makeIntType(self: *Analyze, signed: bool, bits: u16) !Type {
1091 if (signed) {
1092 const int_payload = try self.arena.allocator.create(Type.Payload.IntSigned);
1093 int_payload.* = .{ .bits = bits };
1094 return Type.initPayload(&int_payload.base);
1095 } else {
1096 const int_payload = try self.arena.allocator.create(Type.Payload.IntUnsigned);
1097 int_payload.* = .{ .bits = bits };
1098 return Type.initPayload(&int_payload.base);
1099 }
6901100 }
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 {
6931103 // If the types are the same, we can return the operand.
6941104 if (dest_type.eql(inst.ty))
6951105 return inst;
6961106
6971107 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
6981108 if (in_memory_result == .ok) {
699 return self.bitcast(func, dest_type, inst);
1109 return self.bitcast(block, dest_type, inst);
7001110 }
7011111
7021112 // *[N]T to []T
......@@ -740,14 +1150,14 @@ const Analyze = struct {
7401150 return self.fail(inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type });
7411151 }
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 {
7441154 if (inst.value()) |val| {
7451155 // Keep the comptime Value representation; take the new type.
7461156 return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
7471157 }
7481158 // TODO validate the type size and other compile errors
749 const f = try self.requireFunctionBody(func, inst.src);
750 return self.addNewInstArgs(f, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst });
1159 const b = try self.requireRuntimeBlock(block, inst.src);
1160 return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst });
7511161 }
7521162
7531163 fn coerceArrayPtrToSlice(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
......@@ -831,17 +1241,31 @@ pub fn main() anyerror!void {
8311241 try bos.flush();
8321242 }
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
8341258 const link = @import("link.zig");
835 var result = try link.updateExecutableFilePath(allocator, analyzed_module, std.fs.cwd(), "a.out");
836 defer result.deinit(allocator);
837 if (result.errors.len != 0) {
838 for (result.errors) |err_msg| {
839 const loc = std.zig.findLineColumn(source, err_msg.byte_offset);
840 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
841 }
842 if (debug_error_trace) return error.LinkFailure;
843 std.process.exit(1);
844 }
1259 //var result = try link.updateExecutableFilePath(allocator, analyzed_module, std.fs.cwd(), "a.out");
1260 //defer result.deinit(allocator);
1261 //if (result.errors.len != 0) {
1262 // for (result.errors) |err_msg| {
1263 // const loc = std.zig.findLineColumn(source, err_msg.byte_offset);
1264 // std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
1265 // }
1266 // if (debug_error_trace) return error.LinkFailure;
1267 // std.process.exit(1);
1268 //}
8451269}
8461270
8471271// Performance optimization ideas:
src-self-hosted/ir/text.zig+218-90
......@@ -34,6 +34,10 @@ pub const Inst = struct {
3434 bitcast,
3535 elemptr,
3636 add,
37 cmp,
38 condbr,
39 isnull,
40 isnonnull,
3741 };
3842
3943 pub fn TagToType(tag: Tag) type {
......@@ -54,6 +58,10 @@ pub const Inst = struct {
5458 .bitcast => BitCast,
5559 .elemptr => ElemPtr,
5660 .add => Add,
61 .cmp => Cmp,
62 .condbr => CondBr,
63 .isnull => IsNull,
64 .isnonnull => IsNonNull,
5765 };
5866 }
5967
......@@ -157,13 +165,9 @@ pub const Inst = struct {
157165
158166 positionals: struct {
159167 fn_type: *Inst,
160 body: Body,
168 body: Module.Body,
161169 },
162170 kw_args: struct {},
163
164 pub const Body = struct {
165 instructions: []*Inst,
166 };
167171 };
168172
169173 pub const Export = struct {
......@@ -297,6 +301,50 @@ pub const Inst = struct {
297301 },
298302 kw_args: struct {},
299303 };
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 };
300348};
301349
302350pub const ErrorMsg = struct {
......@@ -309,6 +357,10 @@ pub const Module = struct {
309357 errors: []ErrorMsg,
310358 arena: std.heap.ArenaAllocator,
311359
360 pub const Body = struct {
361 instructions: []*Inst,
362 };
363
312364 pub fn deinit(self: *Module, allocator: *Allocator) void {
313365 allocator.free(self.decls);
314366 allocator.free(self.errors);
......@@ -321,7 +373,7 @@ pub const Module = struct {
321373 self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {};
322374 }
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
326378 /// The allocator is used for temporary storage, but this function always returns
327379 /// with no resources allocated.
......@@ -373,6 +425,10 @@ pub const Module = struct {
373425 .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, decl, inst_table),
374426 .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, decl, inst_table),
375427 .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),
376432 }
377433 }
378434
......@@ -432,7 +488,7 @@ pub const Module = struct {
432488 }
433489 try stream.writeByte(']');
434490 },
435 Inst.Fn.Body => {
491 Module.Body => {
436492 try stream.writeAll("{\n");
437493 for (param.instructions) |inst, i| {
438494 try stream.print(" %{} ", .{i});
......@@ -497,7 +553,7 @@ const Parser = struct {
497553 name_map: std.StringHashMap(usize),
498554 };
499555
500 fn parseBody(self: *Parser) !Inst.Fn.Body {
556 fn parseBody(self: *Parser) !Module.Body {
501557 var body_context = Body{
502558 .instructions = std.ArrayList(*Inst).init(self.allocator),
503559 .name_map = std.StringHashMap(usize).init(self.allocator),
......@@ -535,7 +591,7 @@ const Parser = struct {
535591 // Move the instructions to the arena
536592 const instrs = try self.arena.allocator.alloc(*Inst, body_context.instructions.items.len);
537593 mem.copy(*Inst, instrs, body_context.instructions.items);
538 return Inst.Fn.Body{ .instructions = instrs };
594 return Module.Body{ .instructions = instrs };
539595 }
540596
541597 fn parseStringLiteral(self: *Parser) ![]u8 {
......@@ -754,7 +810,7 @@ const Parser = struct {
754810 };
755811 }
756812 switch (T) {
757 Inst.Fn.Body => return parseBody(self),
813 Module.Body => return parseBody(self),
758814 bool => {
759815 const bool_value = switch (self.source[self.i]) {
760816 '0' => false,
......@@ -880,11 +936,12 @@ const EmitZIR = struct {
880936 }
881937
882938 fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst {
939 const big_int_space = try self.arena.allocator.create(Value.BigIntSpace);
883940 const int_inst = try self.arena.allocator.create(Inst.Int);
884941 int_inst.* = .{
885942 .base = .{ .src = src, .tag = Inst.Int.base_tag },
886943 .positionals = .{
887 .int = try val.toBigInt(&self.arena.allocator),
944 .int = val.toBigInt(big_int_space),
888945 },
889946 .kw_args = .{},
890947 };
......@@ -939,85 +996,7 @@ const EmitZIR = struct {
939996 var instructions = std.ArrayList(*Inst).init(self.allocator);
940997 defer instructions.deinit();
941998
942 for (module_fn.body) |inst| {
943 const new_inst = switch (inst.tag) {
944 .unreach => blk: {
945 const unreach_inst = try self.arena.allocator.create(Inst.Unreachable);
946 unreach_inst.* = .{
947 .base = .{ .src = inst.src, .tag = Inst.Unreachable.base_tag },
948 .positionals = .{},
949 .kw_args = .{},
950 };
951 break :blk &unreach_inst.base;
952 },
953 .constant => unreachable, // excluded from function bodies
954 .assembly => blk: {
955 const old_inst = inst.cast(ir.Inst.Assembly).?;
956 const new_inst = try self.arena.allocator.create(Inst.Asm);
957
958 const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len);
959 for (inputs) |*elem, i| {
960 elem.* = try self.emitStringLiteral(inst.src, old_inst.args.inputs[i]);
961 }
962
963 const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len);
964 for (clobbers) |*elem, i| {
965 elem.* = try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i]);
966 }
967
968 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
969 for (args) |*elem, i| {
970 elem.* = try self.resolveInst(&inst_table, old_inst.args.args[i]);
971 }
972
973 new_inst.* = .{
974 .base = .{ .src = inst.src, .tag = Inst.Asm.base_tag },
975 .positionals = .{
976 .asm_source = try self.emitStringLiteral(inst.src, old_inst.args.asm_source),
977 .return_type = try self.emitType(inst.src, inst.ty),
978 },
979 .kw_args = .{
980 .@"volatile" = old_inst.args.is_volatile,
981 .output = if (old_inst.args.output) |o|
982 try self.emitStringLiteral(inst.src, o)
983 else
984 null,
985 .inputs = inputs,
986 .clobbers = clobbers,
987 .args = args,
988 },
989 };
990 break :blk &new_inst.base;
991 },
992 .ptrtoint => blk: {
993 const old_inst = inst.cast(ir.Inst.PtrToInt).?;
994 const new_inst = try self.arena.allocator.create(Inst.PtrToInt);
995 new_inst.* = .{
996 .base = .{ .src = inst.src, .tag = Inst.PtrToInt.base_tag },
997 .positionals = .{
998 .ptr = try self.resolveInst(&inst_table, old_inst.args.ptr),
999 },
1000 .kw_args = .{},
1001 };
1002 break :blk &new_inst.base;
1003 },
1004 .bitcast => blk: {
1005 const old_inst = inst.cast(ir.Inst.BitCast).?;
1006 const new_inst = try self.arena.allocator.create(Inst.BitCast);
1007 new_inst.* = .{
1008 .base = .{ .src = inst.src, .tag = Inst.BitCast.base_tag },
1009 .positionals = .{
1010 .dest_type = try self.emitType(inst.src, inst.ty),
1011 .operand = try self.resolveInst(&inst_table, old_inst.args.operand),
1012 },
1013 .kw_args = .{},
1014 };
1015 break :blk &new_inst.base;
1016 },
1017 };
1018 try instructions.append(new_inst);
1019 try inst_table.putNoClobber(inst, new_inst);
1020 }
999 try self.emitBody(module_fn.body, &inst_table, &instructions);
10211000
10221001 const fn_type = try self.emitType(src, module_fn.fn_type);
10231002
......@@ -1039,6 +1018,155 @@ const EmitZIR = struct {
10391018 }
10401019 }
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
10421170 fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst {
10431171 switch (ty.tag()) {
10441172 .isize => return self.emitPrimitiveType(src, .isize),
src-self-hosted/type.zig+455-204
......@@ -20,35 +20,37 @@ pub const Type = extern union {
2020
2121 pub fn zigTypeTag(self: Type) std.builtin.TypeId {
2222 switch (self.tag()) {
23 .@"u8",
24 .@"i8",
25 .@"isize",
26 .@"usize",
27 .@"c_short",
28 .@"c_ushort",
29 .@"c_int",
30 .@"c_uint",
31 .@"c_long",
32 .@"c_ulong",
33 .@"c_longlong",
34 .@"c_ulonglong",
35 .@"c_longdouble",
23 .u8,
24 .i8,
25 .isize,
26 .usize,
27 .c_short,
28 .c_ushort,
29 .c_int,
30 .c_uint,
31 .c_long,
32 .c_ulong,
33 .c_longlong,
34 .c_ulonglong,
35 .c_longdouble,
36 .int_signed,
37 .int_unsigned,
3638 => return .Int,
3739
38 .@"f16",
39 .@"f32",
40 .@"f64",
41 .@"f128",
40 .f16,
41 .f32,
42 .f64,
43 .f128,
4244 => return .Float,
4345
44 .@"c_void" => return .Opaque,
45 .@"bool" => return .Bool,
46 .@"void" => return .Void,
47 .@"type" => return .Type,
48 .@"anyerror" => return .ErrorSet,
49 .@"comptime_int" => return .ComptimeInt,
50 .@"comptime_float" => return .ComptimeFloat,
51 .@"noreturn" => return .NoReturn,
46 .c_void => return .Opaque,
47 .bool => return .Bool,
48 .void => return .Void,
49 .type => return .Type,
50 .anyerror => return .ErrorSet,
51 .comptime_int => return .ComptimeInt,
52 .comptime_float => return .ComptimeFloat,
53 .noreturn => return .NoReturn,
5254
5355 .fn_naked_noreturn_no_args => return .Fn,
5456
......@@ -153,31 +155,31 @@ pub const Type = extern union {
153155 while (true) {
154156 const t = ty.tag();
155157 switch (t) {
156 .@"u8",
157 .@"i8",
158 .@"isize",
159 .@"usize",
160 .@"c_short",
161 .@"c_ushort",
162 .@"c_int",
163 .@"c_uint",
164 .@"c_long",
165 .@"c_ulong",
166 .@"c_longlong",
167 .@"c_ulonglong",
168 .@"c_longdouble",
169 .@"c_void",
170 .@"f16",
171 .@"f32",
172 .@"f64",
173 .@"f128",
174 .@"bool",
175 .@"void",
176 .@"type",
177 .@"anyerror",
178 .@"comptime_int",
179 .@"comptime_float",
180 .@"noreturn",
158 .u8,
159 .i8,
160 .isize,
161 .usize,
162 .c_short,
163 .c_ushort,
164 .c_int,
165 .c_uint,
166 .c_long,
167 .c_ulong,
168 .c_longlong,
169 .c_ulonglong,
170 .c_longdouble,
171 .c_void,
172 .f16,
173 .f32,
174 .f64,
175 .f128,
176 .bool,
177 .void,
178 .type,
179 .anyerror,
180 .comptime_int,
181 .comptime_float,
182 .noreturn,
181183 => return out_stream.writeAll(@tagName(t)),
182184
183185 .const_slice_u8 => return out_stream.writeAll("[]const u8"),
......@@ -200,6 +202,14 @@ pub const Type = extern union {
200202 ty = payload.pointee_type;
201203 continue;
202204 },
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 },
203213 }
204214 unreachable;
205215 }
......@@ -207,31 +217,31 @@ pub const Type = extern union {
207217
208218 pub fn toValue(self: Type, allocator: *Allocator) Allocator.Error!Value {
209219 switch (self.tag()) {
210 .@"u8" => return Value.initTag(.u8_type),
211 .@"i8" => return Value.initTag(.i8_type),
212 .@"isize" => return Value.initTag(.isize_type),
213 .@"usize" => return Value.initTag(.usize_type),
214 .@"c_short" => return Value.initTag(.c_short_type),
215 .@"c_ushort" => return Value.initTag(.c_ushort_type),
216 .@"c_int" => return Value.initTag(.c_int_type),
217 .@"c_uint" => return Value.initTag(.c_uint_type),
218 .@"c_long" => return Value.initTag(.c_long_type),
219 .@"c_ulong" => return Value.initTag(.c_ulong_type),
220 .@"c_longlong" => return Value.initTag(.c_longlong_type),
221 .@"c_ulonglong" => return Value.initTag(.c_ulonglong_type),
222 .@"c_longdouble" => return Value.initTag(.c_longdouble_type),
223 .@"c_void" => return Value.initTag(.c_void_type),
224 .@"f16" => return Value.initTag(.f16_type),
225 .@"f32" => return Value.initTag(.f32_type),
226 .@"f64" => return Value.initTag(.f64_type),
227 .@"f128" => return Value.initTag(.f128_type),
228 .@"bool" => return Value.initTag(.bool_type),
229 .@"void" => return Value.initTag(.void_type),
230 .@"type" => return Value.initTag(.type_type),
231 .@"anyerror" => return Value.initTag(.anyerror_type),
232 .@"comptime_int" => return Value.initTag(.comptime_int_type),
233 .@"comptime_float" => return Value.initTag(.comptime_float_type),
234 .@"noreturn" => return Value.initTag(.noreturn_type),
220 .u8 => return Value.initTag(.u8_type),
221 .i8 => return Value.initTag(.i8_type),
222 .isize => return Value.initTag(.isize_type),
223 .usize => return Value.initTag(.usize_type),
224 .c_short => return Value.initTag(.c_short_type),
225 .c_ushort => return Value.initTag(.c_ushort_type),
226 .c_int => return Value.initTag(.c_int_type),
227 .c_uint => return Value.initTag(.c_uint_type),
228 .c_long => return Value.initTag(.c_long_type),
229 .c_ulong => return Value.initTag(.c_ulong_type),
230 .c_longlong => return Value.initTag(.c_longlong_type),
231 .c_ulonglong => return Value.initTag(.c_ulonglong_type),
232 .c_longdouble => return Value.initTag(.c_longdouble_type),
233 .c_void => return Value.initTag(.c_void_type),
234 .f16 => return Value.initTag(.f16_type),
235 .f32 => return Value.initTag(.f32_type),
236 .f64 => return Value.initTag(.f64_type),
237 .f128 => return Value.initTag(.f128_type),
238 .bool => return Value.initTag(.bool_type),
239 .void => return Value.initTag(.void_type),
240 .type => return Value.initTag(.type_type),
241 .anyerror => return Value.initTag(.anyerror_type),
242 .comptime_int => return Value.initTag(.comptime_int_type),
243 .comptime_float => return Value.initTag(.comptime_float_type),
244 .noreturn => return Value.initTag(.noreturn_type),
235245 .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type),
236246 .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type),
237247 .const_slice_u8 => return Value.initTag(.const_slice_u8_type),
......@@ -245,35 +255,37 @@ pub const Type = extern union {
245255
246256 pub fn isSinglePointer(self: Type) bool {
247257 return switch (self.tag()) {
248 .@"u8",
249 .@"i8",
250 .@"isize",
251 .@"usize",
252 .@"c_short",
253 .@"c_ushort",
254 .@"c_int",
255 .@"c_uint",
256 .@"c_long",
257 .@"c_ulong",
258 .@"c_longlong",
259 .@"c_ulonglong",
260 .@"c_longdouble",
261 .@"f16",
262 .@"f32",
263 .@"f64",
264 .@"f128",
265 .@"c_void",
266 .@"bool",
267 .@"void",
268 .@"type",
269 .@"anyerror",
270 .@"comptime_int",
271 .@"comptime_float",
272 .@"noreturn",
258 .u8,
259 .i8,
260 .isize,
261 .usize,
262 .c_short,
263 .c_ushort,
264 .c_int,
265 .c_uint,
266 .c_long,
267 .c_ulong,
268 .c_longlong,
269 .c_ulonglong,
270 .c_longdouble,
271 .f16,
272 .f32,
273 .f64,
274 .f128,
275 .c_void,
276 .bool,
277 .void,
278 .type,
279 .anyerror,
280 .comptime_int,
281 .comptime_float,
282 .noreturn,
273283 .array,
274284 .array_u8_sentinel_0,
275285 .const_slice_u8,
276286 .fn_naked_noreturn_no_args,
287 .int_unsigned,
288 .int_signed,
277289 => false,
278290
279291 .single_const_pointer,
......@@ -284,36 +296,38 @@ pub const Type = extern union {
284296
285297 pub fn isSlice(self: Type) bool {
286298 return switch (self.tag()) {
287 .@"u8",
288 .@"i8",
289 .@"isize",
290 .@"usize",
291 .@"c_short",
292 .@"c_ushort",
293 .@"c_int",
294 .@"c_uint",
295 .@"c_long",
296 .@"c_ulong",
297 .@"c_longlong",
298 .@"c_ulonglong",
299 .@"c_longdouble",
300 .@"f16",
301 .@"f32",
302 .@"f64",
303 .@"f128",
304 .@"c_void",
305 .@"bool",
306 .@"void",
307 .@"type",
308 .@"anyerror",
309 .@"comptime_int",
310 .@"comptime_float",
311 .@"noreturn",
299 .u8,
300 .i8,
301 .isize,
302 .usize,
303 .c_short,
304 .c_ushort,
305 .c_int,
306 .c_uint,
307 .c_long,
308 .c_ulong,
309 .c_longlong,
310 .c_ulonglong,
311 .c_longdouble,
312 .f16,
313 .f32,
314 .f64,
315 .f128,
316 .c_void,
317 .bool,
318 .void,
319 .type,
320 .anyerror,
321 .comptime_int,
322 .comptime_float,
323 .noreturn,
312324 .array,
313325 .array_u8_sentinel_0,
314326 .single_const_pointer,
315327 .single_const_pointer_to_comptime_int,
316328 .fn_naked_noreturn_no_args,
329 .int_unsigned,
330 .int_signed,
317331 => false,
318332
319333 .const_slice_u8 => true,
......@@ -323,34 +337,36 @@ pub const Type = extern union {
323337 /// Asserts the type is a pointer type.
324338 pub fn pointerIsConst(self: Type) bool {
325339 return switch (self.tag()) {
326 .@"u8",
327 .@"i8",
328 .@"isize",
329 .@"usize",
330 .@"c_short",
331 .@"c_ushort",
332 .@"c_int",
333 .@"c_uint",
334 .@"c_long",
335 .@"c_ulong",
336 .@"c_longlong",
337 .@"c_ulonglong",
338 .@"c_longdouble",
339 .@"f16",
340 .@"f32",
341 .@"f64",
342 .@"f128",
343 .@"c_void",
344 .@"bool",
345 .@"void",
346 .@"type",
347 .@"anyerror",
348 .@"comptime_int",
349 .@"comptime_float",
350 .@"noreturn",
340 .u8,
341 .i8,
342 .isize,
343 .usize,
344 .c_short,
345 .c_ushort,
346 .c_int,
347 .c_uint,
348 .c_long,
349 .c_ulong,
350 .c_longlong,
351 .c_ulonglong,
352 .c_longdouble,
353 .f16,
354 .f32,
355 .f64,
356 .f128,
357 .c_void,
358 .bool,
359 .void,
360 .type,
361 .anyerror,
362 .comptime_int,
363 .comptime_float,
364 .noreturn,
351365 .array,
352366 .array_u8_sentinel_0,
353367 .fn_naked_noreturn_no_args,
368 .int_unsigned,
369 .int_signed,
354370 => unreachable,
355371
356372 .single_const_pointer,
......@@ -363,32 +379,34 @@ pub const Type = extern union {
363379 /// Asserts the type is a pointer or array type.
364380 pub fn elemType(self: Type) Type {
365381 return switch (self.tag()) {
366 .@"u8",
367 .@"i8",
368 .@"isize",
369 .@"usize",
370 .@"c_short",
371 .@"c_ushort",
372 .@"c_int",
373 .@"c_uint",
374 .@"c_long",
375 .@"c_ulong",
376 .@"c_longlong",
377 .@"c_ulonglong",
378 .@"c_longdouble",
379 .@"f16",
380 .@"f32",
381 .@"f64",
382 .@"f128",
383 .@"c_void",
384 .@"bool",
385 .@"void",
386 .@"type",
387 .@"anyerror",
388 .@"comptime_int",
389 .@"comptime_float",
390 .@"noreturn",
382 .u8,
383 .i8,
384 .isize,
385 .usize,
386 .c_short,
387 .c_ushort,
388 .c_int,
389 .c_uint,
390 .c_long,
391 .c_ulong,
392 .c_longlong,
393 .c_ulonglong,
394 .c_longdouble,
395 .f16,
396 .f32,
397 .f64,
398 .f128,
399 .c_void,
400 .bool,
401 .void,
402 .type,
403 .anyerror,
404 .comptime_int,
405 .comptime_float,
406 .noreturn,
391407 .fn_naked_noreturn_no_args,
408 .int_unsigned,
409 .int_signed,
392410 => unreachable,
393411
394412 .array => self.cast(Payload.Array).?.elem_type,
......@@ -398,7 +416,7 @@ pub const Type = extern union {
398416 };
399417 }
400418
401 /// Asserts the type is an array.
419 /// Asserts the type is an array or vector.
402420 pub fn arrayLen(self: Type) u64 {
403421 return switch (self.tag()) {
404422 .u8,
......@@ -430,6 +448,8 @@ pub const Type = extern union {
430448 .single_const_pointer,
431449 .single_const_pointer_to_comptime_int,
432450 .const_slice_u8,
451 .int_unsigned,
452 .int_signed,
433453 => unreachable,
434454
435455 .array => self.cast(Payload.Array).?.len,
......@@ -437,22 +457,64 @@ pub const Type = extern union {
437457 };
438458 }
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
440502 /// Asserts the type is a fixed-width integer.
441503 pub fn intInfo(self: Type, target: Target) struct { signed: bool, bits: u16 } {
442504 return switch (self.tag()) {
443 .@"f16",
444 .@"f32",
445 .@"f64",
446 .@"f128",
447 .@"c_longdouble",
448 .@"c_void",
449 .@"bool",
450 .@"void",
451 .@"type",
452 .@"anyerror",
453 .@"comptime_int",
454 .@"comptime_float",
455 .@"noreturn",
505 .f16,
506 .f32,
507 .f64,
508 .f128,
509 .c_longdouble,
510 .c_void,
511 .bool,
512 .void,
513 .type,
514 .anyerror,
515 .comptime_int,
516 .comptime_float,
517 .noreturn,
456518 .fn_naked_noreturn_no_args,
457519 .array,
458520 .single_const_pointer,
......@@ -461,18 +523,46 @@ pub const Type = extern union {
461523 .const_slice_u8,
462524 => unreachable,
463525
464 .@"u8" => .{ .signed = false, .bits = 8 },
465 .@"i8" => .{ .signed = true, .bits = 8 },
466 .@"usize" => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() },
467 .@"isize" => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() },
468 .@"c_short" => .{ .signed = true, .bits = CInteger.short.sizeInBits(target) },
469 .@"c_ushort" => .{ .signed = false, .bits = CInteger.ushort.sizeInBits(target) },
470 .@"c_int" => .{ .signed = true, .bits = CInteger.int.sizeInBits(target) },
471 .@"c_uint" => .{ .signed = false, .bits = CInteger.uint.sizeInBits(target) },
472 .@"c_long" => .{ .signed = true, .bits = CInteger.long.sizeInBits(target) },
473 .@"c_ulong" => .{ .signed = false, .bits = CInteger.ulong.sizeInBits(target) },
474 .@"c_longlong" => .{ .signed = true, .bits = CInteger.longlong.sizeInBits(target) },
475 .@"c_ulonglong" => .{ .signed = false, .bits = CInteger.ulonglong.sizeInBits(target) },
526 .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits },
527 .int_signed => .{ .signed = true, .bits = self.cast(Payload.IntSigned).?.bits },
528 .u8 => .{ .signed = false, .bits = 8 },
529 .i8 => .{ .signed = true, .bits = 8 },
530 .usize => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() },
531 .isize => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() },
532 .c_short => .{ .signed = true, .bits = CType.short.sizeInBits(target) },
533 .c_ushort => .{ .signed = false, .bits = CType.ushort.sizeInBits(target) },
534 .c_int => .{ .signed = true, .bits = CType.int.sizeInBits(target) },
535 .c_uint => .{ .signed = false, .bits = CType.uint.sizeInBits(target) },
536 .c_long => .{ .signed = true, .bits = CType.long.sizeInBits(target) },
537 .c_ulong => .{ .signed = false, .bits = CType.ulong.sizeInBits(target) },
538 .c_longlong => .{ .signed = true, .bits = CType.longlong.sizeInBits(target) },
539 .c_ulonglong => .{ .signed = false, .bits = CType.ulonglong.sizeInBits(target) },
540 };
541 }
542
543 pub fn isFloat(self: Type) bool {
544 return switch (self.tag()) {
545 .f16,
546 .f32,
547 .f64,
548 .f128,
549 .c_longdouble,
550 => true,
551
552 else => false,
553 };
554 }
555
556 /// Asserts the type is a fixed-size float.
557 pub fn floatBits(self: Type, target: Target) u16 {
558 return switch (self.tag()) {
559 .f16 => 16,
560 .f32 => 32,
561 .f64 => 64,
562 .f128 => 128,
563 .c_longdouble => CType.longdouble.sizeInBits(target),
564
565 else => unreachable,
476566 };
477567 }
478568
......@@ -511,6 +601,8 @@ pub const Type = extern union {
511601 .c_ulong,
512602 .c_longlong,
513603 .c_ulonglong,
604 .int_unsigned,
605 .int_signed,
514606 => unreachable,
515607 };
516608 }
......@@ -551,6 +643,8 @@ pub const Type = extern union {
551643 .c_ulong,
552644 .c_longlong,
553645 .c_ulonglong,
646 .int_unsigned,
647 .int_signed,
554648 => unreachable,
555649 }
556650 }
......@@ -590,6 +684,8 @@ pub const Type = extern union {
590684 .c_ulong,
591685 .c_longlong,
592686 .c_ulonglong,
687 .int_unsigned,
688 .int_signed,
593689 => unreachable,
594690 };
595691 }
......@@ -629,10 +725,145 @@ pub const Type = extern union {
629725 .c_ulong,
630726 .c_longlong,
631727 .c_ulonglong,
728 .int_unsigned,
729 .int_signed,
632730 => unreachable,
633731 };
634732 }
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
636867 /// This enum does not directly correspond to `std.builtin.TypeId` because
637868 /// it has extra enum tags in it, as a way of using less memory. For example,
638869 /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
......@@ -674,6 +905,8 @@ pub const Type = extern union {
674905 array_u8_sentinel_0,
675906 array,
676907 single_const_pointer,
908 int_signed,
909 int_unsigned,
677910
678911 pub const last_no_payload_tag = Tag.const_slice_u8;
679912 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
......@@ -700,10 +933,22 @@ pub const Type = extern union {
700933
701934 pointee_type: Type,
702935 };
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 };
703948 };
704949};
705950
706pub const CInteger = enum {
951pub const CType = enum {
707952 short,
708953 ushort,
709954 int,
......@@ -712,8 +957,9 @@ pub const CInteger = enum {
712957 ulong,
713958 longlong,
714959 ulonglong,
960 longdouble,
715961
716 pub fn sizeInBits(self: CInteger, target: Target) u16 {
962 pub fn sizeInBits(self: CType, target: Target) u16 {
717963 const arch = target.cpu.arch;
718964 switch (target.os.tag) {
719965 .freestanding, .other => switch (target.cpu.arch) {
......@@ -729,6 +975,7 @@ pub const CInteger = enum {
729975 .longlong,
730976 .ulonglong,
731977 => return 64,
978 .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
732979 },
733980 else => switch (self) {
734981 .short,
......@@ -743,6 +990,7 @@ pub const CInteger = enum {
743990 .longlong,
744991 .ulonglong,
745992 => return 64,
993 .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
746994 },
747995 },
748996
......@@ -767,6 +1015,7 @@ pub const CInteger = enum {
7671015 .longlong,
7681016 .ulonglong,
7691017 => return 64,
1018 .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
7701019 },
7711020
7721021 .windows, .uefi => switch (self) {
......@@ -781,6 +1030,7 @@ pub const CInteger = enum {
7811030 .longlong,
7821031 .ulonglong,
7831032 => return 64,
1033 .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
7841034 },
7851035
7861036 .ios => switch (self) {
......@@ -795,6 +1045,7 @@ pub const CInteger = enum {
7951045 .longlong,
7961046 .ulonglong,
7971047 => return 64,
1048 .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
7981049 },
7991050
8001051 .ananas,
......@@ -821,7 +1072,7 @@ pub const CInteger = enum {
8211072 .amdpal,
8221073 .hermit,
8231074 .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"),
8251076 }
8261077 }
8271078};
src-self-hosted/value.zig+319-26
......@@ -48,9 +48,10 @@ pub const Value = extern union {
4848 single_const_pointer_to_comptime_int_type,
4949 const_slice_u8_type,
5050
51 undef,
5152 zero,
52 void_value,
53 noreturn_value,
53 the_one_possible_value, // when the type only has one possible value
54 null_value,
5455 bool_true,
5556 bool_false, // See last_no_payload_tag below.
5657 // After this, the tag requires a payload.
......@@ -63,6 +64,7 @@ pub const Value = extern union {
6364 ref,
6465 ref_val,
6566 bytes,
67 repeated, // the value is a value repeated some number of times
6668
6769 pub const last_no_payload_tag = Tag.bool_false;
6870 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
......@@ -135,9 +137,10 @@ pub const Value = extern union {
135137 .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
136138 .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"),
138142 .zero => return out_stream.writeAll("0"),
139 .void_value => return out_stream.writeAll("{}"),
140 .noreturn_value => return out_stream.writeAll("unreachable"),
143 .the_one_possible_value => return out_stream.writeAll("(one possible value)"),
141144 .bool_true => return out_stream.writeAll("true"),
142145 .bool_false => return out_stream.writeAll("false"),
143146 .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream),
......@@ -152,6 +155,10 @@ pub const Value = extern union {
152155 continue;
153156 },
154157 .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 },
155162 };
156163 }
157164
......@@ -198,11 +205,12 @@ pub const Value = extern union {
198205 .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),
199206 .const_slice_u8_type => Type.initTag(.const_slice_u8),
200207
208 .undef,
201209 .zero,
202 .void_value,
203 .noreturn_value,
210 .the_one_possible_value,
204211 .bool_true,
205212 .bool_false,
213 .null_value,
206214 .int_u64,
207215 .int_i64,
208216 .int_big,
......@@ -210,12 +218,13 @@ pub const Value = extern union {
210218 .ref,
211219 .ref_val,
212220 .bytes,
221 .repeated,
213222 => unreachable,
214223 };
215224 }
216225
217226 /// 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 {
219228 switch (self.tag()) {
220229 .ty,
221230 .u8_type,
......@@ -246,20 +255,23 @@ pub const Value = extern union {
246255 .fn_naked_noreturn_no_args_type,
247256 .single_const_pointer_to_comptime_int_type,
248257 .const_slice_u8_type,
249 .void_value,
250 .noreturn_value,
251258 .bool_true,
252259 .bool_false,
260 .null_value,
253261 .function,
254262 .ref,
255263 .ref_val,
256264 .bytes,
265 .undef,
266 .repeated,
257267 => 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),
262 .int_i64 => return BigInt.initSet(allocator, self.cast(Payload.Int_i64).?.int),
273 .int_u64 => return BigInt.initSetFixed(&space.limbs, self.cast(Payload.Int_u64).?.int),
274 .int_i64 => return BigInt.initSetFixed(&space.limbs, self.cast(Payload.Int_i64).?.int),
263275 .int_big => return self.cast(Payload.IntBig).?.big_int,
264276 }
265277 }
......@@ -296,17 +308,20 @@ pub const Value = extern union {
296308 .fn_naked_noreturn_no_args_type,
297309 .single_const_pointer_to_comptime_int_type,
298310 .const_slice_u8_type,
299 .void_value,
300 .noreturn_value,
301311 .bool_true,
302312 .bool_false,
313 .null_value,
303314 .function,
304315 .ref,
305316 .ref_val,
306317 .bytes,
318 .undef,
319 .repeated,
307320 => 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
311326 .int_u64 => return self.cast(Payload.Int_u64).?.int,
312327 .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int),
......@@ -314,6 +329,66 @@ pub const Value = extern union {
314329 }
315330 }
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
317392 /// Asserts the value is an integer, and the destination type is ComptimeInt or Int.
318393 pub fn intFitsInType(self: Value, ty: Type, target: Target) bool {
319394 switch (self.tag()) {
......@@ -346,17 +421,20 @@ pub const Value = extern union {
346421 .fn_naked_noreturn_no_args_type,
347422 .single_const_pointer_to_comptime_int_type,
348423 .const_slice_u8_type,
349 .void_value,
350 .noreturn_value,
351424 .bool_true,
352425 .bool_false,
426 .null_value,
353427 .function,
354428 .ref,
355429 .ref_val,
356430 .bytes,
431 .repeated,
357432 => 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
361439 .int_u64 => switch (ty.zigTypeTag()) {
362440 .Int => {
......@@ -392,9 +470,148 @@ pub const Value = extern union {
392470 }
393471 }
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
395612 /// Asserts the value is a pointer and dereferences it.
396613 pub fn pointerDeref(self: Value) Value {
397 switch (self.tag()) {
614 return switch (self.tag()) {
398615 .ty,
399616 .u8_type,
400617 .i8_type,
......@@ -425,20 +642,22 @@ pub const Value = extern union {
425642 .single_const_pointer_to_comptime_int_type,
426643 .const_slice_u8_type,
427644 .zero,
428 .void_value,
429 .noreturn_value,
430645 .bool_true,
431646 .bool_false,
647 .null_value,
432648 .function,
433649 .int_u64,
434650 .int_i64,
435651 .int_big,
436652 .bytes,
653 .undef,
654 .repeated,
437655 => unreachable,
438656
439 .ref => return self.cast(Payload.Ref).?.cell.contents,
440 .ref_val => return self.cast(Payload.RefVal).?.val,
441 }
657 .the_one_possible_value => Value.initTag(.the_one_possible_value),
658 .ref => self.cast(Payload.Ref).?.cell.contents,
659 .ref_val => self.cast(Payload.RefVal).?.val,
660 };
442661 }
443662
444663 /// Asserts the value is a single-item pointer to an array, or an array,
......@@ -475,14 +694,15 @@ pub const Value = extern union {
475694 .single_const_pointer_to_comptime_int_type,
476695 .const_slice_u8_type,
477696 .zero,
478 .void_value,
479 .noreturn_value,
697 .the_one_possible_value,
480698 .bool_true,
481699 .bool_false,
700 .null_value,
482701 .function,
483702 .int_u64,
484703 .int_i64,
485704 .int_big,
705 .undef,
486706 => unreachable,
487707
488708 .ref => @panic("TODO figure out how MemoryCell works"),
......@@ -493,9 +713,68 @@ pub const Value = extern union {
493713 int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] };
494714 return Value.initPayload(&int_payload.base);
495715 },
716
717 // No matter the index; all the elements are the same!
718 .repeated => return self.cast(Payload.Repeated).?.val,
496719 }
497720 }
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
499778 /// This type is not copyable since it may contain pointers to its inner data.
500779 pub const Payload = struct {
501780 tag: Tag,
......@@ -550,6 +829,20 @@ pub const Value = extern union {
550829 base: Payload = Payload{ .tag = .ty },
551830 ty: Type,
552831 };
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,
553846 };
554847};
555848