stage2: implement enough for assert() function to codegen
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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}),
|
||||
|
||||
@@ -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 }),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user