mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
LLVM: cache LLVM struct field indexes
This is an optimization to avoid O(N) field index lookups. It's also nicer in terms of DRY; the only tradeoff is memory usage.
This commit is contained in:
parent
48e2ba3b3c
commit
c970dbdfec
@ -825,6 +825,18 @@ pub const Object = struct {
|
||||
/// Memoizes a null `?usize` value.
|
||||
null_opt_usize: Builder.Constant,
|
||||
|
||||
/// When an LLVM struct type is created, an entry is inserted into this
|
||||
/// table for every zig source field of the struct that has a corresponding
|
||||
/// LLVM struct field. comptime fields and 0 bit fields are not included.
|
||||
/// The value is the LLVM struct field index.
|
||||
/// This is denormalized data.
|
||||
struct_field_map: std.AutoHashMapUnmanaged(ZigStructField, c_uint),
|
||||
|
||||
const ZigStructField = struct {
|
||||
struct_ty: InternPool.Index,
|
||||
field_index: u32,
|
||||
};
|
||||
|
||||
pub const TypeMap = std.AutoHashMapUnmanaged(InternPool.Index, Builder.Type);
|
||||
|
||||
/// This is an ArrayHashMap as opposed to a HashMap because in `flushModule` we
|
||||
@ -979,6 +991,7 @@ pub const Object = struct {
|
||||
.error_name_table = .none,
|
||||
.extern_collisions = .{},
|
||||
.null_opt_usize = .no_init,
|
||||
.struct_field_map = .{},
|
||||
};
|
||||
}
|
||||
|
||||
@ -994,6 +1007,7 @@ pub const Object = struct {
|
||||
self.type_map.deinit(gpa);
|
||||
self.extern_collisions.deinit(gpa);
|
||||
self.builder.deinit();
|
||||
self.struct_field_map.deinit(gpa);
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
@ -3274,7 +3288,10 @@ pub const Object = struct {
|
||||
|
||||
var llvm_field_types = std.ArrayListUnmanaged(Builder.Type){};
|
||||
defer llvm_field_types.deinit(o.gpa);
|
||||
// Although we can estimate how much capacity to add, these cannot be
|
||||
// relied upon because of the recursive calls to lowerType below.
|
||||
try llvm_field_types.ensureUnusedCapacity(o.gpa, struct_obj.fields.count());
|
||||
try o.struct_field_map.ensureUnusedCapacity(o.gpa, @intCast(struct_obj.fields.count()));
|
||||
|
||||
comptime assert(struct_layout_version == 2);
|
||||
var offset: u64 = 0;
|
||||
@ -3296,6 +3313,10 @@ pub const Object = struct {
|
||||
o.gpa,
|
||||
try o.builder.arrayType(padding_len, .i8),
|
||||
);
|
||||
try o.struct_field_map.put(o.gpa, .{
|
||||
.struct_ty = t.toIntern(),
|
||||
.field_index = field_and_index.index,
|
||||
}, @intCast(llvm_field_types.items.len));
|
||||
try llvm_field_types.append(o.gpa, try o.lowerType(field.ty));
|
||||
|
||||
offset += field.ty.abiSize(mod);
|
||||
@ -3319,7 +3340,10 @@ pub const Object = struct {
|
||||
.anon_struct_type => |anon_struct_type| {
|
||||
var llvm_field_types: std.ArrayListUnmanaged(Builder.Type) = .{};
|
||||
defer llvm_field_types.deinit(o.gpa);
|
||||
// Although we can estimate how much capacity to add, these cannot be
|
||||
// relied upon because of the recursive calls to lowerType below.
|
||||
try llvm_field_types.ensureUnusedCapacity(o.gpa, anon_struct_type.types.len);
|
||||
try o.struct_field_map.ensureUnusedCapacity(o.gpa, anon_struct_type.types.len);
|
||||
|
||||
comptime assert(struct_layout_version == 2);
|
||||
var offset: u64 = 0;
|
||||
@ -3328,7 +3352,8 @@ pub const Object = struct {
|
||||
for (
|
||||
anon_struct_type.types.get(ip),
|
||||
anon_struct_type.values.get(ip),
|
||||
) |field_ty, field_val| {
|
||||
0..,
|
||||
) |field_ty, field_val, field_index| {
|
||||
if (field_val != .none or !field_ty.toType().hasRuntimeBits(mod)) continue;
|
||||
|
||||
const field_align = field_ty.toType().abiAlignment(mod);
|
||||
@ -3341,6 +3366,10 @@ pub const Object = struct {
|
||||
o.gpa,
|
||||
try o.builder.arrayType(padding_len, .i8),
|
||||
);
|
||||
try o.struct_field_map.put(o.gpa, .{
|
||||
.struct_ty = t.toIntern(),
|
||||
.field_index = @intCast(field_index),
|
||||
}, @intCast(llvm_field_types.items.len));
|
||||
try llvm_field_types.append(o.gpa, try o.lowerType(field_ty.toType()));
|
||||
|
||||
offset += field_ty.toType().abiSize(mod);
|
||||
@ -4237,9 +4266,9 @@ pub const Object = struct {
|
||||
try o.lowerType(parent_ty),
|
||||
parent_ptr,
|
||||
null,
|
||||
if (llvmField(parent_ty, field_index, mod)) |llvm_field| &.{
|
||||
if (o.llvmFieldIndex(parent_ty, field_index)) |llvm_field_index| &.{
|
||||
try o.builder.intConst(.i32, 0),
|
||||
try o.builder.intConst(.i32, llvm_field.index),
|
||||
try o.builder.intConst(.i32, llvm_field_index),
|
||||
} else &.{
|
||||
try o.builder.intConst(.i32, @intFromBool(
|
||||
parent_ty.hasRuntimeBitsIgnoreComptime(mod),
|
||||
@ -4396,6 +4425,13 @@ pub const Object = struct {
|
||||
try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = alignment }, &o.builder);
|
||||
if (byval) try attributes.addParamAttr(llvm_arg_i, .{ .byval = param_llvm_ty }, &o.builder);
|
||||
}
|
||||
|
||||
fn llvmFieldIndex(o: *Object, struct_ty: Type, field_index: usize) ?c_uint {
|
||||
return o.struct_field_map.get(.{
|
||||
.struct_ty = struct_ty.toIntern(),
|
||||
.field_index = @intCast(field_index),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
pub const DeclGen = struct {
|
||||
@ -6142,7 +6178,7 @@ pub const FuncGen = struct {
|
||||
return self.wip.cast(.trunc, shifted_value, elem_llvm_ty, "");
|
||||
},
|
||||
else => {
|
||||
const llvm_field_index = llvmField(struct_ty, field_index, mod).?.index;
|
||||
const llvm_field_index = o.llvmFieldIndex(struct_ty, field_index).?;
|
||||
return self.wip.extractValue(struct_llvm_val, &.{llvm_field_index}, "");
|
||||
},
|
||||
},
|
||||
@ -6169,23 +6205,25 @@ pub const FuncGen = struct {
|
||||
|
||||
switch (struct_ty.zigTypeTag(mod)) {
|
||||
.Struct => {
|
||||
assert(struct_ty.containerLayout(mod) != .Packed);
|
||||
const llvm_field = llvmField(struct_ty, field_index, mod).?;
|
||||
const layout = struct_ty.containerLayout(mod);
|
||||
assert(layout != .Packed);
|
||||
const struct_llvm_ty = try o.lowerType(struct_ty);
|
||||
const llvm_field_index = o.llvmFieldIndex(struct_ty, field_index).?;
|
||||
const field_ptr =
|
||||
try self.wip.gepStruct(struct_llvm_ty, struct_llvm_val, llvm_field.index, "");
|
||||
try self.wip.gepStruct(struct_llvm_ty, struct_llvm_val, llvm_field_index, "");
|
||||
const alignment = struct_ty.structFieldAlign(field_index, mod);
|
||||
const field_ptr_ty = try mod.ptrType(.{
|
||||
.child = llvm_field.ty.toIntern(),
|
||||
.child = field_ty.toIntern(),
|
||||
.flags = .{
|
||||
.alignment = InternPool.Alignment.fromNonzeroByteUnits(llvm_field.alignment),
|
||||
.alignment = InternPool.Alignment.fromNonzeroByteUnits(alignment),
|
||||
},
|
||||
});
|
||||
if (isByRef(field_ty, mod)) {
|
||||
if (canElideLoad(self, body_tail))
|
||||
return field_ptr;
|
||||
|
||||
assert(llvm_field.alignment != 0);
|
||||
const field_alignment = Builder.Alignment.fromByteUnits(llvm_field.alignment);
|
||||
assert(alignment != 0);
|
||||
const field_alignment = Builder.Alignment.fromByteUnits(alignment);
|
||||
return self.loadByRef(field_ptr, field_ty, field_alignment, .normal);
|
||||
} else {
|
||||
return self.load(field_ptr, field_ptr_ty);
|
||||
@ -7080,15 +7118,17 @@ pub const FuncGen = struct {
|
||||
const field_index = ty_pl.payload;
|
||||
|
||||
const mod = o.module;
|
||||
const llvm_field = llvmField(struct_ty, field_index, mod).?;
|
||||
const struct_llvm_ty = try o.lowerType(struct_ty);
|
||||
const llvm_field_index = o.llvmFieldIndex(struct_ty, field_index).?;
|
||||
assert(self.err_ret_trace != .none);
|
||||
const field_ptr =
|
||||
try self.wip.gepStruct(struct_llvm_ty, self.err_ret_trace, llvm_field.index, "");
|
||||
try self.wip.gepStruct(struct_llvm_ty, self.err_ret_trace, llvm_field_index, "");
|
||||
const field_alignment = struct_ty.structFieldAlign(field_index, mod);
|
||||
const field_ty = struct_ty.structFieldType(field_index, mod);
|
||||
const field_ptr_ty = try mod.ptrType(.{
|
||||
.child = llvm_field.ty.toIntern(),
|
||||
.child = field_ty.toIntern(),
|
||||
.flags = .{
|
||||
.alignment = InternPool.Alignment.fromNonzeroByteUnits(llvm_field.alignment),
|
||||
.alignment = InternPool.Alignment.fromNonzeroByteUnits(field_alignment),
|
||||
},
|
||||
});
|
||||
return self.load(field_ptr, field_ptr_ty);
|
||||
@ -7640,8 +7680,8 @@ pub const FuncGen = struct {
|
||||
const result_val = try self.wip.extractValue(results, &.{0}, "");
|
||||
const overflow_bit = try self.wip.extractValue(results, &.{1}, "");
|
||||
|
||||
const result_index = llvmField(inst_ty, 0, mod).?.index;
|
||||
const overflow_index = llvmField(inst_ty, 1, mod).?.index;
|
||||
const result_index = o.llvmFieldIndex(inst_ty, 0).?;
|
||||
const overflow_index = o.llvmFieldIndex(inst_ty, 1).?;
|
||||
|
||||
if (isByRef(inst_ty, mod)) {
|
||||
const result_alignment = Builder.Alignment.fromByteUnits(inst_ty.abiAlignment(mod));
|
||||
@ -7998,8 +8038,8 @@ pub const FuncGen = struct {
|
||||
|
||||
const overflow_bit = try self.wip.icmp(.ne, lhs, reconstructed, "");
|
||||
|
||||
const result_index = llvmField(dest_ty, 0, mod).?.index;
|
||||
const overflow_index = llvmField(dest_ty, 1, mod).?.index;
|
||||
const result_index = o.llvmFieldIndex(dest_ty, 0).?;
|
||||
const overflow_index = o.llvmFieldIndex(dest_ty, 1).?;
|
||||
|
||||
if (isByRef(dest_ty, mod)) {
|
||||
const result_alignment = Builder.Alignment.fromByteUnits(dest_ty.abiAlignment(mod));
|
||||
@ -9616,7 +9656,7 @@ pub const FuncGen = struct {
|
||||
if ((try result_ty.structFieldValueComptime(mod, i)) != null) continue;
|
||||
|
||||
const llvm_elem = try self.resolveInst(elem);
|
||||
const llvm_i = llvmField(result_ty, i, mod).?.index;
|
||||
const llvm_i = o.llvmFieldIndex(result_ty, i).?;
|
||||
const field_ptr =
|
||||
try self.wip.gepStruct(llvm_result_ty, alloca_inst, llvm_i, "");
|
||||
const field_ptr_ty = try mod.ptrType(.{
|
||||
@ -9637,7 +9677,7 @@ pub const FuncGen = struct {
|
||||
if ((try result_ty.structFieldValueComptime(mod, i)) != null) continue;
|
||||
|
||||
const llvm_elem = try self.resolveInst(elem);
|
||||
const llvm_i = llvmField(result_ty, i, mod).?.index;
|
||||
const llvm_i = o.llvmFieldIndex(result_ty, i).?;
|
||||
result = try self.wip.insertValue(result, llvm_elem, &.{llvm_i}, "");
|
||||
}
|
||||
return result;
|
||||
@ -10059,8 +10099,8 @@ pub const FuncGen = struct {
|
||||
else => {
|
||||
const struct_llvm_ty = try o.lowerPtrElemTy(struct_ty);
|
||||
|
||||
if (llvmField(struct_ty, field_index, mod)) |llvm_field| {
|
||||
return self.wip.gepStruct(struct_llvm_ty, struct_ptr, llvm_field.index, "");
|
||||
if (o.llvmFieldIndex(struct_ty, field_index)) |llvm_field_index| {
|
||||
return self.wip.gepStruct(struct_llvm_ty, struct_ptr, llvm_field_index, "");
|
||||
} else {
|
||||
// If we found no index then this means this is a zero sized field at the
|
||||
// end of the struct. Treat our struct pointer as an array of two and get
|
||||
@ -10527,90 +10567,6 @@ fn toLlvmGlobalAddressSpace(wanted_address_space: std.builtin.AddressSpace, targ
|
||||
};
|
||||
}
|
||||
|
||||
const LlvmField = struct {
|
||||
index: c_uint,
|
||||
ty: Type,
|
||||
alignment: u32,
|
||||
};
|
||||
|
||||
/// Take into account 0 bit fields and padding. Returns null if an llvm
|
||||
/// field could not be found.
|
||||
/// This only happens if you want the field index of a zero sized field at
|
||||
/// the end of the struct.
|
||||
fn llvmField(ty: Type, field_index: usize, mod: *Module) ?LlvmField {
|
||||
// Detects where we inserted extra padding fields so that we can skip
|
||||
// over them in this function.
|
||||
comptime assert(struct_layout_version == 2);
|
||||
var offset: u64 = 0;
|
||||
var big_align: u32 = 0;
|
||||
|
||||
const ip = &mod.intern_pool;
|
||||
const struct_type = switch (ip.indexToKey(ty.toIntern())) {
|
||||
.anon_struct_type => |tuple| {
|
||||
var llvm_field_index: c_uint = 0;
|
||||
for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, field_val, i| {
|
||||
if (field_val != .none or !field_ty.toType().hasRuntimeBits(mod)) continue;
|
||||
|
||||
const field_align = field_ty.toType().abiAlignment(mod);
|
||||
big_align = @max(big_align, field_align);
|
||||
const prev_offset = offset;
|
||||
offset = std.mem.alignForward(u64, offset, field_align);
|
||||
|
||||
const padding_len = offset - prev_offset;
|
||||
if (padding_len > 0) {
|
||||
llvm_field_index += 1;
|
||||
}
|
||||
|
||||
if (field_index <= i) {
|
||||
return .{
|
||||
.index = llvm_field_index,
|
||||
.ty = field_ty.toType(),
|
||||
.alignment = field_align,
|
||||
};
|
||||
}
|
||||
|
||||
llvm_field_index += 1;
|
||||
offset += field_ty.toType().abiSize(mod);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
.struct_type => |s| s,
|
||||
else => unreachable,
|
||||
};
|
||||
const struct_obj = mod.structPtrUnwrap(struct_type.index).?;
|
||||
const layout = struct_obj.layout;
|
||||
assert(layout != .Packed);
|
||||
|
||||
var llvm_field_index: c_uint = 0;
|
||||
var it = struct_obj.runtimeFieldIterator(mod);
|
||||
while (it.next()) |field_and_index| {
|
||||
const field = field_and_index.field;
|
||||
const field_align = field.alignment(mod, layout);
|
||||
big_align = @max(big_align, field_align);
|
||||
const prev_offset = offset;
|
||||
offset = std.mem.alignForward(u64, offset, field_align);
|
||||
|
||||
const padding_len = offset - prev_offset;
|
||||
if (padding_len > 0) {
|
||||
llvm_field_index += 1;
|
||||
}
|
||||
|
||||
if (field_index == field_and_index.index) {
|
||||
return .{
|
||||
.index = llvm_field_index,
|
||||
.ty = field.ty,
|
||||
.alignment = field_align,
|
||||
};
|
||||
}
|
||||
|
||||
llvm_field_index += 1;
|
||||
offset += field.ty.abiSize(mod);
|
||||
} else {
|
||||
// We did not find an llvm field that corresponds to this zig field.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
fn firstParamSRet(fn_info: InternPool.Key.FuncType, mod: *Module) bool {
|
||||
const return_type = fn_info.return_type.toType();
|
||||
if (!return_type.hasRuntimeBitsIgnoreComptime(mod)) return false;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user