Commit 54f6e74cda

kcbanner <kcbanner@gmail.com>
2024-03-17 17:55:58
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 parent 95cb939
Changed files (1)
src
codegen
src/codegen/c.zig
@@ -965,9 +965,9 @@ pub const DeclGen = struct {
                 .Array, .Vector => {
                     const ai = ty.arrayInfo(mod);
                     if (ai.elem_type.eql(Type.u8, mod)) {
-                        var literal = stringLiteral(writer);
-                        try literal.start();
                         const c_len = ty.arrayLenIncludingSentinel(mod);
+                        var literal = stringLiteral(writer, c_len);
+                        try literal.start();
                         var index: u64 = 0;
                         while (index < c_len) : (index += 1)
                             try literal.writeChar(0xaa);
@@ -1290,46 +1290,24 @@ pub const DeclGen = struct {
                     }
                     // Fall back to generic implementation.
 
-                    // MSVC throws C2078 if an array of size 65536 or greater is initialized with a string literal
-                    const max_string_initializer_len = 65535;
-
                     const ai = ty.arrayInfo(mod);
                     if (ai.elem_type.eql(Type.u8, mod)) {
-                        if (ai.len <= max_string_initializer_len) {
-                            var literal = stringLiteral(writer);
-                            try literal.start();
-                            var index: usize = 0;
-                            while (index < ai.len) : (index += 1) {
-                                const elem_val = try val.elemValue(mod, index);
-                                const elem_val_u8: u8 = if (elem_val.isUndef(mod))
-                                    undefPattern(u8)
-                                else
-                                    @intCast(elem_val.toUnsignedInt(mod));
-                                try literal.writeChar(elem_val_u8);
-                            }
-                            if (ai.sentinel) |s| {
-                                const s_u8: u8 = @intCast(s.toUnsignedInt(mod));
-                                if (s_u8 != 0) try literal.writeChar(s_u8);
-                            }
-                            try literal.end();
-                        } else {
-                            try writer.writeByte('{');
-                            var index: usize = 0;
-                            while (index < ai.len) : (index += 1) {
-                                if (index != 0) try writer.writeByte(',');
-                                const elem_val = try val.elemValue(mod, index);
-                                const elem_val_u8: u8 = if (elem_val.isUndef(mod))
-                                    undefPattern(u8)
-                                else
-                                    @intCast(elem_val.toUnsignedInt(mod));
-                                try writer.print("'\\x{x}'", .{elem_val_u8});
-                            }
-                            if (ai.sentinel) |s| {
-                                if (index != 0) try writer.writeByte(',');
-                                try dg.renderValue(writer, ai.elem_type, s, initializer_type);
-                            }
-                            try writer.writeByte('}');
+                        var literal = stringLiteral(writer, ty.arrayLenIncludingSentinel(mod));
+                        try literal.start();
+                        var index: usize = 0;
+                        while (index < ai.len) : (index += 1) {
+                            const elem_val = try val.elemValue(mod, index);
+                            const elem_val_u8: u8 = if (elem_val.isUndef(mod))
+                                undefPattern(u8)
+                            else
+                                @intCast(elem_val.toUnsignedInt(mod));
+                            try literal.writeChar(elem_val_u8);
                         }
+                        if (ai.sentinel) |s| {
+                            const s_u8: u8 = @intCast(s.toUnsignedInt(mod));
+                            if (s_u8 != 0) try literal.writeChar(s_u8);
+                        }
+                        try literal.end();
                     } else {
                         try writer.writeByte('{');
                         var index: usize = 0;
@@ -7660,11 +7638,17 @@ fn compareOperatorC(operator: std.math.CompareOperator) []const u8 {
 }
 
 fn StringLiteral(comptime WriterType: type) type {
+    // MSVC throws C2078 if an array of size 65536 or greater is initialized with a string literal,
+    // regardless of the length of the string literal initializing it. Array initializer syntax is
+    // used instead.
+    const max_string_initializer_len = 65535;
+
     // MSVC has a length limit of 16380 per string literal (before concatenation)
     const max_char_len = 4;
-    const max_len = 16380 - max_char_len;
+    const max_literal_len = 16380 - max_char_len;
 
     return struct {
+        len: u64,
         cur_len: u64 = 0,
         counting_writer: std.io.CountingWriter(WriterType),
 
@@ -7674,12 +7658,20 @@ fn StringLiteral(comptime WriterType: type) type {
 
         pub fn start(self: *Self) Error!void {
             const writer = self.counting_writer.writer();
-            try writer.writeByte('\"');
+            if (self.len <= max_string_initializer_len) {
+                try writer.writeByte('\"');
+            } else {
+                try writer.writeByte('{');
+            }
         }
 
         pub fn end(self: *Self) Error!void {
             const writer = self.counting_writer.writer();
-            try writer.writeByte('\"');
+            if (self.len <= max_string_initializer_len) {
+                try writer.writeByte('\"');
+            } else {
+                try writer.writeByte('}');
+            }
         }
 
         fn writeStringLiteralChar(writer: anytype, c: u8) !void {
@@ -7701,24 +7693,34 @@ fn StringLiteral(comptime WriterType: type) type {
 
         pub fn writeChar(self: *Self, c: u8) Error!void {
             const writer = self.counting_writer.writer();
+            if (self.len <= max_string_initializer_len) {
+                if (self.cur_len == 0 and self.counting_writer.bytes_written > 1)
+                    try writer.writeAll("\"\"");
 
-            if (self.cur_len == 0 and self.counting_writer.bytes_written > 1)
-                try writer.writeAll("\"\"");
+                const len = self.counting_writer.bytes_written;
+                try writeStringLiteralChar(writer, c);
 
-            const len = self.counting_writer.bytes_written;
-            try writeStringLiteralChar(writer, c);
+                const char_length = self.counting_writer.bytes_written - len;
+                assert(char_length <= max_char_len);
+                self.cur_len += char_length;
 
-            const char_length = self.counting_writer.bytes_written - len;
-            assert(char_length <= max_char_len);
-            self.cur_len += char_length;
-
-            if (self.cur_len >= max_len) self.cur_len = 0;
+                if (self.cur_len >= max_literal_len) self.cur_len = 0;
+            } else {
+                if (self.counting_writer.bytes_written > 1) try writer.writeByte(',');
+                try writer.print("'\\x{x}'", .{c});
+            }
         }
     };
 }
 
-fn stringLiteral(child_stream: anytype) StringLiteral(@TypeOf(child_stream)) {
-    return .{ .counting_writer = std.io.countingWriter(child_stream) };
+fn stringLiteral(
+    child_stream: anytype,
+    len: u64,
+) StringLiteral(@TypeOf(child_stream)) {
+    return .{
+        .len = len,
+        .counting_writer = std.io.countingWriter(child_stream),
+    };
 }
 
 const FormatStringContext = struct { str: []const u8, sentinel: ?u8 };
@@ -7730,7 +7732,7 @@ fn formatStringLiteral(
 ) @TypeOf(writer).Error!void {
     if (fmt.len != 1 or fmt[0] != 's') @compileError("Invalid fmt: " ++ fmt);
 
-    var literal = stringLiteral(writer);
+    var literal = stringLiteral(writer, data.str.len + @intFromBool(data.sentinel != null));
     try literal.start();
     for (data.str) |c| try literal.writeChar(c);
     if (data.sentinel) |sentinel| if (sentinel != 0) try literal.writeChar(sentinel);