pub fn parseString(string_repr: String, builder: *Builder) Allocator.Error!DataLayout {
const gpa = builder.gpa;
var int_specs: PrimitiveSpec.Map = .empty;
defer int_specs.deinit(gpa);
var float_specs: PrimitiveSpec.Map = .empty;
defer float_specs.deinit(gpa);
var vector_specs: PrimitiveSpec.Map = .empty;
defer vector_specs.deinit(gpa);
var pointer_specs: PointerSpec.Map = .empty;
defer pointer_specs.deinit(gpa);
var non_integral_addr_spaces: std.ArrayList(AddrSpace) = .empty;
defer non_integral_addr_spaces.deinit(gpa);
try int_specs.ensureTotalCapacity(gpa, default_int_specs.len);
for (default_int_specs) |int_spec| int_specs.putAssumeCapacityNoClobber(int_spec, {});
try float_specs.ensureTotalCapacity(gpa, default_float_specs.len);
for (default_float_specs) |float_spec| float_specs.putAssumeCapacityNoClobber(float_spec, {});
try vector_specs.ensureTotalCapacity(gpa, default_vector_specs.len);
for (default_vector_specs) |vector_spec| vector_specs.putAssumeCapacityNoClobber(vector_spec, {});
try pointer_specs.putNoClobber(gpa, .default, comptime .{
.bit_width = 64,
.index_bit_width = 64,
.flags = .{
.abi_align = .fromByteUnits(8),
.pref_align = .fromByteUnits(8),
.has_unstable_repr = false,
.has_external_state = false,
.null_ptr_repr = .all_zeros,
},
.addr_space_name = .none,
});
var endian: ?std.lang.Endian = null;
var spec_it = std.mem.splitScalar(u8, string_repr.slice(builder).?, '-');
while (spec_it.next()) |spec| switch (spec[0]) {
else => {},
'E' => {
assert(spec.len == 1);
assert(endian == null);
endian = .big;
},
'e' => {
assert(spec.len == 1);
assert(endian == null);
endian = .little;
},
'p' => {
var field_it = std.mem.splitScalar(u8, spec[1..], ':');
const first = field_it.first();
var has_unstable_repr = false;
var has_external_state = false;
var null_ptr_repr: ?PointerSpec.NullPtrRepr = null;
var addr_space_name: String = .none;
const addr_space = for (first, 0..) |flag, as_start| switch (flag) {
'u' => has_unstable_repr = true,
'e' => has_external_state = true,
'z' => {
assert(null_ptr_repr == null);
null_ptr_repr = .all_zeros;
},
'o' => {
assert(null_ptr_repr == null);
null_ptr_repr = .all_ones;
},
else => {
if (first[first.len - ")".len] != ')') break first[as_start..];
const name_start = std.mem.findScalarPos(u8, first, as_start, '(').?;
addr_space_name = try builder.string(first[name_start + "(".len .. first.len - ")".len]);
break first[as_start..name_start];
},
} else first[first.len..];
const bit_width = std.fmt.parseInt(PointerSpec.BitWidth, field_it.next().?, 10) catch unreachable;
const abi_align: Alignment = .fromByteUnits(std.fmt.parseInt(u64, field_it.next().?, 10) catch unreachable);
const pref_align: Alignment = if (field_it.next()) |pref_align|
.fromByteUnits(std.fmt.parseInt(u64, pref_align, 10) catch unreachable)
else
abi_align;
const index_bit_width = if (field_it.next()) |index_bit_width|
std.fmt.parseInt(PointerSpec.BitWidth, index_bit_width, 10) catch unreachable
else
bit_width;
assert(field_it.peek() == null);
try pointer_specs.put(gpa, switch (addr_space.len) {
0 => .default,
else => @fromBackingInt(std.fmt.parseInt(u24, addr_space, 10) catch unreachable),
}, .{
.bit_width = bit_width,
.index_bit_width = index_bit_width,
.flags = .{
.abi_align = abi_align,
.pref_align = pref_align,
.has_unstable_repr = has_unstable_repr,
.has_external_state = has_external_state,
.null_ptr_repr = null_ptr_repr orelse .all_zeros,
},
.addr_space_name = addr_space_name,
});
},
'i', 'f', 'v' => |kind| {
if (std.mem.eql(u8, spec, "ve")) {
vector_specs.clearRetainingCapacity();
continue;
}
var field_it = std.mem.splitScalar(u8, spec[1..], ':');
const bit_width = std.fmt.parseInt(PrimitiveSpec.BitWidth, field_it.first(), 10) catch unreachable;
const abi_align: Alignment = .fromByteUnits(std.fmt.parseInt(u64, field_it.next().?, 10) catch unreachable);
const pref_align: Alignment = if (field_it.next()) |pref_align|
.fromByteUnits(std.fmt.parseInt(u64, pref_align, 10) catch unreachable)
else
abi_align;
assert(field_it.peek() == null);
const specs = switch (kind) {
else => unreachable,
'i' => &int_specs,
'f' => &float_specs,
'v' => &vector_specs,
};
try specs.put(gpa, .{ .bit_width = bit_width, .abi_align = abi_align, .pref_align = pref_align }, {});
},
'n' => {
var field_it = std.mem.splitScalar(u8, spec[1..], ':');
if (std.mem.eql(u8, field_it.first(), "i")) {
while (field_it.next()) |non_integral_addr_space| try non_integral_addr_spaces.append(
gpa,
@fromBackingInt(std.fmt.parseInt(u24, non_integral_addr_space, 10) catch unreachable),
);
} else {
field_it.reset();
while (field_it.next()) |native_bit_width| {
_ = std.fmt.parseInt(PrimitiveSpec.BitWidth, native_bit_width, 10) catch unreachable;
}
}
},
};
for (non_integral_addr_spaces.items) |non_integral_addr_space| {
const pointer_spec_gop = try pointer_specs.getOrPut(gpa, non_integral_addr_space);
if (!pointer_spec_gop.found_existing) pointer_spec_gop.value_ptr.* = pointer_specs.get(.default).?;
pointer_spec_gop.value_ptr.flags.has_unstable_repr = true;
pointer_spec_gop.value_ptr.flags.has_external_state = false;
}
{
const SortContext = struct {
specs: []const PrimitiveSpec,
pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool {
return ctx.specs[lhs_index].bit_width < ctx.specs[rhs_index].bit_width;
}
};
int_specs.sortUnstable(SortContext{ .specs = int_specs.keys() });
float_specs.sortUnstable(SortContext{ .specs = float_specs.keys() });
vector_specs.sortUnstable(SortContext{ .specs = vector_specs.keys() });
}
{
const SortContext = struct {
addr_spaces: []const AddrSpace,
pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool {
return @backingInt(ctx.addr_spaces[lhs_index]) < @backingInt(ctx.addr_spaces[rhs_index]);
}
};
pointer_specs.sortUnstable(SortContext{ .addr_spaces = pointer_specs.keys() });
assert(pointer_specs.keys()[0] == .default);
}
return .{
.endian = endian,
.int_specs = int_specs.move(),
.float_specs = float_specs.move(),
.vector_specs = vector_specs.move(),
.pointer_specs = pointer_specs.move(),
.string_repr = string_repr,
};
}