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 }