Commit a92990f993

Andrew Kelley <andrew@ziglang.org>
2020-07-14 11:24:12
stage2: implement enough for assert() function to codegen
1 parent 135580c
Changed files (5)
src-self-hosted/codegen.zig
@@ -415,7 +415,46 @@ const Function = struct {
         // No side effects, so if it's unreferenced, do nothing.
         if (inst.base.isUnused())
             return MCValue.dead;
+        const operand = try self.resolveInst(inst.args.operand);
+        switch (operand) {
+            .dead => unreachable,
+            .unreach => unreachable,
+            .compare_flags_unsigned => |op| return MCValue{
+                .compare_flags_unsigned = switch (op) {
+                    .gte => .lt,
+                    .gt => .lte,
+                    .neq => .eq,
+                    .lt => .gte,
+                    .lte => .gt,
+                    .eq => .neq,
+                },
+            },
+            .compare_flags_signed => |op| return MCValue{
+                .compare_flags_signed = switch (op) {
+                    .gte => .lt,
+                    .gt => .lte,
+                    .neq => .eq,
+                    .lt => .gte,
+                    .lte => .gt,
+                    .eq => .neq,
+                },
+            },
+            else => {},
+        }
+
         switch (arch) {
+            .x86_64 => {
+                var imm = ir.Inst.Constant{
+                    .base = .{
+                        .tag = .constant,
+                        .deaths = 0,
+                        .ty = inst.args.operand.ty,
+                        .src = inst.args.operand.src,
+                    },
+                    .val = Value.initTag(.bool_true),
+                };
+                return try self.genX8664BinMath(&inst.base, inst.args.operand, &imm.base, 6, 0x30);
+            },
             else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}),
         }
     }
@@ -444,7 +483,7 @@ const Function = struct {
         }
     }
 
-    /// ADD, SUB
+    /// ADD, SUB, XOR, OR, AND
     fn genX8664BinMath(self: *Function, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue {
         try self.code.ensureCapacity(self.code.items.len + 8);
 
@@ -705,7 +744,7 @@ const Function = struct {
 
     fn genCondBr(self: *Function, inst: *ir.Inst.CondBr, comptime arch: std.Target.Cpu.Arch) !MCValue {
         switch (arch) {
-            .i386, .x86_64 => {
+            .x86_64 => {
                 try self.code.ensureCapacity(self.code.items.len + 6);
 
                 const cond = try self.resolveInst(inst.args.condition);
@@ -734,7 +773,20 @@ const Function = struct {
                         };
                         return self.genX86CondBr(inst, opcode, arch);
                     },
-                    else => return self.fail(inst.base.src, "TODO implement condbr {} when condition not already in the compare flags", .{self.target.cpu.arch}),
+                    .register => |reg_usize| {
+                        const reg = @intToEnum(Reg(arch), @intCast(u8, reg_usize));
+                        // test reg, 1
+                        // TODO detect al, ax, eax
+                        try self.code.ensureCapacity(self.code.items.len + 4);
+                        self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });
+                        self.code.appendSliceAssumeCapacity(&[_]u8{
+                            0xf6,
+                            @as(u8, 0xC0) | (0 << 3) | @truncate(u3, reg.id()),
+                            0x01,
+                        });
+                        return self.genX86CondBr(inst, 0x84, arch);
+                    },
+                    else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {}", .{ self.target.cpu.arch, @tagName(cond) }),
                 }
             },
             else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}),
@@ -892,7 +944,18 @@ const Function = struct {
                 .none => unreachable,
                 .unreach => unreachable,
                 .compare_flags_unsigned => |op| {
-                    return self.fail(src, "TODO set register with compare flags value (unsigned)", .{});
+                    try self.code.ensureCapacity(self.code.items.len + 3);
+                    self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });
+                    const opcode: u8 = switch (op) {
+                        .gte => 0x93,
+                        .gt => 0x97,
+                        .neq => 0x95,
+                        .lt => 0x92,
+                        .lte => 0x96,
+                        .eq => 0x94,
+                    };
+                    const id = @as(u8, reg.id() & 0b111);
+                    self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode, 0xC0 | id });
                 },
                 .compare_flags_signed => |op| {
                     return self.fail(src, "TODO set register with compare flags value (signed)", .{});
@@ -1147,6 +1210,9 @@ const Function = struct {
                 }
                 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
             },
+            .Bool => {
+                return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
+            },
             .ComptimeInt => unreachable, // semantic analysis prevents this
             .ComptimeFloat => unreachable, // semantic analysis prevents this
             else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),
