Bits
All checks were successful
CI / ci (ReleaseFast, ubuntu-latest) (push) Successful in 56s
CI / ci (ReleaseSmall, ubuntu-latest) (push) Successful in 54s
CI / ci (ReleaseSafe, ubuntu-latest) (push) Successful in 1m2s
CI / ci (Debug, ubuntu-latest) (push) Successful in 1m32s

This commit is contained in:
David Gonzalez Martin 2025-04-12 06:51:02 -06:00
parent 30b0b2a259
commit 4338755dbd
2 changed files with 400 additions and 38 deletions

View File

@ -223,7 +223,7 @@ pub const Type = struct {
integer: Type.Integer, integer: Type.Integer,
enumerator: Enumerator, enumerator: Enumerator,
float, float,
bits, bits: Type.Bits,
pointer: Type.Pointer, pointer: Type.Pointer,
function: Type.Function, function: Type.Function,
array: Type.Array, array: Type.Array,
@ -260,6 +260,7 @@ pub const Type = struct {
const struct_type = module.llvm.context.get_struct_type(llvm_types); const struct_type = module.llvm.context.get_struct_type(llvm_types);
break :blk struct_type.to_type(); break :blk struct_type.to_type();
}, },
.bits => |bits| bits.backing_type.resolve(module).handle,
else => @trap(), else => @trap(),
}; };
ty.llvm.handle = llvm_type; ty.llvm.handle = llvm_type;
@ -317,6 +318,16 @@ pub const Type = struct {
forward_declared.replace_all_uses_with(debug_struct_type); forward_declared.replace_all_uses_with(debug_struct_type);
break :blk debug_struct_type.to_type(); break :blk debug_struct_type.to_type();
}, },
.bits => |bits| blk: {
var llvm_debug_member_type_buffer: [64]*llvm.DI.Type.Derived = undefined;
const llvm_debug_member_types = llvm_debug_member_type_buffer[0..bits.fields.len];
for (bits.fields, llvm_debug_member_types) |field, *llvm_debug_member_type| {
llvm_debug_member_type.* = module.llvm.di_builder.create_bit_field_member_type(module.scope.llvm.?, field.name, module.llvm.file, field.line, field.type.get_bit_size(), field.bit_offset, 0, .{}, bits.backing_type.llvm.debug.?);
}
const struct_type = module.llvm.di_builder.create_struct_type(module.scope.llvm.?, ty.name, module.llvm.file, bits.line, ty.get_bit_size(), @intCast(ty.get_bit_alignment()), .{}, llvm_debug_member_types);
break :blk struct_type.to_type();
},
else => @trap(), else => @trap(),
} else null; } else null;
ty.llvm.debug = debug_type; ty.llvm.debug = debug_type;
@ -328,6 +339,13 @@ pub const Type = struct {
} }
} }
const Bits = struct {
fields: []const Field,
backing_type: *Type,
line: u32,
implicit_backing_type: bool,
};
pub const Integer = struct { pub const Integer = struct {
bit_count: u32, bit_count: u32,
signed: bool, signed: bool,
@ -425,8 +443,7 @@ pub const Type = struct {
8, 16, 32, 64, 128 => false, 8, 16, 32, 64, 128 => false,
else => true, else => true,
}, },
.bits => @trap(), .bits => |bits| bits.backing_type.is_arbitrary_bit_integer(),
// .bits => |bits| bits.backing_type.is_arbitrary_bit_integer(),
else => false, else => false,
}; };
} }
@ -448,6 +465,7 @@ pub const Type = struct {
.array => |array| array.element_type.get_byte_alignment(), .array => |array| array.element_type.get_byte_alignment(),
.enumerator => |enumerator| enumerator.backing_type.get_byte_alignment(), .enumerator => |enumerator| enumerator.backing_type.get_byte_alignment(),
.structure => |structure| structure.byte_alignment, .structure => |structure| structure.byte_alignment,
.bits => |bits| bits.backing_type.get_byte_alignment(),
else => @trap(), else => @trap(),
}; };
return result; return result;
@ -457,6 +475,7 @@ pub const Type = struct {
return switch (ty.bb) { return switch (ty.bb) {
.integer => |integer| integer.bit_count, // TODO: is this correct? .integer => |integer| integer.bit_count, // TODO: is this correct?
.pointer => 64, .pointer => 64,
.bits => |bits| bits.backing_type.get_bit_alignment(),
else => @trap(), else => @trap(),
}; };
} }
@ -474,6 +493,7 @@ pub const Type = struct {
const bit_size: u64 = switch (ty.bb) { const bit_size: u64 = switch (ty.bb) {
.integer => |integer| integer.bit_count, .integer => |integer| integer.bit_count,
.pointer => 64, .pointer => 64,
.bits => |bits| bits.backing_type.get_bit_size(),
else => @trap(), else => @trap(),
}; };
return bit_size; return bit_size;
@ -641,6 +661,8 @@ pub const Value = struct {
enum_literal: []const u8, enum_literal: []const u8,
field_access: FieldAccess, field_access: FieldAccess,
string_literal: []const u8, string_literal: []const u8,
aggregate_initialization: AggregateInitialization,
zero,
}, },
type: ?*Type = null, type: ?*Type = null,
llvm: ?*llvm.Value = null, llvm: ?*llvm.Value = null,
@ -656,6 +678,12 @@ pub const Value = struct {
is_constant: bool, is_constant: bool,
}; };
pub const AggregateInitialization = struct {
names: []const []const u8,
values: []const *Value,
is_constant: bool,
zero: bool,
};
const Intrinsic = union(Id) { const Intrinsic = union(Id) {
byte_size: *Type, byte_size: *Type,
@ -1654,6 +1682,19 @@ pub const Module = struct {
}; };
count += 1; count += 1;
r[@intFromEnum(Token.Id.@"{")] = .{
.before = rule_before_brace,
.after = null, // TODO: is this correct?
.precedence = .none,
};
count += 1;
r[@intFromEnum(Token.Id.@"}")] = .{
.before = null,
.after = null,
.precedence = .none,
};
count += 1;
r[@intFromEnum(Token.Id.@",")] = .{ r[@intFromEnum(Token.Id.@",")] = .{
.before = null, .before = null,
.after = null, .after = null,
@ -1905,6 +1946,14 @@ pub const Module = struct {
module.offset += 1; module.offset += 1;
break :blk .@"]"; break :blk .@"]";
}, },
left_brace => blk: {
module.offset += 1;
break :blk .@"{";
},
right_brace => blk: {
module.offset += 1;
break :blk .@"}";
},
',' => blk: { ',' => blk: {
module.offset += 1; module.offset += 1;
break :blk .@","; break :blk .@",";
@ -1919,7 +1968,7 @@ pub const Module = struct {
} }
} }
const string_slice = module.content[string_literal_start..][0..(module.offset - 1) - string_literal_start]; const string_slice = module.content[string_literal_start..][0 .. (module.offset - 1) - string_literal_start];
break :blk .{ break :blk .{
.string_literal = string_slice, .string_literal = string_slice,
@ -2123,7 +2172,7 @@ pub const Module = struct {
}; };
}, },
.@"while" => { .@"while" => {
module.skip_space(); module.skip_space();
module.expect_character(left_parenthesis); module.expect_character(left_parenthesis);
module.skip_space(); module.skip_space();
@ -2348,7 +2397,7 @@ pub const Module = struct {
}, },
.kind = if (variable.type) |t| switch (t.bb) { .kind = if (variable.type) |t| switch (t.bb) {
.array, .function, .structure => .left, .array, .function, .structure => .left,
.integer, .pointer, .enumerator => value_builder.kind, .integer, .pointer, .enumerator, .bits => value_builder.kind,
else => @trap(), else => @trap(),
} else value_builder.kind, } else value_builder.kind,
// if (variable.type != null and variable.type.?.bb == .function) .left else value_builder.kind, // if (variable.type != null and variable.type.?.bb == .function) .left else value_builder.kind,
@ -2357,9 +2406,15 @@ pub const Module = struct {
} }
fn rule_before_value_keyword(noalias module: *Module, value_builder: Value.Builder) *Value { fn rule_before_value_keyword(noalias module: *Module, value_builder: Value.Builder) *Value {
_ = value_builder; const value = module.values.add();
_ = module; const new_value: Value = switch (value_builder.token.value_keyword) {
@trap(); .zero => .{
.bb = .zero,
},
else => @trap(),
};
value.* = new_value;
return value;
} }
fn rule_before_value_intrinsic(noalias module: *Module, value_builder: Value.Builder) *Value { fn rule_before_value_intrinsic(noalias module: *Module, value_builder: Value.Builder) *Value {
@ -2603,6 +2658,84 @@ pub const Module = struct {
return value; return value;
} }
fn rule_before_brace(noalias module: *Module, value_builder: Value.Builder) *Value {
assert(value_builder.left == null);
var name_buffer: [64][]const u8 = undefined;
var value_buffer: [64]*Value = undefined;
var field_count: u32 = 0;
var zero = false;
while (true) : (field_count += 1) {
module.skip_space();
if (module.consume_character_if_match(right_brace)) {
break;
}
if (module.consume_character_if_match('.')) {
const name = module.parse_identifier();
name_buffer[field_count] = name;
module.skip_space();
module.expect_character('=');
module.skip_space();
const value = module.parse_value(.{});
value_buffer[field_count] = value;
module.skip_space();
_ = module.consume_character_if_match(',');
} else {
const token = module.tokenize();
switch (token) {
.value_keyword => |vkw| switch (vkw) {
.zero => {
zero = true;
module.skip_space();
if (module.consume_character_if_match(',')) {
module.skip_space();
}
module.expect_character(right_brace);
break;
},
else => module.report_error(),
},
else => module.report_error(),
}
}
}
const blob = module.arena.allocate_bytes(field_count * @sizeOf([]const u8) + field_count * @sizeOf(*Value), @max(@alignOf([]const u8), @alignOf(*Value)));
const names = @as([*][]const u8, @alignCast(@ptrCast(blob)))[0..field_count];
@memcpy(names, name_buffer[0..field_count]);
const values = @as([*]*Value, @alignCast(@ptrCast(blob + (@sizeOf([]const u8) * field_count))))[0..field_count];
@memcpy(values, value_buffer[0..field_count]);
const value = module.values.add();
value.* = .{
.bb = .{
.aggregate_initialization = .{
.names = names,
.values = values,
.is_constant = false,
.zero = zero,
},
},
};
return value;
}
fn rule_after_brace(noalias module: *Module, value_builder: Value.Builder) *Value {
_ = module;
_ = value_builder;
@trap();
}
// Array initialization // Array initialization
fn rule_before_bracket(noalias module: *Module, value_builder: Value.Builder) *Value { fn rule_before_bracket(noalias module: *Module, value_builder: Value.Builder) *Value {
assert(value_builder.left == null); assert(value_builder.left == null);
@ -2713,10 +2846,10 @@ pub const Module = struct {
_ = struct_type; _ = struct_type;
@trap(); @trap();
} else blk: { } else blk: {
const byte_alignment = @max(pair[0].get_byte_alignment(), pair[1].get_byte_alignment()); const byte_alignment = @max(pair[0].get_byte_alignment(), pair[1].get_byte_alignment());
const byte_size = lib.align_forward_u64(pair[0].get_byte_size() + pair[1].get_byte_size(), byte_alignment); const byte_size = lib.align_forward_u64(pair[0].get_byte_size() + pair[1].get_byte_size(), byte_alignment);
const fields = module.arena.allocate(Field, 2); const fields = module.arena.allocate(Field, 2);
fields[0] = .{ fields[0] = .{
.bit_offset = 0, .bit_offset = 0,
.byte_offset = 0, .byte_offset = 0,
@ -3077,6 +3210,88 @@ pub const Module = struct {
.name = global_name, .name = global_name,
}); });
}, },
.bits => {
const is_implicit_type = module.content[module.offset] == left_brace;
const maybe_backing_type: ?*Type = switch (is_implicit_type) {
true => null,
false => module.parse_type(),
};
module.skip_space();
module.expect_character(left_brace);
var field_buffer: [128]Field = undefined;
var field_line_buffer: [128]u32 = undefined;
var field_count: u64 = 0;
var field_bit_offset: u64 = 0;
while (true) : (field_count += 1) {
module.skip_space();
if (module.consume_character_if_match(right_brace)) {
break;
}
const field_line = module.get_line();
field_line_buffer[field_count] = field_line;
const field_name = module.parse_identifier();
module.skip_space();
module.expect_character(':');
module.skip_space();
const field_type = module.parse_type();
field_buffer[field_count] = .{
.name = field_name,
.type = field_type,
.bit_offset = field_bit_offset,
.byte_offset = 0,
.line = field_line,
};
const field_bit_size = field_type.get_bit_size();
field_bit_offset += field_bit_size;
module.skip_space();
_ = module.consume_character_if_match(',');
}
_ = module.consume_character_if_match(';');
const fields = module.arena.allocate(Field, field_count);
@memcpy(fields, field_buffer[0..field_count]);
// const field_lines = field_line_buffer[0..field_count];
const backing_type = if (maybe_backing_type) |bt| bt else module.integer_type(@intCast(@max(8, lib.next_power_of_two(field_bit_offset))), false);
if (backing_type.bb != .integer) {
module.report_error();
}
if (backing_type.get_bit_size() > 64) {
module.report_error();
}
_ = module.types.append(.{
.name = global_name,
.bb = .{
.bits = .{
.fields = fields,
.backing_type = backing_type,
.line = global_line,
.implicit_backing_type = is_implicit_type,
},
},
});
},
else => @trap(), else => @trap(),
} }
} else { } else {
@ -3780,11 +3995,11 @@ pub const Module = struct {
.is_slice = false, .is_slice = false,
.line = 0, .line = 0,
}, },
}, },
}); });
const element_count = 1; const element_count = 1;
const element_type = result; const element_type = result;
const ty = module.types.append(.{ const ty = module.types.append(.{
.name = array_type_name(module.arena, element_type, element_count), .name = array_type_name(module.arena, element_type, element_count),
.bb = .{ .bb = .{
@ -4065,6 +4280,76 @@ pub const Module = struct {
// module.report_error(); // module.report_error();
// }; // };
}, },
.aggregate_initialization => |*aggregate_initialization| {
switch (expected_type.bb) {
.bits => |bits| {
var is_ordered = true;
var is_constant = true;
for (aggregate_initialization.names, aggregate_initialization.values, 0..) |field_name, field_value, initialization_index| {
const declaration_index = for (bits.fields, 0..) |field, declaration_index| {
if (lib.string.equal(field.name, field_name)) {
break declaration_index;
}
} else module.report_error();
is_ordered = is_ordered and declaration_index == initialization_index;
const field = &bits.fields[declaration_index];
const declaration_type = field.type;
module.analyze_value_type(function, field_value, .{ .type = declaration_type });
is_constant = is_constant and field_value.is_constant();
}
aggregate_initialization.is_constant = is_constant;
},
else => @trap(),
}
},
.field_access => |field_access| {
module.analyze_value_type(function, field_access.aggregate, .{});
const field_name = field_access.field;
switch (field_access.aggregate.kind) {
.left => switch (field_access.aggregate.type.?.bb) {
.pointer => |pointer| switch (pointer.type.bb) {
.structure => |structure| {
const field_type = for (structure.fields) |*field| {
if (lib.string.equal(field_name, field.name)) {
break field.type;
}
} else {
module.report_error();
};
_ = field_type;
@trap();
// break :blk switch (value.kind) {
// .left => @trap(),
// .right => field_type,
// };
},
else => @trap(),
},
else => module.report_error(),
},
.right => switch (field_access.aggregate.type.?.bb) {
.bits => |bits| {
const field_type = for (bits.fields) |*field| {
if (lib.string.equal(field_name, field.name)) {
break field.type;
}
} else {
module.report_error();
};
switch (value.kind) {
.left => module.report_error(),
.right => if (field_type != expected_type) {
module.report_error();
},
}
},
else => @trap(),
},
}
},
.zero => {},
else => @trap(), else => @trap(),
}; };
@ -4214,28 +4499,45 @@ pub const Module = struct {
} }
}, },
.field_access => |field_access| blk: { .field_access => |field_access| blk: {
assert(field_access.aggregate.kind == .left);
module.analyze_value_type(function, field_access.aggregate, .{}); module.analyze_value_type(function, field_access.aggregate, .{});
const field_name = field_access.field; const field_name = field_access.field;
switch (field_access.aggregate.type.?.bb) { switch (field_access.aggregate.kind) {
.pointer => |pointer| switch (pointer.type.bb) { .left => switch (field_access.aggregate.type.?.bb) {
.structure => |structure| { .pointer => |pointer| switch (pointer.type.bb) {
const field_type = for (structure.fields) |*field| { .structure => |structure| {
const field_type = for (structure.fields) |*field| {
if (lib.string.equal(field_name, field.name)) {
break field.type;
}
} else {
module.report_error();
};
break :blk switch (value.kind) {
.left => @trap(),
.right => field_type,
};
},
else => @trap(),
},
else => module.report_error(),
},
.right => switch (field_access.aggregate.type.?.bb) {
.bits => |bits| {
const field_type = for (bits.fields) |*field| {
if (lib.string.equal(field_name, field.name)) { if (lib.string.equal(field_name, field.name)) {
break field.type; break field.type;
} }
} else { } else {
module.report_error(); module.report_error();
}; };
break :blk switch (value.kind) { break :blk switch (value.kind) {
.left => @trap(), .left => module.report_error(),
.right => field_type, .right => field_type,
}; };
}, },
else => @trap(), else => @trap(),
}, },
else => module.report_error(),
} }
}, },
.string_literal => module.get_slice_type(.{ .type = module.integer_type(8, false) }), .string_literal => module.get_slice_type(.{ .type = module.integer_type(8, false) }),
@ -4969,12 +5271,15 @@ pub const Module = struct {
const gep = module.llvm.builder.create_gep(.{ const gep = module.llvm.builder.create_gep(.{
.type = element_type.llvm.handle.?, .type = element_type.llvm.handle.?,
.aggregate = pointer_load, .aggregate = pointer_load,
.indices = &.{ array_expression.index.llvm.? }, .indices = &.{array_expression.index.llvm.?},
}); });
break :blk switch (value.kind) { break :blk switch (value.kind) {
.left => gep, .left => gep,
.right => module.create_load(.{ .type = element_type, .value = gep, }), .right => module.create_load(.{
.type = element_type,
.value = gep,
}),
}; };
}, },
else => @trap(), else => @trap(),
@ -5032,29 +5337,76 @@ pub const Module = struct {
.field_access => |field_access| blk: { .field_access => |field_access| blk: {
module.emit_value(function, field_access.aggregate); module.emit_value(function, field_access.aggregate);
const field_name = field_access.field; const field_name = field_access.field;
switch (field_access.aggregate.type.?.bb) { switch (field_access.aggregate.kind) {
.pointer => |pointer| switch (pointer.type.bb) { .left => switch (field_access.aggregate.type.?.bb) {
.structure => |structure| { .pointer => |pointer| switch (pointer.type.bb) {
const field_index: u32 = for (structure.fields, 0..) |field, field_index| { .structure => |structure| {
const field_index: u32 = for (structure.fields, 0..) |field, field_index| {
if (lib.string.equal(field_name, field.name)) {
break @intCast(field_index);
}
} else module.report_error();
const gep = module.llvm.builder.create_struct_gep(pointer.type.resolve(module).handle.to_struct(), field_access.aggregate.llvm.?, field_index);
break :blk switch (value.kind) {
.left => gep,
.right => module.create_load(.{
.type = structure.fields[field_index].type,
.value = gep,
}),
};
},
else => @trap(),
},
else => @trap(),
},
.right => switch (field_access.aggregate.type.?.bb) {
.bits => |bits| {
const field_index: u32 = for (bits.fields, 0..) |field, field_index| {
if (lib.string.equal(field_name, field.name)) { if (lib.string.equal(field_name, field.name)) {
break @intCast(field_index); break @intCast(field_index);
} }
} else module.report_error(); } else module.report_error();
const field = bits.fields[field_index];
const gep = module.llvm.builder.create_struct_gep(pointer.type.resolve(module).handle.to_struct(), field_access.aggregate.llvm.?, field_index); const shift = module.llvm.builder.create_lshr(field_access.aggregate.llvm.?, bits.backing_type.llvm.handle.?.to_integer().get_constant(field.bit_offset, 0).to_value());
break :blk switch (value.kind) { const trunc = module.llvm.builder.create_truncate(shift, field.type.resolve(module).handle);
.left => gep, break :blk trunc;
.right => module.create_load(.{
.type = structure.fields[field_index].type,
.value = gep,
}),
};
}, },
else => @trap(), else => @trap(),
}, },
else => @trap(),
} }
}, },
.aggregate_initialization => |aggregate_initialization| switch (value_type.bb) {
.bits => |bits| switch (aggregate_initialization.is_constant) {
true => blk: {
var bits_value: u64 = 0;
for (aggregate_initialization.names, aggregate_initialization.values) |field_name, field_value| {
const declaration_index = for (bits.fields, 0..) |field, declaration_index| {
if (lib.string.equal(field_name, field.name)) {
break declaration_index;
}
} else unreachable;
const field = &bits.fields[declaration_index];
_ = field.bit_offset;
const fv = switch (field_value.bb) {
.constant_integer => |ci| ci.value,
else => @trap(),
};
bits_value |= fv << @intCast(field.bit_offset);
}
const llvm_value = bits.backing_type.resolve(module).handle.to_integer().get_constant(bits_value, @intFromBool(false));
break :blk llvm_value.to_value();
},
false => {
@trap();
},
},
else => @trap(),
},
.zero => value_type.resolve(module).handle.get_zero().to_value(),
else => @trap(), else => @trap(),
}; };
@ -5446,7 +5798,7 @@ pub const Module = struct {
const u8_type = module.integer_type(8, false); const u8_type = module.integer_type(8, false);
const global_variable = module.llvm.module.create_global_variable(.{ const global_variable = module.llvm.module.create_global_variable(.{
.linkage = .InternalLinkage, .linkage = .InternalLinkage,
.name = module.arena.join_string(&.{ "conststring" }), .name = module.arena.join_string(&.{"conststring"}),
.initial_value = constant_string, .initial_value = constant_string,
.type = u8_type.llvm.handle.?.get_array_type(string_literal.len + @intFromBool(null_terminate)).to_type(), .type = u8_type.llvm.handle.?.get_array_type(string_literal.len + @intFromBool(null_terminate)).to_type(),
}); });
@ -5718,6 +6070,9 @@ const Token = union(Id) {
// Bracket // Bracket
@"[", @"[",
@"]", @"]",
// Brace
@"{",
@"}",
@",", @",",
@".", @".",
@ -5779,6 +6134,9 @@ const Token = union(Id) {
// Bracket // Bracket
@"[", @"[",
@"]", @"]",
// Brace
@"{",
@"}",
@",", @",",
@".", @".",

View File

@ -213,4 +213,8 @@ const names = &[_][]const u8{
"basic_varargs", "basic_varargs",
"basic_while", "basic_while",
"not_pointer", "not_pointer",
"bits",
"bits_no_backing_type",
"bits_return_u1",
"bits_zero",
}; };