zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob 978bbcff (89061B) - Raw


      1 const std = @import("std");
      2 const assert = std.debug.assert;
      3 const EpochSeconds = std.time.epoch.EpochSeconds;
      4 const Io = std.Io;
      5 const mem = std.mem;
      6 const Allocator = mem.Allocator;
      7 
      8 const backend = @import("../backend.zig");
      9 const Interner = backend.Interner;
     10 const CodeGenOptions = backend.CodeGenOptions;
     11 
     12 const Builtins = @import("Builtins.zig");
     13 const Diagnostics = @import("Diagnostics.zig");
     14 const DepFile = @import("DepFile.zig");
     15 const LangOpts = @import("LangOpts.zig");
     16 const Pragma = @import("Pragma.zig");
     17 const record_layout = @import("record_layout.zig");
     18 const Source = @import("Source.zig");
     19 const StringInterner = @import("StringInterner.zig");
     20 const Target = @import("Target.zig");
     21 const Tokenizer = @import("Tokenizer.zig");
     22 const Token = Tokenizer.Token;
     23 const TypeStore = @import("TypeStore.zig");
     24 const Type = TypeStore.Type;
     25 const QualType = TypeStore.QualType;
     26 
     27 pub const Error = error{
     28     /// A fatal error has ocurred and compilation has stopped.
     29     FatalError,
     30 } || Allocator.Error;
     31 pub const AddSourceError = Error || error{FileTooBig};
     32 
     33 pub const bit_int_max_bits = std.math.maxInt(u16);
     34 const path_buf_stack_limit = 1024;
     35 
     36 /// Environment variables used during compilation / linking.
     37 pub const Environment = struct {
     38     /// Directory to use for temporary files
     39     /// TODO: not implemented yet
     40     tmpdir: ?[]const u8 = null,
     41 
     42     /// PATH environment variable used to search for programs
     43     path: ?[]const u8 = null,
     44 
     45     /// Directories to try when searching for subprograms.
     46     /// TODO: not implemented yet
     47     compiler_path: ?[]const u8 = null,
     48 
     49     /// Directories to try when searching for special linker files, if compiling for the native target
     50     /// TODO: not implemented yet
     51     library_path: ?[]const u8 = null,
     52 
     53     /// List of directories to be searched as if specified with -I, but after any paths given with -I options on the command line
     54     /// Used regardless of the language being compiled
     55     /// TODO: not implemented yet
     56     cpath: ?[]const u8 = null,
     57 
     58     /// List of directories to be searched as if specified with -I, but after any paths given with -I options on the command line
     59     /// Used if the language being compiled is C
     60     /// TODO: not implemented yet
     61     c_include_path: ?[]const u8 = null,
     62 
     63     /// UNIX timestamp to be used instead of the current date and time in the __DATE__ and __TIME__ macros, and instead of the
     64     /// file modification time in the __TIMESTAMP__ macro
     65     source_date_epoch: ?[]const u8 = null,
     66 
     67     pub const SourceEpoch = union(enum) {
     68         /// Represents system time when aro is invoked; used for __DATE__ and __TIME__ macros
     69         system: u64,
     70         /// Represents a user-provided time (typically via the SOURCE_DATE_EPOCH environment variable)
     71         /// used for __DATE__, __TIME__, and __TIMESTAMP__
     72         provided: u64,
     73 
     74         pub const default: SourceEpoch = .{ .provided = 0 };
     75     };
     76 
     77     /// Load all of the environment variables from an environ map. Does not copy values.
     78     pub fn loadAll(environ_map: *const std.process.Environ.Map) Environment {
     79         var env: Environment = .{};
     80 
     81         inline for (@typeInfo(@TypeOf(env)).@"struct".field_names) |field_name| {
     82             std.debug.assert(@field(env, field_name) == null);
     83 
     84             var env_var_buf: [field_name.len]u8 = undefined;
     85             const env_var_name = std.ascii.upperString(&env_var_buf, field_name);
     86             @field(env, field_name) = environ_map.get(env_var_name);
     87         }
     88         return env;
     89     }
     90 
     91     pub fn sourceEpoch(self: *const Environment, io: Io) !SourceEpoch {
     92         const max_timestamp = 253402300799; // Dec 31 9999 23:59:59
     93 
     94         if (self.source_date_epoch) |epoch| {
     95             const parsed = std.fmt.parseInt(u64, epoch, 10) catch return error.InvalidEpoch;
     96             if (parsed > max_timestamp) return error.InvalidEpoch;
     97             return .{ .provided = parsed };
     98         } else {
     99             const timestamp = Io.Clock.real.now(io);
    100             const seconds = std.math.cast(u64, timestamp.toSeconds()) orelse return error.InvalidEpoch;
    101             return .{ .system = std.math.clamp(seconds, 0, max_timestamp) };
    102         }
    103     }
    104 };
    105 
    106 pub const Include = struct {
    107     kind: Kind,
    108     path: []const u8,
    109 
    110     pub const Kind = enum {
    111         quote,
    112         normal,
    113         framework,
    114         system,
    115         system_framework,
    116         after,
    117 
    118         pub fn isFramework(kind: Kind) bool {
    119             return switch (kind) {
    120                 .framework, .system_framework => true,
    121                 else => false,
    122             };
    123         }
    124 
    125         pub fn isSystem(kind: Kind) bool {
    126             return switch (kind) {
    127                 .after, .system, .system_framework => true,
    128                 else => false,
    129             };
    130         }
    131     };
    132 };
    133 
    134 const Compilation = @This();
    135 
    136 gpa: Allocator,
    137 /// Allocations in this arena live all the way until `Compilation.deinit`.
    138 arena: Allocator,
    139 io: Io,
    140 cwd: std.Io.Dir,
    141 diagnostics: *Diagnostics,
    142 
    143 sources: std.StringArrayHashMapUnmanaged(Source) = .empty,
    144 source_aliases: std.ArrayList(Source) = .empty,
    145 /// Allocated into `gpa`, but keys are externally managed.
    146 search_path: std.ArrayList(Include) = .empty,
    147 /// Allocated into `gpa`, but keys are externally managed.
    148 embed_dirs: std.ArrayList([]const u8) = .empty,
    149 
    150 environment: Environment = .{},
    151 target: Target = .default,
    152 darwin_target_variant: ?Target = null,
    153 cmodel: std.builtin.CodeModel = .default,
    154 
    155 code_gen_options: CodeGenOptions = .default,
    156 langopts: LangOpts = .{},
    157 generated_buf: std.ArrayList(u8) = .empty,
    158 builtins: Builtins = .{},
    159 string_interner: StringInterner = .{},
    160 interner: Interner = .{},
    161 type_store: TypeStore = .{},
    162 pragma_handlers: std.StringArrayHashMapUnmanaged(*Pragma) = .empty,
    163 /// If this is not null, the directory containing the specified Source will be searched for includes
    164 /// Used by MS extensions which allow searching for includes relative to the directory of the main source file.
    165 ms_cwd_source_id: ?Source.Id = null,
    166 
    167 pub const InitOptions = struct {
    168     gpa: Allocator,
    169     arena: Allocator,
    170     io: Io,
    171     diagnostics: *Diagnostics,
    172 
    173     /// Used to initiate `Compilation.Environment`, values are not copied.
    174     environ_map: ?*const std.process.Environ.Map,
    175     /// Defaults to `std.Io.Dir.cwd()`
    176     cwd: ?std.Io.Dir = null,
    177 
    178     add_default_pragma_handlers: bool = true,
    179 
    180     pub const testing: InitOptions = .{
    181         .gpa = std.testing.allocator,
    182         .arena = undefined,
    183         .io = std.testing.io,
    184         .diagnostics = undefined,
    185         .environ_map = null,
    186         .add_default_pragma_handlers = false,
    187     };
    188 };
    189 
    190 /// Initialize Compilation with default environment,
    191 /// pragma handlers and emulation mode set to target.
    192 pub fn init(options: InitOptions) !Compilation {
    193     var comp: Compilation = .{
    194         .gpa = options.gpa,
    195         .arena = options.arena,
    196         .io = options.io,
    197         .diagnostics = options.diagnostics,
    198         .cwd = options.cwd orelse .cwd(),
    199     };
    200     errdefer comp.deinit();
    201 
    202     if (options.environ_map) |map| {
    203         comp.environment = .loadAll(map);
    204     }
    205 
    206     if (options.add_default_pragma_handlers) {
    207         try comp.addDefaultPragmaHandlers();
    208     }
    209     return comp;
    210 }
    211 
    212 pub fn deinit(comp: *Compilation) void {
    213     const gpa = comp.gpa;
    214     for (comp.pragma_handlers.values()) |pragma| {
    215         pragma.deinit(pragma, comp);
    216     }
    217     for (comp.sources.values()) |source| {
    218         gpa.free(source.path);
    219         gpa.free(source.buf);
    220         gpa.free(source.splice_locs);
    221     }
    222     comp.sources.deinit(gpa);
    223     comp.source_aliases.deinit(gpa);
    224     comp.search_path.deinit(gpa);
    225     comp.embed_dirs.deinit(gpa);
    226     comp.pragma_handlers.deinit(gpa);
    227     comp.generated_buf.deinit(gpa);
    228     comp.builtins.deinit(gpa);
    229     comp.string_interner.deinit(gpa);
    230     comp.interner.deinit(gpa);
    231     comp.type_store.deinit(gpa);
    232     comp.* = undefined;
    233 }
    234 
    235 pub fn internString(comp: *Compilation, str: []const u8) !StringInterner.StringId {
    236     return comp.string_interner.intern(comp.gpa, str);
    237 }
    238 
    239 /// Which set of system defines to generate via generateBuiltinMacros
    240 pub const SystemDefinesMode = enum {
    241     /// Only define macros required by the C standard (date/time macros and those beginning with `__STDC`)
    242     no_system_defines,
    243     /// Define the standard set of system macros
    244     include_system_defines,
    245 };
    246 
    247 fn generateSystemDefines(comp: *Compilation, w: *Io.Writer) !void {
    248     const define = struct {
    249         fn define(_w: *Io.Writer, name: []const u8) !void {
    250             try _w.print("#define {s} 1\n", .{name});
    251         }
    252     }.define;
    253     const defineStd = struct {
    254         fn defineStd(_w: *Io.Writer, name: []const u8, is_gnu: bool) !void {
    255             if (is_gnu) {
    256                 try _w.print("#define {s} 1\n", .{name});
    257             }
    258             try _w.print(
    259                 \\#define __{s} 1
    260                 \\#define __{s}__ 1
    261                 \\
    262             , .{ name, name });
    263         }
    264     }.defineStd;
    265     const target = &comp.target;
    266     const ptr_width = target.ptrBitWidth();
    267     const is_gnu = comp.langopts.standard.isGNU();
    268 
    269     const gnuc_version = comp.langopts.gnuc_version orelse comp.langopts.emulate.defaultGccVersion();
    270     if (gnuc_version > 0) {
    271         try w.print("#define __GNUC__ {d}\n", .{gnuc_version / 10_000});
    272         try w.print("#define __GNUC_MINOR__ {d}\n", .{gnuc_version / 100 % 100});
    273         try w.print("#define __GNUC_PATCHLEVEL__ {d}\n", .{gnuc_version % 100});
    274     }
    275 
    276     try w.writeAll(
    277         \\#define __ARO_EMULATE_NO__ 0
    278         \\#define __ARO_EMULATE_CLANG__ 1
    279         \\#define __ARO_EMULATE_GCC__ 2
    280         \\#define __ARO_EMULATE_MSVC__ 3
    281         \\
    282     );
    283     try w.print("#define __ARO_EMULATE__ {s}\n", .{switch (comp.langopts.emulate) {
    284         .no => "__ARO_EMULATE_NO__",
    285         .clang => "__ARO_EMULATE_CLANG__",
    286         .gcc => "__ARO_EMULATE_GCC__",
    287         .msvc => "__ARO_EMULATE_MSVC__",
    288     }});
    289 
    290     if (comp.langopts.emulate == .msvc) {
    291         try w.writeAll("#define _MSC_VER 1933\n");
    292         try w.writeAll("#define _MSC_FULL_VER 193300000\n");
    293     }
    294 
    295     // Defined for compatibility with clang.
    296     try w.writeAll("#define __building_module(x) 0\n");
    297 
    298     if (comp.code_gen_options.optimization_level.hasAnyOptimizations()) {
    299         try define(w, "__OPTIMIZE__");
    300     }
    301     if (comp.code_gen_options.optimization_level.isSizeOptimized()) {
    302         try define(w, "__OPTIMIZE_SIZE__");
    303     }
    304 
    305     // os macros
    306     switch (target.os.tag) {
    307         .linux => try defineStd(w, "linux", is_gnu),
    308         .windows => {
    309             try define(w, "_WIN32");
    310             if (ptr_width == 64) {
    311                 try define(w, "_WIN64");
    312             }
    313 
    314             if (target.abi.isGnu()) {
    315                 try defineStd(w, "WIN32", is_gnu);
    316                 try defineStd(w, "WINNT", is_gnu);
    317                 if (ptr_width == 64) {
    318                     try defineStd(w, "WIN64", is_gnu);
    319                     try define(w, "__MINGW64__");
    320                 }
    321                 try define(w, "__MSVCRT__");
    322                 try define(w, "__MINGW32__");
    323             }
    324 
    325             if (target.abi.isGnu()) {
    326                 // MinGW and Cygwin define __declspec(a) to __attribute((a)).
    327                 // Like Clang we make the define no op if -fdeclspec is enabled.
    328                 if (comp.langopts.declspec_attrs) {
    329                     try w.writeAll("#define __declspec __declspec\n");
    330                 } else {
    331                     try w.writeAll("#define __declspec(a) __attribute__((a))\n");
    332                 }
    333                 if (!comp.langopts.ms_extensions) {
    334                     // Provide aliases for the calling convention keywords.
    335                     for ([_][]const u8{ "cdecl", "stdcall", "fastcall", "thiscall" }) |keyword| {
    336                         try w.print(
    337                             \\#define _{[0]s} __attribute__((__{[0]s}__))
    338                             \\#define __{[0]s} __attribute__((__{[0]s}__))
    339                             \\
    340                         , .{keyword});
    341                     }
    342                 }
    343             }
    344         },
    345         .uefi => try define(w, "__UEFI__"),
    346         .freebsd => {
    347             const release = target.os.version_range.semver.min.major;
    348             const cc_version = release * 10_000 + 1;
    349             try w.print(
    350                 \\#define __FreeBSD__ {d}
    351                 \\#define __FreeBSD_cc_version {d}
    352                 \\
    353             , .{ release, cc_version });
    354         },
    355         .ps4, .ps5 => {
    356             try w.writeAll(
    357                 \\#define __FreeBSD__ 9
    358                 \\#define __FreeBSD_cc_version 900001
    359                 \\
    360             );
    361         },
    362         .netbsd => try define(w, "__NetBSD__"),
    363         .openbsd => try define(w, "__OpenBSD__"),
    364         .dragonfly => try define(w, "__DragonFly__"),
    365         .illumos => {
    366             try defineStd(w, "sun", is_gnu);
    367             try define(w, "__illumos__");
    368         },
    369         .maccatalyst,
    370         .macos,
    371         .tvos,
    372         .ios,
    373         .driverkit,
    374         .visionos,
    375         .watchos,
    376         => {
    377             try define(w, "__APPLE__");
    378             try w.writeAll("#define __APPLE_CC__ 6000\n");
    379 
    380             const version = target.os.version_range.semver.min;
    381             var version_buf: [8]u8 = undefined;
    382             const version_str = if (target.os.tag == .macos and version.order(.{ .major = 10, .minor = 10, .patch = 0 }) == .lt)
    383                 std.fmt.bufPrint(&version_buf, "{d}{d}{d}", .{ version.major, @min(version.minor, 9), @min(version.patch, 9) }) catch unreachable
    384             else
    385                 std.fmt.bufPrint(&version_buf, "{d:0>2}{d:0>2}{d:0>2}", .{ version.major, @min(version.minor, 99), @min(version.patch, 99) }) catch unreachable;
    386 
    387             try w.print("#define {s} {s}\n", .{ switch (target.os.tag) {
    388                 .tvos => "__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__",
    389                 .ios, .maccatalyst => "__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__",
    390                 .watchos => "__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__",
    391                 .driverkit => "__ENVIRONMENT_DRIVERKIT_VERSION_MIN_REQUIRED__",
    392                 .macos => "__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__",
    393                 else => unreachable,
    394             }, version_str });
    395 
    396             try w.print("#define __ENVIRONMENT_OS_VERSION_MIN_REQUIRED__ {s}\n", .{version_str});
    397         },
    398         .wasi => try define(w, "__wasi__"),
    399         .emscripten => try define(w, "__EMSCRIPTEN__"),
    400         .@"3ds" => try define(w, "__3DS__"),
    401         .vita => try define(w, "__vita__"),
    402         else => {},
    403     }
    404 
    405     // unix and other additional os macros
    406     switch (target.os.tag) {
    407         .freebsd,
    408         .netbsd,
    409         .openbsd,
    410         .dragonfly,
    411         .linux,
    412         .haiku,
    413         .hurd,
    414         .illumos,
    415         .emscripten,
    416         .ps4,
    417         .ps5,
    418         => try defineStd(w, "unix", is_gnu),
    419         .windows => if (target.abi.isGnu()) {
    420             try defineStd(w, "unix", is_gnu);
    421         },
    422         else => {},
    423     }
    424     if (target.abi.isAndroid()) {
    425         try define(w, "__ANDROID__");
    426     }
    427 
    428     // architecture macros
    429     switch (target.cpu.arch) {
    430         .x86, .x86_64 => {
    431             try w.print("#define __code_model_{s}__ 1\n", .{switch (comp.cmodel) {
    432                 .default => "small",
    433                 else => @tagName(comp.cmodel),
    434             }});
    435 
    436             if (target.cpu.arch == .x86_64) {
    437                 try define(w, "__amd64__");
    438                 try define(w, "__amd64");
    439                 try define(w, "__x86_64__");
    440                 try define(w, "__x86_64");
    441 
    442                 if (target.os.tag == .windows and target.abi == .msvc) {
    443                     try w.writeAll(
    444                         \\#define _M_X64 100
    445                         \\#define _M_AMD64 100
    446                         \\
    447                     );
    448                 }
    449             } else {
    450                 try defineStd(w, "i386", is_gnu);
    451 
    452                 if (target.os.tag == .windows and target.abi == .msvc) {
    453                     try w.print("#define _M_IX86 {d}\n", .{blk: {
    454                         if (target.cpu.model == &std.Target.x86.cpu.i386) break :blk 300;
    455                         if (target.cpu.model == &std.Target.x86.cpu.i486) break :blk 400;
    456                         if (target.cpu.model == &std.Target.x86.cpu.i586) break :blk 500;
    457                         break :blk @as(u32, 600);
    458                     }});
    459                 }
    460             }
    461             try define(w, "__SEG_GS");
    462             try define(w, "__SEG_FS");
    463             try w.writeAll(
    464                 \\#define __seg_gs __attribute__((address_space(256)))
    465                 \\#define __seg_fs __attribute__((address_space(257)))
    466                 \\
    467             );
    468 
    469             if (target.cpu.has(.x86, .sahf) or (comp.langopts.emulate == .clang and target.cpu.arch == .x86)) {
    470                 try define(w, "__LAHF_SAHF__");
    471             }
    472 
    473             const features = target.cpu.features;
    474             for ([_]struct { std.Target.x86.Feature, []const u8 }{
    475                 .{ .aes, "__AES__" },
    476                 .{ .vaes, "__VAES__" },
    477                 .{ .pclmul, "__PCLMUL__" },
    478                 .{ .vpclmulqdq, "__VPCLMULQDQ__" },
    479                 .{ .lzcnt, "__LZCNT__" },
    480                 .{ .rdrnd, "__RDRND__" },
    481                 .{ .fsgsbase, "__FSGSBASE__" },
    482                 .{ .bmi, "__BMI__" },
    483                 .{ .bmi2, "__BMI2__" },
    484                 .{ .popcnt, "__POPCNT__" },
    485                 .{ .rtm, "__RTM__" },
    486                 .{ .prfchw, "__PRFCHW__" },
    487                 .{ .rdseed, "__RDSEED__" },
    488                 .{ .adx, "__ADX__" },
    489                 .{ .tbm, "__TBM__" },
    490                 .{ .lwp, "__LWP__" },
    491                 .{ .mwaitx, "__MWAITX__" },
    492                 .{ .movbe, "__MOVBE__" },
    493 
    494                 .{ .xop, "__XOP__" },
    495                 .{ .fma4, "__FMA4__" },
    496                 .{ .sse4a, "__SSE4A__" },
    497 
    498                 .{ .fma, "__FMA__" },
    499                 .{ .f16c, "__F16C__" },
    500                 .{ .gfni, "__GFNI__" },
    501 
    502                 .{ .avx10_1, "__AVX10_1__" },
    503                 .{ .avx10_1, "__AVX10_1_512__" },
    504 
    505                 .{ .avx10_2, "__AVX10_2__" },
    506                 .{ .avx10_2, "__AVX10_2_512__" },
    507 
    508                 .{ .avx512cd, "__AVX512CD__" },
    509                 .{ .avx512vpopcntdq, "__AVX512VPOPCNTDQ__" },
    510                 .{ .avx512vnni, "__AVX512VNNI__" },
    511                 .{ .avx512bf16, "__AVX512BF16__" },
    512                 .{ .avx512fp16, "__AVX512FP16__" },
    513                 .{ .avx512dq, "__AVX512DQ__" },
    514                 .{ .avx512bitalg, "__AVX512BITALG__" },
    515                 .{ .avx512bw, "__AVX512BW__" },
    516 
    517                 .{ .avx512vl, "__AVX512VL__" },
    518                 .{ .avx512vl, "__EVEX256__" },
    519 
    520                 .{ .avx512vbmi, "__AVX512VBMI__" },
    521                 .{ .avx512vbmi2, "__AVX512VBMI2__" },
    522                 .{ .avx512ifma, "__AVX512IFMA__" },
    523                 .{ .avx512vp2intersect, "__AVX512VP2INTERSECT__" },
    524                 .{ .sha, "__SHA__" },
    525                 .{ .sha512, "__SHA512__" },
    526                 .{ .fxsr, "__FXSR__" },
    527                 .{ .xsave, "__XSAVE__" },
    528                 .{ .xsaveopt, "__XSAVEOPT__" },
    529                 .{ .xsavec, "__XSAVEC__" },
    530                 .{ .xsaves, "__XSAVES__" },
    531                 .{ .pku, "__PKU__" },
    532                 .{ .clflushopt, "__CLFLUSHOPT__" },
    533                 .{ .clwb, "__CLWB__" },
    534                 .{ .wbnoinvd, "__WBNOINVD__" },
    535                 .{ .shstk, "__SHSTK__" },
    536                 .{ .sgx, "__SGX__" },
    537                 .{ .sm3, "__SM3__" },
    538                 .{ .sm4, "__SM4__" },
    539                 .{ .prefetchi, "__PREFETCHI__" },
    540                 .{ .clzero, "__CLZERO__" },
    541                 .{ .kl, "__KL__" },
    542                 .{ .widekl, "__WIDEKL__" },
    543                 .{ .rdpid, "__RDPID__" },
    544                 .{ .rdpru, "__RDPRU__" },
    545                 .{ .cldemote, "__CLDEMOTE__" },
    546                 .{ .waitpkg, "__WAITPKG__" },
    547                 .{ .movdiri, "__MOVDIRI__" },
    548                 .{ .movdir64b, "__MOVDIR64B__" },
    549                 .{ .movrs, "__MOVRS__" },
    550                 .{ .pconfig, "__PCONFIG__" },
    551                 .{ .ptwrite, "__PTWRITE__" },
    552                 .{ .invpcid, "__INVPCID__" },
    553                 .{ .enqcmd, "__ENQCMD__" },
    554                 .{ .hreset, "__HRESET__" },
    555                 .{ .amx_tile, "__AMX_TILE__" },
    556                 .{ .amx_int8, "__AMX_INT8__" },
    557                 .{ .amx_bf16, "__AMX_BF16__" },
    558                 .{ .amx_fp16, "__AMX_FP16__" },
    559                 .{ .amx_complex, "__AMX_COMPLEX__" },
    560                 .{ .amx_fp8, "__AMX_FP8__" },
    561                 .{ .amx_movrs, "__AMX_MOVRS__" },
    562                 .{ .amx_avx512, "__AMX_AVX512__" },
    563                 .{ .amx_tf32, "__AMX_TF32__" },
    564                 .{ .cmpccxadd, "__CMPCCXADD__" },
    565                 .{ .raoint, "__RAOINT__" },
    566                 .{ .avxifma, "__AVXIFMA__" },
    567                 .{ .avxneconvert, "__AVXNECONVERT__" },
    568                 .{ .avxvnni, "__AVXVNNI__" },
    569                 .{ .avxvnniint16, "__AVXVNNIINT16__" },
    570                 .{ .avxvnniint8, "__AVXVNNIINT8__" },
    571                 .{ .serialize, "__SERIALIZE__" },
    572                 .{ .tsxldtrk, "__TSXLDTRK__" },
    573                 .{ .uintr, "__UINTR__" },
    574                 .{ .usermsr, "__USERMSR__" },
    575                 .{ .crc32, "__CRC32__" },
    576                 .{ .egpr, "__EGPR__" },
    577                 .{ .push2pop2, "__PUSH2POP2__" },
    578                 .{ .ppx, "__PPX__" },
    579                 .{ .ndd, "__NDD__" },
    580                 .{ .ccmp, "__CCMP__" },
    581                 .{ .nf, "__NF__" },
    582                 .{ .cf, "__CF__" },
    583                 .{ .zu, "__ZU__" },
    584 
    585                 .{ .avx512f, "__AVX512F__" },
    586                 .{ .avx2, "__AVX2__" },
    587                 .{ .avx, "__AVX__" },
    588                 .{ .sse4_2, "__SSE4_2__" },
    589                 .{ .sse4_1, "__SSE4_1__" },
    590                 .{ .ssse3, "__SSSE3__" },
    591                 .{ .sse3, "__SSE3__" },
    592                 .{ .sse2, "__SSE2__" },
    593                 .{ .sse, "__SSE__" },
    594                 .{ .sse, "__SSE_MATH__" },
    595 
    596                 .{ .mmx, "__MMX__" },
    597             }) |fs| {
    598                 if (features.isEnabled(@intFromEnum(fs[0]))) {
    599                     try define(w, fs[1]);
    600                 }
    601             }
    602 
    603             if (comp.langopts.ms_extensions and target.cpu.arch == .x86) {
    604                 const level = if (target.cpu.has(.x86, .sse2))
    605                     "2"
    606                 else if (target.cpu.has(.x86, .sse))
    607                     "1"
    608                 else
    609                     "0";
    610 
    611                 try w.print("#define _M_IX86_FP {s}\n", .{level});
    612             }
    613 
    614             if (target.cpu.hasAll(.x86, &.{ .egpr, .push2pop2, .ppx, .ndd, .ccmp, .nf, .cf, .zu })) {
    615                 try define(w, "__APX_F__");
    616             }
    617 
    618             if (target.cpu.hasAll(.x86, &.{ .egpr, .inline_asm_use_gpr32 })) {
    619                 try define(w, "__APX_INLINE_ASM_USE_GPR32__");
    620             }
    621 
    622             if (target.cpu.has(.x86, .cx8)) {
    623                 try define(w, "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8");
    624             }
    625             if (target.cpu.has(.x86, .cx16) and target.cpu.arch == .x86_64) {
    626                 try define(w, "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8");
    627             }
    628 
    629             if (comp.hasFloat128()) {
    630                 try w.writeAll("#define __SIZEOF_FLOAT128__ 16\n");
    631             }
    632         },
    633         .mips,
    634         .mipsel,
    635         .mips64,
    636         .mips64el,
    637         => {
    638             try define(w, "__mips__");
    639             try define(w, "_mips");
    640         },
    641         .powerpc,
    642         .powerpcle,
    643         => {
    644             try define(w, "__powerpc__");
    645             try define(w, "__POWERPC__");
    646             try define(w, "__ppc__");
    647             try define(w, "__PPC__");
    648             try define(w, "_ARCH_PPC");
    649             try w.print("#define _CALL_ELF {d}\n", .{target.ppcElfVersion()});
    650         },
    651         .powerpc64,
    652         .powerpc64le,
    653         => {
    654             try define(w, "__powerpc");
    655             try define(w, "__powerpc__");
    656             try define(w, "__powerpc64__");
    657             try define(w, "__POWERPC__");
    658             try define(w, "__ppc__");
    659             try define(w, "__ppc64__");
    660             try define(w, "__PPC__");
    661             try define(w, "__PPC64__");
    662             try define(w, "_ARCH_PPC");
    663             try define(w, "_ARCH_PPC64");
    664             try w.print("#define _CALL_ELF {d}\n", .{target.ppcElfVersion()});
    665         },
    666         .sparc64 => {
    667             try defineStd(w, "sparc", is_gnu);
    668             try define(w, "__sparc_v9__");
    669             try define(w, "__arch64__");
    670             if (target.os.tag != .illumos) {
    671                 try define(w, "__sparc64__");
    672                 try define(w, "__sparc_v9__");
    673                 try define(w, "__sparcv9__");
    674             }
    675         },
    676         .sparc => {
    677             try defineStd(w, "sparc", is_gnu);
    678             if (target.os.tag == .illumos) {
    679                 try define(w, "__sparcv8");
    680             }
    681         },
    682         .arm, .armeb, .thumb, .thumbeb => {
    683             try define(w, "__arm__");
    684             try define(w, "__arm");
    685             if (target.cpu.arch.isThumb()) {
    686                 try define(w, "__thumb__");
    687             }
    688         },
    689         .aarch64, .aarch64_be => |arch| {
    690             try define(w, "__aarch64__");
    691             switch (arch) {
    692                 .aarch64 => {
    693                     try define(w, "__AARCH64EL__");
    694                 },
    695                 .aarch64_be => {
    696                     try define(w, "__AARCH64EB__");
    697                     try define(w, "__AARCH_BIG_ENDIAN");
    698                     try define(w, "__ARM_BIG_ENDIAN");
    699                 },
    700                 else => unreachable,
    701             }
    702             if (target.os.tag.isDarwin()) {
    703                 try define(w, "__AARCH64_SIMD__");
    704                 if (ptr_width == 32) {
    705                     try define(w, "__ARM64_ARCH_8_32__");
    706                 } else {
    707                     try define(w, "__ARM64_ARCH_8__");
    708                 }
    709                 try define(w, "__ARM_NEON__");
    710                 try define(w, "__arm64");
    711                 try define(w, "__arm64__");
    712             }
    713             if (target.os.tag == .windows and target.abi == .msvc) {
    714                 try w.writeAll("#define _M_ARM64 1\n");
    715             }
    716 
    717             {
    718                 const cmodel = switch (comp.cmodel) {
    719                     .default => "small",
    720                     else => @tagName(comp.cmodel),
    721                 };
    722                 try w.writeAll("#define __AARCH64_CMODEL_");
    723                 for (cmodel) |c| {
    724                     try w.writeByte(std.ascii.toUpper(c));
    725                 }
    726                 try w.writeAll("__ 1\n");
    727             }
    728 
    729             if (target.cpu.has(.aarch64, .fp_armv8)) {
    730                 try w.writeAll("#define __ARM_FP 0xE\n");
    731             }
    732             if (target.cpu.has(.aarch64, .neon)) {
    733                 try define(w, "__ARM_NEON");
    734                 try w.writeAll("#define __ARM_NEON_FP 0xE\n");
    735             }
    736             if (target.cpu.has(.aarch64, .bf16)) {
    737                 try define(w, "__ARM_FEATURE_BF16");
    738                 try define(w, "__ARM_FEATURE_BF16_VECTOR_ARITHMETIC");
    739                 try define(w, "__ARM_BF16_FORMAT_ALTERNATIVE");
    740                 try define(w, "__ARM_FEATURE_BF16_SCALAR_ARITHMETIC");
    741                 if (target.cpu.has(.aarch64, .sve)) {
    742                     try define(w, "__ARM_FEATURE_SVE_BF16");
    743                 }
    744             }
    745             if (target.cpu.hasAll(.aarch64, &.{ .sve2, .sve_aes })) {
    746                 try define(w, "__ARM_FEATURE_SVE2_AES");
    747             }
    748             if (target.cpu.hasAll(.aarch64, &.{ .sve2, .sve_bitperm })) {
    749                 try define(w, "__ARM_FEATURE_SVE2_BITPERM");
    750             }
    751             if (target.cpu.has(.aarch64, .sme)) {
    752                 try define(w, "__ARM_FEATURE_SME");
    753                 try define(w, "__ARM_FEATURE_LOCALLY_STREAMING");
    754             }
    755             if (target.cpu.has(.aarch64, .fmv)) {
    756                 try define(w, "__HAVE_FUNCTION_MULTI_VERSIONING");
    757             }
    758             if (target.cpu.has(.aarch64, .sha3)) {
    759                 try define(w, "__ARM_FEATURE_SHA3");
    760                 try define(w, "__ARM_FEATURE_SHA512");
    761             }
    762             if (target.cpu.has(.aarch64, .sm4)) {
    763                 try define(w, "__ARM_FEATURE_SM3");
    764                 try define(w, "__ARM_FEATURE_SM4");
    765             }
    766             if (!target.cpu.has(.aarch64, .strict_align)) {
    767                 try define(w, "__ARM_FEATURE_UNALIGNED");
    768             }
    769             if (target.cpu.hasAll(.aarch64, &.{ .neon, .fullfp16 })) {
    770                 try define(w, "__ARM_FEATURE_FP16_VECTOR_ARITHMETIC");
    771             }
    772             if (target.cpu.has(.aarch64, .rcpc3)) {
    773                 try w.writeAll("#define __ARM_FEATURE_RCPC 3\n");
    774             } else if (target.cpu.has(.aarch64, .rcpc)) {
    775                 try define(w, "__ARM_FEATURE_RCPC");
    776             }
    777 
    778             const features = target.cpu.features;
    779             for ([_]struct { std.Target.aarch64.Feature, []const u8 }{
    780                 .{ .sve, "SVE" },
    781                 .{ .sve2, "SVE2" },
    782                 .{ .sve2p1, "SVE2p1" },
    783                 .{ .sve2_sha3, "SVE2_SHA3" },
    784                 .{ .sve2_sm4, "SVE2_SM4" },
    785                 .{ .sve_b16b16, "SVE_B16B16" },
    786                 .{ .sme2, "SME2" },
    787                 .{ .sme2p1, "SME2p1" },
    788                 .{ .sme_f16f16, "SME_F16F16" },
    789                 .{ .sme_b16b16, "SME_B16B16" },
    790                 .{ .crc, "CRC32" },
    791                 .{ .aes, "AES" },
    792                 .{ .sha2, "SHA2" },
    793                 .{ .pauth, "PAUTH" },
    794                 .{ .pauth_lr, "PAUTH_LR" },
    795                 .{ .bti, "BTI" },
    796                 .{ .fullfp16, "FP16_SCALAR_ARITHMETIC" },
    797                 .{ .dotprod, "DOTPROD" },
    798                 .{ .mte, "MEMORY_TAGGING" },
    799                 .{ .i8mm, "MATMUL_INT8" },
    800                 .{ .lse, "ATOMICS" },
    801                 .{ .f64mm, "SVE_MATMUL_FP64" },
    802                 .{ .f32mm, "SVE_MATMUL_FP32" },
    803                 .{ .i8mm, "SVE_MATMUL_INT8" },
    804                 .{ .fp16fml, "FP16_FML" },
    805                 .{ .ls64, "LS64" },
    806                 .{ .rand, "RNG" },
    807                 .{ .mops, "MOPS" },
    808                 .{ .d128, "SYSREG128" },
    809                 .{ .gcs, "GCS" },
    810             }) |fs| {
    811                 if (features.isEnabled(@intFromEnum(fs[0]))) {
    812                     try w.print("#define __ARM_FEATURE_{s} 1\n", .{fs[1]});
    813                 }
    814             }
    815         },
    816         .msp430 => {
    817             try define(w, "MSP430");
    818             try define(w, "__MSP430__");
    819         },
    820         .arc => {
    821             try define(w, "__arc__");
    822         },
    823         .wasm32, .wasm64 => {
    824             try define(w, "__wasm");
    825             try define(w, "__wasm__");
    826             if (target.cpu.arch == .wasm32) {
    827                 try define(w, "__wasm32");
    828                 try define(w, "__wasm32__");
    829             } else {
    830                 try define(w, "__wasm64");
    831                 try define(w, "__wasm64__");
    832             }
    833 
    834             for (target.cpu.arch.allFeaturesList()) |feature| {
    835                 if (!target.cpu.features.isEnabled(feature.index)) continue;
    836                 try w.print("#define __wasm_{s}__ 1\n", .{feature.name});
    837             }
    838         },
    839         .s390x => {
    840             try define(w, "__s390__");
    841             try define(w, "__s390x__");
    842             try define(w, "__zarch__");
    843 
    844             if (target.cpu.has(.s390x, .transactional_execution)) {
    845                 try define(w, "__HTM__");
    846             }
    847             if (target.cpu.has(.s390x, .vector)) {
    848                 try define(w, "__VX__");
    849             }
    850         },
    851         .riscv32, .riscv32be, .riscv64, .riscv64be => {
    852             try define(w, "__riscv");
    853             try w.print("#define __riscv_xlen {d}\n", .{ptr_width});
    854         },
    855         else => {},
    856     }
    857 
    858     if (ptr_width == 64 and target.cTypeBitSize(.long) == 64 and
    859         target.cTypeBitSize(.int) == 32)
    860     {
    861         try define(w, "_LP64");
    862         try define(w, "__LP64__");
    863     } else if (ptr_width == 32 and target.cTypeBitSize(.long) == 32 and
    864         target.cTypeBitSize(.int) == 32)
    865     {
    866         try define(w, "_ILP32");
    867         try define(w, "__ILP32__");
    868     }
    869 
    870     if (comp.hasFloat128()) {
    871         try define(w, "__FLOAT128__");
    872     }
    873 
    874     try w.writeAll(
    875         \\#define __ORDER_LITTLE_ENDIAN__ 1234
    876         \\#define __ORDER_BIG_ENDIAN__ 4321
    877         \\#define __ORDER_PDP_ENDIAN__ 3412
    878         \\
    879     );
    880     if (target.cpu.arch.endian() == .little) try w.writeAll(
    881         \\#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
    882         \\#define __LITTLE_ENDIAN__ 1
    883         \\
    884     ) else try w.writeAll(
    885         \\#define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__
    886         \\#define __BIG_ENDIAN__ 1
    887         \\
    888     );
    889 
    890     switch (target.ofmt) {
    891         .elf => try define(w, "__ELF__"),
    892         .macho => try define(w, "__MACH__"),
    893         else => {},
    894     }
    895 
    896     if (target.os.tag.isDarwin()) {
    897         try w.writeAll(
    898             \\#define __nonnull _Nonnull
    899             \\#define __null_unspecified _Null_unspecified
    900             \\#define __nullable _Nullable
    901             \\
    902         );
    903     }
    904 
    905     // atomics
    906     try w.writeAll(
    907         \\#define __ATOMIC_RELAXED 0
    908         \\#define __ATOMIC_CONSUME 1
    909         \\#define __ATOMIC_ACQUIRE 2
    910         \\#define __ATOMIC_RELEASE 3
    911         \\#define __ATOMIC_ACQ_REL 4
    912         \\#define __ATOMIC_SEQ_CST 5
    913         \\
    914     );
    915 
    916     // TODO: Set these to target-specific constants depending on backend capabilities
    917     // For now they are just set to the "may be lock-free" value
    918     try w.writeAll(
    919         \\#define __ATOMIC_BOOL_LOCK_FREE 1
    920         \\#define __ATOMIC_CHAR_LOCK_FREE 1
    921         \\#define __ATOMIC_CHAR16_T_LOCK_FREE 1
    922         \\#define __ATOMIC_CHAR32_T_LOCK_FREE 1
    923         \\#define __ATOMIC_WCHAR_T_LOCK_FREE 1
    924         \\#define __ATOMIC_WINT_T_LOCK_FREE 1
    925         \\#define __ATOMIC_SHORT_LOCK_FREE 1
    926         \\#define __ATOMIC_INT_LOCK_FREE 1
    927         \\#define __ATOMIC_LONG_LOCK_FREE 1
    928         \\#define __ATOMIC_LLONG_LOCK_FREE 1
    929         \\#define __ATOMIC_POINTER_LOCK_FREE 1
    930         \\
    931     );
    932     if (comp.langopts.hasChar8_T()) {
    933         try w.writeAll("#define __ATOMIC_CHAR8_T_LOCK_FREE 1\n");
    934     }
    935 
    936     // types
    937     if (comp.getCharSignedness() == .unsigned) {
    938         try w.writeAll("#define __CHAR_UNSIGNED__ 1\n");
    939     }
    940     if (comp.type_store.wchar.signedness(comp) == .unsigned) {
    941         try w.writeAll("#define __WCHAR_UNSIGNED__ 1\n");
    942     }
    943     if (comp.type_store.wint.signedness(comp) == .unsigned) {
    944         try w.writeAll("#define __WINT_UNSIGNED__ 1\n");
    945     }
    946     try w.writeAll("#define __CHAR_BIT__ 8\n");
    947 
    948     // int maxs
    949     try comp.generateIntWidth(w, "BOOL", .bool);
    950     try comp.generateIntMaxAndWidth(w, "SCHAR", .schar);
    951     try comp.generateIntMaxAndWidth(w, "SHRT", .short);
    952     try comp.generateIntMaxAndWidth(w, "INT", .int);
    953     try comp.generateIntMaxAndWidth(w, "LONG", .long);
    954     try comp.generateIntMaxAndWidth(w, "LONG_LONG", .long_long);
    955     try comp.generateIntMaxAndWidth(w, "WCHAR", comp.type_store.wchar);
    956     try comp.generateIntMaxAndWidth(w, "WINT", comp.type_store.wint);
    957     try comp.generateIntMaxAndWidth(w, "INTMAX", comp.type_store.intmax);
    958     try comp.generateIntMaxAndWidth(w, "SIZE", comp.type_store.size);
    959     try comp.generateIntMaxAndWidth(w, "UINTMAX", try comp.type_store.intmax.makeIntUnsigned(comp));
    960     try comp.generateIntMaxAndWidth(w, "PTRDIFF", comp.type_store.ptrdiff);
    961     try comp.generateIntMaxAndWidth(w, "INTPTR", comp.type_store.intptr);
    962     try comp.generateIntMaxAndWidth(w, "UINTPTR", try comp.type_store.intptr.makeIntUnsigned(comp));
    963     try comp.generateIntMaxAndWidth(w, "SIG_ATOMIC", target.sigAtomicType());
    964 
    965     // int widths
    966     try w.print("#define __BITINT_MAXWIDTH__ {d}\n", .{bit_int_max_bits});
    967 
    968     // sizeof types
    969     try comp.generateSizeofType(w, "__SIZEOF_FLOAT__", .float);
    970     try comp.generateSizeofType(w, "__SIZEOF_DOUBLE__", .double);
    971     try comp.generateSizeofType(w, "__SIZEOF_LONG_DOUBLE__", .long_double);
    972     try comp.generateSizeofType(w, "__SIZEOF_SHORT__", .short);
    973     try comp.generateSizeofType(w, "__SIZEOF_INT__", .int);
    974     try comp.generateSizeofType(w, "__SIZEOF_LONG__", .long);
    975     try comp.generateSizeofType(w, "__SIZEOF_LONG_LONG__", .long_long);
    976     try comp.generateSizeofType(w, "__SIZEOF_POINTER__", .void_pointer);
    977     try comp.generateSizeofType(w, "__SIZEOF_PTRDIFF_T__", comp.type_store.ptrdiff);
    978     try comp.generateSizeofType(w, "__SIZEOF_SIZE_T__", comp.type_store.size);
    979     try comp.generateSizeofType(w, "__SIZEOF_WCHAR_T__", comp.type_store.wchar);
    980     try comp.generateSizeofType(w, "__SIZEOF_WINT_T__", comp.type_store.wint);
    981 
    982     if (target.hasInt128()) {
    983         try comp.generateSizeofType(w, "__SIZEOF_INT128__", .int128);
    984     }
    985 
    986     // various int types
    987     try comp.generateTypeMacro(w, "__INTPTR_TYPE__", comp.type_store.intptr);
    988     try comp.generateTypeMacro(w, "__UINTPTR_TYPE__", try comp.type_store.intptr.makeIntUnsigned(comp));
    989 
    990     try comp.generateTypeMacro(w, "__INTMAX_TYPE__", comp.type_store.intmax);
    991     try comp.generateIntLiteralMacros("__INTMAX", w, comp.type_store.intptr);
    992 
    993     try comp.generateTypeMacro(w, "__UINTMAX_TYPE__", try comp.type_store.intmax.makeIntUnsigned(comp));
    994     try comp.generateIntLiteralMacros("__UINTMAX", w, try comp.type_store.intptr.makeIntUnsigned(comp));
    995 
    996     try comp.generateTypeMacro(w, "__PTRDIFF_TYPE__", comp.type_store.ptrdiff);
    997     try comp.generateTypeMacro(w, "__SIZE_TYPE__", comp.type_store.size);
    998     try comp.generateTypeMacro(w, "__WCHAR_TYPE__", comp.type_store.wchar);
    999     try comp.generateTypeMacro(w, "__WINT_TYPE__", comp.type_store.wint);
   1000     try comp.generateTypeMacro(w, "__CHAR16_TYPE__", comp.type_store.uint_least16_t);
   1001     try comp.generateTypeMacro(w, "__CHAR32_TYPE__", comp.type_store.uint_least32_t);
   1002 
   1003     try comp.generateExactWidthTypes(w);
   1004     try comp.generateFastAndLeastWidthTypes(w);
   1005 
   1006     if (Target.FPSemantics.halfPrecisionType(target)) |half| {
   1007         try generateFloatMacros(w, "FLT16", half, "F16");
   1008     }
   1009     try generateFloatMacros(w, "FLT", Target.FPSemantics.forType(.float, target), "F");
   1010     try generateFloatMacros(w, "DBL", Target.FPSemantics.forType(.double, target), "");
   1011     try generateFloatMacros(w, "LDBL", Target.FPSemantics.forType(.longdouble, target), "L");
   1012 
   1013     // TODO: clang treats __FLT_EVAL_METHOD__ as a special-cased macro because evaluating it within a scope
   1014     // where `#pragma clang fp eval_method(X)` has been called produces an error diagnostic.
   1015     const flt_eval_method = comp.langopts.fp_eval_method orelse target.defaultFpEvalMethod();
   1016     try w.print("#define __FLT_EVAL_METHOD__ {d}\n", .{@intFromEnum(flt_eval_method)});
   1017 
   1018     try w.writeAll(
   1019         \\#define __FLT_RADIX__ 2
   1020         \\#define __DECIMAL_DIG__ __LDBL_DECIMAL_DIG__
   1021         \\
   1022     );
   1023 
   1024     switch (comp.code_gen_options.pic_level) {
   1025         .none => {},
   1026         .one, .two => {
   1027             try w.print(
   1028                 \\#define __pic__ {0d}
   1029                 \\#define __PIC__ {0d}
   1030                 \\
   1031             , .{@intFromEnum(comp.code_gen_options.pic_level)});
   1032             if (comp.code_gen_options.is_pie) {
   1033                 try w.print(
   1034                     \\#define __pie__ {0d}
   1035                     \\#define __PIE__ {0d}
   1036                     \\
   1037                 , .{@intFromEnum(comp.code_gen_options.pic_level)});
   1038             }
   1039         },
   1040     }
   1041 }
   1042 
   1043 /// Generate builtin macros that will be available to each source file.
   1044 pub fn generateBuiltinMacros(comp: *Compilation, system_defines_mode: SystemDefinesMode) AddSourceError!Source {
   1045     try comp.type_store.initNamedTypes(comp);
   1046 
   1047     var allocating: Io.Writer.Allocating = try .initCapacity(comp.gpa, 2 << 13);
   1048     defer allocating.deinit();
   1049 
   1050     comp.writeBuiltinMacros(system_defines_mode, &allocating.writer) catch |err| switch (err) {
   1051         error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,
   1052     };
   1053 
   1054     if (allocating.written().len > std.math.maxInt(u32)) return error.FileTooBig;
   1055 
   1056     const contents = try allocating.toOwnedSlice();
   1057     errdefer comp.gpa.free(contents);
   1058     return comp.addSourceFromOwnedBuffer("<builtin>", contents, .user);
   1059 }
   1060 
   1061 fn writeBuiltinMacros(comp: *Compilation, system_defines_mode: SystemDefinesMode, w: *Io.Writer) !void {
   1062     if (system_defines_mode == .include_system_defines) {
   1063         try w.writeAll(
   1064             \\#define __VERSION__ "Aro
   1065         ++ " " ++ @import("../backend.zig").version_str ++ "\"\n" ++
   1066             \\#define __Aro__
   1067             \\
   1068         );
   1069     }
   1070 
   1071     if (comp.langopts.emulate != .msvc) {
   1072         try w.writeAll("#define __STDC__ 1\n");
   1073     }
   1074     try w.print("#define __STDC_HOSTED__ {d}\n", .{@intFromBool(comp.target.os.tag != .freestanding)});
   1075 
   1076     // standard macros
   1077     try w.writeAll(
   1078         \\#define __STDC_UTF_16__ 1
   1079         \\#define __STDC_UTF_32__ 1
   1080         \\#define __STDC_EMBED_NOT_FOUND__ 0
   1081         \\#define __STDC_EMBED_FOUND__ 1
   1082         \\#define __STDC_EMBED_EMPTY__ 2
   1083         \\
   1084     );
   1085     if (comp.langopts.standard.atLeast(.c11)) switch (comp.target.os.tag) {
   1086         .openbsd, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => {
   1087             try w.writeAll("#define __STDC_NO_THREADS__ 1\n");
   1088         },
   1089         .ps4, .ps5 => {
   1090             try w.writeAll(
   1091                 \\#define __STDC_NO_THREADS__ 1
   1092                 \\#define __STDC_NO_COMPLEX__ 1
   1093                 \\
   1094             );
   1095         },
   1096         else => {},
   1097     };
   1098     if (comp.langopts.standard.StdCVersionMacro()) |stdc_version| {
   1099         try w.writeAll("#define __STDC_VERSION__ ");
   1100         try w.writeAll(stdc_version);
   1101         try w.writeByte('\n');
   1102     }
   1103 
   1104     if (system_defines_mode == .include_system_defines) {
   1105         try comp.generateSystemDefines(w);
   1106     }
   1107 }
   1108 
   1109 fn generateFloatMacros(w: *Io.Writer, prefix: []const u8, semantics: Target.FPSemantics, ext: []const u8) !void {
   1110     const denormMin = semantics.chooseValue(
   1111         []const u8,
   1112         .{
   1113             "5.9604644775390625e-8",
   1114             "1.40129846e-45",
   1115             "4.9406564584124654e-324",
   1116             "3.64519953188247460253e-4951",
   1117             "4.94065645841246544176568792868221e-324",
   1118             "6.47517511943802511092443895822764655e-4966",
   1119         },
   1120     );
   1121     const digits = semantics.chooseValue(i32, .{ 3, 6, 15, 18, 31, 33 });
   1122     const decimalDigits = semantics.chooseValue(i32, .{ 5, 9, 17, 21, 33, 36 });
   1123     const epsilon = semantics.chooseValue(
   1124         []const u8,
   1125         .{
   1126             "9.765625e-4",
   1127             "1.19209290e-7",
   1128             "2.2204460492503131e-16",
   1129             "1.08420217248550443401e-19",
   1130             "4.94065645841246544176568792868221e-324",
   1131             "1.92592994438723585305597794258492732e-34",
   1132         },
   1133     );
   1134     const mantissaDigits = semantics.chooseValue(i32, .{ 11, 24, 53, 64, 106, 113 });
   1135 
   1136     const min10Exp = semantics.chooseValue(i32, .{ -4, -37, -307, -4931, -291, -4931 });
   1137     const max10Exp = semantics.chooseValue(i32, .{ 4, 38, 308, 4932, 308, 4932 });
   1138 
   1139     const minExp = semantics.chooseValue(i32, .{ -13, -125, -1021, -16381, -968, -16381 });
   1140     const maxExp = semantics.chooseValue(i32, .{ 16, 128, 1024, 16384, 1024, 16384 });
   1141 
   1142     const min = semantics.chooseValue(
   1143         []const u8,
   1144         .{
   1145             "6.103515625e-5",
   1146             "1.17549435e-38",
   1147             "2.2250738585072014e-308",
   1148             "3.36210314311209350626e-4932",
   1149             "2.00416836000897277799610805135016e-292",
   1150             "3.36210314311209350626267781732175260e-4932",
   1151         },
   1152     );
   1153     const max = semantics.chooseValue(
   1154         []const u8,
   1155         .{
   1156             "6.5504e+4",
   1157             "3.40282347e+38",
   1158             "1.7976931348623157e+308",
   1159             "1.18973149535723176502e+4932",
   1160             "1.79769313486231580793728971405301e+308",
   1161             "1.18973149535723176508575932662800702e+4932",
   1162         },
   1163     );
   1164 
   1165     try w.print("#define __{s}_DENORM_MIN__ {s}{s}\n", .{ prefix, denormMin, ext });
   1166     try w.print("#define __{s}_HAS_DENORM__\n", .{prefix});
   1167     try w.print("#define __{s}_DIG__ {d}\n", .{ prefix, digits });
   1168     try w.print("#define __{s}_DECIMAL_DIG__ {d}\n", .{ prefix, decimalDigits });
   1169 
   1170     try w.print("#define __{s}_EPSILON__ {s}{s}\n", .{ prefix, epsilon, ext });
   1171     try w.print("#define __{s}_HAS_INFINITY__\n", .{prefix});
   1172     try w.print("#define __{s}_HAS_QUIET_NAN__\n", .{prefix});
   1173     try w.print("#define __{s}_MANT_DIG__ {d}\n", .{ prefix, mantissaDigits });
   1174 
   1175     try w.print("#define __{s}_MAX_10_EXP__ {d}\n", .{ prefix, max10Exp });
   1176     try w.print("#define __{s}_MAX_EXP__ {d}\n", .{ prefix, maxExp });
   1177     try w.print("#define __{s}_MAX__ {s}{s}\n", .{ prefix, max, ext });
   1178 
   1179     try w.print("#define __{s}_MIN_10_EXP__ ({d})\n", .{ prefix, min10Exp });
   1180     try w.print("#define __{s}_MIN_EXP__ ({d})\n", .{ prefix, minExp });
   1181     try w.print("#define __{s}_MIN__ {s}{s}\n", .{ prefix, min, ext });
   1182 }
   1183 
   1184 fn generateTypeMacro(comp: *const Compilation, w: *Io.Writer, name: []const u8, qt: QualType) !void {
   1185     try w.print("#define {s} ", .{name});
   1186     try qt.print(comp, w);
   1187     try w.writeByte('\n');
   1188 }
   1189 
   1190 pub fn float80Type(comp: *const Compilation) ?QualType {
   1191     return switch (comp.langopts.emulate) {
   1192         .no, .gcc => comp.target.float80Type(),
   1193         .msvc, .clang => null,
   1194     };
   1195 }
   1196 
   1197 /// Smallest integer type with at least N bits
   1198 pub fn intLeastN(comp: *const Compilation, bits: usize, signedness: std.builtin.Signedness) QualType {
   1199     if (bits == 64 and (comp.target.os.tag.isDarwin() or comp.target.cpu.arch.isWasm())) {
   1200         // WebAssembly and Darwin use `long long` for `int_least64_t` and `int_fast64_t`.
   1201         return if (signedness == .signed) .long_long else .ulong_long;
   1202     }
   1203     if (bits == 16 and comp.target.cpu.arch == .avr) {
   1204         // AVR uses int for int_least16_t and int_fast16_t.
   1205         return if (signedness == .signed) .int else .uint;
   1206     }
   1207     const candidates: [5]QualType = switch (signedness) {
   1208         .signed => .{ .schar, .short, .int, .long, .long_long },
   1209         .unsigned => .{ .uchar, .ushort, .uint, .ulong, .ulong_long },
   1210     };
   1211     for (candidates) |qt| {
   1212         if (qt.bitSizeof(comp) >= bits) return qt;
   1213     } else unreachable;
   1214 }
   1215 
   1216 fn generateFastOrLeastType(
   1217     comp: *Compilation,
   1218     bits: usize,
   1219     kind: enum { least, fast },
   1220     signedness: std.builtin.Signedness,
   1221     w: *Io.Writer,
   1222 ) !void {
   1223     const ty = comp.intLeastN(bits, signedness); // defining the fast types as the least types is permitted
   1224 
   1225     var buf: [32]u8 = undefined;
   1226     const suffix = "_TYPE__";
   1227     const base_name = switch (signedness) {
   1228         .signed => "__INT_",
   1229         .unsigned => "__UINT_",
   1230     };
   1231     const kind_str = switch (kind) {
   1232         .fast => "FAST",
   1233         .least => "LEAST",
   1234     };
   1235 
   1236     const full = std.fmt.bufPrint(&buf, "{s}{s}{d}{s}", .{
   1237         base_name, kind_str, bits, suffix,
   1238     }) catch unreachable;
   1239 
   1240     try comp.generateTypeMacro(w, full, ty);
   1241 
   1242     const prefix = full[2 .. full.len - suffix.len]; // remove "__" and "_TYPE__"
   1243 
   1244     switch (signedness) {
   1245         .signed => try comp.generateIntMaxAndWidth(w, prefix, ty),
   1246         .unsigned => try comp.generateIntMax(w, prefix, ty),
   1247     }
   1248     try comp.generateFmt(prefix, w, ty);
   1249 }
   1250 
   1251 fn generateFastAndLeastWidthTypes(comp: *Compilation, w: *Io.Writer) !void {
   1252     const sizes = [_]usize{ 8, 16, 32, 64 };
   1253     for (sizes) |size| {
   1254         try comp.generateFastOrLeastType(size, .least, .signed, w);
   1255         try comp.generateFastOrLeastType(size, .least, .unsigned, w);
   1256         try comp.generateFastOrLeastType(size, .fast, .signed, w);
   1257         try comp.generateFastOrLeastType(size, .fast, .unsigned, w);
   1258     }
   1259 }
   1260 
   1261 fn generateExactWidthTypes(comp: *Compilation, w: *Io.Writer) !void {
   1262     try comp.generateExactWidthType(w, .schar);
   1263 
   1264     if (QualType.short.sizeof(comp) > QualType.char.sizeof(comp)) {
   1265         try comp.generateExactWidthType(w, .short);
   1266     }
   1267 
   1268     if (QualType.int.sizeof(comp) > QualType.short.sizeof(comp)) {
   1269         try comp.generateExactWidthType(w, .int);
   1270     }
   1271 
   1272     if (QualType.long.sizeof(comp) > QualType.int.sizeof(comp)) {
   1273         try comp.generateExactWidthType(w, .long);
   1274     }
   1275 
   1276     if (QualType.long_long.sizeof(comp) > QualType.long.sizeof(comp)) {
   1277         try comp.generateExactWidthType(w, .long_long);
   1278     }
   1279 
   1280     try comp.generateExactWidthType(w, .uchar);
   1281     try comp.generateExactWidthIntMax(w, .uchar);
   1282     try comp.generateExactWidthIntMax(w, .schar);
   1283 
   1284     if (QualType.short.sizeof(comp) > QualType.char.sizeof(comp)) {
   1285         try comp.generateExactWidthType(w, .ushort);
   1286         try comp.generateExactWidthIntMax(w, .ushort);
   1287         try comp.generateExactWidthIntMax(w, .short);
   1288     }
   1289 
   1290     if (QualType.int.sizeof(comp) > QualType.short.sizeof(comp)) {
   1291         try comp.generateExactWidthType(w, .uint);
   1292         try comp.generateExactWidthIntMax(w, .uint);
   1293         try comp.generateExactWidthIntMax(w, .int);
   1294     }
   1295 
   1296     if (QualType.long.sizeof(comp) > QualType.int.sizeof(comp)) {
   1297         try comp.generateExactWidthType(w, .ulong);
   1298         try comp.generateExactWidthIntMax(w, .ulong);
   1299         try comp.generateExactWidthIntMax(w, .long);
   1300     }
   1301 
   1302     if (QualType.long_long.sizeof(comp) > QualType.long.sizeof(comp)) {
   1303         try comp.generateExactWidthType(w, .ulong_long);
   1304         try comp.generateExactWidthIntMax(w, .ulong_long);
   1305         try comp.generateExactWidthIntMax(w, .long_long);
   1306     }
   1307 }
   1308 
   1309 fn generateFmt(comp: *const Compilation, prefix: []const u8, w: *Io.Writer, qt: QualType) !void {
   1310     const unsigned = qt.signedness(comp) == .unsigned;
   1311     const modifier = qt.formatModifier(comp);
   1312     const formats = if (unsigned) "ouxX" else "di";
   1313     for (formats) |c| {
   1314         try w.print("#define {s}_FMT{c}__ \"{s}{c}\"\n", .{ prefix, c, modifier, c });
   1315     }
   1316 }
   1317 
   1318 fn generateIntLiteralMacros(comp: *const Compilation, prefix: []const u8, w: *Io.Writer, qt: QualType) !void {
   1319     const suffix = qt.intValueSuffix(comp);
   1320     try w.print("#define {s}_C_SUFFIX__ {s}\n", .{ prefix, suffix });
   1321     if (suffix.len == 0) {
   1322         try w.print("#define {s}_C(c) c\n", .{prefix});
   1323     } else {
   1324         try w.print("#define {s}_C(c) c##{s}\n", .{ prefix, suffix });
   1325     }
   1326 }
   1327 
   1328 /// Generate the following for a type:
   1329 ///     Name macro (e.g. #define __UINT32_TYPE__ unsigned int)
   1330 ///     Format strings (e.g. #define __UINT32_FMTu__ "u")
   1331 ///     Suffix macro (e.g. #define __UINT32_C_SUFFIX__ U)
   1332 fn generateExactWidthType(comp: *Compilation, w: *Io.Writer, original_qt: QualType) !void {
   1333     var qt = original_qt;
   1334     const width = qt.sizeof(comp) * 8;
   1335     const unsigned = qt.signedness(comp) == .unsigned;
   1336 
   1337     if (width == 16) {
   1338         qt = if (unsigned) try comp.type_store.int16.makeIntUnsigned(comp) else comp.type_store.int16;
   1339     } else if (width == 64) {
   1340         qt = if (unsigned) try comp.type_store.int64.makeIntUnsigned(comp) else comp.type_store.int64;
   1341     }
   1342 
   1343     var buffer: [16]u8 = undefined;
   1344     const suffix = "_TYPE__";
   1345     const full = std.fmt.bufPrint(&buffer, "{s}{d}{s}", .{
   1346         if (unsigned) "__UINT" else "__INT", width, suffix,
   1347     }) catch unreachable;
   1348 
   1349     try comp.generateTypeMacro(w, full, qt);
   1350 
   1351     const prefix = full[0 .. full.len - suffix.len]; // remove "_TYPE__"
   1352 
   1353     try comp.generateFmt(prefix, w, qt);
   1354     try comp.generateIntLiteralMacros(prefix, w, qt);
   1355 }
   1356 
   1357 pub fn hasFloat128(comp: *const Compilation) bool {
   1358     return comp.target.hasFloat128();
   1359 }
   1360 
   1361 pub fn hasHalfPrecisionFloatABI(comp: *const Compilation) bool {
   1362     return comp.langopts.allow_half_args_and_returns or comp.target.hasHalfPrecisionFloatABI();
   1363 }
   1364 
   1365 fn generateIntMax(comp: *const Compilation, w: *Io.Writer, name: []const u8, qt: QualType) !void {
   1366     const unsigned = qt.signedness(comp) == .unsigned;
   1367     const max: u128 = switch (qt.bitSizeof(comp)) {
   1368         8 => if (unsigned) std.math.maxInt(u8) else std.math.maxInt(i8),
   1369         16 => if (unsigned) std.math.maxInt(u16) else std.math.maxInt(i16),
   1370         32 => if (unsigned) std.math.maxInt(u32) else std.math.maxInt(i32),
   1371         64 => if (unsigned) std.math.maxInt(u64) else std.math.maxInt(i64),
   1372         128 => if (unsigned) std.math.maxInt(u128) else std.math.maxInt(i128),
   1373         else => unreachable,
   1374     };
   1375     try w.print("#define __{s}_MAX__ {d}{s}\n", .{ name, max, qt.intValueSuffix(comp) });
   1376 }
   1377 
   1378 /// Largest value that can be stored in wchar_t
   1379 pub fn wcharMax(comp: *const Compilation) u32 {
   1380     const unsigned = comp.type_store.wchar.signedness(comp) == .unsigned;
   1381     return switch (comp.type_store.wchar.bitSizeof(comp)) {
   1382         8 => if (unsigned) std.math.maxInt(u8) else std.math.maxInt(i8),
   1383         16 => if (unsigned) std.math.maxInt(u16) else std.math.maxInt(i16),
   1384         32 => if (unsigned) std.math.maxInt(u32) else std.math.maxInt(i32),
   1385         else => unreachable,
   1386     };
   1387 }
   1388 
   1389 fn generateExactWidthIntMax(comp: *Compilation, w: *Io.Writer, original_qt: QualType) !void {
   1390     var qt = original_qt;
   1391     const bit_count: u8 = @intCast(qt.sizeof(comp) * 8);
   1392     const unsigned = qt.signedness(comp) == .unsigned;
   1393 
   1394     if (bit_count == 64) {
   1395         qt = if (unsigned) try comp.type_store.int64.makeIntUnsigned(comp) else comp.type_store.int64;
   1396     }
   1397 
   1398     var name_buffer: [6]u8 = undefined;
   1399     const name = std.fmt.bufPrint(&name_buffer, "{s}{d}", .{
   1400         if (unsigned) "UINT" else "INT", bit_count,
   1401     }) catch unreachable;
   1402 
   1403     return comp.generateIntMax(w, name, qt);
   1404 }
   1405 
   1406 fn generateIntWidth(comp: *Compilation, w: *Io.Writer, name: []const u8, qt: QualType) !void {
   1407     try w.print("#define __{s}_WIDTH__ {d}\n", .{ name, qt.sizeof(comp) * 8 });
   1408 }
   1409 
   1410 fn generateIntMaxAndWidth(comp: *Compilation, w: *Io.Writer, name: []const u8, qt: QualType) !void {
   1411     try comp.generateIntMax(w, name, qt);
   1412     try comp.generateIntWidth(w, name, qt);
   1413 }
   1414 
   1415 fn generateSizeofType(comp: *Compilation, w: *Io.Writer, name: []const u8, qt: QualType) !void {
   1416     try w.print("#define {s} {d}\n", .{ name, qt.sizeof(comp) });
   1417 }
   1418 
   1419 pub fn nextLargestIntSameSign(comp: *const Compilation, qt: QualType) ?QualType {
   1420     assert(qt.isInt(comp));
   1421     const candidates: [4]QualType = if (qt.signedness(comp) == .signed)
   1422         .{ .short, .int, .long, .long_long }
   1423     else
   1424         .{ .ushort, .uint, .ulong, .ulong_long };
   1425 
   1426     const size = qt.sizeof(comp);
   1427     for (candidates) |candidate| {
   1428         if (candidate.sizeof(comp) > size) return candidate;
   1429     }
   1430     return null;
   1431 }
   1432 
   1433 /// Maximum size of an array, in bytes
   1434 pub fn maxArrayBytes(comp: *const Compilation) u64 {
   1435     const max_bits = @min(61, comp.target.ptrBitWidth());
   1436     return (@as(u64, 1) << @truncate(max_bits)) - 1;
   1437 }
   1438 
   1439 /// If `enum E { ... }` syntax has a fixed underlying integer type regardless of the presence of
   1440 /// __attribute__((packed)) or the range of values of the corresponding enumerator constants,
   1441 /// specify it here.
   1442 /// TODO: likely incomplete
   1443 pub fn fixedEnumTagType(comp: *const Compilation) ?QualType {
   1444     switch (comp.langopts.emulate) {
   1445         .msvc => return .int,
   1446         .no, .clang => if (comp.target.os.tag == .windows and comp.target.abi == .msvc) return .int,
   1447         .gcc => {},
   1448     }
   1449     return null;
   1450 }
   1451 
   1452 pub fn getCharSignedness(comp: *const Compilation) std.builtin.Signedness {
   1453     return comp.langopts.char_signedness_override orelse comp.target.cCharSignedness();
   1454 }
   1455 
   1456 pub fn hasClangStyleBoundsSafety(comp: *const Compilation) bool {
   1457     return comp.langopts.bounds_safety == .clang;
   1458 }
   1459 
   1460 pub fn getSource(comp: *const Compilation, id: Source.Id) Source {
   1461     if (id.alias) {
   1462         return comp.source_aliases.items[@intFromEnum(id.index)];
   1463     }
   1464     if (id.index == .generated) return .{
   1465         .path = "<scratch space>",
   1466         .buf = comp.generated_buf.items,
   1467         .id = .generated,
   1468         .splice_locs = &.{},
   1469         .kind = .user,
   1470     };
   1471     return comp.sources.values()[@intFromEnum(id.index)];
   1472 }
   1473 
   1474 /// Creates a Source from `buf` and adds it to the Compilation
   1475 /// Performs newline splicing and line-ending normalization to '\n'
   1476 /// `buf` will be modified and the allocation will be resized if newline splicing
   1477 /// or line-ending changes happen.
   1478 /// caller retains ownership of `path`
   1479 /// To add a file's contents given its path, see addSourceFromPath
   1480 pub fn addSourceFromOwnedBuffer(comp: *Compilation, path: []const u8, buf: []u8, kind: Source.Kind) !Source {
   1481     assert(buf.len <= std.math.maxInt(u32));
   1482     try comp.sources.ensureUnusedCapacity(comp.gpa, 1);
   1483 
   1484     var contents = buf;
   1485     const duped_path = try comp.gpa.dupe(u8, path);
   1486     errdefer comp.gpa.free(duped_path);
   1487 
   1488     var splice_list: std.ArrayList(u32) = .empty;
   1489     defer splice_list.deinit(comp.gpa);
   1490 
   1491     const source_id: Source.Id = .{ .index = @enumFromInt(comp.sources.count()) };
   1492 
   1493     var i: u32 = 0;
   1494     var backslash_loc: u32 = undefined;
   1495     var state: enum {
   1496         beginning_of_file,
   1497         bom1,
   1498         bom2,
   1499         start,
   1500         back_slash,
   1501         cr,
   1502         back_slash_cr,
   1503         trailing_ws,
   1504     } = .beginning_of_file;
   1505     var line: u32 = 1;
   1506 
   1507     for (contents) |byte| {
   1508         contents[i] = byte;
   1509 
   1510         switch (byte) {
   1511             '\r' => {
   1512                 switch (state) {
   1513                     .start, .cr, .beginning_of_file => {
   1514                         state = .start;
   1515                         line += 1;
   1516                         state = .cr;
   1517                         contents[i] = '\n';
   1518                         i += 1;
   1519                     },
   1520                     .back_slash, .trailing_ws, .back_slash_cr => {
   1521                         i = backslash_loc;
   1522                         try splice_list.append(comp.gpa, i);
   1523                         if (state == .trailing_ws) {
   1524                             try comp.addNewlineEscapeError(path, buf, splice_list.items, i, line, kind);
   1525                         }
   1526                         state = if (state == .back_slash_cr) .cr else .back_slash_cr;
   1527                         line += 1;
   1528                     },
   1529                     .bom1, .bom2 => break, // invalid utf-8
   1530                 }
   1531             },
   1532             '\n' => {
   1533                 switch (state) {
   1534                     .start, .beginning_of_file => {
   1535                         state = .start;
   1536                         line += 1;
   1537                         i += 1;
   1538                     },
   1539                     .cr, .back_slash_cr => {},
   1540                     .back_slash, .trailing_ws => {
   1541                         i = backslash_loc;
   1542                         if (state == .back_slash or state == .trailing_ws) {
   1543                             try splice_list.append(comp.gpa, i);
   1544                         }
   1545                         if (state == .trailing_ws) {
   1546                             try comp.addNewlineEscapeError(path, buf, splice_list.items, i, line, kind);
   1547                         }
   1548                         line += 1;
   1549                     },
   1550                     .bom1, .bom2 => break,
   1551                 }
   1552                 state = .start;
   1553             },
   1554             '\\' => {
   1555                 backslash_loc = i;
   1556                 state = .back_slash;
   1557                 i += 1;
   1558             },
   1559             '\t', '\x0B', '\x0C', ' ' => {
   1560                 switch (state) {
   1561                     .start, .trailing_ws => {},
   1562                     .beginning_of_file => state = .start,
   1563                     .cr, .back_slash_cr => state = .start,
   1564                     .back_slash => state = .trailing_ws,
   1565                     .bom1, .bom2 => break,
   1566                 }
   1567                 i += 1;
   1568             },
   1569             '\xEF' => {
   1570                 i += 1;
   1571                 state = switch (state) {
   1572                     .beginning_of_file => .bom1,
   1573                     else => .start,
   1574                 };
   1575             },
   1576             '\xBB' => {
   1577                 i += 1;
   1578                 state = switch (state) {
   1579                     .bom1 => .bom2,
   1580                     else => .start,
   1581                 };
   1582             },
   1583             '\xBF' => {
   1584                 switch (state) {
   1585                     .bom2 => i = 0, // rewind and overwrite the BOM
   1586                     else => i += 1,
   1587                 }
   1588                 state = .start;
   1589             },
   1590             else => {
   1591                 i += 1;
   1592                 state = .start;
   1593             },
   1594         }
   1595     }
   1596 
   1597     const splice_locs = try splice_list.toOwnedSlice(comp.gpa);
   1598     errdefer comp.gpa.free(splice_locs);
   1599 
   1600     if (i != contents.len) {
   1601         var list: std.ArrayList(u8) = .{
   1602             .items = contents[0..i],
   1603             .capacity = contents.len,
   1604         };
   1605         contents = try list.toOwnedSlice(comp.gpa);
   1606     }
   1607     errdefer @compileError("errdefers in callers would possibly free the realloced slice using the original len");
   1608 
   1609     const source: Source = .{
   1610         .id = source_id,
   1611         .path = duped_path,
   1612         .buf = contents,
   1613         .splice_locs = splice_locs,
   1614         .kind = kind,
   1615     };
   1616 
   1617     comp.sources.putAssumeCapacityNoClobber(duped_path, source);
   1618     return source;
   1619 }
   1620 
   1621 fn addNewlineEscapeError(
   1622     comp: *Compilation,
   1623     path: []const u8,
   1624     buf: []const u8,
   1625     splice_locs: []const u32,
   1626     byte_offset: u32,
   1627     line: u32,
   1628     kind: Source.Kind,
   1629 ) !void {
   1630     // Temporary source for getting the location for errors.
   1631     var tmp_source: Source = .{
   1632         .path = path,
   1633         .buf = buf,
   1634         .id = undefined,
   1635         .kind = kind,
   1636         .splice_locs = splice_locs,
   1637     };
   1638 
   1639     const diagnostic: Diagnostic = .backslash_newline_escape;
   1640     var loc = tmp_source.lineCol(.{ .id = undefined, .byte_offset = byte_offset, .line = line });
   1641     loc.line = loc.line[0 .. loc.line.len - 1];
   1642     loc.width += 1;
   1643     loc.col += 1;
   1644 
   1645     try comp.diagnostics.add(.{
   1646         .text = diagnostic.fmt,
   1647         .kind = diagnostic.kind,
   1648         .opt = diagnostic.opt,
   1649         .location = loc,
   1650     });
   1651 }
   1652 
   1653 /// Caller retains ownership of `path` and `buf`.
   1654 /// Dupes the source buffer; if it is acceptable to modify the source buffer and possibly resize
   1655 /// the allocation, please use `addSourceFromOwnedBuffer`
   1656 pub fn addSourceFromBuffer(comp: *Compilation, path: []const u8, buf: []const u8) AddSourceError!Source {
   1657     if (comp.sources.get(path)) |some| return some;
   1658     if (buf.len > std.math.maxInt(u32)) return error.FileTooBig;
   1659 
   1660     const contents = try comp.gpa.dupe(u8, buf);
   1661     errdefer comp.gpa.free(contents);
   1662 
   1663     return comp.addSourceFromOwnedBuffer(path, contents, .user);
   1664 }
   1665 
   1666 /// Caller retains ownership of `path`.
   1667 pub fn addSourceFromPath(comp: *Compilation, path: []const u8) !Source {
   1668     return comp.addSourceFromPathExtra(path, .user);
   1669 }
   1670 
   1671 /// Caller retains ownership of `path`.
   1672 fn addSourceFromPathExtra(comp: *Compilation, path: []const u8, kind: Source.Kind) !Source {
   1673     if (comp.sources.get(path)) |some| return some;
   1674 
   1675     if (mem.indexOfScalar(u8, path, 0) != null) {
   1676         return error.FileNotFound;
   1677     }
   1678 
   1679     const file = try comp.cwd.openFile(comp.io, path, .{});
   1680     defer file.close(comp.io);
   1681     return comp.addSourceFromFile(file, path, kind);
   1682 }
   1683 
   1684 pub fn addSourceFromFile(comp: *Compilation, file: std.Io.File, path: []const u8, kind: Source.Kind) !Source {
   1685     const contents = try comp.getFileContents(file, .unlimited);
   1686     errdefer comp.gpa.free(contents);
   1687     return comp.addSourceFromOwnedBuffer(path, contents, kind);
   1688 }
   1689 
   1690 pub fn addSourceAlias(comp: *Compilation, source: Source.Id, new_path: []const u8, new_kind: Source.Kind) !Source.Id {
   1691     var aliased_source = comp.getSource(source);
   1692     aliased_source.path = new_path;
   1693     aliased_source.id = .{ .index = @enumFromInt(comp.source_aliases.items.len), .alias = true };
   1694     aliased_source.kind = new_kind;
   1695     try comp.source_aliases.append(comp.gpa, aliased_source);
   1696     return aliased_source.id;
   1697 }
   1698 
   1699 // Sort and deduplicate a list of includes into a search path.
   1700 pub fn initSearchPath(comp: *Compilation, includes: []const Include, verbose: bool) !void {
   1701     comp.search_path.clearRetainingCapacity();
   1702 
   1703     for (includes) |include| {
   1704         if (include.kind == .quote) {
   1705             try comp.addToSearchPath(include, verbose);
   1706         }
   1707     }
   1708 
   1709     try comp.removeDuplicateSearchPaths(0, verbose);
   1710     const quote_count = comp.search_path.items.len;
   1711 
   1712     for (includes) |include| {
   1713         if (include.kind == .normal or include.kind == .framework) {
   1714             try comp.addToSearchPath(include, verbose);
   1715         }
   1716     }
   1717 
   1718     try comp.removeDuplicateSearchPaths(quote_count, verbose);
   1719 
   1720     for (includes) |include| {
   1721         if (include.kind == .system or include.kind == .system_framework) {
   1722             try comp.addToSearchPath(include, verbose);
   1723         }
   1724     }
   1725 
   1726     for (includes) |include| {
   1727         if (include.kind == .after) {
   1728             try comp.addToSearchPath(include, verbose);
   1729         }
   1730     }
   1731 
   1732     try comp.removeDuplicateSearchPaths(quote_count, verbose);
   1733 
   1734     if (verbose) {
   1735         std.debug.print("#include \"...\" search starts here:\n", .{});
   1736         for (comp.search_path.items, 0..) |include, i| {
   1737             if (i == quote_count) {
   1738                 std.debug.print("#include <...> search starts here:\n", .{});
   1739             }
   1740             std.debug.print(" {s}{s}\n", .{
   1741                 include.path,
   1742                 if (include.kind.isFramework())
   1743                     " (framework directory)"
   1744                 else
   1745                     "",
   1746             });
   1747         }
   1748         std.debug.print("End of search list.\n", .{});
   1749     }
   1750 }
   1751 fn addToSearchPath(comp: *Compilation, include: Include, verbose: bool) !void {
   1752     comp.cwd.access(comp.io, include.path, .{}) catch {
   1753         if (verbose) {
   1754             std.debug.print("ignoring nonexistent directory \"{s}\"\n", .{include.path});
   1755             return;
   1756         }
   1757     };
   1758     try comp.search_path.append(comp.gpa, include);
   1759 }
   1760 fn removeDuplicateSearchPaths(comp: *Compilation, start: usize, verbose: bool) !void {
   1761     var bfa_buf: [1024]u8 = undefined;
   1762     var bfa: std.heap.BufferFirstAllocator = .init(&bfa_buf, comp.gpa);
   1763     const allocator = bfa.allocator();
   1764     var seen_includes: std.StringHashMapUnmanaged(void) = .empty;
   1765     defer seen_includes.deinit(allocator);
   1766     var seen_frameworks: std.StringHashMapUnmanaged(void) = .empty;
   1767     defer seen_frameworks.deinit(allocator);
   1768 
   1769     var i = start;
   1770     for (comp.search_path.items[start..]) |include| {
   1771         if (include.kind.isFramework()) {
   1772             const gop = try seen_frameworks.getOrPut(allocator, include.path);
   1773             if (!gop.found_existing) {
   1774                 comp.search_path.items[i] = include;
   1775                 i += 1;
   1776                 continue;
   1777             }
   1778         } else {
   1779             const gop = try seen_frameworks.getOrPut(allocator, include.path);
   1780             if (!gop.found_existing) {
   1781                 comp.search_path.items[i] = include;
   1782                 i += 1;
   1783                 continue;
   1784             }
   1785         }
   1786 
   1787         var removal_index: ?usize = null;
   1788 
   1789         // If a normal include is later shadowed by a system include remove
   1790         // the normal include instead.
   1791         if (include.kind.isSystem()) {
   1792             for (comp.search_path.items[start..], start..) |previous, previous_index| {
   1793                 if (previous.kind.isFramework() != include.kind.isFramework()) continue;
   1794                 if (!mem.eql(u8, previous.path, include.path)) continue;
   1795 
   1796                 if (!previous.kind.isSystem()) removal_index = previous_index;
   1797                 break;
   1798             } else {
   1799                 unreachable; // Didn't find the duplicate
   1800             }
   1801         }
   1802 
   1803         if (verbose) {
   1804             std.debug.print("ignoring duplicate directory \"{s}\"\n", .{include.path});
   1805             if (removal_index != null)
   1806                 std.debug.print(" as it is a non-system directory that duplicates a system directory\n", .{});
   1807         }
   1808 
   1809         if (removal_index) |ri| {
   1810             const move_size = i - ri;
   1811             @memmove(
   1812                 comp.search_path.items[ri..][0..move_size],
   1813                 comp.search_path.items[ri + 1 ..][0..move_size],
   1814             );
   1815             comp.search_path.items[i - 1] = include;
   1816         }
   1817     }
   1818 
   1819     comp.search_path.shrinkRetainingCapacity(i);
   1820 }
   1821 
   1822 pub fn hasInclude(
   1823     comp: *Compilation,
   1824     filename: []const u8,
   1825     includer_token_source: Source.Id,
   1826     /// angle bracket vs quotes
   1827     include_type: IncludeType,
   1828     /// __has_include vs __has_include_next
   1829     which: WhichInclude,
   1830     opt_dep_file: ?*DepFile,
   1831 ) Compilation.Error!bool {
   1832     if (try FindInclude.run(comp, filename, include_type, switch (which) {
   1833         .next => .{ .only_search_after_dir = comp.getSource(includer_token_source).path },
   1834         .first => switch (include_type) {
   1835             .cli => unreachable,
   1836             .quotes => .{ .allow_same_dir = comp.getSource(includer_token_source).path },
   1837             .angle_brackets => .only_search,
   1838         },
   1839     })) |found| {
   1840         if (opt_dep_file) |dep_file| {
   1841             const source = comp.getSource(found.source);
   1842             try dep_file.addDependency(comp.gpa, source.path);
   1843         }
   1844         return true;
   1845     } else {
   1846         return false;
   1847     }
   1848 }
   1849 
   1850 const FindInclude = struct {
   1851     comp: *Compilation,
   1852     include_path: []const u8,
   1853     /// We won't actually consider any include directories until after this directory.
   1854     wait_for: ?[]const u8,
   1855 
   1856     const Result = struct {
   1857         source: Source.Id,
   1858         kind: Source.Kind,
   1859         used_ms_search_rule: bool,
   1860     };
   1861 
   1862     fn run(
   1863         comp: *Compilation,
   1864         include_path: []const u8,
   1865         include_type: IncludeType,
   1866         search_strat: union(enum) {
   1867             allow_same_dir: []const u8,
   1868             only_search,
   1869             only_search_after_dir: []const u8,
   1870         },
   1871     ) Allocator.Error!?Result {
   1872         var find: FindInclude = .{
   1873             .comp = comp,
   1874             .include_path = include_path,
   1875             .wait_for = null,
   1876         };
   1877 
   1878         if (std.fs.path.isAbsolute(include_path)) {
   1879             switch (search_strat) {
   1880                 .allow_same_dir, .only_search => {},
   1881                 .only_search_after_dir => return null,
   1882             }
   1883             return find.check("{s}", .{include_path}, .user, false);
   1884         }
   1885 
   1886         switch (search_strat) {
   1887             .allow_same_dir => |other_file| {
   1888                 const dir = std.fs.path.dirname(other_file) orelse ".";
   1889                 if (try find.checkIncludeDir(dir, .user)) |res| return res;
   1890             },
   1891             .only_search => {},
   1892             .only_search_after_dir => |other_file| {
   1893                 // TODO: this is not the correct interpretation of `#include_next` and friends,
   1894                 // because a file might not be directly inside of an include directory. To implement
   1895                 // this correctly, we will need to track which include directory a file has been
   1896                 // included from.
   1897                 find.wait_for = std.fs.path.dirname(other_file);
   1898             },
   1899         }
   1900         for (comp.search_path.items) |include| {
   1901             if (include.kind == .quote) {
   1902                 if (include_type != .angle_brackets) {
   1903                     if (try find.checkIncludeDir(include.path, .user)) |res| return res;
   1904                 }
   1905                 continue;
   1906             }
   1907             const source_kind: Source.Kind = if (include.kind.isSystem()) .system else .user;
   1908             if (include.kind.isFramework()) {
   1909                 if (try find.checkFrameworkDir(include.path, source_kind)) |res| return res;
   1910             } else {
   1911                 if (try find.checkIncludeDir(include.path, source_kind)) |res| return res;
   1912             }
   1913         }
   1914         if (comp.ms_cwd_source_id) |source_id| {
   1915             if (try find.checkMsCwdIncludeDir(source_id)) |res| return res;
   1916         }
   1917         return null;
   1918     }
   1919     fn checkIncludeDir(find: *FindInclude, include_dir: []const u8, kind: Source.Kind) Allocator.Error!?Result {
   1920         if (find.wait_for) |wait_for| {
   1921             if (std.mem.eql(u8, include_dir, wait_for)) find.wait_for = null;
   1922             return null;
   1923         }
   1924         return find.check("{s}{c}{s}", .{
   1925             include_dir,
   1926             std.fs.path.sep,
   1927             find.include_path,
   1928         }, kind, false);
   1929     }
   1930     fn checkMsCwdIncludeDir(find: *FindInclude, source_id: Source.Id) Allocator.Error!?Result {
   1931         const path = find.comp.getSource(source_id).path;
   1932         const dir = std.fs.path.dirname(path) orelse ".";
   1933         if (find.wait_for) |wait_for| {
   1934             if (std.mem.eql(u8, dir, wait_for)) find.wait_for = null;
   1935             return null;
   1936         }
   1937         return find.check("{s}{c}{s}", .{
   1938             dir,
   1939             std.fs.path.sep,
   1940             find.include_path,
   1941         }, .user, true);
   1942     }
   1943     fn checkFrameworkDir(find: *FindInclude, framework_dir: []const u8, kind: Source.Kind) Allocator.Error!?Result {
   1944         if (find.wait_for) |wait_for| {
   1945             match: {
   1946                 // If this is a match, then `wait_for` looks like '.../Foo.framework/Headers'.
   1947                 const wait_framework = std.fs.path.dirname(wait_for) orelse break :match;
   1948                 const wait_framework_dir = std.fs.path.dirname(wait_framework) orelse break :match;
   1949                 if (!std.mem.eql(u8, framework_dir, wait_framework_dir)) break :match;
   1950                 find.wait_for = null;
   1951             }
   1952             return null;
   1953         }
   1954         // For an include like 'Foo/Bar.h', search in '<framework_dir>/Foo.framework/Headers/Bar.h'.
   1955         const framework_name: []const u8, const header_sub_path: []const u8 = f: {
   1956             const i = std.mem.indexOfScalar(u8, find.include_path, '/') orelse return null;
   1957             break :f .{ find.include_path[0..i], find.include_path[i + 1 ..] };
   1958         };
   1959         return find.check("{s}{c}{s}.framework{c}Headers{c}{s}", .{
   1960             framework_dir,
   1961             std.fs.path.sep,
   1962             framework_name,
   1963             std.fs.path.sep,
   1964             std.fs.path.sep,
   1965             header_sub_path,
   1966         }, kind, false);
   1967     }
   1968     fn check(
   1969         find: *FindInclude,
   1970         comptime format: []const u8,
   1971         args: anytype,
   1972         kind: Source.Kind,
   1973         used_ms_search_rule: bool,
   1974     ) Allocator.Error!?Result {
   1975         const comp = find.comp;
   1976 
   1977         var bfa_buf: [path_buf_stack_limit]u8 = undefined;
   1978         var bfa_state: std.heap.BufferFirstAllocator = .init(&bfa_buf, comp.gpa);
   1979         const bfa = bfa_state.allocator();
   1980         const header_path = try std.fmt.allocPrint(bfa, format, args);
   1981         defer bfa.free(header_path);
   1982         find.comp.normalizePath(header_path);
   1983 
   1984         const source = comp.addSourceFromPathExtra(header_path, kind) catch |err| switch (err) {
   1985             error.OutOfMemory => |e| return e,
   1986             else => return null,
   1987         };
   1988 
   1989         return .{
   1990             .source = source.id,
   1991             .kind = kind,
   1992             .used_ms_search_rule = used_ms_search_rule,
   1993         };
   1994     }
   1995 };
   1996 
   1997 pub const WhichInclude = enum {
   1998     first,
   1999     next,
   2000 };
   2001 
   2002 pub const IncludeType = enum {
   2003     quotes,
   2004     angle_brackets,
   2005     /// cli behaves like quotes but it is relative to the Driver cwd instead of the main source file's directory.
   2006     cli,
   2007 };
   2008 
   2009 fn getPathContents(comp: *Compilation, path: []const u8, limit: Io.Limit) ![]u8 {
   2010     if (mem.indexOfScalar(u8, path, 0) != null) {
   2011         return error.FileNotFound;
   2012     }
   2013 
   2014     const file = try comp.cwd.openFile(comp.io, path, .{});
   2015     defer file.close(comp.io);
   2016     return comp.getFileContents(file, limit);
   2017 }
   2018 
   2019 fn getFileContents(comp: *Compilation, file: std.Io.File, limit: Io.Limit) ![]u8 {
   2020     var file_buf: [4096]u8 = undefined;
   2021     var file_reader = file.reader(comp.io, &file_buf);
   2022 
   2023     var allocating: Io.Writer.Allocating = .init(comp.gpa);
   2024     defer allocating.deinit();
   2025     if (file_reader.getSize()) |size| {
   2026         const limited_size = limit.minInt64(size);
   2027         if (limited_size > std.math.maxInt(u32)) return error.FileTooBig;
   2028         try allocating.ensureUnusedCapacity(limited_size);
   2029     } else |_| {}
   2030 
   2031     var remaining = limit.min(.limited(std.math.maxInt(u32)));
   2032     while (remaining.nonzero()) {
   2033         const n = file_reader.interface.stream(&allocating.writer, remaining) catch |err| switch (err) {
   2034             error.EndOfStream => return allocating.toOwnedSlice(),
   2035             error.WriteFailed => return error.OutOfMemory,
   2036             error.ReadFailed => return file_reader.err.?,
   2037         };
   2038         remaining = remaining.subtract(n).?;
   2039     }
   2040     if (limit == .unlimited) return error.FileTooBig;
   2041     return allocating.toOwnedSlice();
   2042 }
   2043 
   2044 fn normalizePath(comp: *Compilation, path: []u8) void {
   2045     if (comp.langopts.ms_extensions and @import("builtin").target.os.tag != .windows) {
   2046         std.mem.replaceScalar(u8, path, std.fs.path.sep_windows, std.fs.path.sep_posix);
   2047     }
   2048 }
   2049 
   2050 pub fn findEmbed(
   2051     comp: *Compilation,
   2052     filename: []const u8,
   2053     includer_token_source: Source.Id,
   2054     /// angle bracket vs quotes
   2055     include_type: IncludeType,
   2056     limit: Io.Limit,
   2057     opt_dep_file: ?*DepFile,
   2058 ) !?[]u8 {
   2059     if (std.fs.path.isAbsolute(filename)) {
   2060         if (comp.getPathContents(filename, limit)) |some| {
   2061             errdefer comp.gpa.free(some);
   2062             if (opt_dep_file) |dep_file| try dep_file.addDependencyDupe(comp.gpa, comp.arena, filename);
   2063             return some;
   2064         } else |err| switch (err) {
   2065             error.OutOfMemory => |e| return e,
   2066             else => return null,
   2067         }
   2068     }
   2069 
   2070     var bfa_buf: [path_buf_stack_limit]u8 = undefined;
   2071     var bfa_state: std.heap.BufferFirstAllocator = .init(&bfa_buf, comp.gpa);
   2072     const bfa = bfa_state.allocator();
   2073 
   2074     switch (include_type) {
   2075         .quotes, .cli => {
   2076             const dir = std.fs.path.dirname(comp.getSource(includer_token_source).path) orelse ".";
   2077             const path = try std.fs.path.join(bfa, &.{ dir, filename });
   2078             defer bfa.free(path);
   2079             comp.normalizePath(path);
   2080             if (comp.getPathContents(path, limit)) |some| {
   2081                 errdefer comp.gpa.free(some);
   2082                 if (opt_dep_file) |dep_file| try dep_file.addDependencyDupe(comp.gpa, comp.arena, filename);
   2083                 return some;
   2084             } else |err| switch (err) {
   2085                 error.OutOfMemory => |e| return e,
   2086                 else => {},
   2087             }
   2088         },
   2089         .angle_brackets => {},
   2090     }
   2091     for (comp.embed_dirs.items) |embed_dir| {
   2092         const path = try std.fs.path.join(bfa, &.{ embed_dir, filename });
   2093         defer bfa.free(path);
   2094         comp.normalizePath(path);
   2095         if (comp.getPathContents(path, limit)) |some| {
   2096             errdefer comp.gpa.free(some);
   2097             if (opt_dep_file) |dep_file| try dep_file.addDependencyDupe(comp.gpa, comp.arena, filename);
   2098             return some;
   2099         } else |err| switch (err) {
   2100             error.OutOfMemory => |e| return e,
   2101             else => {},
   2102         }
   2103     }
   2104     return null;
   2105 }
   2106 
   2107 pub fn findInclude(
   2108     comp: *Compilation,
   2109     filename: []const u8,
   2110     includer_token: Token,
   2111     /// angle bracket vs quotes
   2112     include_type: IncludeType,
   2113     /// include vs include_next
   2114     which: WhichInclude,
   2115 ) Compilation.Error!?Source {
   2116     const found = try FindInclude.run(comp, filename, include_type, switch (which) {
   2117         .next => .{ .only_search_after_dir = comp.getSource(includer_token.source).path },
   2118         .first => switch (include_type) {
   2119             .cli => .{ .allow_same_dir = "." },
   2120             .quotes => .{ .allow_same_dir = comp.getSource(includer_token.source).path },
   2121             .angle_brackets => .only_search,
   2122         },
   2123     }) orelse return null;
   2124     if (found.used_ms_search_rule) {
   2125         const diagnostic: Diagnostic = .ms_search_rule;
   2126         try comp.diagnostics.add(.{
   2127             .text = diagnostic.fmt,
   2128             .kind = diagnostic.kind,
   2129             .opt = diagnostic.opt,
   2130             .extension = diagnostic.extension,
   2131             .location = (Source.Location{
   2132                 .id = includer_token.source,
   2133                 .byte_offset = includer_token.start,
   2134                 .line = includer_token.line,
   2135             }).expand(comp),
   2136         });
   2137     }
   2138     return comp.getSource(found.source);
   2139 }
   2140 
   2141 pub fn addPragmaHandler(comp: *Compilation, name: []const u8, handler: *Pragma) Allocator.Error!void {
   2142     try comp.pragma_handlers.putNoClobber(comp.gpa, name, handler);
   2143 }
   2144 
   2145 pub fn addDefaultPragmaHandlers(comp: *Compilation) Allocator.Error!void {
   2146     const GCC = @import("pragmas/gcc.zig");
   2147     var gcc = try GCC.init(comp.gpa);
   2148     errdefer gcc.deinit(gcc, comp);
   2149 
   2150     const Once = @import("pragmas/once.zig");
   2151     var once = try Once.init(comp.gpa);
   2152     errdefer once.deinit(once, comp);
   2153 
   2154     const Message = @import("pragmas/message.zig");
   2155     var message = try Message.init(comp.gpa);
   2156     errdefer message.deinit(message, comp);
   2157 
   2158     const Pack = @import("pragmas/pack.zig");
   2159     var pack = try Pack.init(comp.gpa);
   2160     errdefer pack.deinit(pack, comp);
   2161 
   2162     try comp.addPragmaHandler("GCC", gcc);
   2163     try comp.addPragmaHandler("once", once);
   2164     try comp.addPragmaHandler("message", message);
   2165     try comp.addPragmaHandler("pack", pack);
   2166 }
   2167 
   2168 pub fn getPragma(comp: *Compilation, name: []const u8) ?*Pragma {
   2169     return comp.pragma_handlers.get(name);
   2170 }
   2171 
   2172 const PragmaEvent = enum {
   2173     before_preprocess,
   2174     before_parse,
   2175     after_parse,
   2176 };
   2177 
   2178 pub fn pragmaEvent(comp: *Compilation, event: PragmaEvent) void {
   2179     for (comp.pragma_handlers.values()) |pragma| {
   2180         const maybe_func = switch (event) {
   2181             .before_preprocess => pragma.beforePreprocess,
   2182             .before_parse => pragma.beforeParse,
   2183             .after_parse => pragma.afterParse,
   2184         };
   2185         if (maybe_func) |func| func(pragma, comp);
   2186     }
   2187 }
   2188 
   2189 pub fn locSlice(comp: *const Compilation, loc: Source.Location) []const u8 {
   2190     var tmp_tokenizer: Tokenizer = .{
   2191         .buf = comp.getSource(loc.id).buf,
   2192         .langopts = comp.langopts,
   2193         .index = loc.byte_offset,
   2194         .source = .generated,
   2195         .splice_locs = &.{},
   2196     };
   2197     const tok = tmp_tokenizer.next();
   2198     return tmp_tokenizer.buf[tok.start..tok.end];
   2199 }
   2200 
   2201 pub fn getSourceMTimeUncached(comp: *const Compilation, source_id: Source.Id) ?u64 {
   2202     const source = comp.getSource(source_id);
   2203     if (comp.cwd.statFile(comp.io, source.path, .{})) |stat| {
   2204         return std.math.cast(u64, stat.mtime.toSeconds());
   2205     } else |_| {
   2206         return null;
   2207     }
   2208 }
   2209 
   2210 pub fn isTargetArch(comp: *const Compilation, query: []const u8) bool {
   2211     const arch, const opt_sub_arch = Target.parseArchName(query) orelse return false;
   2212     if (arch != comp.target.cpu.arch) return false;
   2213     const sub_arch = opt_sub_arch orelse return true;
   2214     const feature = sub_arch.toFeature(arch) orelse return true;
   2215     return comp.target.cpu.features.isEnabled(feature);
   2216 }
   2217 
   2218 pub fn isTargetOs(comp: *const Compilation, query: []const u8) bool {
   2219     return Target.isOs(&comp.target, query);
   2220 }
   2221 
   2222 pub fn isTargetVendor(comp: *const Compilation, query: []const u8) bool {
   2223     return Target.parseVendorName(query) == comp.target.vendor;
   2224 }
   2225 
   2226 pub fn isTargetAbi(comp: *const Compilation, query: []const u8) bool {
   2227     return Target.isAbi(&comp.target, query);
   2228 }
   2229 
   2230 pub fn isTargetVariantOs(comp: *const Compilation, query: []const u8) bool {
   2231     if (comp.target.os.tag.isDarwin()) {
   2232         const variant_target = &(comp.darwin_target_variant orelse return false);
   2233         return Target.isOs(variant_target, query);
   2234     }
   2235     return false;
   2236 }
   2237 
   2238 pub fn isTargetVariantEnvironment(comp: *const Compilation, query: []const u8) bool {
   2239     if (comp.target.os.tag.isDarwin()) {
   2240         const variant_target = &(comp.darwin_target_variant orelse return false);
   2241         return Target.isAbi(variant_target, query);
   2242     }
   2243     return false;
   2244 }
   2245 
   2246 pub const CharUnitSize = enum(u32) {
   2247     @"1" = 1,
   2248     @"2" = 2,
   2249     @"4" = 4,
   2250 
   2251     pub fn Type(comptime self: CharUnitSize) type {
   2252         return switch (self) {
   2253             .@"1" => u8,
   2254             .@"2" => u16,
   2255             .@"4" => u32,
   2256         };
   2257     }
   2258 };
   2259 
   2260 pub const Diagnostic = struct {
   2261     fmt: []const u8,
   2262     kind: Diagnostics.Message.Kind,
   2263     opt: ?Diagnostics.Option = null,
   2264     extension: bool = false,
   2265 
   2266     pub const backslash_newline_escape: Diagnostic = .{
   2267         .fmt = "backslash and newline separated by space",
   2268         .kind = .warning,
   2269         .opt = .@"backslash-newline-escape",
   2270     };
   2271 
   2272     pub const ms_search_rule: Diagnostic = .{
   2273         .fmt = "#include resolved using non-portable Microsoft search rules as: {s}",
   2274         .kind = .warning,
   2275         .opt = .@"microsoft-include",
   2276         .extension = true,
   2277     };
   2278 
   2279     pub const ctrl_z_eof: Diagnostic = .{
   2280         .fmt = "treating Ctrl-Z as end-of-file is a Microsoft extension",
   2281         .kind = .off,
   2282         .opt = .@"microsoft-end-of-file",
   2283         .extension = true,
   2284     };
   2285 };
   2286 
   2287 test "addSourceFromBuffer" {
   2288     const Test = struct {
   2289         fn addSourceFromBuffer(str: []const u8, expected: []const u8, warning_count: u32, splices: []const u32) !void {
   2290             var diagnostics: Diagnostics = .{ .output = .ignore };
   2291             var comp = try Compilation.init(.testing);
   2292             comp.diagnostics = &diagnostics;
   2293             defer comp.deinit();
   2294 
   2295             const source = try comp.addSourceFromBuffer("path", str);
   2296 
   2297             try std.testing.expectEqualStrings(expected, source.buf);
   2298             try std.testing.expectEqual(warning_count, @as(u32, @intCast(diagnostics.warnings)));
   2299             try std.testing.expectEqualSlices(u32, splices, source.splice_locs);
   2300         }
   2301 
   2302         fn withAllocationFailures(allocator: std.mem.Allocator) !void {
   2303             var comp = try Compilation.init(.testing);
   2304             comp.gpa = allocator;
   2305             defer comp.deinit();
   2306 
   2307             _ = try comp.addSourceFromBuffer("path", "spliced\\\nbuffer\n");
   2308             _ = try comp.addSourceFromBuffer("path", "non-spliced buffer\n");
   2309         }
   2310     };
   2311     try Test.addSourceFromBuffer("ab\\\nc", "abc", 0, &.{2});
   2312     try Test.addSourceFromBuffer("ab\\\rc", "abc", 0, &.{2});
   2313     try Test.addSourceFromBuffer("ab\\\r\nc", "abc", 0, &.{2});
   2314     try Test.addSourceFromBuffer("ab\\ \nc", "abc", 1, &.{2});
   2315     try Test.addSourceFromBuffer("ab\\\t\nc", "abc", 1, &.{2});
   2316     try Test.addSourceFromBuffer("ab\\                     \t\nc", "abc", 1, &.{2});
   2317     try Test.addSourceFromBuffer("ab\\\r \nc", "ab \nc", 0, &.{2});
   2318     try Test.addSourceFromBuffer("ab\\\\\nc", "ab\\c", 0, &.{3});
   2319     try Test.addSourceFromBuffer("ab\\   \r\nc", "abc", 1, &.{2});
   2320     try Test.addSourceFromBuffer("ab\\ \\\nc", "ab\\ c", 0, &.{4});
   2321     try Test.addSourceFromBuffer("ab\\\r\\\nc", "abc", 0, &.{ 2, 2 });
   2322     try Test.addSourceFromBuffer("ab\\  \rc", "abc", 1, &.{2});
   2323     try Test.addSourceFromBuffer("ab\\", "ab\\", 0, &.{});
   2324     try Test.addSourceFromBuffer("ab\\\\", "ab\\\\", 0, &.{});
   2325     try Test.addSourceFromBuffer("ab\\ ", "ab\\ ", 0, &.{});
   2326     try Test.addSourceFromBuffer("ab\\\n", "ab", 0, &.{2});
   2327     try Test.addSourceFromBuffer("ab\\\r\n", "ab", 0, &.{2});
   2328     try Test.addSourceFromBuffer("ab\\\r", "ab", 0, &.{2});
   2329 
   2330     // carriage return normalization
   2331     try Test.addSourceFromBuffer("ab\r", "ab\n", 0, &.{});
   2332     try Test.addSourceFromBuffer("ab\r\r", "ab\n\n", 0, &.{});
   2333     try Test.addSourceFromBuffer("ab\r\r\n", "ab\n\n", 0, &.{});
   2334     try Test.addSourceFromBuffer("ab\r\r\n\r", "ab\n\n\n", 0, &.{});
   2335     try Test.addSourceFromBuffer("\r\\", "\n\\", 0, &.{});
   2336     try Test.addSourceFromBuffer("\\\r\\", "\\", 0, &.{0});
   2337 
   2338     try std.testing.checkAllAllocationFailures(std.testing.allocator, Test.withAllocationFailures, .{});
   2339 }
   2340 
   2341 test "addSourceFromBuffer - exhaustive check for carriage return elimination" {
   2342     var arena: std.heap.ArenaAllocator = .init(std.testing.allocator);
   2343     defer arena.deinit();
   2344 
   2345     const alphabet = [_]u8{ '\r', '\n', ' ', '\\', 'a' };
   2346     const alen = alphabet.len;
   2347     var buf: [alphabet.len]u8 = @splat(alphabet[0]);
   2348 
   2349     var diagnostics: Diagnostics = .{ .output = .ignore };
   2350     var comp = try Compilation.init(.testing);
   2351     comp.diagnostics = &diagnostics;
   2352     defer comp.deinit();
   2353 
   2354     var source_count: u32 = 0;
   2355 
   2356     while (true) {
   2357         const source = try comp.addSourceFromBuffer(&buf, &buf);
   2358         source_count += 1;
   2359         try std.testing.expect(std.mem.indexOfScalar(u8, source.buf, '\r') == null);
   2360 
   2361         if (std.mem.allEqual(u8, &buf, alphabet[alen - 1])) break;
   2362 
   2363         var idx = std.mem.indexOfScalar(u8, &alphabet, buf[buf.len - 1]).?;
   2364         buf[buf.len - 1] = alphabet[(idx + 1) % alen];
   2365         var j = buf.len - 1;
   2366         while (j > 0) : (j -= 1) {
   2367             idx = std.mem.indexOfScalar(u8, &alphabet, buf[j - 1]).?;
   2368             if (buf[j] == alphabet[0]) buf[j - 1] = alphabet[(idx + 1) % alen] else break;
   2369         }
   2370     }
   2371     try std.testing.expect(source_count == std.math.powi(usize, alen, alen) catch unreachable);
   2372 }
   2373 
   2374 test "ignore BOM at beginning of file" {
   2375     const BOM = "\xEF\xBB\xBF";
   2376     const Test = struct {
   2377         fn run(buf: []const u8) !void {
   2378             var comp = try Compilation.init(.testing);
   2379             defer comp.deinit();
   2380 
   2381             const source = try comp.addSourceFromBuffer("file.c", buf);
   2382             const expected_output = if (mem.startsWith(u8, buf, BOM)) buf[BOM.len..] else buf;
   2383             try std.testing.expectEqualStrings(expected_output, source.buf);
   2384         }
   2385     };
   2386 
   2387     try Test.run(BOM);
   2388     try Test.run(BOM ++ "x");
   2389     try Test.run("x" ++ BOM);
   2390     try Test.run(BOM ++ " ");
   2391     try Test.run(BOM ++ "\n");
   2392     try Test.run(BOM ++ "\\");
   2393 
   2394     try Test.run(BOM[0..1] ++ "x");
   2395     try Test.run(BOM[0..2] ++ "x");
   2396     try Test.run(BOM[1..] ++ "x");
   2397     try Test.run(BOM[2..] ++ "x");
   2398 }