Commit 6dba9bc6fc

Andrew Kelley <andrew@ziglang.org>
2021-04-13 06:46:20
stage2: implement `@bitSizeOf`
1 parent df983b3
src/AstGen.zig
@@ -1360,6 +1360,7 @@ fn blockExprStmts(
                         .enum_to_int,
                         .type_info,
                         .size_of,
+                        .bit_size_of,
                         => break :b false,
 
                         // ZIR instructions that are always either `noreturn` or `void`.
@@ -4349,6 +4350,12 @@ fn builtinCall(
             return rvalue(gz, scope, rl, result, node);
         },
 
+        .bit_size_of => {
+            const operand = try typeExpr(gz, scope, params[0]);
+            const result = try gz.addUnNode(.bit_size_of, operand, node);
+            return rvalue(gz, scope, rl, result, node);
+        },
+
         .add_with_overflow,
         .align_cast,
         .align_of,
@@ -4357,7 +4364,6 @@ fn builtinCall(
         .atomic_store,
         .bit_offset_of,
         .bool_to_int,
-        .bit_size_of,
         .mul_add,
         .byte_swap,
         .bit_reverse,
src/Sema.zig
@@ -265,6 +265,7 @@ pub fn analyzeBody(
             .switch_capture_else_ref => try sema.zirSwitchCaptureElse(block, inst, true),
             .type_info => try sema.zirTypeInfo(block, inst),
             .size_of => try sema.zirSizeOf(block, inst),
+            .bit_size_of => try sema.zirBitSizeOf(block, inst),
             .typeof => try sema.zirTypeof(block, inst),
             .typeof_elem => try sema.zirTypeofElem(block, inst),
             .typeof_peer => try sema.zirTypeofPeer(block, inst),
@@ -4365,6 +4366,16 @@ fn zirSizeOf(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!
     return sema.mod.constIntUnsigned(sema.arena, src, Type.initTag(.comptime_int), abi_size);
 }
 
+fn zirBitSizeOf(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const src = inst_data.src();
+    const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+    const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand);
+    const target = sema.mod.getTarget();
+    const bit_size = operand_ty.bitSize(target);
+    return sema.mod.constIntUnsigned(sema.arena, src, Type.initTag(.comptime_int), bit_size);
+}
+
 fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
     const src = inst_data.src();
src/type.zig
@@ -1377,6 +1377,151 @@ pub const Type = extern union {
         };
     }
 
+    /// Asserts the type has the bit size already resolved.
+    pub fn bitSize(self: Type, target: Target) u64 {
+        return switch (self.tag()) {
+            .fn_noreturn_no_args => unreachable, // represents machine code; not a pointer
+            .fn_void_no_args => unreachable, // represents machine code; not a pointer
+            .fn_naked_noreturn_no_args => unreachable, // represents machine code; not a pointer
+            .fn_ccc_void_no_args => unreachable, // represents machine code; not a pointer
+            .function => unreachable, // represents machine code; not a pointer
+            .c_void => unreachable,
+            .void => unreachable,
+            .type => unreachable,
+            .comptime_int => unreachable,
+            .comptime_float => unreachable,
+            .noreturn => unreachable,
+            .@"null" => unreachable,
+            .@"undefined" => unreachable,
+            .enum_literal => unreachable,
+            .single_const_pointer_to_comptime_int => unreachable,
+            .empty_struct => unreachable,
+            .empty_struct_literal => unreachable,
+            .inferred_alloc_const => unreachable,
+            .inferred_alloc_mut => unreachable,
+            .@"opaque" => unreachable,
+            .var_args_param => unreachable,
+
+            .@"struct" => {
+                @panic("TODO bitSize struct");
+            },
+            .enum_simple, .enum_full, .enum_nonexhaustive => {
+                var buffer: Payload.Bits = undefined;
+                const int_tag_ty = self.intTagType(&buffer);
+                return int_tag_ty.bitSize(target);
+            },
+
+            .u8, .i8 => 8,
+
+            .bool => 1,
+
+            .array_u8 => 8 * self.castTag(.array_u8).?.data,
+            .array_u8_sentinel_0 => 8 * (self.castTag(.array_u8_sentinel_0).?.data + 1),
+            .array => {
+                const payload = self.castTag(.array).?.data;
+                const elem_size = std.math.max(payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target));
+                if (elem_size == 0 or payload.len == 0)
+                    return 0;
+                return (payload.len - 1) * 8 * elem_size + payload.elem_type.bitSize(target);
+            },
+            .array_sentinel => {
+                const payload = self.castTag(.array_sentinel).?.data;
+                const elem_size = std.math.max(
+                    payload.elem_type.abiAlignment(target),
+                    payload.elem_type.abiSize(target),
+                );
+                return payload.len * 8 * elem_size + payload.elem_type.bitSize(target);
+            },
+            .i16, .u16, .f16 => 16,
+            .i32, .u32, .f32 => 32,
+            .i64, .u64, .f64 => 64,
+            .u128, .i128, .f128 => 128,
+
+            .isize, .usize => target.cpu.arch.ptrBitWidth(),
+
+            .const_slice,
+            .mut_slice,
+            => {
+                if (self.elemType().hasCodeGenBits()) {
+                    return target.cpu.arch.ptrBitWidth() * 2;
+                } else {
+                    return target.cpu.arch.ptrBitWidth();
+                }
+            },
+            .const_slice_u8 => target.cpu.arch.ptrBitWidth() * 2,
+
+            .optional_single_const_pointer,
+            .optional_single_mut_pointer,
+            => {
+                if (self.elemType().hasCodeGenBits()) {
+                    return target.cpu.arch.ptrBitWidth();
+                } else {
+                    return 1;
+                }
+            },
+
+            .single_const_pointer,
+            .single_mut_pointer,
+            .many_const_pointer,
+            .many_mut_pointer,
+            .c_const_pointer,
+            .c_mut_pointer,
+            .pointer,
+            => {
+                if (self.elemType().hasCodeGenBits()) {
+                    return target.cpu.arch.ptrBitWidth();
+                } else {
+                    return 0;
+                }
+            },
+
+            .c_short => return CType.short.sizeInBits(target),
+            .c_ushort => return CType.ushort.sizeInBits(target),
+            .c_int => return CType.int.sizeInBits(target),
+            .c_uint => return CType.uint.sizeInBits(target),
+            .c_long => return CType.long.sizeInBits(target),
+            .c_ulong => return CType.ulong.sizeInBits(target),
+            .c_longlong => return CType.longlong.sizeInBits(target),
+            .c_ulonglong => return CType.ulonglong.sizeInBits(target),
+            .c_longdouble => 128,
+
+            .error_set,
+            .error_set_single,
+            .anyerror_void_error_union,
+            .anyerror,
+            => return 16, // TODO revisit this when we have the concept of the error tag type
+
+            .int_signed, .int_unsigned => self.cast(Payload.Bits).?.data,
+
+            .optional => {
+                var buf: Payload.ElemType = undefined;
+                const child_type = self.optionalChild(&buf);
+                if (!child_type.hasCodeGenBits()) return 8;
+
+                if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr())
+                    return target.cpu.arch.ptrBitWidth();
+
+                // Optional types are represented as a struct with the child type as the first
+                // field and a boolean as the second. Since the child type's abi alignment is
+                // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal
+                // to the child type's ABI alignment.
+                return child_type.bitSize(target) + 1;
+            },
+
+            .error_union => {
+                const payload = self.castTag(.error_union).?.data;
+                if (!payload.error_set.hasCodeGenBits() and !payload.payload.hasCodeGenBits()) {
+                    return 0;
+                } else if (!payload.error_set.hasCodeGenBits()) {
+                    return payload.payload.bitSize(target);
+                } else if (!payload.payload.hasCodeGenBits()) {
+                    return payload.error_set.bitSize(target);
+                }
+                @panic("TODO abiSize error union");
+            },
+        };
+    }
+
     /// Asserts the type is an enum.
     pub fn intTagType(self: Type, buffer: *Payload.Bits) Type {
         switch (self.tag()) {
src/zir.zig
@@ -697,6 +697,8 @@ pub const Inst = struct {
         type_info,
         /// Implements the `@sizeOf` builtin. Uses `un_node`.
         size_of,
+        /// Implements the `@bitSizeOf` builtin. Uses `un_node`.
+        bit_size_of,
 
         /// Returns whether the instruction is one of the control flow "noreturn" types.
         /// Function calls do not count.
@@ -864,6 +866,7 @@ pub const Inst = struct {
                 .enum_to_int,
                 .type_info,
                 .size_of,
+                .bit_size_of,
                 => false,
 
                 .@"break",
@@ -1674,6 +1677,7 @@ const Writer = struct {
             .enum_to_int,
             .type_info,
             .size_of,
+            .bit_size_of,
             => try self.writeUnNode(stream, inst),
 
             .ref,