src-self-hosted/Module.zig
@@ -1358,17 +1358,19 @@ fn astGenInfixOp(self: *Module, scope: *Scope, infix_node: *ast.Node.InfixOp) In
             const tree = scope.tree();
             const src = tree.token_locs[infix_node.op_token].start;
 
+            const op: std.math.CompareOperator = switch (infix_node.op) {
+                .BangEqual => .neq,
+                .EqualEqual => .eq,
+                .GreaterThan => .gt,
+                .GreaterOrEqual => .gte,
+                .LessThan => .lt,
+                .LessOrEqual => .lte,
+                else => unreachable,
+            };
+
             return self.addZIRInst(scope, src, zir.Inst.Cmp, .{
                 .lhs = lhs,
-                .op = @as(std.math.CompareOperator, switch (infix_node.op) {
-                    .BangEqual => .neq,
-                    .EqualEqual => .eq,
-                    .GreaterThan => .gt,
-                    .GreaterOrEqual => .gte,
-                    .LessThan => .lt,
-                    .LessOrEqual => .lte,
-                    else => unreachable,
-                }),
+                .op = op,
                 .rhs = rhs,
             }, .{});
         },
@@ -1415,11 +1417,13 @@ fn astGenIf(self: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir
     defer then_scope.instructions.deinit(self.gpa);
 
     const then_result = try self.astGenExpr(&then_scope.base, if_node.body);
-    const then_src = tree.token_locs[if_node.body.lastToken()].start;
-    _ = try self.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{
-        .block = block,
-        .operand = then_result,
-    }, .{});
+    if (!then_result.tag.isNoReturn()) {
+        const then_src = tree.token_locs[if_node.body.lastToken()].start;
+        _ = try self.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{
+            .block = block,
+            .operand = then_result,
+        }, .{});
+    }
     condbr.positionals.true_body = .{
         .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
     };
@@ -1433,11 +1437,13 @@ fn astGenIf(self: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir
 
     if (if_node.@"else") |else_node| {
         const else_result = try self.astGenExpr(&else_scope.base, else_node.body);
-        const else_src = tree.token_locs[else_node.body.lastToken()].start;
-        _ = try self.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{
-            .block = block,
-            .operand = else_result,
-        }, .{});
+        if (!else_result.tag.isNoReturn()) {
+            const else_src = tree.token_locs[else_node.body.lastToken()].start;
+            _ = try self.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{
+                .block = block,
+                .operand = else_result,
+            }, .{});
+        }
     } else {
         // TODO Optimization opportunity: we can avoid an allocation and a memcpy here
         // by directly allocating the body for this one instruction.
src-self-hosted/type.zig
@@ -163,6 +163,22 @@ pub const Type = extern union {
                     return sentinel_b == null;
                 }
             },
+            .Fn => {
+                if (!a.fnReturnType().eql(b.fnReturnType()))
+                    return false;
+                if (a.fnCallingConvention() != b.fnCallingConvention())
+                    return false;
+                const a_param_len = a.fnParamLen();
+                const b_param_len = b.fnParamLen();
+                if (a_param_len != b_param_len)
+                    return false;
+                var i: usize = 0;
+                while (i < a_param_len) : (i += 1) {
+                    if (!a.fnParamType(i).eql(b.fnParamType(i)))
+                        return false;
+                }
+                return true;
+            },
             .Float,
             .Struct,
             .Optional,
@@ -170,14 +186,13 @@ pub const Type = extern union {
             .ErrorSet,
             .Enum,
             .Union,
-            .Fn,
             .BoundFn,
             .Opaque,
             .Frame,
             .AnyFrame,
             .Vector,
             .EnumLiteral,
-            => @panic("TODO implement more Type equality comparison"),
+            => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }),
         }
     }
 
