| author | |
| committer | |
| log | a92990f99312c946b5e527517a27a67a5a5513c0 |
| tree | 7362b21717b6a8cf27b2fbf4d04de74b4db7d00f |
| parent | 135580c1621513e7cfeed8098c087d1d6941fa97 |
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 | 1358 | const tree = scope.tree(); |
| 1359 | 1359 | const src = tree.token_locs[infix_node.op_token].start; |
| 1360 | 1360 | |
| 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 | 1371 | return self.addZIRInst(scope, src, zir.Inst.Cmp, .{ |
| 1362 | 1372 | .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, | |
| 1372 | 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 | 1417 | defer then_scope.instructions.deinit(self.gpa); |
| 1416 | 1418 | |
| 1417 | 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; | |
| 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 | } | |
| 1423 | 1427 | condbr.positionals.true_body = .{ |
| 1424 | 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 | 1437 | |
| 1434 | 1438 | if (if_node.@"else") |else_node| { |
| 1435 | 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; | |
| 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 | } | |
| 1441 | 1447 | } else { |
| 1442 | 1448 | // TODO Optimization opportunity: we can avoid an allocation and a memcpy here |
| 1443 | 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 | 415 | // No side effects, so if it's unreferenced, do nothing. |
| 416 | 416 | if (inst.base.isUnused()) |
| 417 | 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 | 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 | 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 | 483 | } |
| 445 | 484 | } |
| 446 | 485 | |
| 447 | /// ADD, SUB | |
| 486 | /// ADD, SUB, XOR, OR, AND | |
| 448 | 487 | fn genX8664BinMath(self: *Function, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue { |
| 449 | 488 | try self.code.ensureCapacity(self.code.items.len + 8); |
| 450 | 489 | |
| ... | ... | @@ -705,7 +744,7 @@ const Function = struct { |
| 705 | 744 | |
| 706 | 745 | fn genCondBr(self: *Function, inst: *ir.Inst.CondBr, comptime arch: std.Target.Cpu.Arch) !MCValue { |
| 707 | 746 | switch (arch) { |
| 708 | .i386, .x86_64 => { | |
| 747 | .x86_64 => { | |
| 709 | 748 | try self.code.ensureCapacity(self.code.items.len + 6); |
| 710 | 749 | |
| 711 | 750 | const cond = try self.resolveInst(inst.args.condition); |
| ... | ... | @@ -734,7 +773,20 @@ const Function = struct { |
| 734 | 773 | }; |
| 735 | 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 | 792 | else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}), |
| ... | ... | @@ -892,7 +944,18 @@ const Function = struct { |
| 892 | 944 | .none => unreachable, |
| 893 | 945 | .unreach => unreachable, |
| 894 | 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 | 960 | .compare_flags_signed => |op| { |
| 898 | 961 | return self.fail(src, "TODO set register with compare flags value (signed)", .{}); |
| ... | ... | @@ -1147,6 +1210,9 @@ const Function = struct { |
| 1147 | 1210 | } |
| 1148 | 1211 | return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; |
| 1149 | 1212 | }, |
| 1213 | .Bool => { | |
| 1214 | return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; | |
| 1215 | }, | |
| 1150 | 1216 | .ComptimeInt => unreachable, // semantic analysis prevents this |
| 1151 | 1217 | .ComptimeFloat => unreachable, // semantic analysis prevents this |
| 1152 | 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 | 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 | 182 | .Float, |
| 167 | 183 | .Struct, |
| 168 | 184 | .Optional, |
| ... | ... | @@ -170,14 +186,13 @@ pub const Type = extern union { |
| 170 | 186 | .ErrorSet, |
| 171 | 187 | .Enum, |
| 172 | 188 | .Union, |
| 173 | .Fn, | |
| 174 | 189 | .BoundFn, |
| 175 | 190 | .Opaque, |
| 176 | 191 | .Frame, |
| 177 | 192 | .AnyFrame, |
| 178 | 193 | .Vector, |
| 179 | 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 | } |
| 183 | 198 |
src-self-hosted/value.zig+23-11| ... | ... | @@ -427,8 +427,6 @@ pub const Value = extern union { |
| 427 | 427 | .fn_ccc_void_no_args_type, |
| 428 | 428 | .single_const_pointer_to_comptime_int_type, |
| 429 | 429 | .const_slice_u8_type, |
| 430 | .bool_true, | |
| 431 | .bool_false, | |
| 432 | 430 | .null_value, |
| 433 | 431 | .function, |
| 434 | 432 | .ref_val, |
| ... | ... | @@ -441,8 +439,11 @@ pub const Value = extern union { |
| 441 | 439 | |
| 442 | 440 | .the_one_possible_value, // An integer with one possible value is always zero. |
| 443 | 441 | .zero, |
| 442 | .bool_false, | |
| 444 | 443 | => return BigIntMutable.init(&space.limbs, 0).toConst(), |
| 445 | 444 | |
| 445 | .bool_true => return BigIntMutable.init(&space.limbs, 1).toConst(), | |
| 446 | ||
| 446 | 447 | .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(), |
| 447 | 448 | .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(), |
| 448 | 449 | .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt(), |
| ... | ... | @@ -493,8 +494,6 @@ pub const Value = extern union { |
| 493 | 494 | .fn_ccc_void_no_args_type, |
| 494 | 495 | .single_const_pointer_to_comptime_int_type, |
| 495 | 496 | .const_slice_u8_type, |
| 496 | .bool_true, | |
| 497 | .bool_false, | |
| 498 | 497 | .null_value, |
| 499 | 498 | .function, |
| 500 | 499 | .ref_val, |
| ... | ... | @@ -507,8 +506,11 @@ pub const Value = extern union { |
| 507 | 506 | |
| 508 | 507 | .zero, |
| 509 | 508 | .the_one_possible_value, // an integer with one possible value is always zero |
| 509 | .bool_false, | |
| 510 | 510 | => return 0, |
| 511 | 511 | |
| 512 | .bool_true => return 1, | |
| 513 | ||
| 512 | 514 | .int_u64 => return self.cast(Payload.Int_u64).?.int, |
| 513 | 515 | .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int), |
| 514 | 516 | .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable, |
| ... | ... | @@ -560,8 +562,6 @@ pub const Value = extern union { |
| 560 | 562 | .fn_ccc_void_no_args_type, |
| 561 | 563 | .single_const_pointer_to_comptime_int_type, |
| 562 | 564 | .const_slice_u8_type, |
| 563 | .bool_true, | |
| 564 | .bool_false, | |
| 565 | 565 | .null_value, |
| 566 | 566 | .function, |
| 567 | 567 | .ref_val, |
| ... | ... | @@ -574,8 +574,11 @@ pub const Value = extern union { |
| 574 | 574 | |
| 575 | 575 | .the_one_possible_value, // an integer with one possible value is always zero |
| 576 | 576 | .zero, |
| 577 | .bool_false, | |
| 577 | 578 | => return 0, |
| 578 | 579 | |
| 580 | .bool_true => return 1, | |
| 581 | ||
| 579 | 582 | .int_u64 => { |
| 580 | 583 | const x = self.cast(Payload.Int_u64).?.int; |
| 581 | 584 | if (x == 0) return 0; |
| ... | ... | @@ -632,8 +635,6 @@ pub const Value = extern union { |
| 632 | 635 | .fn_ccc_void_no_args_type, |
| 633 | 636 | .single_const_pointer_to_comptime_int_type, |
| 634 | 637 | .const_slice_u8_type, |
| 635 | .bool_true, | |
| 636 | .bool_false, | |
| 637 | 638 | .null_value, |
| 638 | 639 | .function, |
| 639 | 640 | .ref_val, |
| ... | ... | @@ -646,8 +647,18 @@ pub const Value = extern union { |
| 646 | 647 | .zero, |
| 647 | 648 | .undef, |
| 648 | 649 | .the_one_possible_value, // an integer with one possible value is always zero |
| 650 | .bool_false, | |
| 649 | 651 | => return true, |
| 650 | 652 | |
| 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 | 662 | .int_u64 => switch (ty.zigTypeTag()) { |
| 652 | 663 | .Int => { |
| 653 | 664 | const x = self.cast(Payload.Int_u64).?.int; |
| ... | ... | @@ -796,8 +807,6 @@ pub const Value = extern union { |
| 796 | 807 | .fn_ccc_void_no_args_type, |
| 797 | 808 | .single_const_pointer_to_comptime_int_type, |
| 798 | 809 | .const_slice_u8_type, |
| 799 | .bool_true, | |
| 800 | .bool_false, | |
| 801 | 810 | .null_value, |
| 802 | 811 | .function, |
| 803 | 812 | .ref_val, |
| ... | ... | @@ -810,8 +819,11 @@ pub const Value = extern union { |
| 810 | 819 | |
| 811 | 820 | .zero, |
| 812 | 821 | .the_one_possible_value, // an integer with one possible value is always zero |
| 822 | .bool_false, | |
| 813 | 823 | => return .eq, |
| 814 | 824 | |
| 825 | .bool_true => return .gt, | |
| 826 | ||
| 815 | 827 | .int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0), |
| 816 | 828 | .int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0), |
| 817 | 829 | .int_big_positive => return lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0), |
| ... | ... | @@ -855,7 +867,7 @@ pub const Value = extern union { |
| 855 | 867 | pub fn toBool(self: Value) bool { |
| 856 | 868 | return switch (self.tag()) { |
| 857 | 869 | .bool_true => true, |
| 858 | .bool_false => false, | |
| 870 | .bool_false, .zero => false, | |
| 859 | 871 | else => unreachable, |
| 860 | 872 | }; |
| 861 | 873 | } |
test/stage2/compare_output.zig+57| ... | ... | @@ -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 | } |