zig

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

Compilation.zig (66441B) - Raw


      1 const std = @import("std");
      2 const Allocator = mem.Allocator;
      3 const assert = std.debug.assert;
      4 const EpochSeconds = std.time.epoch.EpochSeconds;
      5 const mem = std.mem;
      6 const Interner = @import("../backend.zig").Interner;
      7 const Builtins = @import("Builtins.zig");
      8 const Builtin = Builtins.Builtin;
      9 const Diagnostics = @import("Diagnostics.zig");
     10 const LangOpts = @import("LangOpts.zig");
     11 const Source = @import("Source.zig");
     12 const Tokenizer = @import("Tokenizer.zig");
     13 const Token = Tokenizer.Token;
     14 const Type = @import("Type.zig");
     15 const Pragma = @import("Pragma.zig");
     16 const StrInt = @import("StringInterner.zig");
     17 const record_layout = @import("record_layout.zig");
     18 const target_util = @import("target.zig");
     19 
     20 pub const Error = error{
     21     /// A fatal error has ocurred and compilation has stopped.
     22     FatalError,
     23 } || Allocator.Error;
     24 
     25 pub const bit_int_max_bits = std.math.maxInt(u16);
     26 const path_buf_stack_limit = 1024;
     27 
     28 /// Environment variables used during compilation / linking.
     29 pub const Environment = struct {
     30     /// Directory to use for temporary files
     31     /// TODO: not implemented yet
     32     tmpdir: ?[]const u8 = null,
     33 
     34     /// PATH environment variable used to search for programs
     35     path: ?[]const u8 = null,
     36 
     37     /// Directories to try when searching for subprograms.
     38     /// TODO: not implemented yet
     39     compiler_path: ?[]const u8 = null,
     40 
     41     /// Directories to try when searching for special linker files, if compiling for the native target
     42     /// TODO: not implemented yet
     43     library_path: ?[]const u8 = null,
     44 
     45     /// List of directories to be searched as if specified with -I, but after any paths given with -I options on the command line
     46     /// Used regardless of the language being compiled
     47     /// TODO: not implemented yet
     48     cpath: ?[]const u8 = null,
     49 
     50     /// List of directories to be searched as if specified with -I, but after any paths given with -I options on the command line
     51     /// Used if the language being compiled is C
     52     /// TODO: not implemented yet
     53     c_include_path: ?[]const u8 = null,
     54 
     55     /// UNIX timestamp to be used instead of the current date and time in the __DATE__ and __TIME__ macros
     56     source_date_epoch: ?[]const u8 = null,
     57 
     58     /// Load all of the environment variables using the std.process API. Do not use if using Aro as a shared library on Linux without libc
     59     /// See https://github.com/ziglang/zig/issues/4524
     60     pub fn loadAll(allocator: std.mem.Allocator) !Environment {
     61         var env: Environment = .{};
     62         errdefer env.deinit(allocator);
     63 
     64         inline for (@typeInfo(@TypeOf(env)).@"struct".fields) |field| {
     65             std.debug.assert(@field(env, field.name) == null);
     66 
     67             var env_var_buf: [field.name.len]u8 = undefined;
     68             const env_var_name = std.ascii.upperString(&env_var_buf, field.name);
     69             const val: ?[]const u8 = std.process.getEnvVarOwned(allocator, env_var_name) catch |err| switch (err) {
     70                 error.OutOfMemory => |e| return e,
     71                 error.EnvironmentVariableNotFound => null,
     72                 error.InvalidWtf8 => null,
     73             };
     74             @field(env, field.name) = val;
     75         }
     76         return env;
     77     }
     78 
     79     /// Use this only if environment slices were allocated with `allocator` (such as via `loadAll`)
     80     pub fn deinit(self: *Environment, allocator: std.mem.Allocator) void {
     81         inline for (@typeInfo(@TypeOf(self.*)).@"struct".fields) |field| {
     82             if (@field(self, field.name)) |slice| {
     83                 allocator.free(slice);
     84             }
     85         }
     86         self.* = undefined;
     87     }
     88 };
     89 
     90 const Compilation = @This();
     91 
     92 gpa: Allocator,
     93 diagnostics: Diagnostics,
     94 
     95 environment: Environment = .{},
     96 sources: std.StringArrayHashMapUnmanaged(Source) = .empty,
     97 include_dirs: std.ArrayListUnmanaged([]const u8) = .empty,
     98 system_include_dirs: std.ArrayListUnmanaged([]const u8) = .empty,
     99 target: std.Target = @import("builtin").target,
    100 pragma_handlers: std.StringArrayHashMapUnmanaged(*Pragma) = .empty,
    101 langopts: LangOpts = .{},
    102 generated_buf: std.ArrayListUnmanaged(u8) = .empty,
    103 builtins: Builtins = .{},
    104 types: struct {
    105     wchar: Type = undefined,
    106     uint_least16_t: Type = undefined,
    107     uint_least32_t: Type = undefined,
    108     ptrdiff: Type = undefined,
    109     size: Type = undefined,
    110     va_list: Type = undefined,
    111     pid_t: Type = undefined,
    112     ns_constant_string: struct {
    113         ty: Type = undefined,
    114         record: Type.Record = undefined,
    115         fields: [4]Type.Record.Field = undefined,
    116         int_ty: Type = .{ .specifier = .int, .qual = .{ .@"const" = true } },
    117         char_ty: Type = .{ .specifier = .char, .qual = .{ .@"const" = true } },
    118     } = .{},
    119     file: Type = .{ .specifier = .invalid },
    120     jmp_buf: Type = .{ .specifier = .invalid },
    121     sigjmp_buf: Type = .{ .specifier = .invalid },
    122     ucontext_t: Type = .{ .specifier = .invalid },
    123     intmax: Type = .{ .specifier = .invalid },
    124     intptr: Type = .{ .specifier = .invalid },
    125     int16: Type = .{ .specifier = .invalid },
    126     int64: Type = .{ .specifier = .invalid },
    127 } = .{},
    128 string_interner: StrInt = .{},
    129 interner: Interner = .{},
    130 /// If this is not null, the directory containing the specified Source will be searched for includes
    131 /// Used by MS extensions which allow searching for includes relative to the directory of the main source file.
    132 ms_cwd_source_id: ?Source.Id = null,
    133 cwd: std.fs.Dir,
    134 
    135 pub fn init(gpa: Allocator, cwd: std.fs.Dir) Compilation {
    136     return .{
    137         .gpa = gpa,
    138         .diagnostics = Diagnostics.init(gpa),
    139         .cwd = cwd,
    140     };
    141 }
    142 
    143 /// Initialize Compilation with default environment,
    144 /// pragma handlers and emulation mode set to target.
    145 pub fn initDefault(gpa: Allocator, cwd: std.fs.Dir) !Compilation {
    146     var comp: Compilation = .{
    147         .gpa = gpa,
    148         .environment = try Environment.loadAll(gpa),
    149         .diagnostics = Diagnostics.init(gpa),
    150         .cwd = cwd,
    151     };
    152     errdefer comp.deinit();
    153     try comp.addDefaultPragmaHandlers();
    154     comp.langopts.setEmulatedCompiler(target_util.systemCompiler(comp.target));
    155     return comp;
    156 }
    157 
    158 pub fn deinit(comp: *Compilation) void {
    159     for (comp.pragma_handlers.values()) |pragma| {
    160         pragma.deinit(pragma, comp);
    161     }
    162     for (comp.sources.values()) |source| {
    163         comp.gpa.free(source.path);
    164         comp.gpa.free(source.buf);
    165         comp.gpa.free(source.splice_locs);
    166     }
    167     comp.sources.deinit(comp.gpa);
    168     comp.diagnostics.deinit();
    169     comp.include_dirs.deinit(comp.gpa);
    170     for (comp.system_include_dirs.items) |path| comp.gpa.free(path);
    171     comp.system_include_dirs.deinit(comp.gpa);
    172     comp.pragma_handlers.deinit(comp.gpa);
    173     comp.generated_buf.deinit(comp.gpa);
    174     comp.builtins.deinit(comp.gpa);
    175     comp.string_interner.deinit(comp.gpa);
    176     comp.interner.deinit(comp.gpa);
    177     comp.environment.deinit(comp.gpa);
    178 }
    179 
    180 pub fn getSourceEpoch(self: *const Compilation, max: i64) !?i64 {
    181     const provided = self.environment.source_date_epoch orelse return null;
    182     const parsed = std.fmt.parseInt(i64, provided, 10) catch return error.InvalidEpoch;
    183     if (parsed < 0 or parsed > max) return error.InvalidEpoch;
    184     return parsed;
    185 }
    186 
    187 /// Dec 31 9999 23:59:59
    188 const max_timestamp = 253402300799;
    189 
    190 fn getTimestamp(comp: *Compilation) !u47 {
    191     const provided: ?i64 = comp.getSourceEpoch(max_timestamp) catch blk: {
    192         try comp.addDiagnostic(.{
    193             .tag = .invalid_source_epoch,
    194             .loc = .{ .id = .unused, .byte_offset = 0, .line = 0 },
    195         }, &.{});
    196         break :blk null;
    197     };
    198     const timestamp = provided orelse std.time.timestamp();
    199     return @intCast(std.math.clamp(timestamp, 0, max_timestamp));
    200 }
    201 
    202 fn generateDateAndTime(w: anytype, timestamp: u47) !void {
    203     const epoch_seconds = EpochSeconds{ .secs = timestamp };
    204     const epoch_day = epoch_seconds.getEpochDay();
    205     const day_seconds = epoch_seconds.getDaySeconds();
    206     const year_day = epoch_day.calculateYearDay();
    207     const month_day = year_day.calculateMonthDay();
    208 
    209     const month_names = [_][]const u8{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
    210     std.debug.assert(std.time.epoch.Month.jan.numeric() == 1);
    211 
    212     const month_name = month_names[month_day.month.numeric() - 1];
    213     try w.print("#define __DATE__ \"{s} {d: >2} {d}\"\n", .{
    214         month_name,
    215         month_day.day_index + 1,
    216         year_day.year,
    217     });
    218     try w.print("#define __TIME__ \"{d:0>2}:{d:0>2}:{d:0>2}\"\n", .{
    219         day_seconds.getHoursIntoDay(),
    220         day_seconds.getMinutesIntoHour(),
    221         day_seconds.getSecondsIntoMinute(),
    222     });
    223 
    224     const day_names = [_][]const u8{ "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
    225     const day_name = day_names[@intCast((epoch_day.day + 3) % 7)];
    226     try w.print("#define __TIMESTAMP__ \"{s} {s} {d: >2} {d:0>2}:{d:0>2}:{d:0>2} {d}\"\n", .{
    227         day_name,
    228         month_name,
    229         month_day.day_index + 1,
    230         day_seconds.getHoursIntoDay(),
    231         day_seconds.getMinutesIntoHour(),
    232         day_seconds.getSecondsIntoMinute(),
    233         year_day.year,
    234     });
    235 }
    236 
    237 /// Which set of system defines to generate via generateBuiltinMacros
    238 pub const SystemDefinesMode = enum {
    239     /// Only define macros required by the C standard (date/time macros and those beginning with `__STDC`)
    240     no_system_defines,
    241     /// Define the standard set of system macros
    242     include_system_defines,
    243 };
    244 
    245 fn generateSystemDefines(comp: *Compilation, w: anytype) !void {
    246     const ptr_width = comp.target.ptrBitWidth();
    247 
    248     if (comp.langopts.gnuc_version > 0) {
    249         try w.print("#define __GNUC__ {d}\n", .{comp.langopts.gnuc_version / 10_000});
    250         try w.print("#define __GNUC_MINOR__ {d}\n", .{comp.langopts.gnuc_version / 100 % 100});
    251         try w.print("#define __GNUC_PATCHLEVEL__ {d}\n", .{comp.langopts.gnuc_version % 100});
    252     }
    253 
    254     // os macros
    255     switch (comp.target.os.tag) {
    256         .linux => try w.writeAll(
    257             \\#define linux 1
    258             \\#define __linux 1
    259             \\#define __linux__ 1
    260             \\
    261         ),
    262         .windows => if (ptr_width == 32) try w.writeAll(
    263             \\#define WIN32 1
    264             \\#define _WIN32 1
    265             \\#define __WIN32 1
    266             \\#define __WIN32__ 1
    267             \\
    268         ) else try w.writeAll(
    269             \\#define WIN32 1
    270             \\#define WIN64 1
    271             \\#define _WIN32 1
    272             \\#define _WIN64 1
    273             \\#define __WIN32 1
    274             \\#define __WIN64 1
    275             \\#define __WIN32__ 1
    276             \\#define __WIN64__ 1
    277             \\
    278         ),
    279         .freebsd => try w.print("#define __FreeBSD__ {d}\n", .{comp.target.os.version_range.semver.min.major}),
    280         .netbsd => try w.writeAll("#define __NetBSD__ 1\n"),
    281         .openbsd => try w.writeAll("#define __OpenBSD__ 1\n"),
    282         .dragonfly => try w.writeAll("#define __DragonFly__ 1\n"),
    283         .solaris => try w.writeAll(
    284             \\#define sun 1
    285             \\#define __sun 1
    286             \\
    287         ),
    288         .macos => try w.writeAll(
    289             \\#define __APPLE__ 1
    290             \\#define __MACH__ 1
    291             \\
    292         ),
    293         else => {},
    294     }
    295 
    296     // unix and other additional os macros
    297     switch (comp.target.os.tag) {
    298         .freebsd,
    299         .netbsd,
    300         .openbsd,
    301         .dragonfly,
    302         .linux,
    303         => try w.writeAll(
    304             \\#define unix 1
    305             \\#define __unix 1
    306             \\#define __unix__ 1
    307             \\
    308         ),
    309         else => {},
    310     }
    311     if (comp.target.abi.isAndroid()) {
    312         try w.writeAll("#define __ANDROID__ 1\n");
    313     }
    314 
    315     // architecture macros
    316     switch (comp.target.cpu.arch) {
    317         .x86_64 => try w.writeAll(
    318             \\#define __amd64__ 1
    319             \\#define __amd64 1
    320             \\#define __x86_64 1
    321             \\#define __x86_64__ 1
    322             \\
    323         ),
    324         .x86 => try w.writeAll(
    325             \\#define i386 1
    326             \\#define __i386 1
    327             \\#define __i386__ 1
    328             \\
    329         ),
    330         .mips,
    331         .mipsel,
    332         .mips64,
    333         .mips64el,
    334         => try w.writeAll(
    335             \\#define __mips__ 1
    336             \\#define mips 1
    337             \\
    338         ),
    339         .powerpc,
    340         .powerpcle,
    341         => try w.writeAll(
    342             \\#define __powerpc__ 1
    343             \\#define __POWERPC__ 1
    344             \\#define __ppc__ 1
    345             \\#define __PPC__ 1
    346             \\#define _ARCH_PPC 1
    347             \\
    348         ),
    349         .powerpc64,
    350         .powerpc64le,
    351         => try w.writeAll(
    352             \\#define __powerpc 1
    353             \\#define __powerpc__ 1
    354             \\#define __powerpc64__ 1
    355             \\#define __POWERPC__ 1
    356             \\#define __ppc__ 1
    357             \\#define __ppc64__ 1
    358             \\#define __PPC__ 1
    359             \\#define __PPC64__ 1
    360             \\#define _ARCH_PPC 1
    361             \\#define _ARCH_PPC64 1
    362             \\
    363         ),
    364         .sparc64 => try w.writeAll(
    365             \\#define __sparc__ 1
    366             \\#define __sparc 1
    367             \\#define __sparc_v9__ 1
    368             \\
    369         ),
    370         .sparc => try w.writeAll(
    371             \\#define __sparc__ 1
    372             \\#define __sparc 1
    373             \\
    374         ),
    375         .arm, .armeb => try w.writeAll(
    376             \\#define __arm__ 1
    377             \\#define __arm 1
    378             \\
    379         ),
    380         .thumb, .thumbeb => try w.writeAll(
    381             \\#define __arm__ 1
    382             \\#define __arm 1
    383             \\#define __thumb__ 1
    384             \\
    385         ),
    386         .aarch64, .aarch64_be => try w.writeAll("#define __aarch64__ 1\n"),
    387         .msp430 => try w.writeAll(
    388             \\#define MSP430 1
    389             \\#define __MSP430__ 1
    390             \\
    391         ),
    392         else => {},
    393     }
    394 
    395     if (comp.target.os.tag != .windows) switch (ptr_width) {
    396         64 => try w.writeAll(
    397             \\#define _LP64 1
    398             \\#define __LP64__ 1
    399             \\
    400         ),
    401         32 => try w.writeAll("#define _ILP32 1\n"),
    402         else => {},
    403     };
    404 
    405     try w.writeAll(
    406         \\#define __ORDER_LITTLE_ENDIAN__ 1234
    407         \\#define __ORDER_BIG_ENDIAN__ 4321
    408         \\#define __ORDER_PDP_ENDIAN__ 3412
    409         \\
    410     );
    411     if (comp.target.cpu.arch.endian() == .little) try w.writeAll(
    412         \\#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
    413         \\#define __LITTLE_ENDIAN__ 1
    414         \\
    415     ) else try w.writeAll(
    416         \\#define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__
    417         \\#define __BIG_ENDIAN__ 1
    418         \\
    419     );
    420 
    421     // atomics
    422     try w.writeAll(
    423         \\#define __ATOMIC_RELAXED 0
    424         \\#define __ATOMIC_CONSUME 1
    425         \\#define __ATOMIC_ACQUIRE 2
    426         \\#define __ATOMIC_RELEASE 3
    427         \\#define __ATOMIC_ACQ_REL 4
    428         \\#define __ATOMIC_SEQ_CST 5
    429         \\
    430     );
    431 
    432     // TODO: Set these to target-specific constants depending on backend capabilities
    433     // For now they are just set to the "may be lock-free" value
    434     try w.writeAll(
    435         \\#define __ATOMIC_BOOL_LOCK_FREE 1
    436         \\#define __ATOMIC_CHAR_LOCK_FREE 1
    437         \\#define __ATOMIC_CHAR16_T_LOCK_FREE 1
    438         \\#define __ATOMIC_CHAR32_T_LOCK_FREE 1
    439         \\#define __ATOMIC_WCHAR_T_LOCK_FREE 1
    440         \\#define __ATOMIC_SHORT_LOCK_FREE 1
    441         \\#define __ATOMIC_INT_LOCK_FREE 1
    442         \\#define __ATOMIC_LONG_LOCK_FREE 1
    443         \\#define __ATOMIC_LLONG_LOCK_FREE 1
    444         \\#define __ATOMIC_POINTER_LOCK_FREE 1
    445         \\
    446     );
    447     if (comp.langopts.hasChar8_T()) {
    448         try w.writeAll("#define __ATOMIC_CHAR8_T_LOCK_FREE 1\n");
    449     }
    450 
    451     // types
    452     if (comp.getCharSignedness() == .unsigned) try w.writeAll("#define __CHAR_UNSIGNED__ 1\n");
    453     try w.writeAll("#define __CHAR_BIT__ 8\n");
    454 
    455     // int maxs
    456     try comp.generateIntWidth(w, "BOOL", .{ .specifier = .bool });
    457     try comp.generateIntMaxAndWidth(w, "SCHAR", .{ .specifier = .schar });
    458     try comp.generateIntMaxAndWidth(w, "SHRT", .{ .specifier = .short });
    459     try comp.generateIntMaxAndWidth(w, "INT", .{ .specifier = .int });
    460     try comp.generateIntMaxAndWidth(w, "LONG", .{ .specifier = .long });
    461     try comp.generateIntMaxAndWidth(w, "LONG_LONG", .{ .specifier = .long_long });
    462     try comp.generateIntMaxAndWidth(w, "WCHAR", comp.types.wchar);
    463     // try comp.generateIntMax(w, "WINT", comp.types.wchar);
    464     try comp.generateIntMaxAndWidth(w, "INTMAX", comp.types.intmax);
    465     try comp.generateIntMaxAndWidth(w, "SIZE", comp.types.size);
    466     try comp.generateIntMaxAndWidth(w, "UINTMAX", comp.types.intmax.makeIntegerUnsigned());
    467     try comp.generateIntMaxAndWidth(w, "PTRDIFF", comp.types.ptrdiff);
    468     try comp.generateIntMaxAndWidth(w, "INTPTR", comp.types.intptr);
    469     try comp.generateIntMaxAndWidth(w, "UINTPTR", comp.types.intptr.makeIntegerUnsigned());
    470     try comp.generateIntMaxAndWidth(w, "SIG_ATOMIC", target_util.sigAtomicType(comp.target));
    471 
    472     // int widths
    473     try w.print("#define __BITINT_MAXWIDTH__ {d}\n", .{bit_int_max_bits});
    474 
    475     // sizeof types
    476     try comp.generateSizeofType(w, "__SIZEOF_FLOAT__", .{ .specifier = .float });
    477     try comp.generateSizeofType(w, "__SIZEOF_DOUBLE__", .{ .specifier = .double });
    478     try comp.generateSizeofType(w, "__SIZEOF_LONG_DOUBLE__", .{ .specifier = .long_double });
    479     try comp.generateSizeofType(w, "__SIZEOF_SHORT__", .{ .specifier = .short });
    480     try comp.generateSizeofType(w, "__SIZEOF_INT__", .{ .specifier = .int });
    481     try comp.generateSizeofType(w, "__SIZEOF_LONG__", .{ .specifier = .long });
    482     try comp.generateSizeofType(w, "__SIZEOF_LONG_LONG__", .{ .specifier = .long_long });
    483     try comp.generateSizeofType(w, "__SIZEOF_POINTER__", .{ .specifier = .pointer });
    484     try comp.generateSizeofType(w, "__SIZEOF_PTRDIFF_T__", comp.types.ptrdiff);
    485     try comp.generateSizeofType(w, "__SIZEOF_SIZE_T__", comp.types.size);
    486     try comp.generateSizeofType(w, "__SIZEOF_WCHAR_T__", comp.types.wchar);
    487     // try comp.generateSizeofType(w, "__SIZEOF_WINT_T__", .{ .specifier = .pointer });
    488 
    489     if (target_util.hasInt128(comp.target)) {
    490         try comp.generateSizeofType(w, "__SIZEOF_INT128__", .{ .specifier = .int128 });
    491     }
    492 
    493     // various int types
    494     const mapper = comp.string_interner.getSlowTypeMapper();
    495     try generateTypeMacro(w, mapper, "__INTPTR_TYPE__", comp.types.intptr, comp.langopts);
    496     try generateTypeMacro(w, mapper, "__UINTPTR_TYPE__", comp.types.intptr.makeIntegerUnsigned(), comp.langopts);
    497 
    498     try generateTypeMacro(w, mapper, "__INTMAX_TYPE__", comp.types.intmax, comp.langopts);
    499     try comp.generateSuffixMacro("__INTMAX", w, comp.types.intptr);
    500 
    501     try generateTypeMacro(w, mapper, "__UINTMAX_TYPE__", comp.types.intmax.makeIntegerUnsigned(), comp.langopts);
    502     try comp.generateSuffixMacro("__UINTMAX", w, comp.types.intptr.makeIntegerUnsigned());
    503 
    504     try generateTypeMacro(w, mapper, "__PTRDIFF_TYPE__", comp.types.ptrdiff, comp.langopts);
    505     try generateTypeMacro(w, mapper, "__SIZE_TYPE__", comp.types.size, comp.langopts);
    506     try generateTypeMacro(w, mapper, "__WCHAR_TYPE__", comp.types.wchar, comp.langopts);
    507     try generateTypeMacro(w, mapper, "__CHAR16_TYPE__", comp.types.uint_least16_t, comp.langopts);
    508     try generateTypeMacro(w, mapper, "__CHAR32_TYPE__", comp.types.uint_least32_t, comp.langopts);
    509 
    510     try comp.generateExactWidthTypes(w, mapper);
    511     try comp.generateFastAndLeastWidthTypes(w, mapper);
    512 
    513     if (target_util.FPSemantics.halfPrecisionType(comp.target)) |half| {
    514         try generateFloatMacros(w, "FLT16", half, "F16");
    515     }
    516     try generateFloatMacros(w, "FLT", target_util.FPSemantics.forType(.float, comp.target), "F");
    517     try generateFloatMacros(w, "DBL", target_util.FPSemantics.forType(.double, comp.target), "");
    518     try generateFloatMacros(w, "LDBL", target_util.FPSemantics.forType(.longdouble, comp.target), "L");
    519 
    520     // TODO: clang treats __FLT_EVAL_METHOD__ as a special-cased macro because evaluating it within a scope
    521     // where `#pragma clang fp eval_method(X)` has been called produces an error diagnostic.
    522     const flt_eval_method = comp.langopts.fp_eval_method orelse target_util.defaultFpEvalMethod(comp.target);
    523     try w.print("#define __FLT_EVAL_METHOD__ {d}\n", .{@intFromEnum(flt_eval_method)});
    524 
    525     try w.writeAll(
    526         \\#define __FLT_RADIX__ 2
    527         \\#define __DECIMAL_DIG__ __LDBL_DECIMAL_DIG__
    528         \\
    529     );
    530 }
    531 
    532 /// Generate builtin macros that will be available to each source file.
    533 pub fn generateBuiltinMacros(comp: *Compilation, system_defines_mode: SystemDefinesMode) !Source {
    534     try comp.generateBuiltinTypes();
    535 
    536     var buf = std.array_list.Managed(u8).init(comp.gpa);
    537     defer buf.deinit();
    538 
    539     if (system_defines_mode == .include_system_defines) {
    540         try buf.appendSlice(
    541             \\#define __VERSION__ "Aro
    542         ++ " " ++ @import("../backend.zig").version_str ++ "\"\n" ++
    543             \\#define __Aro__
    544             \\
    545         );
    546     }
    547 
    548     try buf.appendSlice("#define __STDC__ 1\n");
    549     try buf.writer().print("#define __STDC_HOSTED__ {d}\n", .{@intFromBool(comp.target.os.tag != .freestanding)});
    550 
    551     // standard macros
    552     try buf.appendSlice(
    553         \\#define __STDC_NO_COMPLEX__ 1
    554         \\#define __STDC_NO_THREADS__ 1
    555         \\#define __STDC_NO_VLA__ 1
    556         \\#define __STDC_UTF_16__ 1
    557         \\#define __STDC_UTF_32__ 1
    558         \\#define __STDC_EMBED_NOT_FOUND__ 0
    559         \\#define __STDC_EMBED_FOUND__ 1
    560         \\#define __STDC_EMBED_EMPTY__ 2
    561         \\
    562     );
    563     if (comp.langopts.standard.StdCVersionMacro()) |stdc_version| {
    564         try buf.appendSlice("#define __STDC_VERSION__ ");
    565         try buf.appendSlice(stdc_version);
    566         try buf.append('\n');
    567     }
    568 
    569     // timestamps
    570     const timestamp = try comp.getTimestamp();
    571     try generateDateAndTime(buf.writer(), timestamp);
    572 
    573     if (system_defines_mode == .include_system_defines) {
    574         try comp.generateSystemDefines(buf.writer());
    575     }
    576 
    577     return comp.addSourceFromBuffer("<builtin>", buf.items);
    578 }
    579 
    580 fn generateFloatMacros(w: anytype, prefix: []const u8, semantics: target_util.FPSemantics, ext: []const u8) !void {
    581     const denormMin = semantics.chooseValue(
    582         []const u8,
    583         .{
    584             "5.9604644775390625e-8",
    585             "1.40129846e-45",
    586             "4.9406564584124654e-324",
    587             "3.64519953188247460253e-4951",
    588             "4.94065645841246544176568792868221e-324",
    589             "6.47517511943802511092443895822764655e-4966",
    590         },
    591     );
    592     const digits = semantics.chooseValue(i32, .{ 3, 6, 15, 18, 31, 33 });
    593     const decimalDigits = semantics.chooseValue(i32, .{ 5, 9, 17, 21, 33, 36 });
    594     const epsilon = semantics.chooseValue(
    595         []const u8,
    596         .{
    597             "9.765625e-4",
    598             "1.19209290e-7",
    599             "2.2204460492503131e-16",
    600             "1.08420217248550443401e-19",
    601             "4.94065645841246544176568792868221e-324",
    602             "1.92592994438723585305597794258492732e-34",
    603         },
    604     );
    605     const mantissaDigits = semantics.chooseValue(i32, .{ 11, 24, 53, 64, 106, 113 });
    606 
    607     const min10Exp = semantics.chooseValue(i32, .{ -4, -37, -307, -4931, -291, -4931 });
    608     const max10Exp = semantics.chooseValue(i32, .{ 4, 38, 308, 4932, 308, 4932 });
    609 
    610     const minExp = semantics.chooseValue(i32, .{ -13, -125, -1021, -16381, -968, -16381 });
    611     const maxExp = semantics.chooseValue(i32, .{ 16, 128, 1024, 16384, 1024, 16384 });
    612 
    613     const min = semantics.chooseValue(
    614         []const u8,
    615         .{
    616             "6.103515625e-5",
    617             "1.17549435e-38",
    618             "2.2250738585072014e-308",
    619             "3.36210314311209350626e-4932",
    620             "2.00416836000897277799610805135016e-292",
    621             "3.36210314311209350626267781732175260e-4932",
    622         },
    623     );
    624     const max = semantics.chooseValue(
    625         []const u8,
    626         .{
    627             "6.5504e+4",
    628             "3.40282347e+38",
    629             "1.7976931348623157e+308",
    630             "1.18973149535723176502e+4932",
    631             "1.79769313486231580793728971405301e+308",
    632             "1.18973149535723176508575932662800702e+4932",
    633         },
    634     );
    635 
    636     var def_prefix_buf: [32]u8 = undefined;
    637     const prefix_slice = std.fmt.bufPrint(&def_prefix_buf, "__{s}_", .{prefix}) catch
    638         return error.OutOfMemory;
    639 
    640     try w.print("#define {s}DENORM_MIN__ {s}{s}\n", .{ prefix_slice, denormMin, ext });
    641     try w.print("#define {s}HAS_DENORM__\n", .{prefix_slice});
    642     try w.print("#define {s}DIG__ {d}\n", .{ prefix_slice, digits });
    643     try w.print("#define {s}DECIMAL_DIG__ {d}\n", .{ prefix_slice, decimalDigits });
    644 
    645     try w.print("#define {s}EPSILON__ {s}{s}\n", .{ prefix_slice, epsilon, ext });
    646     try w.print("#define {s}HAS_INFINITY__\n", .{prefix_slice});
    647     try w.print("#define {s}HAS_QUIET_NAN__\n", .{prefix_slice});
    648     try w.print("#define {s}MANT_DIG__ {d}\n", .{ prefix_slice, mantissaDigits });
    649 
    650     try w.print("#define {s}MAX_10_EXP__ {d}\n", .{ prefix_slice, max10Exp });
    651     try w.print("#define {s}MAX_EXP__ {d}\n", .{ prefix_slice, maxExp });
    652     try w.print("#define {s}MAX__ {s}{s}\n", .{ prefix_slice, max, ext });
    653 
    654     try w.print("#define {s}MIN_10_EXP__ ({d})\n", .{ prefix_slice, min10Exp });
    655     try w.print("#define {s}MIN_EXP__ ({d})\n", .{ prefix_slice, minExp });
    656     try w.print("#define {s}MIN__ {s}{s}\n", .{ prefix_slice, min, ext });
    657 }
    658 
    659 fn generateTypeMacro(w: anytype, mapper: StrInt.TypeMapper, name: []const u8, ty: Type, langopts: LangOpts) !void {
    660     try w.print("#define {s} ", .{name});
    661     try ty.print(mapper, langopts, w);
    662     try w.writeByte('\n');
    663 }
    664 
    665 fn generateBuiltinTypes(comp: *Compilation) !void {
    666     const os = comp.target.os.tag;
    667     const wchar: Type = switch (comp.target.cpu.arch) {
    668         .xcore => .{ .specifier = .uchar },
    669         .ve, .msp430 => .{ .specifier = .uint },
    670         .arm, .armeb, .thumb, .thumbeb => .{
    671             .specifier = if (os != .windows and os != .netbsd and os != .openbsd) .uint else .int,
    672         },
    673         .aarch64, .aarch64_be => .{
    674             .specifier = if (!os.isDarwin() and os != .netbsd) .uint else .int,
    675         },
    676         .x86_64, .x86 => .{ .specifier = if (os == .windows) .ushort else .int },
    677         else => .{ .specifier = .int },
    678     };
    679 
    680     const ptr_width = comp.target.ptrBitWidth();
    681     const ptrdiff = if (os == .windows and ptr_width == 64)
    682         Type{ .specifier = .long_long }
    683     else switch (ptr_width) {
    684         16 => Type{ .specifier = .int },
    685         32 => Type{ .specifier = .int },
    686         64 => Type{ .specifier = .long },
    687         else => unreachable,
    688     };
    689 
    690     const size = if (os == .windows and ptr_width == 64)
    691         Type{ .specifier = .ulong_long }
    692     else switch (ptr_width) {
    693         16 => Type{ .specifier = .uint },
    694         32 => Type{ .specifier = .uint },
    695         64 => Type{ .specifier = .ulong },
    696         else => unreachable,
    697     };
    698 
    699     const va_list = try comp.generateVaListType();
    700 
    701     const pid_t: Type = switch (os) {
    702         .haiku => .{ .specifier = .long },
    703         // Todo: pid_t is required to "a signed integer type"; are there any systems
    704         // on which it is `short int`?
    705         else => .{ .specifier = .int },
    706     };
    707 
    708     const intmax = target_util.intMaxType(comp.target);
    709     const intptr = target_util.intPtrType(comp.target);
    710     const int16 = target_util.int16Type(comp.target);
    711     const int64 = target_util.int64Type(comp.target);
    712 
    713     comp.types = .{
    714         .wchar = wchar,
    715         .ptrdiff = ptrdiff,
    716         .size = size,
    717         .va_list = va_list,
    718         .pid_t = pid_t,
    719         .intmax = intmax,
    720         .intptr = intptr,
    721         .int16 = int16,
    722         .int64 = int64,
    723         .uint_least16_t = comp.intLeastN(16, .unsigned),
    724         .uint_least32_t = comp.intLeastN(32, .unsigned),
    725     };
    726 
    727     try comp.generateNsConstantStringType();
    728 }
    729 
    730 pub fn float80Type(comp: *const Compilation) ?Type {
    731     if (comp.langopts.emulate != .gcc) return null;
    732     return target_util.float80Type(comp.target);
    733 }
    734 
    735 /// Smallest integer type with at least N bits
    736 pub fn intLeastN(comp: *const Compilation, bits: usize, signedness: std.builtin.Signedness) Type {
    737     if (bits == 64 and (comp.target.os.tag.isDarwin() or comp.target.cpu.arch.isWasm())) {
    738         // WebAssembly and Darwin use `long long` for `int_least64_t` and `int_fast64_t`.
    739         return .{ .specifier = if (signedness == .signed) .long_long else .ulong_long };
    740     }
    741     if (bits == 16 and comp.target.cpu.arch == .avr) {
    742         // AVR uses int for int_least16_t and int_fast16_t.
    743         return .{ .specifier = if (signedness == .signed) .int else .uint };
    744     }
    745     const candidates = switch (signedness) {
    746         .signed => &[_]Type.Specifier{ .schar, .short, .int, .long, .long_long },
    747         .unsigned => &[_]Type.Specifier{ .uchar, .ushort, .uint, .ulong, .ulong_long },
    748     };
    749     for (candidates) |specifier| {
    750         const ty: Type = .{ .specifier = specifier };
    751         if (ty.sizeof(comp).? * 8 >= bits) return ty;
    752     } else unreachable;
    753 }
    754 
    755 fn intSize(comp: *const Compilation, specifier: Type.Specifier) u64 {
    756     const ty = Type{ .specifier = specifier };
    757     return ty.sizeof(comp).?;
    758 }
    759 
    760 fn generateFastOrLeastType(
    761     comp: *Compilation,
    762     bits: usize,
    763     kind: enum { least, fast },
    764     signedness: std.builtin.Signedness,
    765     w: anytype,
    766     mapper: StrInt.TypeMapper,
    767 ) !void {
    768     const ty = comp.intLeastN(bits, signedness); // defining the fast types as the least types is permitted
    769 
    770     var buf: [32]u8 = undefined;
    771     const suffix = "_TYPE__";
    772     const base_name = switch (signedness) {
    773         .signed => "__INT_",
    774         .unsigned => "__UINT_",
    775     };
    776     const kind_str = switch (kind) {
    777         .fast => "FAST",
    778         .least => "LEAST",
    779     };
    780 
    781     const full = std.fmt.bufPrint(&buf, "{s}{s}{d}{s}", .{
    782         base_name, kind_str, bits, suffix,
    783     }) catch return error.OutOfMemory;
    784 
    785     try generateTypeMacro(w, mapper, full, ty, comp.langopts);
    786 
    787     const prefix = full[2 .. full.len - suffix.len]; // remove "__" and "_TYPE__"
    788 
    789     switch (signedness) {
    790         .signed => try comp.generateIntMaxAndWidth(w, prefix, ty),
    791         .unsigned => try comp.generateIntMax(w, prefix, ty),
    792     }
    793     try comp.generateFmt(prefix, w, ty);
    794 }
    795 
    796 fn generateFastAndLeastWidthTypes(comp: *Compilation, w: anytype, mapper: StrInt.TypeMapper) !void {
    797     const sizes = [_]usize{ 8, 16, 32, 64 };
    798     for (sizes) |size| {
    799         try comp.generateFastOrLeastType(size, .least, .signed, w, mapper);
    800         try comp.generateFastOrLeastType(size, .least, .unsigned, w, mapper);
    801         try comp.generateFastOrLeastType(size, .fast, .signed, w, mapper);
    802         try comp.generateFastOrLeastType(size, .fast, .unsigned, w, mapper);
    803     }
    804 }
    805 
    806 fn generateExactWidthTypes(comp: *const Compilation, w: anytype, mapper: StrInt.TypeMapper) !void {
    807     try comp.generateExactWidthType(w, mapper, .schar);
    808 
    809     if (comp.intSize(.short) > comp.intSize(.char)) {
    810         try comp.generateExactWidthType(w, mapper, .short);
    811     }
    812 
    813     if (comp.intSize(.int) > comp.intSize(.short)) {
    814         try comp.generateExactWidthType(w, mapper, .int);
    815     }
    816 
    817     if (comp.intSize(.long) > comp.intSize(.int)) {
    818         try comp.generateExactWidthType(w, mapper, .long);
    819     }
    820 
    821     if (comp.intSize(.long_long) > comp.intSize(.long)) {
    822         try comp.generateExactWidthType(w, mapper, .long_long);
    823     }
    824 
    825     try comp.generateExactWidthType(w, mapper, .uchar);
    826     try comp.generateExactWidthIntMax(w, .uchar);
    827     try comp.generateExactWidthIntMax(w, .schar);
    828 
    829     if (comp.intSize(.short) > comp.intSize(.char)) {
    830         try comp.generateExactWidthType(w, mapper, .ushort);
    831         try comp.generateExactWidthIntMax(w, .ushort);
    832         try comp.generateExactWidthIntMax(w, .short);
    833     }
    834 
    835     if (comp.intSize(.int) > comp.intSize(.short)) {
    836         try comp.generateExactWidthType(w, mapper, .uint);
    837         try comp.generateExactWidthIntMax(w, .uint);
    838         try comp.generateExactWidthIntMax(w, .int);
    839     }
    840 
    841     if (comp.intSize(.long) > comp.intSize(.int)) {
    842         try comp.generateExactWidthType(w, mapper, .ulong);
    843         try comp.generateExactWidthIntMax(w, .ulong);
    844         try comp.generateExactWidthIntMax(w, .long);
    845     }
    846 
    847     if (comp.intSize(.long_long) > comp.intSize(.long)) {
    848         try comp.generateExactWidthType(w, mapper, .ulong_long);
    849         try comp.generateExactWidthIntMax(w, .ulong_long);
    850         try comp.generateExactWidthIntMax(w, .long_long);
    851     }
    852 }
    853 
    854 fn generateFmt(comp: *const Compilation, prefix: []const u8, w: anytype, ty: Type) !void {
    855     const unsigned = ty.isUnsignedInt(comp);
    856     const modifier = ty.formatModifier();
    857     const formats = if (unsigned) "ouxX" else "di";
    858     for (formats) |c| {
    859         try w.print("#define {s}_FMT{c}__ \"{s}{c}\"\n", .{ prefix, c, modifier, c });
    860     }
    861 }
    862 
    863 fn generateSuffixMacro(comp: *const Compilation, prefix: []const u8, w: anytype, ty: Type) !void {
    864     return w.print("#define {s}_C_SUFFIX__ {s}\n", .{ prefix, ty.intValueSuffix(comp) });
    865 }
    866 
    867 /// Generate the following for ty:
    868 ///     Name macro (e.g. #define __UINT32_TYPE__ unsigned int)
    869 ///     Format strings (e.g. #define __UINT32_FMTu__ "u")
    870 ///     Suffix macro (e.g. #define __UINT32_C_SUFFIX__ U)
    871 fn generateExactWidthType(comp: *const Compilation, w: anytype, mapper: StrInt.TypeMapper, specifier: Type.Specifier) !void {
    872     var ty = Type{ .specifier = specifier };
    873     const width = 8 * ty.sizeof(comp).?;
    874     const unsigned = ty.isUnsignedInt(comp);
    875 
    876     if (width == 16) {
    877         ty = if (unsigned) comp.types.int16.makeIntegerUnsigned() else comp.types.int16;
    878     } else if (width == 64) {
    879         ty = if (unsigned) comp.types.int64.makeIntegerUnsigned() else comp.types.int64;
    880     }
    881 
    882     var buffer: [16]u8 = undefined;
    883     const suffix = "_TYPE__";
    884     const full = std.fmt.bufPrint(&buffer, "{s}{d}{s}", .{
    885         if (unsigned) "__UINT" else "__INT", width, suffix,
    886     }) catch return error.OutOfMemory;
    887 
    888     try generateTypeMacro(w, mapper, full, ty, comp.langopts);
    889 
    890     const prefix = full[0 .. full.len - suffix.len]; // remove "_TYPE__"
    891 
    892     try comp.generateFmt(prefix, w, ty);
    893     try comp.generateSuffixMacro(prefix, w, ty);
    894 }
    895 
    896 pub fn hasFloat128(comp: *const Compilation) bool {
    897     return target_util.hasFloat128(comp.target);
    898 }
    899 
    900 pub fn hasHalfPrecisionFloatABI(comp: *const Compilation) bool {
    901     return comp.langopts.allow_half_args_and_returns or target_util.hasHalfPrecisionFloatABI(comp.target);
    902 }
    903 
    904 fn generateNsConstantStringType(comp: *Compilation) !void {
    905     comp.types.ns_constant_string.record = .{
    906         .name = try StrInt.intern(comp, "__NSConstantString_tag"),
    907         .fields = &comp.types.ns_constant_string.fields,
    908         .field_attributes = null,
    909         .type_layout = undefined,
    910     };
    911     const const_int_ptr = Type{ .specifier = .pointer, .data = .{ .sub_type = &comp.types.ns_constant_string.int_ty } };
    912     const const_char_ptr = Type{ .specifier = .pointer, .data = .{ .sub_type = &comp.types.ns_constant_string.char_ty } };
    913 
    914     comp.types.ns_constant_string.fields[0] = .{ .name = try StrInt.intern(comp, "isa"), .ty = const_int_ptr };
    915     comp.types.ns_constant_string.fields[1] = .{ .name = try StrInt.intern(comp, "flags"), .ty = .{ .specifier = .int } };
    916     comp.types.ns_constant_string.fields[2] = .{ .name = try StrInt.intern(comp, "str"), .ty = const_char_ptr };
    917     comp.types.ns_constant_string.fields[3] = .{ .name = try StrInt.intern(comp, "length"), .ty = .{ .specifier = .long } };
    918     comp.types.ns_constant_string.ty = .{ .specifier = .@"struct", .data = .{ .record = &comp.types.ns_constant_string.record } };
    919     record_layout.compute(&comp.types.ns_constant_string.record, comp.types.ns_constant_string.ty, comp, null) catch unreachable;
    920 }
    921 
    922 fn generateVaListType(comp: *Compilation) !Type {
    923     const Kind = enum { char_ptr, void_ptr, aarch64_va_list, x86_64_va_list };
    924     const kind: Kind = switch (comp.target.cpu.arch) {
    925         .aarch64 => switch (comp.target.os.tag) {
    926             .windows => @as(Kind, .char_ptr),
    927             .ios, .macos, .tvos, .watchos => .char_ptr,
    928             else => .aarch64_va_list,
    929         },
    930         .sparc, .wasm32, .wasm64, .bpfel, .bpfeb, .riscv32, .riscv64, .avr, .spirv32, .spirv64 => .void_ptr,
    931         .powerpc => switch (comp.target.os.tag) {
    932             .ios, .macos, .tvos, .watchos, .aix => @as(Kind, .char_ptr),
    933             else => return Type{ .specifier = .void }, // unknown
    934         },
    935         .x86, .msp430 => .char_ptr,
    936         .x86_64 => switch (comp.target.os.tag) {
    937             .windows => @as(Kind, .char_ptr),
    938             else => .x86_64_va_list,
    939         },
    940         else => return Type{ .specifier = .void }, // unknown
    941     };
    942 
    943     // TODO this might be bad?
    944     const arena = comp.diagnostics.arena.allocator();
    945 
    946     var ty: Type = undefined;
    947     switch (kind) {
    948         .char_ptr => ty = .{ .specifier = .char },
    949         .void_ptr => ty = .{ .specifier = .void },
    950         .aarch64_va_list => {
    951             const record_ty = try arena.create(Type.Record);
    952             record_ty.* = .{
    953                 .name = try StrInt.intern(comp, "__va_list_tag"),
    954                 .fields = try arena.alloc(Type.Record.Field, 5),
    955                 .field_attributes = null,
    956                 .type_layout = undefined, // computed below
    957             };
    958             const void_ty = try arena.create(Type);
    959             void_ty.* = .{ .specifier = .void };
    960             const void_ptr = Type{ .specifier = .pointer, .data = .{ .sub_type = void_ty } };
    961             record_ty.fields[0] = .{ .name = try StrInt.intern(comp, "__stack"), .ty = void_ptr };
    962             record_ty.fields[1] = .{ .name = try StrInt.intern(comp, "__gr_top"), .ty = void_ptr };
    963             record_ty.fields[2] = .{ .name = try StrInt.intern(comp, "__vr_top"), .ty = void_ptr };
    964             record_ty.fields[3] = .{ .name = try StrInt.intern(comp, "__gr_offs"), .ty = .{ .specifier = .int } };
    965             record_ty.fields[4] = .{ .name = try StrInt.intern(comp, "__vr_offs"), .ty = .{ .specifier = .int } };
    966             ty = .{ .specifier = .@"struct", .data = .{ .record = record_ty } };
    967             record_layout.compute(record_ty, ty, comp, null) catch unreachable;
    968         },
    969         .x86_64_va_list => {
    970             const record_ty = try arena.create(Type.Record);
    971             record_ty.* = .{
    972                 .name = try StrInt.intern(comp, "__va_list_tag"),
    973                 .fields = try arena.alloc(Type.Record.Field, 4),
    974                 .field_attributes = null,
    975                 .type_layout = undefined, // computed below
    976             };
    977             const void_ty = try arena.create(Type);
    978             void_ty.* = .{ .specifier = .void };
    979             const void_ptr = Type{ .specifier = .pointer, .data = .{ .sub_type = void_ty } };
    980             record_ty.fields[0] = .{ .name = try StrInt.intern(comp, "gp_offset"), .ty = .{ .specifier = .uint } };
    981             record_ty.fields[1] = .{ .name = try StrInt.intern(comp, "fp_offset"), .ty = .{ .specifier = .uint } };
    982             record_ty.fields[2] = .{ .name = try StrInt.intern(comp, "overflow_arg_area"), .ty = void_ptr };
    983             record_ty.fields[3] = .{ .name = try StrInt.intern(comp, "reg_save_area"), .ty = void_ptr };
    984             ty = .{ .specifier = .@"struct", .data = .{ .record = record_ty } };
    985             record_layout.compute(record_ty, ty, comp, null) catch unreachable;
    986         },
    987     }
    988     if (kind == .char_ptr or kind == .void_ptr) {
    989         const elem_ty = try arena.create(Type);
    990         elem_ty.* = ty;
    991         ty = Type{ .specifier = .pointer, .data = .{ .sub_type = elem_ty } };
    992     } else {
    993         const arr_ty = try arena.create(Type.Array);
    994         arr_ty.* = .{ .len = 1, .elem = ty };
    995         ty = Type{ .specifier = .array, .data = .{ .array = arr_ty } };
    996     }
    997 
    998     return ty;
    999 }
   1000 
   1001 fn generateIntMax(comp: *const Compilation, w: anytype, name: []const u8, ty: Type) !void {
   1002     const bit_count: u8 = @intCast(ty.sizeof(comp).? * 8);
   1003     const unsigned = ty.isUnsignedInt(comp);
   1004     const max: u128 = switch (bit_count) {
   1005         8 => if (unsigned) std.math.maxInt(u8) else std.math.maxInt(i8),
   1006         16 => if (unsigned) std.math.maxInt(u16) else std.math.maxInt(i16),
   1007         32 => if (unsigned) std.math.maxInt(u32) else std.math.maxInt(i32),
   1008         64 => if (unsigned) std.math.maxInt(u64) else std.math.maxInt(i64),
   1009         128 => if (unsigned) std.math.maxInt(u128) else std.math.maxInt(i128),
   1010         else => unreachable,
   1011     };
   1012     try w.print("#define __{s}_MAX__ {d}{s}\n", .{ name, max, ty.intValueSuffix(comp) });
   1013 }
   1014 
   1015 /// Largest value that can be stored in wchar_t
   1016 pub fn wcharMax(comp: *const Compilation) u32 {
   1017     const unsigned = comp.types.wchar.isUnsignedInt(comp);
   1018     return switch (comp.types.wchar.bitSizeof(comp).?) {
   1019         8 => if (unsigned) std.math.maxInt(u8) else std.math.maxInt(i8),
   1020         16 => if (unsigned) std.math.maxInt(u16) else std.math.maxInt(i16),
   1021         32 => if (unsigned) std.math.maxInt(u32) else std.math.maxInt(i32),
   1022         else => unreachable,
   1023     };
   1024 }
   1025 
   1026 fn generateExactWidthIntMax(comp: *const Compilation, w: anytype, specifier: Type.Specifier) !void {
   1027     var ty = Type{ .specifier = specifier };
   1028     const bit_count: u8 = @intCast(ty.sizeof(comp).? * 8);
   1029     const unsigned = ty.isUnsignedInt(comp);
   1030 
   1031     if (bit_count == 64) {
   1032         ty = if (unsigned) comp.types.int64.makeIntegerUnsigned() else comp.types.int64;
   1033     }
   1034 
   1035     var name_buffer: [6]u8 = undefined;
   1036     const name = std.fmt.bufPrint(&name_buffer, "{s}{d}", .{
   1037         if (unsigned) "UINT" else "INT", bit_count,
   1038     }) catch return error.OutOfMemory;
   1039 
   1040     return comp.generateIntMax(w, name, ty);
   1041 }
   1042 
   1043 fn generateIntWidth(comp: *Compilation, w: anytype, name: []const u8, ty: Type) !void {
   1044     try w.print("#define __{s}_WIDTH__ {d}\n", .{ name, 8 * ty.sizeof(comp).? });
   1045 }
   1046 
   1047 fn generateIntMaxAndWidth(comp: *Compilation, w: anytype, name: []const u8, ty: Type) !void {
   1048     try comp.generateIntMax(w, name, ty);
   1049     try comp.generateIntWidth(w, name, ty);
   1050 }
   1051 
   1052 fn generateSizeofType(comp: *Compilation, w: anytype, name: []const u8, ty: Type) !void {
   1053     try w.print("#define {s} {d}\n", .{ name, ty.sizeof(comp).? });
   1054 }
   1055 
   1056 pub fn nextLargestIntSameSign(comp: *const Compilation, ty: Type) ?Type {
   1057     assert(ty.isInt());
   1058     const specifiers = if (ty.isUnsignedInt(comp))
   1059         [_]Type.Specifier{ .short, .int, .long, .long_long }
   1060     else
   1061         [_]Type.Specifier{ .ushort, .uint, .ulong, .ulong_long };
   1062     const size = ty.sizeof(comp).?;
   1063     for (specifiers) |specifier| {
   1064         const candidate = Type{ .specifier = specifier };
   1065         if (candidate.sizeof(comp).? > size) return candidate;
   1066     }
   1067     return null;
   1068 }
   1069 
   1070 /// Maximum size of an array, in bytes
   1071 pub fn maxArrayBytes(comp: *const Compilation) u64 {
   1072     const max_bits = @min(61, comp.target.ptrBitWidth());
   1073     return (@as(u64, 1) << @truncate(max_bits)) - 1;
   1074 }
   1075 
   1076 /// If `enum E { ... }` syntax has a fixed underlying integer type regardless of the presence of
   1077 /// __attribute__((packed)) or the range of values of the corresponding enumerator constants,
   1078 /// specify it here.
   1079 /// TODO: likely incomplete
   1080 pub fn fixedEnumTagSpecifier(comp: *const Compilation) ?Type.Specifier {
   1081     switch (comp.langopts.emulate) {
   1082         .msvc => return .int,
   1083         .clang => if (comp.target.os.tag == .windows) return .int,
   1084         .gcc => {},
   1085     }
   1086     return null;
   1087 }
   1088 
   1089 pub fn getCharSignedness(comp: *const Compilation) std.builtin.Signedness {
   1090     return comp.langopts.char_signedness_override orelse comp.target.cCharSignedness();
   1091 }
   1092 
   1093 /// Add built-in aro headers directory to system include paths
   1094 pub fn addBuiltinIncludeDir(comp: *Compilation, aro_dir: []const u8) !void {
   1095     var search_path = aro_dir;
   1096     while (std.fs.path.dirname(search_path)) |dirname| : (search_path = dirname) {
   1097         var base_dir = comp.cwd.openDir(dirname, .{}) catch continue;
   1098         defer base_dir.close();
   1099 
   1100         base_dir.access("include/stddef.h", .{}) catch continue;
   1101         const path = try std.fs.path.join(comp.gpa, &.{ dirname, "include" });
   1102         errdefer comp.gpa.free(path);
   1103         try comp.system_include_dirs.append(comp.gpa, path);
   1104         break;
   1105     } else return error.AroIncludeNotFound;
   1106 }
   1107 
   1108 pub fn addSystemIncludeDir(comp: *Compilation, path: []const u8) !void {
   1109     const duped = try comp.gpa.dupe(u8, path);
   1110     errdefer comp.gpa.free(duped);
   1111     try comp.system_include_dirs.append(comp.gpa, duped);
   1112 }
   1113 
   1114 pub fn getSource(comp: *const Compilation, id: Source.Id) Source {
   1115     if (id == .generated) return .{
   1116         .path = "<scratch space>",
   1117         .buf = comp.generated_buf.items,
   1118         .id = .generated,
   1119         .splice_locs = &.{},
   1120         .kind = .user,
   1121     };
   1122     return comp.sources.values()[@intFromEnum(id) - 2];
   1123 }
   1124 
   1125 /// Creates a Source from the contents of `reader` and adds it to the Compilation
   1126 pub fn addSourceFromReader(comp: *Compilation, reader: anytype, path: []const u8, kind: Source.Kind) !Source {
   1127     const contents = try reader.readAllAlloc(comp.gpa, std.math.maxInt(u32));
   1128     errdefer comp.gpa.free(contents);
   1129     return comp.addSourceFromOwnedBuffer(contents, path, kind);
   1130 }
   1131 
   1132 /// Creates a Source from `buf` and adds it to the Compilation
   1133 /// Performs newline splicing and line-ending normalization to '\n'
   1134 /// `buf` will be modified and the allocation will be resized if newline splicing
   1135 /// or line-ending changes happen.
   1136 /// caller retains ownership of `path`
   1137 /// To add the contents of an arbitrary reader as a Source, see addSourceFromReader
   1138 /// To add a file's contents given its path, see addSourceFromPath
   1139 pub fn addSourceFromOwnedBuffer(comp: *Compilation, buf: []u8, path: []const u8, kind: Source.Kind) !Source {
   1140     try comp.sources.ensureUnusedCapacity(comp.gpa, 1);
   1141 
   1142     var contents = buf;
   1143     const duped_path = try comp.gpa.dupe(u8, path);
   1144     errdefer comp.gpa.free(duped_path);
   1145 
   1146     var splice_list = std.array_list.Managed(u32).init(comp.gpa);
   1147     defer splice_list.deinit();
   1148 
   1149     const source_id: Source.Id = @enumFromInt(comp.sources.count() + 2);
   1150 
   1151     var i: u32 = 0;
   1152     var backslash_loc: u32 = undefined;
   1153     var state: enum {
   1154         beginning_of_file,
   1155         bom1,
   1156         bom2,
   1157         start,
   1158         back_slash,
   1159         cr,
   1160         back_slash_cr,
   1161         trailing_ws,
   1162     } = .beginning_of_file;
   1163     var line: u32 = 1;
   1164 
   1165     for (contents) |byte| {
   1166         contents[i] = byte;
   1167 
   1168         switch (byte) {
   1169             '\r' => {
   1170                 switch (state) {
   1171                     .start, .cr, .beginning_of_file => {
   1172                         state = .start;
   1173                         line += 1;
   1174                         state = .cr;
   1175                         contents[i] = '\n';
   1176                         i += 1;
   1177                     },
   1178                     .back_slash, .trailing_ws, .back_slash_cr => {
   1179                         i = backslash_loc;
   1180                         try splice_list.append(i);
   1181                         if (state == .trailing_ws) {
   1182                             try comp.addDiagnostic(.{
   1183                                 .tag = .backslash_newline_escape,
   1184                                 .loc = .{ .id = source_id, .byte_offset = i, .line = line },
   1185                             }, &.{});
   1186                         }
   1187                         state = if (state == .back_slash_cr) .cr else .back_slash_cr;
   1188                     },
   1189                     .bom1, .bom2 => break, // invalid utf-8
   1190                 }
   1191             },
   1192             '\n' => {
   1193                 switch (state) {
   1194                     .start, .beginning_of_file => {
   1195                         state = .start;
   1196                         line += 1;
   1197                         i += 1;
   1198                     },
   1199                     .cr, .back_slash_cr => {},
   1200                     .back_slash, .trailing_ws => {
   1201                         i = backslash_loc;
   1202                         if (state == .back_slash or state == .trailing_ws) {
   1203                             try splice_list.append(i);
   1204                         }
   1205                         if (state == .trailing_ws) {
   1206                             try comp.addDiagnostic(.{
   1207                                 .tag = .backslash_newline_escape,
   1208                                 .loc = .{ .id = source_id, .byte_offset = i, .line = line },
   1209                             }, &.{});
   1210                         }
   1211                     },
   1212                     .bom1, .bom2 => break,
   1213                 }
   1214                 state = .start;
   1215             },
   1216             '\\' => {
   1217                 backslash_loc = i;
   1218                 state = .back_slash;
   1219                 i += 1;
   1220             },
   1221             '\t', '\x0B', '\x0C', ' ' => {
   1222                 switch (state) {
   1223                     .start, .trailing_ws => {},
   1224                     .beginning_of_file => state = .start,
   1225                     .cr, .back_slash_cr => state = .start,
   1226                     .back_slash => state = .trailing_ws,
   1227                     .bom1, .bom2 => break,
   1228                 }
   1229                 i += 1;
   1230             },
   1231             '\xEF' => {
   1232                 i += 1;
   1233                 state = switch (state) {
   1234                     .beginning_of_file => .bom1,
   1235                     else => .start,
   1236                 };
   1237             },
   1238             '\xBB' => {
   1239                 i += 1;
   1240                 state = switch (state) {
   1241                     .bom1 => .bom2,
   1242                     else => .start,
   1243                 };
   1244             },
   1245             '\xBF' => {
   1246                 switch (state) {
   1247                     .bom2 => i = 0, // rewind and overwrite the BOM
   1248                     else => i += 1,
   1249                 }
   1250                 state = .start;
   1251             },
   1252             else => {
   1253                 i += 1;
   1254                 state = .start;
   1255             },
   1256         }
   1257     }
   1258 
   1259     const splice_locs = try splice_list.toOwnedSlice();
   1260     errdefer comp.gpa.free(splice_locs);
   1261 
   1262     if (i != contents.len) contents = try comp.gpa.realloc(contents, i);
   1263     errdefer @compileError("errdefers in callers would possibly free the realloced slice using the original len");
   1264 
   1265     const source = Source{
   1266         .id = source_id,
   1267         .path = duped_path,
   1268         .buf = contents,
   1269         .splice_locs = splice_locs,
   1270         .kind = kind,
   1271     };
   1272 
   1273     comp.sources.putAssumeCapacityNoClobber(duped_path, source);
   1274     return source;
   1275 }
   1276 
   1277 /// Caller retains ownership of `path` and `buf`.
   1278 /// Dupes the source buffer; if it is acceptable to modify the source buffer and possibly resize
   1279 /// the allocation, please use `addSourceFromOwnedBuffer`
   1280 pub fn addSourceFromBuffer(comp: *Compilation, path: []const u8, buf: []const u8) !Source {
   1281     if (comp.sources.get(path)) |some| return some;
   1282     if (@as(u64, buf.len) > std.math.maxInt(u32)) return error.StreamTooLong;
   1283 
   1284     const contents = try comp.gpa.dupe(u8, buf);
   1285     errdefer comp.gpa.free(contents);
   1286 
   1287     return comp.addSourceFromOwnedBuffer(contents, path, .user);
   1288 }
   1289 
   1290 /// Caller retains ownership of `path`.
   1291 pub fn addSourceFromPath(comp: *Compilation, path: []const u8) !Source {
   1292     return comp.addSourceFromPathExtra(path, .user);
   1293 }
   1294 
   1295 /// Caller retains ownership of `path`.
   1296 fn addSourceFromPathExtra(comp: *Compilation, path: []const u8, kind: Source.Kind) !Source {
   1297     if (comp.sources.get(path)) |some| return some;
   1298 
   1299     if (mem.indexOfScalar(u8, path, 0) != null) {
   1300         return error.FileNotFound;
   1301     }
   1302 
   1303     const file = try comp.cwd.openFile(path, .{});
   1304     defer file.close();
   1305 
   1306     const contents = file.readToEndAlloc(comp.gpa, std.math.maxInt(u32)) catch |err| switch (err) {
   1307         error.FileTooBig => return error.StreamTooLong,
   1308         else => |e| return e,
   1309     };
   1310     errdefer comp.gpa.free(contents);
   1311 
   1312     return comp.addSourceFromOwnedBuffer(contents, path, kind);
   1313 }
   1314 
   1315 pub const IncludeDirIterator = struct {
   1316     comp: *const Compilation,
   1317     cwd_source_id: ?Source.Id,
   1318     include_dirs_idx: usize = 0,
   1319     sys_include_dirs_idx: usize = 0,
   1320     tried_ms_cwd: bool = false,
   1321 
   1322     const FoundSource = struct {
   1323         path: []const u8,
   1324         kind: Source.Kind,
   1325     };
   1326 
   1327     fn next(self: *IncludeDirIterator) ?FoundSource {
   1328         if (self.cwd_source_id) |source_id| {
   1329             self.cwd_source_id = null;
   1330             const path = self.comp.getSource(source_id).path;
   1331             return .{ .path = std.fs.path.dirname(path) orelse ".", .kind = .user };
   1332         }
   1333         if (self.include_dirs_idx < self.comp.include_dirs.items.len) {
   1334             defer self.include_dirs_idx += 1;
   1335             return .{ .path = self.comp.include_dirs.items[self.include_dirs_idx], .kind = .user };
   1336         }
   1337         if (self.sys_include_dirs_idx < self.comp.system_include_dirs.items.len) {
   1338             defer self.sys_include_dirs_idx += 1;
   1339             return .{ .path = self.comp.system_include_dirs.items[self.sys_include_dirs_idx], .kind = .system };
   1340         }
   1341         if (self.comp.ms_cwd_source_id) |source_id| {
   1342             if (self.tried_ms_cwd) return null;
   1343             self.tried_ms_cwd = true;
   1344             const path = self.comp.getSource(source_id).path;
   1345             return .{ .path = std.fs.path.dirname(path) orelse ".", .kind = .user };
   1346         }
   1347         return null;
   1348     }
   1349 
   1350     /// Returned value's path field must be freed by allocator
   1351     fn nextWithFile(self: *IncludeDirIterator, filename: []const u8, allocator: Allocator) !?FoundSource {
   1352         while (self.next()) |found| {
   1353             const path = try std.fs.path.join(allocator, &.{ found.path, filename });
   1354             if (self.comp.langopts.ms_extensions) {
   1355                 std.mem.replaceScalar(u8, path, '\\', '/');
   1356             }
   1357             return .{ .path = path, .kind = found.kind };
   1358         }
   1359         return null;
   1360     }
   1361 
   1362     /// Advance the iterator until it finds an include directory that matches
   1363     /// the directory which contains `source`.
   1364     fn skipUntilDirMatch(self: *IncludeDirIterator, source: Source.Id) void {
   1365         const path = self.comp.getSource(source).path;
   1366         const includer_path = std.fs.path.dirname(path) orelse ".";
   1367         while (self.next()) |found| {
   1368             if (mem.eql(u8, includer_path, found.path)) break;
   1369         }
   1370     }
   1371 };
   1372 
   1373 pub fn hasInclude(
   1374     comp: *const Compilation,
   1375     filename: []const u8,
   1376     includer_token_source: Source.Id,
   1377     /// angle bracket vs quotes
   1378     include_type: IncludeType,
   1379     /// __has_include vs __has_include_next
   1380     which: WhichInclude,
   1381 ) !bool {
   1382     if (mem.indexOfScalar(u8, filename, 0) != null) {
   1383         return false;
   1384     }
   1385 
   1386     if (std.fs.path.isAbsolute(filename)) {
   1387         if (which == .next) return false;
   1388         return !std.meta.isError(comp.cwd.access(filename, .{}));
   1389     }
   1390 
   1391     const cwd_source_id = switch (include_type) {
   1392         .quotes => switch (which) {
   1393             .first => includer_token_source,
   1394             .next => null,
   1395         },
   1396         .angle_brackets => null,
   1397     };
   1398     var it = IncludeDirIterator{ .comp = comp, .cwd_source_id = cwd_source_id };
   1399     if (which == .next) {
   1400         it.skipUntilDirMatch(includer_token_source);
   1401     }
   1402 
   1403     var stack_fallback = std.heap.stackFallback(path_buf_stack_limit, comp.gpa);
   1404     const sf_allocator = stack_fallback.get();
   1405 
   1406     while (try it.nextWithFile(filename, sf_allocator)) |found| {
   1407         defer sf_allocator.free(found.path);
   1408         if (!std.meta.isError(comp.cwd.access(found.path, .{}))) return true;
   1409     }
   1410     return false;
   1411 }
   1412 
   1413 pub const WhichInclude = enum {
   1414     first,
   1415     next,
   1416 };
   1417 
   1418 pub const IncludeType = enum {
   1419     quotes,
   1420     angle_brackets,
   1421 };
   1422 
   1423 fn getFileContents(comp: *Compilation, path: []const u8, limit: ?u32) ![]const u8 {
   1424     if (mem.indexOfScalar(u8, path, 0) != null) {
   1425         return error.FileNotFound;
   1426     }
   1427 
   1428     const file = try comp.cwd.openFile(path, .{});
   1429     defer file.close();
   1430 
   1431     var buf = std.array_list.Managed(u8).init(comp.gpa);
   1432     defer buf.deinit();
   1433 
   1434     const max = limit orelse std.math.maxInt(u32);
   1435     file.deprecatedReader().readAllArrayList(&buf, max) catch |e| switch (e) {
   1436         error.StreamTooLong => if (limit == null) return e,
   1437         else => return e,
   1438     };
   1439 
   1440     return buf.toOwnedSlice();
   1441 }
   1442 
   1443 pub fn findEmbed(
   1444     comp: *Compilation,
   1445     filename: []const u8,
   1446     includer_token_source: Source.Id,
   1447     /// angle bracket vs quotes
   1448     include_type: IncludeType,
   1449     limit: ?u32,
   1450 ) !?[]const u8 {
   1451     if (std.fs.path.isAbsolute(filename)) {
   1452         return if (comp.getFileContents(filename, limit)) |some|
   1453             some
   1454         else |err| switch (err) {
   1455             error.OutOfMemory => |e| return e,
   1456             else => null,
   1457         };
   1458     }
   1459 
   1460     const cwd_source_id = switch (include_type) {
   1461         .quotes => includer_token_source,
   1462         .angle_brackets => null,
   1463     };
   1464     var it = IncludeDirIterator{ .comp = comp, .cwd_source_id = cwd_source_id };
   1465     var stack_fallback = std.heap.stackFallback(path_buf_stack_limit, comp.gpa);
   1466     const sf_allocator = stack_fallback.get();
   1467 
   1468     while (try it.nextWithFile(filename, sf_allocator)) |found| {
   1469         defer sf_allocator.free(found.path);
   1470         if (comp.getFileContents(found.path, limit)) |some|
   1471             return some
   1472         else |err| switch (err) {
   1473             error.OutOfMemory => return error.OutOfMemory,
   1474             else => {},
   1475         }
   1476     }
   1477     return null;
   1478 }
   1479 
   1480 pub fn findInclude(
   1481     comp: *Compilation,
   1482     filename: []const u8,
   1483     includer_token: Token,
   1484     /// angle bracket vs quotes
   1485     include_type: IncludeType,
   1486     /// include vs include_next
   1487     which: WhichInclude,
   1488 ) !?Source {
   1489     if (std.fs.path.isAbsolute(filename)) {
   1490         if (which == .next) return null;
   1491         // TODO: classify absolute file as belonging to system includes or not?
   1492         return if (comp.addSourceFromPath(filename)) |some|
   1493             some
   1494         else |err| switch (err) {
   1495             error.OutOfMemory => |e| return e,
   1496             else => null,
   1497         };
   1498     }
   1499     const cwd_source_id = switch (include_type) {
   1500         .quotes => switch (which) {
   1501             .first => includer_token.source,
   1502             .next => null,
   1503         },
   1504         .angle_brackets => null,
   1505     };
   1506     var it = IncludeDirIterator{ .comp = comp, .cwd_source_id = cwd_source_id };
   1507 
   1508     if (which == .next) {
   1509         it.skipUntilDirMatch(includer_token.source);
   1510     }
   1511 
   1512     var stack_fallback = std.heap.stackFallback(path_buf_stack_limit, comp.gpa);
   1513     const sf_allocator = stack_fallback.get();
   1514 
   1515     while (try it.nextWithFile(filename, sf_allocator)) |found| {
   1516         defer sf_allocator.free(found.path);
   1517         if (comp.addSourceFromPathExtra(found.path, found.kind)) |some| {
   1518             if (it.tried_ms_cwd) {
   1519                 try comp.addDiagnostic(.{
   1520                     .tag = .ms_search_rule,
   1521                     .extra = .{ .str = some.path },
   1522                     .loc = .{
   1523                         .id = includer_token.source,
   1524                         .byte_offset = includer_token.start,
   1525                         .line = includer_token.line,
   1526                     },
   1527                 }, &.{});
   1528             }
   1529             return some;
   1530         } else |err| switch (err) {
   1531             error.OutOfMemory => return error.OutOfMemory,
   1532             else => {},
   1533         }
   1534     }
   1535     return null;
   1536 }
   1537 
   1538 pub fn addPragmaHandler(comp: *Compilation, name: []const u8, handler: *Pragma) Allocator.Error!void {
   1539     try comp.pragma_handlers.putNoClobber(comp.gpa, name, handler);
   1540 }
   1541 
   1542 pub fn addDefaultPragmaHandlers(comp: *Compilation) Allocator.Error!void {
   1543     const GCC = @import("pragmas/gcc.zig");
   1544     var gcc = try GCC.init(comp.gpa);
   1545     errdefer gcc.deinit(gcc, comp);
   1546 
   1547     const Once = @import("pragmas/once.zig");
   1548     var once = try Once.init(comp.gpa);
   1549     errdefer once.deinit(once, comp);
   1550 
   1551     const Message = @import("pragmas/message.zig");
   1552     var message = try Message.init(comp.gpa);
   1553     errdefer message.deinit(message, comp);
   1554 
   1555     const Pack = @import("pragmas/pack.zig");
   1556     var pack = try Pack.init(comp.gpa);
   1557     errdefer pack.deinit(pack, comp);
   1558 
   1559     try comp.addPragmaHandler("GCC", gcc);
   1560     try comp.addPragmaHandler("once", once);
   1561     try comp.addPragmaHandler("message", message);
   1562     try comp.addPragmaHandler("pack", pack);
   1563 }
   1564 
   1565 pub fn getPragma(comp: *Compilation, name: []const u8) ?*Pragma {
   1566     return comp.pragma_handlers.get(name);
   1567 }
   1568 
   1569 const PragmaEvent = enum {
   1570     before_preprocess,
   1571     before_parse,
   1572     after_parse,
   1573 };
   1574 
   1575 pub fn pragmaEvent(comp: *Compilation, event: PragmaEvent) void {
   1576     for (comp.pragma_handlers.values()) |pragma| {
   1577         const maybe_func = switch (event) {
   1578             .before_preprocess => pragma.beforePreprocess,
   1579             .before_parse => pragma.beforeParse,
   1580             .after_parse => pragma.afterParse,
   1581         };
   1582         if (maybe_func) |func| func(pragma, comp);
   1583     }
   1584 }
   1585 
   1586 pub fn hasBuiltin(comp: *const Compilation, name: []const u8) bool {
   1587     if (std.mem.eql(u8, name, "__builtin_va_arg") or
   1588         std.mem.eql(u8, name, "__builtin_choose_expr") or
   1589         std.mem.eql(u8, name, "__builtin_bitoffsetof") or
   1590         std.mem.eql(u8, name, "__builtin_offsetof") or
   1591         std.mem.eql(u8, name, "__builtin_types_compatible_p")) return true;
   1592 
   1593     const builtin = Builtin.fromName(name) orelse return false;
   1594     return comp.hasBuiltinFunction(builtin);
   1595 }
   1596 
   1597 pub fn hasBuiltinFunction(comp: *const Compilation, builtin: Builtin) bool {
   1598     if (!target_util.builtinEnabled(comp.target, builtin.properties.target_set)) return false;
   1599 
   1600     switch (builtin.properties.language) {
   1601         .all_languages => return true,
   1602         .all_ms_languages => return comp.langopts.emulate == .msvc,
   1603         .gnu_lang, .all_gnu_languages => return comp.langopts.standard.isGNU(),
   1604     }
   1605 }
   1606 
   1607 pub fn locSlice(comp: *const Compilation, loc: Source.Location) []const u8 {
   1608     var tmp_tokenizer = Tokenizer{
   1609         .buf = comp.getSource(loc.id).buf,
   1610         .langopts = comp.langopts,
   1611         .index = loc.byte_offset,
   1612         .source = .generated,
   1613     };
   1614     const tok = tmp_tokenizer.next();
   1615     return tmp_tokenizer.buf[tok.start..tok.end];
   1616 }
   1617 
   1618 pub const CharUnitSize = enum(u32) {
   1619     @"1" = 1,
   1620     @"2" = 2,
   1621     @"4" = 4,
   1622 
   1623     pub fn Type(comptime self: CharUnitSize) type {
   1624         return switch (self) {
   1625             .@"1" => u8,
   1626             .@"2" => u16,
   1627             .@"4" => u32,
   1628         };
   1629     }
   1630 };
   1631 
   1632 pub const addDiagnostic = Diagnostics.add;
   1633 
   1634 test "addSourceFromReader" {
   1635     const Test = struct {
   1636         fn addSourceFromReader(str: []const u8, expected: []const u8, warning_count: u32, splices: []const u32) !void {
   1637             var comp = Compilation.init(std.testing.allocator, std.fs.cwd());
   1638             defer comp.deinit();
   1639 
   1640             var buf_reader = std.io.fixedBufferStream(str);
   1641             const source = try comp.addSourceFromReader(buf_reader.reader(), "path", .user);
   1642 
   1643             try std.testing.expectEqualStrings(expected, source.buf);
   1644             try std.testing.expectEqual(warning_count, @as(u32, @intCast(comp.diagnostics.list.items.len)));
   1645             try std.testing.expectEqualSlices(u32, splices, source.splice_locs);
   1646         }
   1647 
   1648         fn withAllocationFailures(allocator: std.mem.Allocator) !void {
   1649             var comp = Compilation.init(allocator, std.fs.cwd());
   1650             defer comp.deinit();
   1651 
   1652             _ = try comp.addSourceFromBuffer("path", "spliced\\\nbuffer\n");
   1653             _ = try comp.addSourceFromBuffer("path", "non-spliced buffer\n");
   1654         }
   1655     };
   1656     try Test.addSourceFromReader("ab\\\nc", "abc", 0, &.{2});
   1657     try Test.addSourceFromReader("ab\\\rc", "abc", 0, &.{2});
   1658     try Test.addSourceFromReader("ab\\\r\nc", "abc", 0, &.{2});
   1659     try Test.addSourceFromReader("ab\\ \nc", "abc", 1, &.{2});
   1660     try Test.addSourceFromReader("ab\\\t\nc", "abc", 1, &.{2});
   1661     try Test.addSourceFromReader("ab\\                     \t\nc", "abc", 1, &.{2});
   1662     try Test.addSourceFromReader("ab\\\r \nc", "ab \nc", 0, &.{2});
   1663     try Test.addSourceFromReader("ab\\\\\nc", "ab\\c", 0, &.{3});
   1664     try Test.addSourceFromReader("ab\\   \r\nc", "abc", 1, &.{2});
   1665     try Test.addSourceFromReader("ab\\ \\\nc", "ab\\ c", 0, &.{4});
   1666     try Test.addSourceFromReader("ab\\\r\\\nc", "abc", 0, &.{ 2, 2 });
   1667     try Test.addSourceFromReader("ab\\  \rc", "abc", 1, &.{2});
   1668     try Test.addSourceFromReader("ab\\", "ab\\", 0, &.{});
   1669     try Test.addSourceFromReader("ab\\\\", "ab\\\\", 0, &.{});
   1670     try Test.addSourceFromReader("ab\\ ", "ab\\ ", 0, &.{});
   1671     try Test.addSourceFromReader("ab\\\n", "ab", 0, &.{2});
   1672     try Test.addSourceFromReader("ab\\\r\n", "ab", 0, &.{2});
   1673     try Test.addSourceFromReader("ab\\\r", "ab", 0, &.{2});
   1674 
   1675     // carriage return normalization
   1676     try Test.addSourceFromReader("ab\r", "ab\n", 0, &.{});
   1677     try Test.addSourceFromReader("ab\r\r", "ab\n\n", 0, &.{});
   1678     try Test.addSourceFromReader("ab\r\r\n", "ab\n\n", 0, &.{});
   1679     try Test.addSourceFromReader("ab\r\r\n\r", "ab\n\n\n", 0, &.{});
   1680     try Test.addSourceFromReader("\r\\", "\n\\", 0, &.{});
   1681     try Test.addSourceFromReader("\\\r\\", "\\", 0, &.{0});
   1682 
   1683     try std.testing.checkAllAllocationFailures(std.testing.allocator, Test.withAllocationFailures, .{});
   1684 }
   1685 
   1686 test "addSourceFromReader - exhaustive check for carriage return elimination" {
   1687     const alphabet = [_]u8{ '\r', '\n', ' ', '\\', 'a' };
   1688     const alen = alphabet.len;
   1689     var buf: [alphabet.len]u8 = [1]u8{alphabet[0]} ** alen;
   1690 
   1691     var comp = Compilation.init(std.testing.allocator, std.fs.cwd());
   1692     defer comp.deinit();
   1693 
   1694     var source_count: u32 = 0;
   1695 
   1696     while (true) {
   1697         const source = try comp.addSourceFromBuffer(&buf, &buf);
   1698         source_count += 1;
   1699         try std.testing.expect(std.mem.indexOfScalar(u8, source.buf, '\r') == null);
   1700 
   1701         if (std.mem.allEqual(u8, &buf, alphabet[alen - 1])) break;
   1702 
   1703         var idx = std.mem.indexOfScalar(u8, &alphabet, buf[buf.len - 1]).?;
   1704         buf[buf.len - 1] = alphabet[(idx + 1) % alen];
   1705         var j = buf.len - 1;
   1706         while (j > 0) : (j -= 1) {
   1707             idx = std.mem.indexOfScalar(u8, &alphabet, buf[j - 1]).?;
   1708             if (buf[j] == alphabet[0]) buf[j - 1] = alphabet[(idx + 1) % alen] else break;
   1709         }
   1710     }
   1711     try std.testing.expect(source_count == std.math.powi(usize, alen, alen) catch unreachable);
   1712 }
   1713 
   1714 test "ignore BOM at beginning of file" {
   1715     const BOM = "\xEF\xBB\xBF";
   1716 
   1717     const Test = struct {
   1718         fn run(buf: []const u8) !void {
   1719             var comp = Compilation.init(std.testing.allocator, std.fs.cwd());
   1720             defer comp.deinit();
   1721 
   1722             var buf_reader = std.io.fixedBufferStream(buf);
   1723             const source = try comp.addSourceFromReader(buf_reader.reader(), "file.c", .user);
   1724             const expected_output = if (mem.startsWith(u8, buf, BOM)) buf[BOM.len..] else buf;
   1725             try std.testing.expectEqualStrings(expected_output, source.buf);
   1726         }
   1727     };
   1728 
   1729     try Test.run(BOM);
   1730     try Test.run(BOM ++ "x");
   1731     try Test.run("x" ++ BOM);
   1732     try Test.run(BOM ++ " ");
   1733     try Test.run(BOM ++ "\n");
   1734     try Test.run(BOM ++ "\\");
   1735 
   1736     try Test.run(BOM[0..1] ++ "x");
   1737     try Test.run(BOM[0..2] ++ "x");
   1738     try Test.run(BOM[1..] ++ "x");
   1739     try Test.run(BOM[2..] ++ "x");
   1740 }