src-self-hosted/value.zig
@@ -427,8 +427,6 @@ pub const Value = extern union {
             .fn_ccc_void_no_args_type,
             .single_const_pointer_to_comptime_int_type,
             .const_slice_u8_type,
-            .bool_true,
-            .bool_false,
             .null_value,
             .function,
             .ref_val,
@@ -441,8 +439,11 @@ pub const Value = extern union {
 
             .the_one_possible_value, // An integer with one possible value is always zero.
             .zero,
+            .bool_false,
             => return BigIntMutable.init(&space.limbs, 0).toConst(),
 
+            .bool_true => return BigIntMutable.init(&space.limbs, 1).toConst(),
+
             .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(),
             .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(),
             .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt(),
@@ -493,8 +494,6 @@ pub const Value = extern union {
             .fn_ccc_void_no_args_type,
             .single_const_pointer_to_comptime_int_type,
             .const_slice_u8_type,
-            .bool_true,
-            .bool_false,
             .null_value,
             .function,
             .ref_val,
@@ -507,8 +506,11 @@ pub const Value = extern union {
 
             .zero,
             .the_one_possible_value, // an integer with one possible value is always zero
+            .bool_false,
             => return 0,
 
+            .bool_true => return 1,
+
             .int_u64 => return self.cast(Payload.Int_u64).?.int,
             .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int),
             .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable,
@@ -560,8 +562,6 @@ pub const Value = extern union {
             .fn_ccc_void_no_args_type,
             .single_const_pointer_to_comptime_int_type,
             .const_slice_u8_type,
-            .bool_true,
-            .bool_false,
             .null_value,
             .function,
             .ref_val,
@@ -574,8 +574,11 @@ pub const Value = extern union {
 
             .the_one_possible_value, // an integer with one possible value is always zero
             .zero,
+            .bool_false,
             => return 0,
 
+            .bool_true => return 1,
+
             .int_u64 => {
                 const x = self.cast(Payload.Int_u64).?.int;
                 if (x == 0) return 0;
@@ -632,8 +635,6 @@ pub const Value = extern union {
             .fn_ccc_void_no_args_type,
             .single_const_pointer_to_comptime_int_type,
             .const_slice_u8_type,
-            .bool_true,
-            .bool_false,
             .null_value,
             .function,
             .ref_val,
@@ -646,8 +647,18 @@ pub const Value = extern union {
             .zero,
             .undef,
             .the_one_possible_value, // an integer with one possible value is always zero
+            .bool_false,
             => return true,
 
+            .bool_true => {
+                const info = ty.intInfo(target);
+                if (info.signed) {
+                    return info.bits >= 2;
+                } else {
+                    return info.bits >= 1;
+                }
+            },
+
             .int_u64 => switch (ty.zigTypeTag()) {
                 .Int => {
                     const x = self.cast(Payload.Int_u64).?.int;
@@ -796,8 +807,6 @@ pub const Value = extern union {
             .fn_ccc_void_no_args_type,
             .single_const_pointer_to_comptime_int_type,
             .const_slice_u8_type,
-            .bool_true,
-            .bool_false,
             .null_value,
             .function,
             .ref_val,
@@ -810,8 +819,11 @@ pub const Value = extern union {
 
             .zero,
             .the_one_possible_value, // an integer with one possible value is always zero
+            .bool_false,
             => return .eq,
 
+            .bool_true => return .gt,
+
             .int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),
             .int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),
             .int_big_positive => return lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0),
@@ -855,7 +867,7 @@ pub const Value = extern union {
     pub fn toBool(self: Value) bool {
         return switch (self.tag()) {
             .bool_true => true,
-            .bool_false => false,
+            .bool_false, .zero => false,
             else => unreachable,
         };
     }
test/stage2/compare_output.zig
@@ -170,4 +170,61 @@ pub fn addCases(ctx: *TestContext) !void {
             "",
         );
     }
+    {
+        var case = ctx.exe("assert function", linux_x64);
+        case.addCompareOutput(
+            \\export fn _start() noreturn {
+            \\    add(3, 4);
+            \\
+            \\    exit();
+            \\}
+            \\
+            \\fn add(a: u32, b: u32) void {
+            \\    assert(a + b == 7);
+            \\}
+            \\
+            \\pub fn assert(ok: bool) void {
+            \\    if (!ok) unreachable; // assertion failure
+            \\}
+            \\
+            \\fn exit() noreturn {
+            \\    asm volatile ("syscall"
+            \\        :
+            \\        : [number] "{rax}" (231),
+            \\          [arg1] "{rdi}" (0)
+            \\        : "rcx", "r11", "memory"
+            \\    );
+            \\    unreachable;
+            \\}
+        ,
+            "",
+        );
+        case.addCompareOutput(
+            \\export fn _start() noreturn {
+            \\    add(100, 200);
+            \\
+            \\    exit();
+            \\}
+            \\
+            \\fn add(a: u32, b: u32) void {
+            \\    assert(a + b == 300);
+            \\}
+            \\
+            \\pub fn assert(ok: bool) void {
+            \\    if (!ok) unreachable; // assertion failure
+            \\}
+            \\
+            \\fn exit() noreturn {
+            \\    asm volatile ("syscall"
+            \\        :
+            \\        : [number] "{rax}" (231),
+            \\          [arg1] "{rdi}" (0)
+            \\        : "rcx", "r11", "memory"
+            \\    );
+            \\    unreachable;
+            \\}
+        ,
+            "",
+        );
+    }
 }