mirror of
https://github.com/ziglang/zig.git
synced 2024-11-14 16:13:24 +00:00
stage2: implement @boolToInt
This is the first commit in which some behavior tests are passing for both stage1 and stage2.
This commit is contained in:
parent
66e5920dc3
commit
dc88864c97
@ -7165,8 +7165,8 @@ fn func(y: *i32) void {
|
||||
{#header_open|@boolToInt#}
|
||||
<pre>{#syntax#}@boolToInt(value: bool) u1{#endsyntax#}</pre>
|
||||
<p>
|
||||
Converts {#syntax#}true{#endsyntax#} to {#syntax#}u1(1){#endsyntax#} and {#syntax#}false{#endsyntax#} to
|
||||
{#syntax#}u1(0){#endsyntax#}.
|
||||
Converts {#syntax#}true{#endsyntax#} to {#syntax#}@as(u1, 1){#endsyntax#} and {#syntax#}false{#endsyntax#} to
|
||||
{#syntax#}@as(u1, 0){#endsyntax#}.
|
||||
</p>
|
||||
<p>
|
||||
If the value is known at compile-time, the return type is {#syntax#}comptime_int{#endsyntax#}
|
||||
|
@ -189,6 +189,10 @@ pub const Inst = struct {
|
||||
/// Converts a pointer to its address. Result type is always `usize`.
|
||||
/// Uses the `un_op` field.
|
||||
ptrtoint,
|
||||
/// Given a boolean, returns 0 or 1.
|
||||
/// Result type is always `u1`.
|
||||
/// Uses the `un_op` field.
|
||||
bool_to_int,
|
||||
/// Stores a value onto the stack and returns a pointer to it.
|
||||
/// TODO audit where this AIR instruction is emitted, maybe it should instead be emitting
|
||||
/// alloca instruction and storing to the alloca.
|
||||
@ -490,6 +494,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
|
||||
.slice_len,
|
||||
=> return Type.initTag(.usize),
|
||||
|
||||
.bool_to_int => return Type.initTag(.u1),
|
||||
|
||||
.call => {
|
||||
const callee_ty = air.typeOf(datas[inst].pl_op.operand);
|
||||
return callee_ty.fnReturnType();
|
||||
|
@ -7754,6 +7754,7 @@ pub const simple_types = std.ComptimeStringMap(Zir.Inst.Ref, .{
|
||||
.{ "u32", .u32_type },
|
||||
.{ "u64", .u64_type },
|
||||
.{ "u128", .u128_type },
|
||||
.{ "u1", .u1_type },
|
||||
.{ "u8", .u8_type },
|
||||
.{ "undefined", .undef },
|
||||
.{ "usize", .usize_type },
|
||||
@ -8400,6 +8401,7 @@ fn rvalue(
|
||||
const as_usize = @as(u64, @enumToInt(Zir.Inst.Ref.usize_type)) << 32;
|
||||
const as_void = @as(u64, @enumToInt(Zir.Inst.Ref.void_type)) << 32;
|
||||
switch ((@as(u64, @enumToInt(ty_inst)) << 32) | @as(u64, @enumToInt(result))) {
|
||||
as_ty | @enumToInt(Zir.Inst.Ref.u1_type),
|
||||
as_ty | @enumToInt(Zir.Inst.Ref.u8_type),
|
||||
as_ty | @enumToInt(Zir.Inst.Ref.i8_type),
|
||||
as_ty | @enumToInt(Zir.Inst.Ref.u16_type),
|
||||
|
@ -291,6 +291,7 @@ fn analyzeInst(
|
||||
.is_err_ptr,
|
||||
.is_non_err_ptr,
|
||||
.ptrtoint,
|
||||
.bool_to_int,
|
||||
.ret,
|
||||
=> {
|
||||
const operand = inst_datas[inst].un_op;
|
||||
|
11
src/Sema.zig
11
src/Sema.zig
@ -5848,8 +5848,14 @@ fn zirAlignOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
|
||||
|
||||
fn zirBoolToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
|
||||
const src = inst_data.src();
|
||||
return sema.mod.fail(&block.base, src, "TODO: Sema.zirBoolToInt", .{});
|
||||
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
|
||||
const operand = sema.resolveInst(inst_data.operand);
|
||||
if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
|
||||
if (val.isUndef()) return sema.addConstUndef(Type.initTag(.u1));
|
||||
const bool_ints = [2]Air.Inst.Ref{ .zero, .one };
|
||||
return bool_ints[@boolToInt(val.toBool())];
|
||||
}
|
||||
return block.addUnOp(.bool_to_int, operand);
|
||||
}
|
||||
|
||||
fn zirEmbedFile(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
@ -8252,6 +8258,7 @@ fn typeHasOnePossibleValue(
|
||||
.c_longdouble,
|
||||
.comptime_int,
|
||||
.comptime_float,
|
||||
.u1,
|
||||
.u8,
|
||||
.i8,
|
||||
.u16,
|
||||
|
@ -1633,6 +1633,7 @@ pub const Inst = struct {
|
||||
/// value and may instead be used as a sentinel to indicate null.
|
||||
none,
|
||||
|
||||
u1_type,
|
||||
u8_type,
|
||||
i8_type,
|
||||
u16_type,
|
||||
@ -1719,6 +1720,10 @@ pub const Inst = struct {
|
||||
pub const typed_value_map = std.enums.directEnumArray(Ref, TypedValue, 0, .{
|
||||
.none = undefined,
|
||||
|
||||
.u1_type = .{
|
||||
.ty = Type.initTag(.type),
|
||||
.val = Value.initTag(.u1_type),
|
||||
},
|
||||
.u8_type = .{
|
||||
.ty = Type.initTag(.type),
|
||||
.val = Value.initTag(.u8_type),
|
||||
|
@ -835,6 +835,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.dbg_stmt => try self.airDbgStmt(inst),
|
||||
.floatcast => try self.airFloatCast(inst),
|
||||
.intcast => try self.airIntCast(inst),
|
||||
.bool_to_int => try self.airBoolToInt(inst),
|
||||
.is_non_null => try self.airIsNonNull(inst),
|
||||
.is_non_null_ptr => try self.airIsNonNullPtr(inst),
|
||||
.is_null => try self.airIsNull(inst),
|
||||
@ -1110,6 +1111,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const un_op = self.air.instructions.items(.data)[inst].un_op;
|
||||
const operand = try self.resolveInst(un_op);
|
||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand;
|
||||
return self.finishAir(inst, result, .{ un_op, .none, .none });
|
||||
}
|
||||
|
||||
fn airNot(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
|
||||
|
@ -925,6 +925,7 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM
|
||||
.call => try airCall(o, inst),
|
||||
.dbg_stmt => try airDbgStmt(o, inst),
|
||||
.intcast => try airIntCast(o, inst),
|
||||
.bool_to_int => try airBoolToInt(o, inst),
|
||||
.load => try airLoad(o, inst),
|
||||
.ret => try airRet(o, inst),
|
||||
.store => try airStore(o, inst),
|
||||
@ -1083,6 +1084,20 @@ fn airIntCast(o: *Object, inst: Air.Inst.Index) !CValue {
|
||||
return local;
|
||||
}
|
||||
|
||||
fn airBoolToInt(o: *Object, inst: Air.Inst.Index) !CValue {
|
||||
if (o.liveness.isUnused(inst))
|
||||
return CValue.none;
|
||||
const un_op = o.air.instructions.items(.data)[inst].un_op;
|
||||
const writer = o.writer();
|
||||
const inst_ty = o.air.typeOfIndex(inst);
|
||||
const operand = try o.resolveInst(un_op);
|
||||
const local = try o.allocLocal(inst_ty, .Const);
|
||||
try writer.writeAll(" = ");
|
||||
try o.writeCValue(writer, operand);
|
||||
try writer.writeAll(";\n");
|
||||
return local;
|
||||
}
|
||||
|
||||
fn airStore(o: *Object, inst: Air.Inst.Index) !CValue {
|
||||
// *a = b;
|
||||
const bin_op = o.air.instructions.items(.data)[inst].bin_op;
|
||||
|
@ -961,6 +961,7 @@ pub const FuncGen = struct {
|
||||
.alloc => try self.airAlloc(inst),
|
||||
.arg => try self.airArg(inst),
|
||||
.bitcast => try self.airBitCast(inst),
|
||||
.bool_to_int=> try self.airBoolToInt(inst),
|
||||
.block => try self.airBlock(inst),
|
||||
.br => try self.airBr(inst),
|
||||
.switch_br => try self.airSwitchBr(inst),
|
||||
@ -1656,6 +1657,15 @@ pub const FuncGen = struct {
|
||||
return self.builder.buildBitCast(operand, dest_type, "");
|
||||
}
|
||||
|
||||
fn airBoolToInt(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
if (self.liveness.isUnused(inst))
|
||||
return null;
|
||||
|
||||
const un_op = self.air.instructions.items(.data)[inst].un_op;
|
||||
const operand = try self.resolveInst(un_op);
|
||||
return operand;
|
||||
}
|
||||
|
||||
fn airArg(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
const arg_val = self.args[self.arg_index];
|
||||
self.arg_index += 1;
|
||||
|
@ -885,7 +885,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
|
||||
// Both stage1 and stage2 LLVM backend put the object file in the cache directory.
|
||||
if (self.base.options.use_llvm) {
|
||||
// Stage2 has to call flushModule since that outputs the LLVM object file.
|
||||
if (!build_options.is_stage1) try self.flushModule(comp);
|
||||
if (!build_options.is_stage1 or !self.base.options.use_stage1) try self.flushModule(comp);
|
||||
|
||||
const obj_basename = try std.zig.binNameAlloc(arena, .{
|
||||
.root_name = self.base.options.root_name,
|
||||
@ -1269,7 +1269,10 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
|
||||
|
||||
// TODO: remove when stage2 can build compiler_rt.zig, c.zig and ssp.zig
|
||||
// compiler-rt, libc and libssp
|
||||
if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies and build_options.is_stage1) {
|
||||
if (is_exe_or_dyn_lib and
|
||||
!self.base.options.skip_linker_dependencies and
|
||||
build_options.is_stage1 and self.base.options.use_stage1)
|
||||
{
|
||||
if (!self.base.options.link_libc) {
|
||||
try argv.append(comp.libc_static_lib.?.full_object_path);
|
||||
}
|
||||
|
@ -1257,7 +1257,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
|
||||
// Both stage1 and stage2 LLVM backend put the object file in the cache directory.
|
||||
if (self.base.options.use_llvm) {
|
||||
// Stage2 has to call flushModule since that outputs the LLVM object file.
|
||||
if (!build_options.is_stage1) try self.flushModule(comp);
|
||||
if (!build_options.is_stage1 or !self.base.options.use_stage1) try self.flushModule(comp);
|
||||
|
||||
const obj_basename = try std.zig.binNameAlloc(arena, .{
|
||||
.root_name = self.base.options.root_name,
|
||||
@ -1287,7 +1287,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
|
||||
const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os;
|
||||
const compiler_rt_path: ?[]const u8 = if (self.base.options.include_compiler_rt) blk: {
|
||||
// TODO: remove when stage2 can build compiler_rt.zig
|
||||
if (!build_options.is_stage1) break :blk null;
|
||||
if (!build_options.is_stage1 or !self.base.options.use_stage1) break :blk null;
|
||||
|
||||
// In the case of build-obj we include the compiler-rt symbols directly alongside
|
||||
// the symbols of the root source file, in the same compilation unit.
|
||||
@ -1605,7 +1605,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
|
||||
if (is_exe_or_dyn_lib and
|
||||
!self.base.options.skip_linker_dependencies and
|
||||
!self.base.options.link_libc and
|
||||
build_options.is_stage1)
|
||||
build_options.is_stage1 and
|
||||
self.base.options.use_stage1)
|
||||
{
|
||||
try argv.append(comp.libc_static_lib.?.full_object_path);
|
||||
}
|
||||
|
@ -137,6 +137,7 @@ const Writer = struct {
|
||||
.is_err_ptr,
|
||||
.is_non_err_ptr,
|
||||
.ptrtoint,
|
||||
.bool_to_int,
|
||||
.ret,
|
||||
=> try w.writeUnOp(s, inst),
|
||||
|
||||
|
17
src/type.zig
17
src/type.zig
@ -23,6 +23,7 @@ pub const Type = extern union {
|
||||
|
||||
pub fn zigTypeTag(self: Type) std.builtin.TypeId {
|
||||
switch (self.tag()) {
|
||||
.u1,
|
||||
.u8,
|
||||
.i8,
|
||||
.u16,
|
||||
@ -638,6 +639,7 @@ pub const Type = extern union {
|
||||
if (self.tag_if_small_enough < Tag.no_payload_count) {
|
||||
return Type{ .tag_if_small_enough = self.tag_if_small_enough };
|
||||
} else switch (self.ptr_otherwise.tag) {
|
||||
.u1,
|
||||
.u8,
|
||||
.i8,
|
||||
.u16,
|
||||
@ -819,6 +821,7 @@ pub const Type = extern union {
|
||||
while (true) {
|
||||
const t = ty.tag();
|
||||
switch (t) {
|
||||
.u1,
|
||||
.u8,
|
||||
.i8,
|
||||
.u16,
|
||||
@ -1082,6 +1085,7 @@ pub const Type = extern union {
|
||||
|
||||
pub fn toValue(self: Type, allocator: *Allocator) Allocator.Error!Value {
|
||||
switch (self.tag()) {
|
||||
.u1 => return Value.initTag(.u1_type),
|
||||
.u8 => return Value.initTag(.u8_type),
|
||||
.i8 => return Value.initTag(.i8_type),
|
||||
.u16 => return Value.initTag(.u16_type),
|
||||
@ -1141,6 +1145,7 @@ pub const Type = extern union {
|
||||
|
||||
pub fn hasCodeGenBits(self: Type) bool {
|
||||
return switch (self.tag()) {
|
||||
.u1,
|
||||
.u8,
|
||||
.i8,
|
||||
.u16,
|
||||
@ -1321,6 +1326,7 @@ pub const Type = extern union {
|
||||
/// Asserts that hasCodeGenBits() is true.
|
||||
pub fn abiAlignment(self: Type, target: Target) u32 {
|
||||
return switch (self.tag()) {
|
||||
.u1,
|
||||
.u8,
|
||||
.i8,
|
||||
.bool,
|
||||
@ -1539,6 +1545,7 @@ pub const Type = extern union {
|
||||
@panic("TODO abiSize unions");
|
||||
},
|
||||
|
||||
.u1,
|
||||
.u8,
|
||||
.i8,
|
||||
.bool,
|
||||
@ -1704,7 +1711,7 @@ pub const Type = extern union {
|
||||
|
||||
.u8, .i8 => 8,
|
||||
|
||||
.bool => 1,
|
||||
.bool, .u1 => 1,
|
||||
|
||||
.vector => {
|
||||
const payload = self.castTag(.vector).?.data;
|
||||
@ -2217,12 +2224,13 @@ pub const Type = extern union {
|
||||
pub fn isUnsignedInt(self: Type) bool {
|
||||
return switch (self.tag()) {
|
||||
.int_unsigned,
|
||||
.u8,
|
||||
.usize,
|
||||
.c_ushort,
|
||||
.c_uint,
|
||||
.c_ulong,
|
||||
.c_ulonglong,
|
||||
.u1,
|
||||
.u8,
|
||||
.u16,
|
||||
.u32,
|
||||
.u64,
|
||||
@ -2244,6 +2252,7 @@ pub const Type = extern union {
|
||||
.signedness = .signed,
|
||||
.bits = self.castTag(.int_signed).?.data,
|
||||
},
|
||||
.u1 => .{ .signedness = .unsigned, .bits = 1 },
|
||||
.u8 => .{ .signedness = .unsigned, .bits = 8 },
|
||||
.i8 => .{ .signedness = .signed, .bits = 8 },
|
||||
.u16 => .{ .signedness = .unsigned, .bits = 16 },
|
||||
@ -2406,6 +2415,7 @@ pub const Type = extern union {
|
||||
.c_longdouble,
|
||||
.comptime_int,
|
||||
.comptime_float,
|
||||
.u1,
|
||||
.u8,
|
||||
.i8,
|
||||
.u16,
|
||||
@ -2446,6 +2456,7 @@ pub const Type = extern union {
|
||||
.c_longdouble,
|
||||
.comptime_int,
|
||||
.comptime_float,
|
||||
.u1,
|
||||
.u8,
|
||||
.i8,
|
||||
.u16,
|
||||
@ -2911,6 +2922,7 @@ pub const Type = extern union {
|
||||
/// See `zigTypeTag` for the function that corresponds to `std.builtin.TypeId`.
|
||||
pub const Tag = enum {
|
||||
// The first section of this enum are tags that require no payload.
|
||||
u1,
|
||||
u8,
|
||||
i8,
|
||||
u16,
|
||||
@ -3018,6 +3030,7 @@ pub const Type = extern union {
|
||||
|
||||
pub fn Type(comptime t: Tag) type {
|
||||
return switch (t) {
|
||||
.u1,
|
||||
.u8,
|
||||
.i8,
|
||||
.u16,
|
||||
|
@ -22,6 +22,7 @@ pub const Value = extern union {
|
||||
|
||||
pub const Tag = enum {
|
||||
// The first section of this enum are tags that require no payload.
|
||||
u1_type,
|
||||
u8_type,
|
||||
i8_type,
|
||||
u16_type,
|
||||
@ -138,6 +139,7 @@ pub const Value = extern union {
|
||||
|
||||
pub fn Type(comptime t: Tag) type {
|
||||
return switch (t) {
|
||||
.u1_type,
|
||||
.u8_type,
|
||||
.i8_type,
|
||||
.u16_type,
|
||||
@ -314,6 +316,7 @@ pub const Value = extern union {
|
||||
if (self.tag_if_small_enough < Tag.no_payload_count) {
|
||||
return Value{ .tag_if_small_enough = self.tag_if_small_enough };
|
||||
} else switch (self.ptr_otherwise.tag) {
|
||||
.u1_type,
|
||||
.u8_type,
|
||||
.i8_type,
|
||||
.u16_type,
|
||||
@ -520,6 +523,7 @@ pub const Value = extern union {
|
||||
comptime assert(fmt.len == 0);
|
||||
var val = start_val;
|
||||
while (true) switch (val.tag()) {
|
||||
.u1_type => return out_stream.writeAll("u1"),
|
||||
.u8_type => return out_stream.writeAll("u8"),
|
||||
.i8_type => return out_stream.writeAll("i8"),
|
||||
.u16_type => return out_stream.writeAll("u16"),
|
||||
@ -671,6 +675,7 @@ pub const Value = extern union {
|
||||
pub fn toType(self: Value, allocator: *Allocator) !Type {
|
||||
return switch (self.tag()) {
|
||||
.ty => self.castTag(.ty).?.data,
|
||||
.u1_type => Type.initTag(.u1),
|
||||
.u8_type => Type.initTag(.u8),
|
||||
.i8_type => Type.initTag(.i8),
|
||||
.u16_type => Type.initTag(.u16),
|
||||
@ -1150,6 +1155,7 @@ pub const Value = extern union {
|
||||
var hasher = std.hash.Wyhash.init(0);
|
||||
|
||||
switch (self.tag()) {
|
||||
.u1_type,
|
||||
.u8_type,
|
||||
.i8_type,
|
||||
.u16_type,
|
||||
@ -1502,6 +1508,7 @@ pub const Value = extern union {
|
||||
return switch (self.tag()) {
|
||||
.ty,
|
||||
.int_type,
|
||||
.u1_type,
|
||||
.u8_type,
|
||||
.i8_type,
|
||||
.u16_type,
|
||||
|
@ -2,11 +2,9 @@ const builtin = @import("builtin");
|
||||
|
||||
test {
|
||||
// Tests that pass for both.
|
||||
{}
|
||||
_ = @import("behavior/bool.zig");
|
||||
|
||||
if (builtin.zig_is_stage2) {
|
||||
// Tests that only pass for stage2.
|
||||
} else {
|
||||
if (!builtin.zig_is_stage2) {
|
||||
// Tests that only pass for stage1.
|
||||
_ = @import("behavior/align.zig");
|
||||
_ = @import("behavior/alignof.zig");
|
||||
@ -20,7 +18,6 @@ test {
|
||||
_ = @import("behavior/bit_shifting.zig");
|
||||
_ = @import("behavior/bitcast.zig");
|
||||
_ = @import("behavior/bitreverse.zig");
|
||||
_ = @import("behavior/bool.zig");
|
||||
_ = @import("behavior/bugs/1025.zig");
|
||||
_ = @import("behavior/bugs/1076.zig");
|
||||
_ = @import("behavior/bugs/1111.zig");
|
||||
|
Loading…
Reference in New Issue
Block a user