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 {...@@ -965,9 +965,9 @@ pub const DeclGen = struct {
965 .Array, .Vector => {965 .Array, .Vector => {
966 const ai = ty.arrayInfo(mod);966 const ai = ty.arrayInfo(mod);
967 if (ai.elem_type.eql(Type.u8, mod)) {967 if (ai.elem_type.eql(Type.u8, mod)) {
968 var literal = stringLiteral(writer);
969 try literal.start();
970 const c_len = ty.arrayLenIncludingSentinel(mod);968 const c_len = ty.arrayLenIncludingSentinel(mod);
969 var literal = stringLiteral(writer, c_len);
970 try literal.start();
971 var index: u64 = 0;971 var index: u64 = 0;
972 while (index < c_len) : (index += 1)972 while (index < c_len) : (index += 1)
973 try literal.writeChar(0xaa);973 try literal.writeChar(0xaa);
...@@ -1290,46 +1290,24 @@ pub const DeclGen = struct {...@@ -1290,46 +1290,24 @@ pub const DeclGen = struct {
1290 }1290 }
1291 // Fall back to generic implementation.1291 // 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
1296 const ai = ty.arrayInfo(mod);1293 const ai = ty.arrayInfo(mod);
1297 if (ai.elem_type.eql(Type.u8, mod)) {1294 if (ai.elem_type.eql(Type.u8, mod)) {
1298 if (ai.len <= max_string_initializer_len) {1295 var literal = stringLiteral(writer, ty.arrayLenIncludingSentinel(mod));
1299 var literal = stringLiteral(writer);1296 try literal.start();
1300 try literal.start();1297 var index: usize = 0;
1301 var index: usize = 0;1298 while (index < ai.len) : (index += 1) {
1302 while (index < ai.len) : (index += 1) {1299 const elem_val = try val.elemValue(mod, index);
1303 const elem_val = try val.elemValue(mod, index);1300 const elem_val_u8: u8 = if (elem_val.isUndef(mod))
1304 const elem_val_u8: u8 = if (elem_val.isUndef(mod))1301 undefPattern(u8)
1305 undefPattern(u8)1302 else
1306 else1303 @intCast(elem_val.toUnsignedInt(mod));
1307 @intCast(elem_val.toUnsignedInt(mod));1304 try literal.writeChar(elem_val_u8);
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('}');
1332 }1305 }
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();
1333 } else {1311 } else {
1334 try writer.writeByte('{');1312 try writer.writeByte('{');
1335 var index: usize = 0;1313 var index: usize = 0;
...@@ -7660,11 +7638,17 @@ fn compareOperatorC(operator: std.math.CompareOperator) []const u8 {...@@ -7660,11 +7638,17 @@ fn compareOperatorC(operator: std.math.CompareOperator) []const u8 {
7660}7638}
76617639
7662fn StringLiteral(comptime WriterType: type) type {7640fn 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
7663 // MSVC has a length limit of 16380 per string literal (before concatenation)7646 // MSVC has a length limit of 16380 per string literal (before concatenation)
7664 const max_char_len = 4;7647 const max_char_len = 4;
7665 const max_len = 16380 - max_char_len;7648 const max_literal_len = 16380 - max_char_len;
76667649
7667 return struct {7650 return struct {
7651 len: u64,
7668 cur_len: u64 = 0,7652 cur_len: u64 = 0,
7669 counting_writer: std.io.CountingWriter(WriterType),7653 counting_writer: std.io.CountingWriter(WriterType),
76707654
...@@ -7674,12 +7658,20 @@ fn StringLiteral(comptime WriterType: type) type {...@@ -7674,12 +7658,20 @@ fn StringLiteral(comptime WriterType: type) type {
76747658
7675 pub fn start(self: *Self) Error!void {7659 pub fn start(self: *Self) Error!void {
7676 const writer = self.counting_writer.writer();7660 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 }
7678 }7666 }
76797667
7680 pub fn end(self: *Self) Error!void {7668 pub fn end(self: *Self) Error!void {
7681 const writer = self.counting_writer.writer();7669 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 }
7683 }7675 }
76847676
7685 fn writeStringLiteralChar(writer: anytype, c: u8) !void {7677 fn writeStringLiteralChar(writer: anytype, c: u8) !void {
...@@ -7701,24 +7693,34 @@ fn StringLiteral(comptime WriterType: type) type {...@@ -7701,24 +7693,34 @@ fn StringLiteral(comptime WriterType: type) type {
77017693
7702 pub fn writeChar(self: *Self, c: u8) Error!void {7694 pub fn writeChar(self: *Self, c: u8) Error!void {
7703 const writer = self.counting_writer.writer();7695 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)7700 const len = self.counting_writer.bytes_written;
7706 try writer.writeAll("\"\"");7701 try writeStringLiteralChar(writer, c);
77077702
7708 const len = self.counting_writer.bytes_written;7703 const char_length = self.counting_writer.bytes_written - len;
7709 try writeStringLiteralChar(writer, c);7704 assert(char_length <= max_char_len);
7705 self.cur_len += char_length;
77107706
7711 const char_length = self.counting_writer.bytes_written - len;7707 if (self.cur_len >= max_literal_len) self.cur_len = 0;
7712 assert(char_length <= max_char_len);7708 } else {
7713 self.cur_len += char_length;7709 if (self.counting_writer.bytes_written > 1) try writer.writeByte(',');
77147710 try writer.print("'\\x{x}'", .{c});
7715 if (self.cur_len >= max_len) self.cur_len = 0;7711 }
7716 }7712 }
7717 };7713 };
7718}7714}
77197715
7720fn stringLiteral(child_stream: anytype) StringLiteral(@TypeOf(child_stream)) {7716fn stringLiteral(
7721 return .{ .counting_writer = std.io.countingWriter(child_stream) };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 };
7722}7724}
77237725
7724const FormatStringContext = struct { str: []const u8, sentinel: ?u8 };7726const FormatStringContext = struct { str: []const u8, sentinel: ?u8 };
...@@ -7730,7 +7732,7 @@ fn formatStringLiteral(...@@ -7730,7 +7732,7 @@ fn formatStringLiteral(
7730) @TypeOf(writer).Error!void {7732) @TypeOf(writer).Error!void {
7731 if (fmt.len != 1 or fmt[0] != 's') @compileError("Invalid fmt: " ++ fmt);7733 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));
7734 try literal.start();7736 try literal.start();
7735 for (data.str) |c| try literal.writeChar(c);7737 for (data.str) |c| try literal.writeChar(c);
7736 if (data.sentinel) |sentinel| if (sentinel != 0) try literal.writeChar(sentinel);7738 if (data.sentinel) |sentinel| if (sentinel != 0) try literal.writeChar(sentinel);