authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-10-24 14:41:22+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-10-27 01:31:17+03:00
logdd437ae39948031dc04836f245c8b77d459a428a
treeea2cf8ea5dce382549deaf9a2c8801ec1c7403d1
parent9db293492bbbc5b8d70638bd9c59dea19d13596c

stage2: optimize size of optional slices


5 files changed, 45 insertions(+), 45 deletions(-)

src/codegen/c.zig+9-5
...@@ -726,7 +726,11 @@ pub const DeclGen = struct {...@@ -726,7 +726,11 @@ pub const DeclGen = struct {
726 }726 }
727727
728 if (ty.optionalReprIsPayload()) {728 if (ty.optionalReprIsPayload()) {
729 return dg.renderValue(writer, payload_ty, val, location);729 if (val.castTag(.opt_payload)) |payload| {
730 return dg.renderValue(writer, payload_ty, payload.data, location);
731 } else {
732 return dg.renderValue(writer, payload_ty, val, location);
733 }
730 }734 }
731735
732 try writer.writeByte('(');736 try writer.writeByte('(');
...@@ -3263,11 +3267,9 @@ fn airIsNull(...@@ -3263,11 +3267,9 @@ fn airIsNull(
3263 try f.writeCValue(writer, operand);3267 try f.writeCValue(writer, operand);
32643268
3265 const ty = f.air.typeOf(un_op);3269 const ty = f.air.typeOf(un_op);
3270 const opt_ty = if (deref_suffix[0] != 0) ty.childType() else ty;
3266 var opt_buf: Type.Payload.ElemType = undefined;3271 var opt_buf: Type.Payload.ElemType = undefined;
3267 const payload_ty = if (deref_suffix[0] != 0)3272 const payload_ty = opt_ty.optionalChild(&opt_buf);
3268 ty.childType().optionalChild(&opt_buf)
3269 else
3270 ty.optionalChild(&opt_buf);
32713273
3272 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {3274 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
3273 try writer.print("){s} {s} true;\n", .{ deref_suffix, operator });3275 try writer.print("){s} {s} true;\n", .{ deref_suffix, operator });
...@@ -3276,6 +3278,8 @@ fn airIsNull(...@@ -3276,6 +3278,8 @@ fn airIsNull(
3276 try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator });3278 try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator });
3277 } else if (payload_ty.zigTypeTag() == .ErrorSet) {3279 } else if (payload_ty.zigTypeTag() == .ErrorSet) {
3278 try writer.print("){s} {s} 0;\n", .{ deref_suffix, operator });3280 try writer.print("){s} {s} 0;\n", .{ deref_suffix, operator });
3281 } else if (payload_ty.isSlice() and opt_ty.optionalReprIsPayload()) {
3282 try writer.print("){s}.ptr {s} NULL;\n", .{ deref_suffix, operator });
3279 } else {3283 } else {
3280 try writer.print("){s}.is_null {s} true;\n", .{ deref_suffix, operator });3284 try writer.print("){s}.is_null {s} true;\n", .{ deref_suffix, operator });
3281 }3285 }
src/codegen/llvm.zig+8-2
...@@ -6316,18 +6316,24 @@ pub const FuncGen = struct {...@@ -6316,18 +6316,24 @@ pub const FuncGen = struct {
6316 const operand_ty = self.air.typeOf(un_op);6316 const operand_ty = self.air.typeOf(un_op);
6317 const optional_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty;6317 const optional_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty;
6318 const optional_llvm_ty = try self.dg.lowerType(optional_ty);6318 const optional_llvm_ty = try self.dg.lowerType(optional_ty);
6319 var buf: Type.Payload.ElemType = undefined;
6320 const payload_ty = optional_ty.optionalChild(&buf);
6319 if (optional_ty.optionalReprIsPayload()) {6321 if (optional_ty.optionalReprIsPayload()) {
6320 const loaded = if (operand_is_ptr)6322 const loaded = if (operand_is_ptr)
6321 self.builder.buildLoad(optional_llvm_ty, operand, "")6323 self.builder.buildLoad(optional_llvm_ty, operand, "")
6322 else6324 else
6323 operand;6325 operand;
6326 if (payload_ty.isSlice()) {
6327 const slice_ptr = self.builder.buildExtractValue(loaded, 0, "");
6328 var slice_buf: Type.SlicePtrFieldTypeBuffer = undefined;
6329 const ptr_ty = try self.dg.lowerType(payload_ty.slicePtrFieldType(&slice_buf));
6330 return self.builder.buildICmp(pred, slice_ptr, ptr_ty.constNull(), "");
6331 }
6324 return self.builder.buildICmp(pred, loaded, optional_llvm_ty.constNull(), "");6332 return self.builder.buildICmp(pred, loaded, optional_llvm_ty.constNull(), "");
6325 }6333 }
63266334
6327 comptime assert(optional_layout_version == 3);6335 comptime assert(optional_layout_version == 3);
63286336
6329 var buf: Type.Payload.ElemType = undefined;
6330 const payload_ty = optional_ty.optionalChild(&buf);
6331 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {6337 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
6332 const loaded = if (operand_is_ptr)6338 const loaded = if (operand_is_ptr)
6333 self.builder.buildLoad(optional_llvm_ty, operand, "")6339 self.builder.buildLoad(optional_llvm_ty, operand, "")
src/type.zig+8-38
...@@ -3469,20 +3469,8 @@ pub const Type = extern union {...@@ -3469,20 +3469,8 @@ pub const Type = extern union {
34693469
3470 if (!child_type.hasRuntimeBits()) return AbiSizeAdvanced{ .scalar = 1 };3470 if (!child_type.hasRuntimeBits()) return AbiSizeAdvanced{ .scalar = 1 };
34713471
3472 switch (child_type.zigTypeTag()) {3472 if (ty.optionalReprIsPayload()) {
3473 .Pointer => {3473 return abiSizeAdvanced(child_type, target, strat);
3474 const ptr_info = child_type.ptrInfo().data;
3475 const has_null = switch (ptr_info.size) {
3476 .Slice, .C => true,
3477 else => ptr_info.@"allowzero",
3478 };
3479 if (!has_null) {
3480 const ptr_size_bytes = @divExact(target.cpu.arch.ptrBitWidth(), 8);
3481 return AbiSizeAdvanced{ .scalar = ptr_size_bytes };
3482 }
3483 },
3484 .ErrorSet => return abiSizeAdvanced(Type.anyerror, target, strat),
3485 else => {},
3486 }3474 }
34873475
3488 const payload_size = switch (try child_type.abiSizeAdvanced(target, strat)) {3476 const payload_size = switch (try child_type.abiSizeAdvanced(target, strat)) {
...@@ -3747,28 +3735,10 @@ pub const Type = extern union {...@@ -3747,28 +3735,10 @@ pub const Type = extern union {
37473735
3748 .int_signed, .int_unsigned => return ty.cast(Payload.Bits).?.data,3736 .int_signed, .int_unsigned => return ty.cast(Payload.Bits).?.data,
37493737
3750 .optional => {3738 .optional, .error_union => {
3751 var buf: Payload.ElemType = undefined;3739 // Optionals and error unions are not packed so their bitsize
3752 const child_type = ty.optionalChild(&buf);3740 // includes padding bits.
3753 if (!child_type.hasRuntimeBits()) return 8;3741 return (try abiSizeAdvanced(ty, target, if (sema_kit) |sk| .{ .sema_kit = sk } else .eager)).scalar * 8;
3754
3755 if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr() and !child_type.isSlice())
3756 return target.cpu.arch.ptrBitWidth();
3757
3758 // Optional types are represented as a struct with the child type as the first
3759 // field and a boolean as the second. Since the child type's abi alignment is
3760 // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal
3761 // to the child type's ABI alignment.
3762 const child_bit_size = try bitSizeAdvanced(child_type, target, sema_kit);
3763 return child_bit_size + 1;
3764 },
3765
3766 .error_union => {
3767 const payload = ty.castTag(.error_union).?.data;
3768 if (!payload.payload.hasRuntimeBits()) {
3769 return payload.error_set.bitSizeAdvanced(target, sema_kit);
3770 }
3771 @panic("TODO bitSize error union");
3772 },3742 },
37733743
3774 .atomic_order,3744 .atomic_order,
...@@ -4045,8 +4015,8 @@ pub const Type = extern union {...@@ -4045,8 +4015,8 @@ pub const Type = extern union {
4045 .Pointer => {4015 .Pointer => {
4046 const info = child_ty.ptrInfo().data;4016 const info = child_ty.ptrInfo().data;
4047 switch (info.size) {4017 switch (info.size) {
4048 .Slice, .C => return false,4018 .C => return false,
4049 .Many, .One => return !info.@"allowzero",4019 .Slice, .Many, .One => return !info.@"allowzero",
4050 }4020 }
4051 },4021 },
4052 .ErrorSet => return true,4022 .ErrorSet => return true,
test/behavior/cast.zig+4
...@@ -1170,6 +1170,7 @@ test "implicitly cast from [N]T to ?[]const T" {...@@ -1170,6 +1170,7 @@ test "implicitly cast from [N]T to ?[]const T" {
1170 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;1170 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1171 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO1171 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1172 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1172 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1173 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
11731174
1174 try expect(mem.eql(u8, castToOptionalSlice().?, "hi"));1175 try expect(mem.eql(u8, castToOptionalSlice().?, "hi"));
1175 comptime try expect(mem.eql(u8, castToOptionalSlice().?, "hi"));1176 comptime try expect(mem.eql(u8, castToOptionalSlice().?, "hi"));
...@@ -1256,6 +1257,7 @@ test "*const [N]null u8 to ?[]const u8" {...@@ -1256,6 +1257,7 @@ test "*const [N]null u8 to ?[]const u8" {
1256 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;1257 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1257 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO1258 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1258 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1259 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1260 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
12591261
1260 const S = struct {1262 const S = struct {
1261 fn doTheTest() !void {1263 fn doTheTest() !void {
...@@ -1394,6 +1396,8 @@ test "cast i8 fn call peers to i32 result" {...@@ -1394,6 +1396,8 @@ test "cast i8 fn call peers to i32 result" {
1394test "cast compatible optional types" {1396test "cast compatible optional types" {
1395 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1397 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1396 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1398 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1399 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1400 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
13971401
1398 var a: ?[:0]const u8 = null;1402 var a: ?[:0]const u8 = null;
1399 var b: ?[]const u8 = a;1403 var b: ?[]const u8 = a;
test/behavior/optional.zig+16
...@@ -3,6 +3,7 @@ const std = @import("std");...@@ -3,6 +3,7 @@ const std = @import("std");
3const testing = std.testing;3const testing = std.testing;
4const expect = testing.expect;4const expect = testing.expect;
5const expectEqual = testing.expectEqual;5const expectEqual = testing.expectEqual;
6const expectEqualStrings = std.testing.expectEqualStrings;
67
7test "passing an optional integer as a parameter" {8test "passing an optional integer as a parameter" {
8 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;9 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
...@@ -428,3 +429,18 @@ test "alignment of wrapping an optional payload" {...@@ -428,3 +429,18 @@ test "alignment of wrapping an optional payload" {
428 };429 };
429 try expect(S.foo().?.x == 1234);430 try expect(S.foo().?.x == 1234);
430}431}
432
433test "Optional slice size is optimized" {
434 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
435 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
436 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
437 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
438 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
439 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
440
441 try expect(@sizeOf(?[]u8) == @sizeOf([]u8));
442 var a: ?[]const u8 = null;
443 try expect(a == null);
444 a = "hello";
445 try expectEqualStrings(a.?, "hello");
446}