Commit a92990f993
Changed files (5)
src-self-hosted
test
stage2
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;
+ \\}
+ ,
+ "",
+ );
+ }
}