stage2: implement @bitSizeOf

This commit is contained in:
Andrew Kelley
2021-04-12 21:46:20 -07:00
parent df983b30d2
commit 6dba9bc6fc
4 changed files with 167 additions and 1 deletions

View File

@@ -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,

View File

@@ -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();

View File

@@ -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()) {

View File

@@ -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,