authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2024-03-17 12:55:58-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-03-17 16:17:04-07:00
log54f6e74cda07a1153fd205afc8973665397f6cc7
treea65eaa88f52c46740542ea32b07841498b6e136e
parent95cb93944060d04ec49e9d2e21ef911ad2b09ccd

cbe: rework StringLiteral to decide between string literal or array initializator syntax

This fixes an issue with boostrapping the compiler using MSVC. There is a CircularBuffer with an array of length 65536 initialized to undefined, and because the undefined path of `renderValue` was using `StringLiteral` to render this, the resulting zig2.c would fail to compile using MSVC. The solution was to move the already-existing array initializer path (used in the non-undefined path) into StringLiteral, and make StringLiteral aware of the total length so it could decide between which style of initialization to use. We prefer to use string literals if we can, as this results in the least amount of emitted C source.

1 files changed, 56 insertions(+), 54 deletions(-)

src/codegen/c.zig+56-54
......@@ -965,9 +965,9 @@ pub const DeclGen = struct {
965965 .Array, .Vector => {
966966 const ai = ty.arrayInfo(mod);
967967 if (ai.elem_type.eql(Type.u8, mod)) {
968 var literal = stringLiteral(writer);
969 try literal.start();
970968 const c_len = ty.arrayLenIncludingSentinel(mod);
969 var literal = stringLiteral(writer, c_len);
970 try literal.start();
971971 var index: u64 = 0;
972972 while (index < c_len) : (index += 1)
973973 try literal.writeChar(0xaa);
......@@ -1290,46 +1290,24 @@ pub const DeclGen = struct {
12901290 }
12911291 // Fall back to generic implementation.
12921292
1293 // MSVC throws C2078 if an array of size 65536 or greater is initialized with a string literal
1294 const max_string_initializer_len = 65535;
1295
12961293 const ai = ty.arrayInfo(mod);
12971294 if (ai.elem_type.eql(Type.u8, mod)) {
1298 if (ai.len <= max_string_initializer_len) {
1299 var literal = stringLiteral(writer);
1300 try literal.start();
1301 var index: usize = 0;
1302 while (index < ai.len) : (index += 1) {
1303 const elem_val = try val.elemValue(mod, index);
1304 const elem_val_u8: u8 = if (elem_val.isUndef(mod))
1305 undefPattern(u8)
1306 else
1307 @intCast(elem_val.toUnsignedInt(mod));
1308 try literal.writeChar(elem_val_u8);
1309 }
1310 if (ai.sentinel) |s| {
1311 const s_u8: u8 = @intCast(s.toUnsignedInt(mod));
1312 if (s_u8 != 0) try literal.writeChar(s_u8);
1313 }
1314 try literal.end();
1315 } else {
1316 try writer.writeByte('{');
1317 var index: usize = 0;
1318 while (index < ai.len) : (index += 1) {
1319 if (index != 0) try writer.writeByte(',');
1320 const elem_val = try val.elemValue(mod, index);
1321 const elem_val_u8: u8 = if (elem_val.isUndef(mod))
1322 undefPattern(u8)
1323 else
1324 @intCast(elem_val.toUnsignedInt(mod));
1325 try writer.print("'\\x{x}'", .{elem_val_u8});
1326 }
1327 if (ai.sentinel) |s| {
1328 if (index != 0) try writer.writeByte(',');
1329 try dg.renderValue(writer, ai.elem_type, s, initializer_type);
1330 }
1331 try writer.writeByte('}');
1295 var literal = stringLiteral(writer, ty.arrayLenIncludingSentinel(mod));
1296 try literal.start();
1297 var index: usize = 0;
1298 while (index < ai.len) : (index += 1) {
1299 const elem_val = try val.elemValue(mod, index);
1300 const elem_val_u8: u8 = if (elem_val.isUndef(mod))
1301 undefPattern(u8)
1302 else
1303 @intCast(elem_val.toUnsignedInt(mod));
1304 try literal.writeChar(elem_val_u8);
13321305 }
1306 if (ai.sentinel) |s| {
1307 const s_u8: u8 = @intCast(s.toUnsignedInt(mod));
1308 if (s_u8 != 0) try literal.writeChar(s_u8);
1309 }
1310 try literal.end();
13331311 } else {
13341312 try writer.writeByte('{');
13351313 var index: usize = 0;
......@@ -7660,11 +7638,17 @@ fn compareOperatorC(operator: std.math.CompareOperator) []const u8 {
76607638}
76617639
76627640fn StringLiteral(comptime WriterType: type) type {
7641 // MSVC throws C2078 if an array of size 65536 or greater is initialized with a string literal,
7642 // regardless of the length of the string literal initializing it. Array initializer syntax is
7643 // used instead.
7644 const max_string_initializer_len = 65535;
7645
76637646 // MSVC has a length limit of 16380 per string literal (before concatenation)
76647647 const max_char_len = 4;
7665 const max_len = 16380 - max_char_len;
7648 const max_literal_len = 16380 - max_char_len;
76667649
76677650 return struct {
7651 len: u64,
76687652 cur_len: u64 = 0,
76697653 counting_writer: std.io.CountingWriter(WriterType),
76707654
......@@ -7674,12 +7658,20 @@ fn StringLiteral(comptime WriterType: type) type {
76747658
76757659 pub fn start(self: *Self) Error!void {
76767660 const writer = self.counting_writer.writer();
7677 try writer.writeByte('\"');
7661 if (self.len <= max_string_initializer_len) {
7662 try writer.writeByte('\"');
7663 } else {
7664 try writer.writeByte('{');
7665 }
76787666 }
76797667
76807668 pub fn end(self: *Self) Error!void {
76817669 const writer = self.counting_writer.writer();
7682 try writer.writeByte('\"');
7670 if (self.len <= max_string_initializer_len) {
7671 try writer.writeByte('\"');
7672 } else {
7673 try writer.writeByte('}');
7674 }
76837675 }
76847676
76857677 fn writeStringLiteralChar(writer: anytype, c: u8) !void {
......@@ -7701,24 +7693,34 @@ fn StringLiteral(comptime WriterType: type) type {
77017693
77027694 pub fn writeChar(self: *Self, c: u8) Error!void {
77037695 const writer = self.counting_writer.writer();
7696 if (self.len <= max_string_initializer_len) {
7697 if (self.cur_len == 0 and self.counting_writer.bytes_written > 1)
7698 try writer.writeAll("\"\"");
77047699
7705 if (self.cur_len == 0 and self.counting_writer.bytes_written > 1)
7706 try writer.writeAll("\"\"");
7700 const len = self.counting_writer.bytes_written;
7701 try writeStringLiteralChar(writer, c);
77077702
7708 const len = self.counting_writer.bytes_written;
7709 try writeStringLiteralChar(writer, c);
7703 const char_length = self.counting_writer.bytes_written - len;
7704 assert(char_length <= max_char_len);
7705 self.cur_len += char_length;
77107706
7711 const char_length = self.counting_writer.bytes_written - len;
7712 assert(char_length <= max_char_len);
7713 self.cur_len += char_length;
7714
7715 if (self.cur_len >= max_len) self.cur_len = 0;
7707 if (self.cur_len >= max_literal_len) self.cur_len = 0;
7708 } else {
7709 if (self.counting_writer.bytes_written > 1) try writer.writeByte(',');
7710 try writer.print("'\\x{x}'", .{c});
7711 }
77167712 }
77177713 };
77187714}
77197715
7720fn stringLiteral(child_stream: anytype) StringLiteral(@TypeOf(child_stream)) {
7721 return .{ .counting_writer = std.io.countingWriter(child_stream) };
7716fn stringLiteral(
7717 child_stream: anytype,
7718 len: u64,
7719) StringLiteral(@TypeOf(child_stream)) {
7720 return .{
7721 .len = len,
7722 .counting_writer = std.io.countingWriter(child_stream),
7723 };
77227724}
77237725
77247726const FormatStringContext = struct { str: []const u8, sentinel: ?u8 };
......@@ -7730,7 +7732,7 @@ fn formatStringLiteral(
77307732) @TypeOf(writer).Error!void {
77317733 if (fmt.len != 1 or fmt[0] != 's') @compileError("Invalid fmt: " ++ fmt);
77327734
7733 var literal = stringLiteral(writer);
7735 var literal = stringLiteral(writer, data.str.len + @intFromBool(data.sentinel != null));
77347736 try literal.start();
77357737 for (data.str) |c| try literal.writeChar(c);
77367738 if (data.sentinel) |sentinel| if (sentinel != 0) try literal.writeChar(sentinel);