diff --git a/src/aro/Compilation.zig b/src/aro/Compilation.zig index da3e655d..b3ac93d9 100644 --- a/src/aro/Compilation.zig +++ b/src/aro/Compilation.zig @@ -241,6 +241,12 @@ pub const SystemDefinesMode = enum { fn generateSystemDefines(comp: *Compilation, w: anytype) !void { const ptr_width = comp.target.ptrBitWidth(); + if (comp.langopts.gnuc_version > 0) { + try w.print("#define __GNUC__ {d}\n", .{comp.langopts.gnuc_version / 10_000}); + try w.print("#define __GNUC_MINOR__ {d}\n", .{comp.langopts.gnuc_version / 100 % 100}); + try w.print("#define __GNUC_PATCHLEVEL__ {d}\n", .{comp.langopts.gnuc_version % 100}); + } + // os macros switch (comp.target.os.tag) { .linux => try w.writeAll( diff --git a/src/aro/Driver.zig b/src/aro/Driver.zig index 1738b140..6927d705 100644 --- a/src/aro/Driver.zig +++ b/src/aro/Driver.zig @@ -12,6 +12,7 @@ const Preprocessor = @import("Preprocessor.zig"); const Source = @import("Source.zig"); const Toolchain = @import("Toolchain.zig"); const target_util = @import("target.zig"); +const GCCVersion = @import("Driver/GCCVersion.zig"); pub const Linker = enum { ld, @@ -95,6 +96,7 @@ pub const usage = \\ -fcolor-diagnostics Enable colors in diagnostics \\ -fno-color-diagnostics Disable colors in diagnostics \\ -fdeclspec Enable support for __declspec attributes + \\ -fgnuc-version= Controls value of __GNUC__ and related macros. Set to 0 or empty to disable them. \\ -fno-declspec Disable support for __declspec attributes \\ -ffp-eval-method=[source|double|extended] \\ Evaluation method to use for floating-point arithmetic @@ -180,6 +182,7 @@ pub fn parseArgs( var i: usize = 1; var comment_arg: []const u8 = ""; var hosted: ?bool = null; + var gnuc_version: []const u8 = "4.2.1"; // default value set by clang while (i < args.len) : (i += 1) { const arg = args[i]; if (mem.startsWith(u8, arg, "-") and arg.len > 1) { @@ -303,6 +306,10 @@ pub fn parseArgs( d.only_syntax = true; } else if (mem.startsWith(u8, arg, "-fno-syntax-only")) { d.only_syntax = false; + } else if (mem.eql(u8, arg, "-fgnuc-version=")) { + gnuc_version = "0"; + } else if (option(arg, "-fgnuc-version=")) |version| { + gnuc_version = version; } else if (mem.startsWith(u8, arg, "-isystem")) { var path = arg["-isystem".len..]; if (path.len == 0) { @@ -459,6 +466,11 @@ pub fn parseArgs( d.comp.target.os.tag = .freestanding; } } + const version = GCCVersion.parse(gnuc_version); + if (version.major == -1) { + return d.fatal("invalid value '{0s}' in '-fgnuc-version={0s}'", .{gnuc_version}); + } + d.comp.langopts.gnuc_version = version.toUnsigned(); return false; } diff --git a/src/aro/Driver/GCCVersion.zig b/src/aro/Driver/GCCVersion.zig index c4d6a65e..a9bdb470 100644 --- a/src/aro/Driver/GCCVersion.zig +++ b/src/aro/Driver/GCCVersion.zig @@ -98,6 +98,16 @@ pub fn order(a: GCCVersion, b: GCCVersion) Order { return .eq; } +/// Used for determining __GNUC__ macro values +/// This matches clang's logic for overflowing values +pub fn toUnsigned(self: GCCVersion) u32 { + var result: u32 = 0; + if (self.major > 0) result = @as(u32, @intCast(self.major)) *% 10_000; + if (self.minor > 0) result +%= @as(u32, @intCast(self.minor)) *% 100; + if (self.patch > 0) result +%= @as(u32, @intCast(self.patch)); + return result; +} + test parse { const versions = [10]GCCVersion{ parse("5"), diff --git a/src/aro/LangOpts.zig b/src/aro/LangOpts.zig index 1f5c5cd9..f2c15c59 100644 --- a/src/aro/LangOpts.zig +++ b/src/aro/LangOpts.zig @@ -135,6 +135,11 @@ preserve_comments: bool = false, /// Preserve comments in macros when preprocessing preserve_comments_in_macros: bool = false, +/// Used ONLY for generating __GNUC__ and related macros. Does not control the presence/absence of any features +/// Encoded as major * 10,000 + minor * 100 + patch +/// e.g. 4.2.1 == 40201 +gnuc_version: u32 = 0, + pub fn setStandard(self: *LangOpts, name: []const u8) error{InvalidStandard}!void { self.standard = Standard.NameMap.get(name) orelse return error.InvalidStandard; } diff --git a/test/cases/gnuc version default.c b/test/cases/gnuc version default.c new file mode 100644 index 00000000..0e56e752 --- /dev/null +++ b/test/cases/gnuc version default.c @@ -0,0 +1,3 @@ +_Static_assert(__GNUC__ == 4, ""); +_Static_assert(__GNUC_MINOR__ == 2, ""); +_Static_assert(__GNUC_PATCHLEVEL__ == 1, ""); diff --git a/test/cases/gnuc version empty.c b/test/cases/gnuc version empty.c new file mode 100644 index 00000000..72af5446 --- /dev/null +++ b/test/cases/gnuc version empty.c @@ -0,0 +1,5 @@ +//aro-args -fgnuc-version= + +#if defined(__GNUC__) || defined(__GNUC_MINOR__) || defined(__GNUC_PATCHLEVEL__) +#error "__GNUC__ macros should not be defined" +#endif diff --git a/test/cases/gnuc version override.c b/test/cases/gnuc version override.c new file mode 100644 index 00000000..e8f410c4 --- /dev/null +++ b/test/cases/gnuc version override.c @@ -0,0 +1,5 @@ +//aro-args -fgnuc-version=5.3.42 + +_Static_assert(__GNUC__ == 5, ""); +_Static_assert(__GNUC_MINOR__ == 3, ""); +_Static_assert(__GNUC_PATCHLEVEL__ == 42, ""); diff --git a/test/runner.zig b/test/runner.zig index 6afa376e..113c1217 100644 --- a/test/runner.zig +++ b/test/runner.zig @@ -15,6 +15,7 @@ fn addCommandLineArgs(comp: *aro.Compilation, file: aro.Source, macro_buf: anyty var only_preprocess = false; var line_markers: aro.Preprocessor.Linemarkers = .none; var system_defines: aro.Compilation.SystemDefinesMode = .include_system_defines; + comp.langopts.gnuc_version = 40201; // Set to clang default value since we do not call parseArgs if there are no args if (std.mem.startsWith(u8, file.buf, "//aro-args")) { var test_args = std.ArrayList([]const u8).init(comp.gpa); defer test_args.deinit();