authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-14 02:24:12-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-14 02:24:12-07:00
loga92990f99312c946b5e527517a27a67a5a5513c0
tree7362b21717b6a8cf27b2fbf4d04de74b4db7d00f
parent135580c1621513e7cfeed8098c087d1d6941fa97

stage2: implement enough for assert() function to codegen


5 files changed, 192 insertions(+), 36 deletions(-)

src-self-hosted/Module.zig+25-19
...@@ -1358,17 +1358,19 @@ fn astGenInfixOp(self: *Module, scope: *Scope, infix_node: *ast.Node.InfixOp) In...@@ -1358,17 +1358,19 @@ fn astGenInfixOp(self: *Module, scope: *Scope, infix_node: *ast.Node.InfixOp) In
1358 const tree = scope.tree();1358 const tree = scope.tree();
1359 const src = tree.token_locs[infix_node.op_token].start;1359 const src = tree.token_locs[infix_node.op_token].start;
13601360
1361 const op: std.math.CompareOperator = switch (infix_node.op) {
1362 .BangEqual => .neq,
1363 .EqualEqual => .eq,
1364 .GreaterThan => .gt,
1365 .GreaterOrEqual => .gte,
1366 .LessThan => .lt,
1367 .LessOrEqual => .lte,
1368 else => unreachable,
1369 };
1370
1361 return self.addZIRInst(scope, src, zir.Inst.Cmp, .{1371 return self.addZIRInst(scope, src, zir.Inst.Cmp, .{
1362 .lhs = lhs,1372 .lhs = lhs,
1363 .op = @as(std.math.CompareOperator, switch (infix_node.op) {1373 .op = op,
1364 .BangEqual => .neq,
1365 .EqualEqual => .eq,
1366 .GreaterThan => .gt,
1367 .GreaterOrEqual => .gte,
1368 .LessThan => .lt,
1369 .LessOrEqual => .lte,
1370 else => unreachable,
1371 }),
1372 .rhs = rhs,1374 .rhs = rhs,
1373 }, .{});1375 }, .{});
1374 },1376 },
...@@ -1415,11 +1417,13 @@ fn astGenIf(self: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir...@@ -1415,11 +1417,13 @@ fn astGenIf(self: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir
1415 defer then_scope.instructions.deinit(self.gpa);1417 defer then_scope.instructions.deinit(self.gpa);
14161418
1417 const then_result = try self.astGenExpr(&then_scope.base, if_node.body);1419 const then_result = try self.astGenExpr(&then_scope.base, if_node.body);
1418 const then_src = tree.token_locs[if_node.body.lastToken()].start;1420 if (!then_result.tag.isNoReturn()) {
1419 _ = try self.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{1421 const then_src = tree.token_locs[if_node.body.lastToken()].start;
1420 .block = block,1422 _ = try self.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{
1421 .operand = then_result,1423 .block = block,
1422 }, .{});1424 .operand = then_result,
1425 }, .{});
1426 }
1423 condbr.positionals.true_body = .{1427 condbr.positionals.true_body = .{
1424 .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),1428 .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
1425 };1429 };
...@@ -1433,11 +1437,13 @@ fn astGenIf(self: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir...@@ -1433,11 +1437,13 @@ fn astGenIf(self: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir
14331437
1434 if (if_node.@"else") |else_node| {1438 if (if_node.@"else") |else_node| {
1435 const else_result = try self.astGenExpr(&else_scope.base, else_node.body);1439 const else_result = try self.astGenExpr(&else_scope.base, else_node.body);
1436 const else_src = tree.token_locs[else_node.body.lastToken()].start;1440 if (!else_result.tag.isNoReturn()) {
1437 _ = try self.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{1441 const else_src = tree.token_locs[else_node.body.lastToken()].start;
1438 .block = block,1442 _ = try self.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{
1439 .operand = else_result,1443 .block = block,
1440 }, .{});1444 .operand = else_result,
1445 }, .{});
1446 }
1441 } else {1447 } else {
1442 // TODO Optimization opportunity: we can avoid an allocation and a memcpy here1448 // TODO Optimization opportunity: we can avoid an allocation and a memcpy here
1443 // by directly allocating the body for this one instruction.1449 // by directly allocating the body for this one instruction.
src-self-hosted/codegen.zig+70-4
...@@ -415,7 +415,46 @@ const Function = struct {...@@ -415,7 +415,46 @@ const Function = struct {
415 // No side effects, so if it's unreferenced, do nothing.415 // No side effects, so if it's unreferenced, do nothing.
416 if (inst.base.isUnused())416 if (inst.base.isUnused())
417 return MCValue.dead;417 return MCValue.dead;
418 const operand = try self.resolveInst(inst.args.operand);
419 switch (operand) {
420 .dead => unreachable,
421 .unreach => unreachable,
422 .compare_flags_unsigned => |op| return MCValue{
423 .compare_flags_unsigned = switch (op) {
424 .gte => .lt,
425 .gt => .lte,
426 .neq => .eq,
427 .lt => .gte,
428 .lte => .gt,
429 .eq => .neq,
430 },
431 },
432 .compare_flags_signed => |op| return MCValue{
433 .compare_flags_signed = switch (op) {
434 .gte => .lt,
435 .gt => .lte,
436 .neq => .eq,
437 .lt => .gte,
438 .lte => .gt,
439 .eq => .neq,
440 },
441 },
442 else => {},
443 }
444
418 switch (arch) {445 switch (arch) {
446 .x86_64 => {
447 var imm = ir.Inst.Constant{
448 .base = .{
449 .tag = .constant,
450 .deaths = 0,
451 .ty = inst.args.operand.ty,
452 .src = inst.args.operand.src,
453 },
454 .val = Value.initTag(.bool_true),
455 };
456 return try self.genX8664BinMath(&inst.base, inst.args.operand, &imm.base, 6, 0x30);
457 },
419 else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}),458 else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}),
420 }459 }
421 }460 }
...@@ -444,7 +483,7 @@ const Function = struct {...@@ -444,7 +483,7 @@ const Function = struct {
444 }483 }
445 }484 }
446485
447 /// ADD, SUB486 /// ADD, SUB, XOR, OR, AND
448 fn genX8664BinMath(self: *Function, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue {487 fn genX8664BinMath(self: *Function, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue {
449 try self.code.ensureCapacity(self.code.items.len + 8);488 try self.code.ensureCapacity(self.code.items.len + 8);
450489
...@@ -705,7 +744,7 @@ const Function = struct {...@@ -705,7 +744,7 @@ const Function = struct {
705744
706 fn genCondBr(self: *Function, inst: *ir.Inst.CondBr, comptime arch: std.Target.Cpu.Arch) !MCValue {745 fn genCondBr(self: *Function, inst: *ir.Inst.CondBr, comptime arch: std.Target.Cpu.Arch) !MCValue {
707 switch (arch) {746 switch (arch) {
708 .i386, .x86_64 => {747 .x86_64 => {
709 try self.code.ensureCapacity(self.code.items.len + 6);748 try self.code.ensureCapacity(self.code.items.len + 6);
710749
711 const cond = try self.resolveInst(inst.args.condition);750 const cond = try self.resolveInst(inst.args.condition);
...@@ -734,7 +773,20 @@ const Function = struct {...@@ -734,7 +773,20 @@ const Function = struct {
734 };773 };
735 return self.genX86CondBr(inst, opcode, arch);774 return self.genX86CondBr(inst, opcode, arch);
736 },775 },
737 else => return self.fail(inst.base.src, "TODO implement condbr {} when condition not already in the compare flags", .{self.target.cpu.arch}),776 .register => |reg_usize| {
777 const reg = @intToEnum(Reg(arch), @intCast(u8, reg_usize));
778 // test reg, 1
779 // TODO detect al, ax, eax
780 try self.code.ensureCapacity(self.code.items.len + 4);
781 self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });
782 self.code.appendSliceAssumeCapacity(&[_]u8{
783 0xf6,
784 @as(u8, 0xC0) | (0 << 3) | @truncate(u3, reg.id()),
785 0x01,
786 });
787 return self.genX86CondBr(inst, 0x84, arch);
788 },
789 else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {}", .{ self.target.cpu.arch, @tagName(cond) }),
738 }790 }
739 },791 },
740 else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}),792 else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}),
...@@ -892,7 +944,18 @@ const Function = struct {...@@ -892,7 +944,18 @@ const Function = struct {
892 .none => unreachable,944 .none => unreachable,
893 .unreach => unreachable,945 .unreach => unreachable,
894 .compare_flags_unsigned => |op| {946 .compare_flags_unsigned => |op| {
895 return self.fail(src, "TODO set register with compare flags value (unsigned)", .{});947 try self.code.ensureCapacity(self.code.items.len + 3);
948 self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });
949 const opcode: u8 = switch (op) {
950 .gte => 0x93,
951 .gt => 0x97,
952 .neq => 0x95,
953 .lt => 0x92,
954 .lte => 0x96,
955 .eq => 0x94,
956 };
957 const id = @as(u8, reg.id() & 0b111);
958 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode, 0xC0 | id });
896 },959 },
897 .compare_flags_signed => |op| {960 .compare_flags_signed => |op| {
898 return self.fail(src, "TODO set register with compare flags value (signed)", .{});961 return self.fail(src, "TODO set register with compare flags value (signed)", .{});
...@@ -1147,6 +1210,9 @@ const Function = struct {...@@ -1147,6 +1210,9 @@ const Function = struct {
1147 }1210 }
1148 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };1211 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
1149 },1212 },
1213 .Bool => {
1214 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
1215 },
1150 .ComptimeInt => unreachable, // semantic analysis prevents this1216 .ComptimeInt => unreachable, // semantic analysis prevents this
1151 .ComptimeFloat => unreachable, // semantic analysis prevents this1217 .ComptimeFloat => unreachable, // semantic analysis prevents this
1152 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),1218 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),
src-self-hosted/type.zig+17-2
...@@ -163,6 +163,22 @@ pub const Type = extern union {...@@ -163,6 +163,22 @@ pub const Type = extern union {
163 return sentinel_b == null;163 return sentinel_b == null;
164 }164 }
165 },165 },
166 .Fn => {
167 if (!a.fnReturnType().eql(b.fnReturnType()))
168 return false;
169 if (a.fnCallingConvention() != b.fnCallingConvention())
170 return false;
171 const a_param_len = a.fnParamLen();
172 const b_param_len = b.fnParamLen();
173 if (a_param_len != b_param_len)
174 return false;
175 var i: usize = 0;
176 while (i < a_param_len) : (i += 1) {
177 if (!a.fnParamType(i).eql(b.fnParamType(i)))
178 return false;
179 }
180 return true;
181 },
166 .Float,182 .Float,
167 .Struct,183 .Struct,
168 .Optional,184 .Optional,
...@@ -170,14 +186,13 @@ pub const Type = extern union {...@@ -170,14 +186,13 @@ pub const Type = extern union {
170 .ErrorSet,186 .ErrorSet,
171 .Enum,187 .Enum,
172 .Union,188 .Union,
173 .Fn,
174 .BoundFn,189 .BoundFn,
175 .Opaque,190 .Opaque,
176 .Frame,191 .Frame,
177 .AnyFrame,192 .AnyFrame,
178 .Vector,193 .Vector,
179 .EnumLiteral,194 .EnumLiteral,
180 => @panic("TODO implement more Type equality comparison"),195 => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }),
181 }196 }
182 }197 }
183198
src-self-hosted/value.zig+23-11
...@@ -427,8 +427,6 @@ pub const Value = extern union {...@@ -427,8 +427,6 @@ pub const Value = extern union {
427 .fn_ccc_void_no_args_type,427 .fn_ccc_void_no_args_type,
428 .single_const_pointer_to_comptime_int_type,428 .single_const_pointer_to_comptime_int_type,
429 .const_slice_u8_type,429 .const_slice_u8_type,
430 .bool_true,
431 .bool_false,
432 .null_value,430 .null_value,
433 .function,431 .function,
434 .ref_val,432 .ref_val,
...@@ -441,8 +439,11 @@ pub const Value = extern union {...@@ -441,8 +439,11 @@ pub const Value = extern union {
441439
442 .the_one_possible_value, // An integer with one possible value is always zero.440 .the_one_possible_value, // An integer with one possible value is always zero.
443 .zero,441 .zero,
442 .bool_false,
444 => return BigIntMutable.init(&space.limbs, 0).toConst(),443 => return BigIntMutable.init(&space.limbs, 0).toConst(),
445444
445 .bool_true => return BigIntMutable.init(&space.limbs, 1).toConst(),
446
446 .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(),447 .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(),
447 .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(),448 .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(),
448 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt(),449 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt(),
...@@ -493,8 +494,6 @@ pub const Value = extern union {...@@ -493,8 +494,6 @@ pub const Value = extern union {
493 .fn_ccc_void_no_args_type,494 .fn_ccc_void_no_args_type,
494 .single_const_pointer_to_comptime_int_type,495 .single_const_pointer_to_comptime_int_type,
495 .const_slice_u8_type,496 .const_slice_u8_type,
496 .bool_true,
497 .bool_false,
498 .null_value,497 .null_value,
499 .function,498 .function,
500 .ref_val,499 .ref_val,
...@@ -507,8 +506,11 @@ pub const Value = extern union {...@@ -507,8 +506,11 @@ pub const Value = extern union {
507506
508 .zero,507 .zero,
509 .the_one_possible_value, // an integer with one possible value is always zero508 .the_one_possible_value, // an integer with one possible value is always zero
509 .bool_false,
510 => return 0,510 => return 0,
511511
512 .bool_true => return 1,
513
512 .int_u64 => return self.cast(Payload.Int_u64).?.int,514 .int_u64 => return self.cast(Payload.Int_u64).?.int,
513 .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int),515 .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int),
514 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable,516 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable,
...@@ -560,8 +562,6 @@ pub const Value = extern union {...@@ -560,8 +562,6 @@ pub const Value = extern union {
560 .fn_ccc_void_no_args_type,562 .fn_ccc_void_no_args_type,
561 .single_const_pointer_to_comptime_int_type,563 .single_const_pointer_to_comptime_int_type,
562 .const_slice_u8_type,564 .const_slice_u8_type,
563 .bool_true,
564 .bool_false,
565 .null_value,565 .null_value,
566 .function,566 .function,
567 .ref_val,567 .ref_val,
...@@ -574,8 +574,11 @@ pub const Value = extern union {...@@ -574,8 +574,11 @@ pub const Value = extern union {
574574
575 .the_one_possible_value, // an integer with one possible value is always zero575 .the_one_possible_value, // an integer with one possible value is always zero
576 .zero,576 .zero,
577 .bool_false,
577 => return 0,578 => return 0,
578579
580 .bool_true => return 1,
581
579 .int_u64 => {582 .int_u64 => {
580 const x = self.cast(Payload.Int_u64).?.int;583 const x = self.cast(Payload.Int_u64).?.int;
581 if (x == 0) return 0;584 if (x == 0) return 0;
...@@ -632,8 +635,6 @@ pub const Value = extern union {...@@ -632,8 +635,6 @@ pub const Value = extern union {
632 .fn_ccc_void_no_args_type,635 .fn_ccc_void_no_args_type,
633 .single_const_pointer_to_comptime_int_type,636 .single_const_pointer_to_comptime_int_type,
634 .const_slice_u8_type,637 .const_slice_u8_type,
635 .bool_true,
636 .bool_false,
637 .null_value,638 .null_value,
638 .function,639 .function,
639 .ref_val,640 .ref_val,
...@@ -646,8 +647,18 @@ pub const Value = extern union {...@@ -646,8 +647,18 @@ pub const Value = extern union {
646 .zero,647 .zero,
647 .undef,648 .undef,
648 .the_one_possible_value, // an integer with one possible value is always zero649 .the_one_possible_value, // an integer with one possible value is always zero
650 .bool_false,
649 => return true,651 => return true,
650652
653 .bool_true => {
654 const info = ty.intInfo(target);
655 if (info.signed) {
656 return info.bits >= 2;
657 } else {
658 return info.bits >= 1;
659 }
660 },
661
651 .int_u64 => switch (ty.zigTypeTag()) {662 .int_u64 => switch (ty.zigTypeTag()) {
652 .Int => {663 .Int => {
653 const x = self.cast(Payload.Int_u64).?.int;664 const x = self.cast(Payload.Int_u64).?.int;
...@@ -796,8 +807,6 @@ pub const Value = extern union {...@@ -796,8 +807,6 @@ pub const Value = extern union {
796 .fn_ccc_void_no_args_type,807 .fn_ccc_void_no_args_type,
797 .single_const_pointer_to_comptime_int_type,808 .single_const_pointer_to_comptime_int_type,
798 .const_slice_u8_type,809 .const_slice_u8_type,
799 .bool_true,
800 .bool_false,
801 .null_value,810 .null_value,
802 .function,811 .function,
803 .ref_val,812 .ref_val,
...@@ -810,8 +819,11 @@ pub const Value = extern union {...@@ -810,8 +819,11 @@ pub const Value = extern union {
810819
811 .zero,820 .zero,
812 .the_one_possible_value, // an integer with one possible value is always zero821 .the_one_possible_value, // an integer with one possible value is always zero
822 .bool_false,
813 => return .eq,823 => return .eq,
814824
825 .bool_true => return .gt,
826
815 .int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),827 .int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),
816 .int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),828 .int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),
817 .int_big_positive => return lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0),829 .int_big_positive => return lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0),
...@@ -855,7 +867,7 @@ pub const Value = extern union {...@@ -855,7 +867,7 @@ pub const Value = extern union {
855 pub fn toBool(self: Value) bool {867 pub fn toBool(self: Value) bool {
856 return switch (self.tag()) {868 return switch (self.tag()) {
857 .bool_true => true,869 .bool_true => true,
858 .bool_false => false,870 .bool_false, .zero => false,
859 else => unreachable,871 else => unreachable,
860 };872 };
861 }873 }
test/stage2/compare_output.zig+57
...@@ -170,4 +170,61 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -170,4 +170,61 @@ pub fn addCases(ctx: *TestContext) !void {
170 "",170 "",
171 );171 );
172 }172 }
173 {
174 var case = ctx.exe("assert function", linux_x64);
175 case.addCompareOutput(
176 \\export fn _start() noreturn {
177 \\ add(3, 4);
178 \\
179 \\ exit();
180 \\}
181 \\
182 \\fn add(a: u32, b: u32) void {
183 \\ assert(a + b == 7);
184 \\}
185 \\
186 \\pub fn assert(ok: bool) void {
187 \\ if (!ok) unreachable; // assertion failure
188 \\}
189 \\
190 \\fn exit() noreturn {
191 \\ asm volatile ("syscall"
192 \\ :
193 \\ : [number] "{rax}" (231),
194 \\ [arg1] "{rdi}" (0)
195 \\ : "rcx", "r11", "memory"
196 \\ );
197 \\ unreachable;
198 \\}
199 ,
200 "",
201 );
202 case.addCompareOutput(
203 \\export fn _start() noreturn {
204 \\ add(100, 200);
205 \\
206 \\ exit();
207 \\}
208 \\
209 \\fn add(a: u32, b: u32) void {
210 \\ assert(a + b == 300);
211 \\}
212 \\
213 \\pub fn assert(ok: bool) void {
214 \\ if (!ok) unreachable; // assertion failure
215 \\}
216 \\
217 \\fn exit() noreturn {
218 \\ asm volatile ("syscall"
219 \\ :
220 \\ : [number] "{rax}" (231),
221 \\ [arg1] "{rdi}" (0)
222 \\ : "rcx", "r11", "memory"
223 \\ );
224 \\ unreachable;
225 \\}
226 ,
227 "",
228 );
229 }
173}230}