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
13581358 const tree = scope.tree();
13591359 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
13611371 return self.addZIRInst(scope, src, zir.Inst.Cmp, .{
13621372 .lhs = lhs,
1363 .op = @as(std.math.CompareOperator, switch (infix_node.op) {
1364 .BangEqual => .neq,
1365 .EqualEqual => .eq,
1366 .GreaterThan => .gt,
1367 .GreaterOrEqual => .gte,
1368 .LessThan => .lt,
1369 .LessOrEqual => .lte,
1370 else => unreachable,
1371 }),
1373 .op = op,
13721374 .rhs = rhs,
13731375 }, .{});
13741376 },
......@@ -1415,11 +1417,13 @@ fn astGenIf(self: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir
14151417 defer then_scope.instructions.deinit(self.gpa);
14161418
14171419 const then_result = try self.astGenExpr(&then_scope.base, if_node.body);
1418 const then_src = tree.token_locs[if_node.body.lastToken()].start;
1419 _ = try self.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{
1420 .block = block,
1421 .operand = then_result,
1422 }, .{});
1420 if (!then_result.tag.isNoReturn()) {
1421 const then_src = tree.token_locs[if_node.body.lastToken()].start;
1422 _ = try self.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{
1423 .block = block,
1424 .operand = then_result,
1425 }, .{});
1426 }
14231427 condbr.positionals.true_body = .{
14241428 .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
14251429 };
......@@ -1433,11 +1437,13 @@ fn astGenIf(self: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir
14331437
14341438 if (if_node.@"else") |else_node| {
14351439 const else_result = try self.astGenExpr(&else_scope.base, else_node.body);
1436 const else_src = tree.token_locs[else_node.body.lastToken()].start;
1437 _ = try self.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{
1438 .block = block,
1439 .operand = else_result,
1440 }, .{});
1440 if (!else_result.tag.isNoReturn()) {
1441 const else_src = tree.token_locs[else_node.body.lastToken()].start;
1442 _ = try self.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{
1443 .block = block,
1444 .operand = else_result,
1445 }, .{});
1446 }
14411447 } else {
14421448 // TODO Optimization opportunity: we can avoid an allocation and a memcpy here
14431449 // by directly allocating the body for this one instruction.
src-self-hosted/codegen.zig+70-4
......@@ -415,7 +415,46 @@ const Function = struct {
415415 // No side effects, so if it's unreferenced, do nothing.
416416 if (inst.base.isUnused())
417417 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
418445 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 },
419458 else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}),
420459 }
421460 }
......@@ -444,7 +483,7 @@ const Function = struct {
444483 }
445484 }
446485
447 /// ADD, SUB
486 /// ADD, SUB, XOR, OR, AND
448487 fn genX8664BinMath(self: *Function, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue {
449488 try self.code.ensureCapacity(self.code.items.len + 8);
450489
......@@ -705,7 +744,7 @@ const Function = struct {
705744
706745 fn genCondBr(self: *Function, inst: *ir.Inst.CondBr, comptime arch: std.Target.Cpu.Arch) !MCValue {
707746 switch (arch) {
708 .i386, .x86_64 => {
747 .x86_64 => {
709748 try self.code.ensureCapacity(self.code.items.len + 6);
710749
711750 const cond = try self.resolveInst(inst.args.condition);
......@@ -734,7 +773,20 @@ const Function = struct {
734773 };
735774 return self.genX86CondBr(inst, opcode, arch);
736775 },
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) }),
738790 }
739791 },
740792 else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}),
......@@ -892,7 +944,18 @@ const Function = struct {
892944 .none => unreachable,
893945 .unreach => unreachable,
894946 .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 });
896959 },
897960 .compare_flags_signed => |op| {
898961 return self.fail(src, "TODO set register with compare flags value (signed)", .{});
......@@ -1147,6 +1210,9 @@ const Function = struct {
11471210 }
11481211 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
11491212 },
1213 .Bool => {
1214 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
1215 },
11501216 .ComptimeInt => unreachable, // semantic analysis prevents this
11511217 .ComptimeFloat => unreachable, // semantic analysis prevents this
11521218 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 {
163163 return sentinel_b == null;
164164 }
165165 },
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 },
166182 .Float,
167183 .Struct,
168184 .Optional,
......@@ -170,14 +186,13 @@ pub const Type = extern union {
170186 .ErrorSet,
171187 .Enum,
172188 .Union,
173 .Fn,
174189 .BoundFn,
175190 .Opaque,
176191 .Frame,
177192 .AnyFrame,
178193 .Vector,
179194 .EnumLiteral,
180 => @panic("TODO implement more Type equality comparison"),
195 => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }),
181196 }
182197 }
183198
src-self-hosted/value.zig+23-11
......@@ -427,8 +427,6 @@ pub const Value = extern union {
427427 .fn_ccc_void_no_args_type,
428428 .single_const_pointer_to_comptime_int_type,
429429 .const_slice_u8_type,
430 .bool_true,
431 .bool_false,
432430 .null_value,
433431 .function,
434432 .ref_val,
......@@ -441,8 +439,11 @@ pub const Value = extern union {
441439
442440 .the_one_possible_value, // An integer with one possible value is always zero.
443441 .zero,
442 .bool_false,
444443 => return BigIntMutable.init(&space.limbs, 0).toConst(),
445444
445 .bool_true => return BigIntMutable.init(&space.limbs, 1).toConst(),
446
446447 .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(),
447448 .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(),
448449 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt(),
......@@ -493,8 +494,6 @@ pub const Value = extern union {
493494 .fn_ccc_void_no_args_type,
494495 .single_const_pointer_to_comptime_int_type,
495496 .const_slice_u8_type,
496 .bool_true,
497 .bool_false,
498497 .null_value,
499498 .function,
500499 .ref_val,
......@@ -507,8 +506,11 @@ pub const Value = extern union {
507506
508507 .zero,
509508 .the_one_possible_value, // an integer with one possible value is always zero
509 .bool_false,
510510 => return 0,
511511
512 .bool_true => return 1,
513
512514 .int_u64 => return self.cast(Payload.Int_u64).?.int,
513515 .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int),
514516 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable,
......@@ -560,8 +562,6 @@ pub const Value = extern union {
560562 .fn_ccc_void_no_args_type,
561563 .single_const_pointer_to_comptime_int_type,
562564 .const_slice_u8_type,
563 .bool_true,
564 .bool_false,
565565 .null_value,
566566 .function,
567567 .ref_val,
......@@ -574,8 +574,11 @@ pub const Value = extern union {
574574
575575 .the_one_possible_value, // an integer with one possible value is always zero
576576 .zero,
577 .bool_false,
577578 => return 0,
578579
580 .bool_true => return 1,
581
579582 .int_u64 => {
580583 const x = self.cast(Payload.Int_u64).?.int;
581584 if (x == 0) return 0;
......@@ -632,8 +635,6 @@ pub const Value = extern union {
632635 .fn_ccc_void_no_args_type,
633636 .single_const_pointer_to_comptime_int_type,
634637 .const_slice_u8_type,
635 .bool_true,
636 .bool_false,
637638 .null_value,
638639 .function,
639640 .ref_val,
......@@ -646,8 +647,18 @@ pub const Value = extern union {
646647 .zero,
647648 .undef,
648649 .the_one_possible_value, // an integer with one possible value is always zero
650 .bool_false,
649651 => 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
651662 .int_u64 => switch (ty.zigTypeTag()) {
652663 .Int => {
653664 const x = self.cast(Payload.Int_u64).?.int;
......@@ -796,8 +807,6 @@ pub const Value = extern union {
796807 .fn_ccc_void_no_args_type,
797808 .single_const_pointer_to_comptime_int_type,
798809 .const_slice_u8_type,
799 .bool_true,
800 .bool_false,
801810 .null_value,
802811 .function,
803812 .ref_val,
......@@ -810,8 +819,11 @@ pub const Value = extern union {
810819
811820 .zero,
812821 .the_one_possible_value, // an integer with one possible value is always zero
822 .bool_false,
813823 => return .eq,
814824
825 .bool_true => return .gt,
826
815827 .int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),
816828 .int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),
817829 .int_big_positive => return lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0),
......@@ -855,7 +867,7 @@ pub const Value = extern union {
855867 pub fn toBool(self: Value) bool {
856868 return switch (self.tag()) {
857869 .bool_true => true,
858 .bool_false => false,
870 .bool_false, .zero => false,
859871 else => unreachable,
860872 };
861873 }
test/stage2/compare_output.zig+57
......@@ -170,4 +170,61 @@ pub fn addCases(ctx: *TestContext) !void {
170170 "",
171171 );
172172 }
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 }
173230}