From dc88864c9742029c2980fc16cd2c9e6f04ff3568 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 27 Jul 2021 17:08:37 -0700 Subject: [PATCH] stage2: implement `@boolToInt` This is the first commit in which some behavior tests are passing for both stage1 and stage2. --- doc/langref.html.in | 4 ++-- src/Air.zig | 6 ++++++ src/AstGen.zig | 2 ++ src/Liveness.zig | 1 + src/Sema.zig | 11 +++++++++-- src/Zir.zig | 5 +++++ src/codegen.zig | 8 ++++++++ src/codegen/c.zig | 15 +++++++++++++++ src/codegen/llvm.zig | 10 ++++++++++ src/link/Coff.zig | 7 +++++-- src/link/Elf.zig | 7 ++++--- src/print_air.zig | 1 + src/type.zig | 17 +++++++++++++++-- src/value.zig | 7 +++++++ test/behavior.zig | 7 ++----- 15 files changed, 92 insertions(+), 16 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 38b91468e2..4efa7d0e3c 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -7165,8 +7165,8 @@ fn func(y: *i32) void { {#header_open|@boolToInt#}
{#syntax#}@boolToInt(value: bool) u1{#endsyntax#}

- 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#}.

If the value is known at compile-time, the return type is {#syntax#}comptime_int{#endsyntax#} diff --git a/src/Air.zig b/src/Air.zig index 8cb7b943e3..bd7b3af733 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -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(); diff --git a/src/AstGen.zig b/src/AstGen.zig index 34f906fab1..b5e5d60b2c 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -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), diff --git a/src/Liveness.zig b/src/Liveness.zig index a4c6d8c016..a44b582424 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -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; diff --git a/src/Sema.zig b/src/Sema.zig index 46a41426c8..88a83fd661 100644 --- a/src/Sema.zig +++ b/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, diff --git a/src/Zir.zig b/src/Zir.zig index 2b88a3415d..a8320b65d4 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -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), diff --git a/src/codegen.zig b/src/codegen.zig index 4924b68ca3..3b822c0f88 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -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: { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index fa254af293..7299b21a61 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -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; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 74a51e6634..22f117aa1c 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -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; diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 0c9e513742..4f5df73f8d 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -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); } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 502575f3c8..9ddebd3453 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -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); } diff --git a/src/print_air.zig b/src/print_air.zig index c20a6995e5..2d5d6b588e 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -137,6 +137,7 @@ const Writer = struct { .is_err_ptr, .is_non_err_ptr, .ptrtoint, + .bool_to_int, .ret, => try w.writeUnOp(s, inst), diff --git a/src/type.zig b/src/type.zig index 4dd1a15fdd..d828df550b 100644 --- a/src/type.zig +++ b/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, diff --git a/src/value.zig b/src/value.zig index fc34473921..5d9fd27414 100644 --- a/src/value.zig +++ b/src/value.zig @@ -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, diff --git a/test/behavior.zig b/test/behavior.zig index 101ee2ce53..a286b1a6e2 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -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");