Skip to content

Commit e867127

Browse files
authored
Merge pull request #12127 from ziglang/llvm-abi-size
Audit LLVM ABI size
2 parents 6ba2fb3 + 9329b93 commit e867127

File tree

3 files changed

+107
-25
lines changed

3 files changed

+107
-25
lines changed

src/codegen/llvm.zig

Lines changed: 95 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2424,6 +2424,24 @@ pub const DeclGen = struct {
24242424
}
24252425

24262426
fn lowerType(dg: *DeclGen, t: Type) Allocator.Error!*const llvm.Type {
2427+
const llvm_ty = try lowerTypeInner(dg, t);
2428+
if (std.debug.runtime_safety and false) check: {
2429+
if (t.zigTypeTag() == .Opaque) break :check;
2430+
if (!t.hasRuntimeBits()) break :check;
2431+
if (!llvm_ty.isSized().toBool()) break :check;
2432+
2433+
const zig_size = t.abiSize(dg.module.getTarget());
2434+
const llvm_size = dg.object.target_data.abiSizeOfType(llvm_ty);
2435+
if (llvm_size != zig_size) {
2436+
log.err("when lowering {}, Zig ABI size = {d} but LLVM ABI size = {d}", .{
2437+
t.fmt(dg.module), zig_size, llvm_size,
2438+
});
2439+
}
2440+
}
2441+
return llvm_ty;
2442+
}
2443+
2444+
fn lowerTypeInner(dg: *DeclGen, t: Type) Allocator.Error!*const llvm.Type {
24272445
const gpa = dg.gpa;
24282446
const target = dg.module.getTarget();
24292447
switch (t.zigTypeTag()) {
@@ -2519,10 +2537,18 @@ pub const DeclGen = struct {
25192537
return payload_llvm_ty;
25202538
}
25212539

2522-
const fields: [2]*const llvm.Type = .{
2523-
payload_llvm_ty, dg.context.intType(1),
2540+
comptime assert(optional_layout_version == 2);
2541+
var fields_buf: [3]*const llvm.Type = .{
2542+
payload_llvm_ty, dg.context.intType(1), undefined,
25242543
};
2525-
return dg.context.structType(&fields, fields.len, .False);
2544+
const offset = child_ty.abiSize(target) + 1;
2545+
const abi_size = t.abiSize(target);
2546+
const padding = @intCast(c_uint, abi_size - offset);
2547+
if (padding == 0) {
2548+
return dg.context.structType(&fields_buf, 2, .False);
2549+
}
2550+
fields_buf[2] = dg.context.intType(8).arrayType(padding);
2551+
return dg.context.structType(&fields_buf, 3, .False);
25262552
},
25272553
.ErrorUnion => {
25282554
const payload_ty = t.errorUnionPayload();
@@ -2534,12 +2560,37 @@ pub const DeclGen = struct {
25342560

25352561
const payload_align = payload_ty.abiAlignment(target);
25362562
const error_align = Type.anyerror.abiAlignment(target);
2563+
2564+
const payload_size = payload_ty.abiSize(target);
2565+
const error_size = Type.anyerror.abiSize(target);
2566+
2567+
var fields_buf: [3]*const llvm.Type = undefined;
25372568
if (error_align > payload_align) {
2538-
const fields: [2]*const llvm.Type = .{ llvm_error_type, llvm_payload_type };
2539-
return dg.context.structType(&fields, fields.len, .False);
2569+
fields_buf[0] = llvm_error_type;
2570+
fields_buf[1] = llvm_payload_type;
2571+
const payload_end =
2572+
std.mem.alignForwardGeneric(u64, error_size, payload_align) +
2573+
payload_size;
2574+
const abi_size = std.mem.alignForwardGeneric(u64, payload_end, error_align);
2575+
const padding = @intCast(c_uint, abi_size - payload_end);
2576+
if (padding == 0) {
2577+
return dg.context.structType(&fields_buf, 2, .False);
2578+
}
2579+
fields_buf[2] = dg.context.intType(8).arrayType(padding);
2580+
return dg.context.structType(&fields_buf, 3, .False);
25402581
} else {
2541-
const fields: [2]*const llvm.Type = .{ llvm_payload_type, llvm_error_type };
2542-
return dg.context.structType(&fields, fields.len, .False);
2582+
fields_buf[0] = llvm_payload_type;
2583+
fields_buf[1] = llvm_error_type;
2584+
const error_end =
2585+
std.mem.alignForwardGeneric(u64, payload_size, error_align) +
2586+
error_size;
2587+
const abi_size = std.mem.alignForwardGeneric(u64, error_end, payload_align);
2588+
const padding = @intCast(c_uint, abi_size - error_end);
2589+
if (padding == 0) {
2590+
return dg.context.structType(&fields_buf, 2, .False);
2591+
}
2592+
fields_buf[2] = dg.context.intType(8).arrayType(padding);
2593+
return dg.context.structType(&fields_buf, 3, .False);
25432594
}
25442595
},
25452596
.ErrorSet => return dg.context.intType(16),
@@ -2704,7 +2755,7 @@ pub const DeclGen = struct {
27042755
llvm_aligned_field_ty,
27052756
dg.context.intType(8).arrayType(padding_len),
27062757
};
2707-
break :t dg.context.structType(&fields, fields.len, .False);
2758+
break :t dg.context.structType(&fields, fields.len, .True);
27082759
};
27092760

27102761
if (layout.tag_size == 0) {
@@ -3020,7 +3071,7 @@ pub const DeclGen = struct {
30203071
return dg.context.constStruct(
30213072
llvm_elems.ptr,
30223073
@intCast(c_uint, llvm_elems.len),
3023-
.False,
3074+
.True,
30243075
);
30253076
} else {
30263077
const llvm_elem_ty = try dg.lowerType(elem_ty);
@@ -3057,7 +3108,7 @@ pub const DeclGen = struct {
30573108
return dg.context.constStruct(
30583109
llvm_elems.ptr,
30593110
@intCast(c_uint, llvm_elems.len),
3060-
.False,
3111+
.True,
30613112
);
30623113
} else {
30633114
const llvm_elem_ty = try dg.lowerType(elem_ty);
@@ -3074,7 +3125,7 @@ pub const DeclGen = struct {
30743125
const llvm_elems: [1]*const llvm.Value = .{sentinel};
30753126
const need_unnamed = dg.isUnnamedType(elem_ty, llvm_elems[0]);
30763127
if (need_unnamed) {
3077-
return dg.context.constStruct(&llvm_elems, llvm_elems.len, .False);
3128+
return dg.context.constStruct(&llvm_elems, llvm_elems.len, .True);
30783129
} else {
30793130
const llvm_elem_ty = try dg.lowerType(elem_ty);
30803131
return llvm_elem_ty.constArray(&llvm_elems, llvm_elems.len);
@@ -3083,6 +3134,7 @@ pub const DeclGen = struct {
30833134
else => unreachable,
30843135
},
30853136
.Optional => {
3137+
comptime assert(optional_layout_version == 2);
30863138
var buf: Type.Payload.ElemType = undefined;
30873139
const payload_ty = tv.ty.optionalChild(&buf);
30883140
const llvm_i1 = dg.context.intType(1);
@@ -3091,25 +3143,30 @@ pub const DeclGen = struct {
30913143
if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
30923144
return non_null_bit;
30933145
}
3146+
const llvm_ty = try dg.lowerType(tv.ty);
30943147
if (tv.ty.optionalReprIsPayload()) {
30953148
if (tv.val.castTag(.opt_payload)) |payload| {
30963149
return dg.lowerValue(.{ .ty = payload_ty, .val = payload.data });
30973150
} else if (is_pl) {
30983151
return dg.lowerValue(.{ .ty = payload_ty, .val = tv.val });
30993152
} else {
3100-
const llvm_ty = try dg.lowerType(tv.ty);
31013153
return llvm_ty.constNull();
31023154
}
31033155
}
31043156
assert(payload_ty.zigTypeTag() != .Fn);
3105-
const fields: [2]*const llvm.Value = .{
3106-
try dg.lowerValue(.{
3107-
.ty = payload_ty,
3108-
.val = if (tv.val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef),
3109-
}),
3110-
non_null_bit,
3111-
};
3112-
return dg.context.constStruct(&fields, fields.len, .False);
3157+
3158+
const llvm_field_count = llvm_ty.countStructElementTypes();
3159+
var fields_buf: [3]*const llvm.Value = undefined;
3160+
fields_buf[0] = try dg.lowerValue(.{
3161+
.ty = payload_ty,
3162+
.val = if (tv.val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef),
3163+
});
3164+
fields_buf[1] = non_null_bit;
3165+
if (llvm_field_count > 2) {
3166+
assert(llvm_field_count == 3);
3167+
fields_buf[2] = llvm_ty.structGetTypeAtIndex(2).getUndef();
3168+
}
3169+
return dg.context.constStruct(&fields_buf, llvm_field_count, .False);
31133170
},
31143171
.Fn => {
31153172
const fn_decl_index = switch (tv.val.tag()) {
@@ -3155,12 +3212,23 @@ pub const DeclGen = struct {
31553212
.ty = payload_type,
31563213
.val = if (tv.val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef),
31573214
});
3215+
var fields_buf: [3]*const llvm.Value = undefined;
3216+
3217+
const llvm_ty = try dg.lowerType(tv.ty);
3218+
const llvm_field_count = llvm_ty.countStructElementTypes();
3219+
if (llvm_field_count > 2) {
3220+
assert(llvm_field_count == 3);
3221+
fields_buf[2] = llvm_ty.structGetTypeAtIndex(2).getUndef();
3222+
}
3223+
31583224
if (error_align > payload_align) {
3159-
const fields: [2]*const llvm.Value = .{ llvm_error_value, llvm_payload_value };
3160-
return dg.context.constStruct(&fields, fields.len, .False);
3225+
fields_buf[0] = llvm_error_value;
3226+
fields_buf[1] = llvm_payload_value;
3227+
return dg.context.constStruct(&fields_buf, llvm_field_count, .False);
31613228
} else {
3162-
const fields: [2]*const llvm.Value = .{ llvm_payload_value, llvm_error_value };
3163-
return dg.context.constStruct(&fields, fields.len, .False);
3229+
fields_buf[0] = llvm_payload_value;
3230+
fields_buf[1] = llvm_error_value;
3231+
return dg.context.constStruct(&fields_buf, llvm_field_count, .False);
31643232
}
31653233
},
31663234
.Struct => {
@@ -3361,7 +3429,7 @@ pub const DeclGen = struct {
33613429
const fields: [2]*const llvm.Value = .{
33623430
field, dg.context.intType(8).arrayType(padding_len).getUndef(),
33633431
};
3364-
break :p dg.context.constStruct(&fields, fields.len, .False);
3432+
break :p dg.context.constStruct(&fields, fields.len, .True);
33653433
};
33663434

33673435
if (layout.tag_size == 0) {
@@ -5847,6 +5915,7 @@ pub const FuncGen = struct {
58475915
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
58485916
const payload_ty = self.air.typeOf(ty_op.operand);
58495917
const non_null_bit = self.context.intType(1).constAllOnes();
5918+
comptime assert(optional_layout_version == 2);
58505919
if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return non_null_bit;
58515920
const operand = try self.resolveInst(ty_op.operand);
58525921
const optional_ty = self.air.typeOfIndex(inst);
@@ -9300,6 +9369,7 @@ fn intrinsicsAllowed(scalar_ty: Type, target: std.Target) bool {
93009369
/// We can do this because for all types, Zig ABI alignment >= LLVM ABI
93019370
/// alignment.
93029371
const struct_layout_version = 2;
9372+
const optional_layout_version = 2;
93039373

93049374
/// We use the least significant bit of the pointer address to tell us
93059375
/// whether the type is fully resolved. Types that are only fwd declared

src/codegen/llvm/bindings.zig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,12 @@ pub const Type = opaque {
301301

302302
pub const countStructElementTypes = LLVMCountStructElementTypes;
303303
extern fn LLVMCountStructElementTypes(StructTy: *const Type) c_uint;
304+
305+
pub const isOpaqueStruct = LLVMIsOpaqueStruct;
306+
extern fn LLVMIsOpaqueStruct(StructTy: *const Type) Bool;
307+
308+
pub const isSized = LLVMTypeIsSized;
309+
extern fn LLVMTypeIsSized(Ty: *const Type) Bool;
304310
};
305311

306312
pub const Module = opaque {
@@ -1032,6 +1038,9 @@ pub const TargetData = opaque {
10321038

10331039
pub const abiAlignmentOfType = LLVMABIAlignmentOfType;
10341040
extern fn LLVMABIAlignmentOfType(TD: *const TargetData, Ty: *const Type) c_uint;
1041+
1042+
pub const abiSizeOfType = LLVMABISizeOfType;
1043+
extern fn LLVMABISizeOfType(TD: *const TargetData, Ty: *const Type) c_ulonglong;
10351044
};
10361045

10371046
pub const CodeModel = enum(c_int) {

src/type.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2305,6 +2305,9 @@ pub const Type = extern union {
23052305
/// true if and only if the type takes up space in memory at runtime.
23062306
/// There are two reasons a type will return false:
23072307
/// * the type is a comptime-only type. For example, the type `type` itself.
2308+
/// - note, however, that a struct can have mixed fields and only the non-comptime-only
2309+
/// fields will count towards the ABI size. For example, `struct {T: type, x: i32}`
2310+
/// hasRuntimeBits()=true and abiSize()=4
23082311
/// * the type has only one possible value, making its ABI size 0.
23092312
/// When `ignore_comptime_only` is true, then types that are comptime only
23102313
/// may return false positives.

0 commit comments

Comments
 (0)