Merge remote-tracking branch 'origin/master' into zld-incremental-2
This commit is contained in:
@@ -792,6 +792,7 @@ set(BUILD_ZIG1_ARGS
|
||||
--name zig1
|
||||
--zig-lib-dir "${CMAKE_SOURCE_DIR}/lib"
|
||||
"-femit-bin=${ZIG1_OBJECT}"
|
||||
-fcompiler-rt
|
||||
"${ZIG1_RELEASE_ARG}"
|
||||
"${ZIG1_SINGLE_THREADED_ARG}"
|
||||
-lc
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const std = @import("std");
|
||||
const builtin = std.builtin;
|
||||
const builtin = @import("builtin");
|
||||
const io = std.io;
|
||||
const fs = std.fs;
|
||||
const process = std.process;
|
||||
@@ -13,7 +13,7 @@ const Allocator = std.mem.Allocator;
|
||||
const max_doc_file_size = 10 * 1024 * 1024;
|
||||
|
||||
const exe_ext = @as(std.zig.CrossTarget, .{}).exeFileExt();
|
||||
const obj_ext = @as(std.zig.CrossTarget, .{}).oFileExt();
|
||||
const obj_ext = builtin.object_format.fileExt(builtin.cpu.arch);
|
||||
const tmp_dir_name = "docgen_tmp";
|
||||
const test_out_path = tmp_dir_name ++ fs.path.sep_str ++ "test" ++ exe_ext;
|
||||
|
||||
@@ -281,7 +281,7 @@ const Code = struct {
|
||||
name: []const u8,
|
||||
source_token: Token,
|
||||
is_inline: bool,
|
||||
mode: builtin.Mode,
|
||||
mode: std.builtin.Mode,
|
||||
link_objects: []const []const u8,
|
||||
target_str: ?[]const u8,
|
||||
link_libc: bool,
|
||||
@@ -531,7 +531,7 @@ fn genToc(allocator: *Allocator, tokenizer: *Tokenizer) !Toc {
|
||||
return parseError(tokenizer, code_kind_tok, "unrecognized code kind: {s}", .{code_kind_str});
|
||||
}
|
||||
|
||||
var mode: builtin.Mode = .Debug;
|
||||
var mode: std.builtin.Mode = .Debug;
|
||||
var link_objects = std.ArrayList([]const u8).init(allocator);
|
||||
defer link_objects.deinit();
|
||||
var target_str: ?[]const u8 = null;
|
||||
|
||||
@@ -5337,16 +5337,15 @@ test "implicit cast to comptime_int" {
|
||||
}
|
||||
{#code_end#}
|
||||
{#header_close#}
|
||||
{#header_open|Type Coercion: Arrays and Pointers#}
|
||||
{#code_begin|test|coerce_arrays_and_ptrs#}
|
||||
{#header_open|Type Coercion: Slices, Arrays and Pointers#}
|
||||
{#code_begin|test|coerce__slices_arrays_and_ptrs#}
|
||||
const std = @import("std");
|
||||
const expect = std.testing.expect;
|
||||
|
||||
// This cast exists primarily so that string literals can be
|
||||
// passed to functions that accept const slices. However
|
||||
// it is probably going to be removed from the language when
|
||||
// https://github.com/ziglang/zig/issues/265 is implemented.
|
||||
test "[N]T to []const T" {
|
||||
// You can assign constant pointers to arrays to a slice with
|
||||
// const modifier on the element type. Useful in particular for
|
||||
// String literals.
|
||||
test "*const [N]T to []const T" {
|
||||
var x1: []const u8 = "hello";
|
||||
var x2: []const u8 = &[5]u8{ 'h', 'e', 'l', 'l', 111 };
|
||||
try expect(std.mem.eql(u8, x1, x2));
|
||||
@@ -5356,7 +5355,7 @@ test "[N]T to []const T" {
|
||||
}
|
||||
|
||||
// Likewise, it works when the destination type is an error union.
|
||||
test "[N]T to E![]const T" {
|
||||
test "*const [N]T to E![]const T" {
|
||||
var x1: anyerror![]const u8 = "hello";
|
||||
var x2: anyerror![]const u8 = &[5]u8{ 'h', 'e', 'l', 'l', 111 };
|
||||
try expect(std.mem.eql(u8, try x1, try x2));
|
||||
@@ -5366,7 +5365,7 @@ test "[N]T to E![]const T" {
|
||||
}
|
||||
|
||||
// Likewise, it works when the destination type is an optional.
|
||||
test "[N]T to ?[]const T" {
|
||||
test "*const [N]T to ?[]const T" {
|
||||
var x1: ?[]const u8 = "hello";
|
||||
var x2: ?[]const u8 = &[5]u8{ 'h', 'e', 'l', 'l', 111 };
|
||||
try expect(std.mem.eql(u8, x1.?, x2.?));
|
||||
|
||||
@@ -93,7 +93,7 @@ pub const Fe = struct {
|
||||
return s;
|
||||
}
|
||||
|
||||
/// Map a 64-bit big endian string into a field element
|
||||
/// Map a 64 bytes big endian string into a field element
|
||||
pub fn fromBytes64(s: [64]u8) Fe {
|
||||
var fl: [32]u8 = undefined;
|
||||
var gl: [32]u8 = undefined;
|
||||
@@ -106,7 +106,7 @@ pub const Fe = struct {
|
||||
gl[31] &= 0x7f;
|
||||
var fe_f = fromBytes(fl);
|
||||
const fe_g = fromBytes(gl);
|
||||
fe_f.limbs[0] += (s[32] >> 7) * 19;
|
||||
fe_f.limbs[0] += (s[32] >> 7) * 19 + @as(u10, s[0] >> 7) * 722;
|
||||
i = 0;
|
||||
while (i < 5) : (i += 1) {
|
||||
fe_f.limbs[i] += 38 * fe_g.limbs[i];
|
||||
|
||||
@@ -339,19 +339,19 @@ pub const O_RDWR = 0o2;
|
||||
pub const kernel_rwf = u32;
|
||||
|
||||
/// high priority request, poll if possible
|
||||
pub const RWF_HIPRI = kernel_rwf(0x00000001);
|
||||
pub const RWF_HIPRI: kernel_rwf = 0x00000001;
|
||||
|
||||
/// per-IO O_DSYNC
|
||||
pub const RWF_DSYNC = kernel_rwf(0x00000002);
|
||||
pub const RWF_DSYNC: kernel_rwf = 0x00000002;
|
||||
|
||||
/// per-IO O_SYNC
|
||||
pub const RWF_SYNC = kernel_rwf(0x00000004);
|
||||
pub const RWF_SYNC: kernel_rwf = 0x00000004;
|
||||
|
||||
/// per-IO, return -EAGAIN if operation would block
|
||||
pub const RWF_NOWAIT = kernel_rwf(0x00000008);
|
||||
pub const RWF_NOWAIT: kernel_rwf = 0x00000008;
|
||||
|
||||
/// per-IO O_APPEND
|
||||
pub const RWF_APPEND = kernel_rwf(0x00000010);
|
||||
pub const RWF_APPEND: kernel_rwf = 0x00000010;
|
||||
|
||||
pub const SEEK_SET = 0;
|
||||
pub const SEEK_CUR = 1;
|
||||
|
||||
@@ -1336,7 +1336,7 @@ pub extern "user32" fn AdjustWindowRectEx(lpRect: *RECT, dwStyle: DWORD, bMenu:
|
||||
pub fn adjustWindowRectEx(lpRect: *RECT, dwStyle: u32, bMenu: bool, dwExStyle: u32) !void {
|
||||
assert(dwStyle & WS_OVERLAPPED == 0);
|
||||
|
||||
if (AdjustWindowRectEx(lpRect, dwStyle, bMenu, dwExStyle) == 0) {
|
||||
if (AdjustWindowRectEx(lpRect, dwStyle, @boolToInt(bMenu), dwExStyle) == 0) {
|
||||
switch (GetLastError()) {
|
||||
.INVALID_PARAMETER => unreachable,
|
||||
else => |err| return windows.unexpectedError(err),
|
||||
|
||||
@@ -549,16 +549,36 @@ pub const Target = struct {
|
||||
};
|
||||
|
||||
pub const ObjectFormat = enum {
|
||||
/// Common Object File Format (Windows)
|
||||
coff,
|
||||
pe,
|
||||
/// Executable and Linking Format
|
||||
elf,
|
||||
/// macOS relocatables
|
||||
macho,
|
||||
/// WebAssembly
|
||||
wasm,
|
||||
/// C source code
|
||||
c,
|
||||
/// Standard, Portable Intermediate Representation V
|
||||
spirv,
|
||||
/// Intel IHEX
|
||||
hex,
|
||||
/// Machine code with no metadata.
|
||||
raw,
|
||||
/// Plan 9 from Bell Labs
|
||||
plan9,
|
||||
|
||||
pub fn fileExt(of: ObjectFormat, cpu_arch: Cpu.Arch) [:0]const u8 {
|
||||
return switch (of) {
|
||||
.coff => ".obj",
|
||||
.elf, .macho, .wasm => ".o",
|
||||
.c => ".c",
|
||||
.spirv => ".spv",
|
||||
.hex => ".ihex",
|
||||
.raw => ".bin",
|
||||
.plan9 => plan9Ext(cpu_arch),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const SubSystem = enum {
|
||||
@@ -1290,30 +1310,16 @@ pub const Target = struct {
|
||||
return linuxTripleSimple(allocator, self.cpu.arch, self.os.tag, self.abi);
|
||||
}
|
||||
|
||||
pub fn oFileExt_os_abi(os_tag: Os.Tag, abi: Abi) [:0]const u8 {
|
||||
if (abi == .msvc) {
|
||||
return ".obj";
|
||||
}
|
||||
switch (os_tag) {
|
||||
.windows, .uefi => return ".obj",
|
||||
else => return ".o",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn oFileExt(self: Target) [:0]const u8 {
|
||||
return oFileExt_os_abi(self.os.tag, self.abi);
|
||||
}
|
||||
|
||||
pub fn exeFileExtSimple(cpu_arch: Cpu.Arch, os_tag: Os.Tag) [:0]const u8 {
|
||||
switch (os_tag) {
|
||||
.windows => return ".exe",
|
||||
.uefi => return ".efi",
|
||||
else => if (cpu_arch.isWasm()) {
|
||||
return ".wasm";
|
||||
} else {
|
||||
return "";
|
||||
return switch (os_tag) {
|
||||
.windows => ".exe",
|
||||
.uefi => ".efi",
|
||||
.plan9 => plan9Ext(cpu_arch),
|
||||
else => switch (cpu_arch) {
|
||||
.wasm32, .wasm64 => ".wasm",
|
||||
else => "",
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn exeFileExt(self: Target) [:0]const u8 {
|
||||
@@ -1353,20 +1359,16 @@ pub const Target = struct {
|
||||
}
|
||||
|
||||
pub fn getObjectFormatSimple(os_tag: Os.Tag, cpu_arch: Cpu.Arch) ObjectFormat {
|
||||
if (os_tag == .windows or os_tag == .uefi) {
|
||||
return .coff;
|
||||
} else if (os_tag.isDarwin()) {
|
||||
return .macho;
|
||||
}
|
||||
if (cpu_arch.isWasm()) {
|
||||
return .wasm;
|
||||
}
|
||||
if (cpu_arch.isSPIRV()) {
|
||||
return .spirv;
|
||||
}
|
||||
if (os_tag == .plan9)
|
||||
return .plan9;
|
||||
return .elf;
|
||||
return switch (os_tag) {
|
||||
.windows, .uefi => .coff,
|
||||
.ios, .macos, .watchos, .tvos => .macho,
|
||||
.plan9 => .plan9,
|
||||
else => return switch (cpu_arch) {
|
||||
.wasm32, .wasm64 => .wasm,
|
||||
.spirv32, .spirv64 => .spirv,
|
||||
else => .elf,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getObjectFormat(self: Target) ObjectFormat {
|
||||
@@ -1677,6 +1679,30 @@ pub const Target = struct {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// 0c spim little-endian MIPS 3000 family
|
||||
/// 1c 68000 Motorola MC68000
|
||||
/// 2c 68020 Motorola MC68020
|
||||
/// 5c arm little-endian ARM
|
||||
/// 6c amd64 AMD64 and compatibles (e.g., Intel EM64T)
|
||||
/// 7c arm64 ARM64 (ARMv8)
|
||||
/// 8c 386 Intel i386, i486, Pentium, etc.
|
||||
/// kc sparc Sun SPARC
|
||||
/// qc power Power PC
|
||||
/// vc mips big-endian MIPS 3000 family
|
||||
pub fn plan9Ext(cpu_arch: Cpu.Arch) [:0]const u8 {
|
||||
return switch (cpu_arch) {
|
||||
.arm => ".5",
|
||||
.x86_64 => ".6",
|
||||
.aarch64 => ".7",
|
||||
.i386 => ".8",
|
||||
.sparc => ".k",
|
||||
.powerpc, .powerpcle => ".q",
|
||||
.mips, .mipsel => ".v",
|
||||
// ISAs without designated characters get 'X' for lack of a better option.
|
||||
else => ".X",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
test {
|
||||
|
||||
@@ -108,8 +108,9 @@ pub const BinNameOptions = struct {
|
||||
pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 {
|
||||
const root_name = options.root_name;
|
||||
const target = options.target;
|
||||
switch (options.object_format orelse target.getObjectFormat()) {
|
||||
.coff, .pe => switch (options.output_mode) {
|
||||
const ofmt = options.object_format orelse target.getObjectFormat();
|
||||
switch (ofmt) {
|
||||
.coff => switch (options.output_mode) {
|
||||
.Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.exeFileExt() }),
|
||||
.Lib => {
|
||||
const suffix = switch (options.link_mode orelse .Static) {
|
||||
@@ -118,7 +119,7 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro
|
||||
};
|
||||
return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, suffix });
|
||||
},
|
||||
.Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
|
||||
.Obj => return std.fmt.allocPrint(allocator, "{s}.obj", .{root_name}),
|
||||
},
|
||||
.elf => switch (options.output_mode) {
|
||||
.Exe => return allocator.dupe(u8, root_name),
|
||||
@@ -140,7 +141,7 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro
|
||||
},
|
||||
}
|
||||
},
|
||||
.Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
|
||||
.Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}),
|
||||
},
|
||||
.macho => switch (options.output_mode) {
|
||||
.Exe => return allocator.dupe(u8, root_name),
|
||||
@@ -163,7 +164,7 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro
|
||||
}
|
||||
return std.fmt.allocPrint(allocator, "{s}{s}{s}", .{ target.libPrefix(), root_name, suffix });
|
||||
},
|
||||
.Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
|
||||
.Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}),
|
||||
},
|
||||
.wasm => switch (options.output_mode) {
|
||||
.Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.exeFileExt() }),
|
||||
@@ -175,36 +176,15 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro
|
||||
.Dynamic => return std.fmt.allocPrint(allocator, "{s}.wasm", .{root_name}),
|
||||
}
|
||||
},
|
||||
.Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.oFileExt() }),
|
||||
.Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}),
|
||||
},
|
||||
.c => return std.fmt.allocPrint(allocator, "{s}.c", .{root_name}),
|
||||
.spirv => return std.fmt.allocPrint(allocator, "{s}.spv", .{root_name}),
|
||||
.hex => return std.fmt.allocPrint(allocator, "{s}.ihex", .{root_name}),
|
||||
.raw => return std.fmt.allocPrint(allocator, "{s}.bin", .{root_name}),
|
||||
.plan9 => {
|
||||
// copied from 2c(1)
|
||||
// 0c spim little-endian MIPS 3000 family
|
||||
// 1c 68000 Motorola MC68000
|
||||
// 2c 68020 Motorola MC68020
|
||||
// 5c arm little-endian ARM
|
||||
// 6c amd64 AMD64 and compatibles (e.g., Intel EM64T)
|
||||
// 7c arm64 ARM64 (ARMv8)
|
||||
// 8c 386 Intel i386, i486, Pentium, etc.
|
||||
// kc sparc Sun SPARC
|
||||
// qc power Power PC
|
||||
// vc mips big-endian MIPS 3000 family
|
||||
const char: u8 = switch (target.cpu.arch) {
|
||||
.arm => '5',
|
||||
.x86_64 => '6',
|
||||
.aarch64 => '7',
|
||||
.i386 => '8',
|
||||
.sparc => 'k',
|
||||
.powerpc, .powerpcle => 'q',
|
||||
.mips, .mipsel => 'v',
|
||||
else => 'X', // this arch does not have a char or maybe was not ported to plan9 so we just use X
|
||||
};
|
||||
return std.fmt.allocPrint(allocator, "{s}.{c}", .{ root_name, char });
|
||||
},
|
||||
.plan9 => return std.fmt.allocPrint(allocator, "{s}{s}", .{
|
||||
root_name, ofmt.fileExt(target.cpu.arch),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -350,3 +350,135 @@ test "Flexible Array Type" {
|
||||
try testing.expectEqual(FlexibleArrayType(*volatile Container, c_int), [*c]volatile c_int);
|
||||
try testing.expectEqual(FlexibleArrayType(*const volatile Container, c_int), [*c]const volatile c_int);
|
||||
}
|
||||
|
||||
pub const Macros = struct {
|
||||
pub fn U_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_uint, n, .decimal)) {
|
||||
return promoteIntLiteral(c_uint, n, .decimal);
|
||||
}
|
||||
|
||||
fn L_SUFFIX_ReturnType(comptime number: anytype) type {
|
||||
switch (@TypeOf(number)) {
|
||||
comptime_int => return @TypeOf(promoteIntLiteral(c_long, number, .decimal)),
|
||||
comptime_float => return c_longdouble,
|
||||
else => @compileError("Invalid value for L suffix"),
|
||||
}
|
||||
}
|
||||
pub fn L_SUFFIX(comptime number: anytype) L_SUFFIX_ReturnType(number) {
|
||||
switch (@TypeOf(number)) {
|
||||
comptime_int => return promoteIntLiteral(c_long, number, .decimal),
|
||||
comptime_float => @compileError("TODO: c_longdouble initialization from comptime_float not supported"),
|
||||
else => @compileError("Invalid value for L suffix"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn UL_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_ulong, n, .decimal)) {
|
||||
return promoteIntLiteral(c_ulong, n, .decimal);
|
||||
}
|
||||
|
||||
pub fn LL_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_longlong, n, .decimal)) {
|
||||
return promoteIntLiteral(c_longlong, n, .decimal);
|
||||
}
|
||||
|
||||
pub fn ULL_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_ulonglong, n, .decimal)) {
|
||||
return promoteIntLiteral(c_ulonglong, n, .decimal);
|
||||
}
|
||||
|
||||
pub fn F_SUFFIX(comptime f: comptime_float) f32 {
|
||||
return @as(f32, f);
|
||||
}
|
||||
|
||||
pub fn WL_CONTAINER_OF(ptr: anytype, sample: anytype, comptime member: []const u8) @TypeOf(sample) {
|
||||
return @fieldParentPtr(@TypeOf(sample.*), member, ptr);
|
||||
}
|
||||
|
||||
/// A 2-argument function-like macro defined as #define FOO(A, B) (A)(B)
|
||||
/// could be either: cast B to A, or call A with the value B.
|
||||
pub fn CAST_OR_CALL(a: anytype, b: anytype) switch (@typeInfo(@TypeOf(a))) {
|
||||
.Type => a,
|
||||
.Fn => |fn_info| fn_info.return_type orelse void,
|
||||
else => |info| @compileError("Unexpected argument type: " ++ @tagName(info)),
|
||||
} {
|
||||
switch (@typeInfo(@TypeOf(a))) {
|
||||
.Type => return cast(a, b),
|
||||
.Fn => return a(b),
|
||||
else => unreachable, // return type will be a compile error otherwise
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
test "Macro suffix functions" {
|
||||
try testing.expect(@TypeOf(Macros.F_SUFFIX(1)) == f32);
|
||||
|
||||
try testing.expect(@TypeOf(Macros.U_SUFFIX(1)) == c_uint);
|
||||
if (math.maxInt(c_ulong) > math.maxInt(c_uint)) {
|
||||
try testing.expect(@TypeOf(Macros.U_SUFFIX(math.maxInt(c_uint) + 1)) == c_ulong);
|
||||
}
|
||||
if (math.maxInt(c_ulonglong) > math.maxInt(c_ulong)) {
|
||||
try testing.expect(@TypeOf(Macros.U_SUFFIX(math.maxInt(c_ulong) + 1)) == c_ulonglong);
|
||||
}
|
||||
|
||||
try testing.expect(@TypeOf(Macros.L_SUFFIX(1)) == c_long);
|
||||
if (math.maxInt(c_long) > math.maxInt(c_int)) {
|
||||
try testing.expect(@TypeOf(Macros.L_SUFFIX(math.maxInt(c_int) + 1)) == c_long);
|
||||
}
|
||||
if (math.maxInt(c_longlong) > math.maxInt(c_long)) {
|
||||
try testing.expect(@TypeOf(Macros.L_SUFFIX(math.maxInt(c_long) + 1)) == c_longlong);
|
||||
}
|
||||
|
||||
try testing.expect(@TypeOf(Macros.UL_SUFFIX(1)) == c_ulong);
|
||||
if (math.maxInt(c_ulonglong) > math.maxInt(c_ulong)) {
|
||||
try testing.expect(@TypeOf(Macros.UL_SUFFIX(math.maxInt(c_ulong) + 1)) == c_ulonglong);
|
||||
}
|
||||
|
||||
try testing.expect(@TypeOf(Macros.LL_SUFFIX(1)) == c_longlong);
|
||||
try testing.expect(@TypeOf(Macros.ULL_SUFFIX(1)) == c_ulonglong);
|
||||
}
|
||||
|
||||
test "WL_CONTAINER_OF" {
|
||||
const S = struct {
|
||||
a: u32 = 0,
|
||||
b: u32 = 0,
|
||||
};
|
||||
var x = S{};
|
||||
var y = S{};
|
||||
var ptr = Macros.WL_CONTAINER_OF(&x.b, &y, "b");
|
||||
try testing.expectEqual(&x, ptr);
|
||||
}
|
||||
|
||||
test "CAST_OR_CALL casting" {
|
||||
var arg = @as(c_int, 1000);
|
||||
var casted = Macros.CAST_OR_CALL(u8, arg);
|
||||
try testing.expectEqual(cast(u8, arg), casted);
|
||||
|
||||
const S = struct {
|
||||
x: u32 = 0,
|
||||
};
|
||||
var s = S{};
|
||||
var casted_ptr = Macros.CAST_OR_CALL(*u8, &s);
|
||||
try testing.expectEqual(cast(*u8, &s), casted_ptr);
|
||||
}
|
||||
|
||||
test "CAST_OR_CALL calling" {
|
||||
const Helper = struct {
|
||||
var last_val: bool = false;
|
||||
fn returnsVoid(val: bool) void {
|
||||
last_val = val;
|
||||
}
|
||||
fn returnsBool(f: f32) bool {
|
||||
return f > 0;
|
||||
}
|
||||
fn identity(self: c_uint) c_uint {
|
||||
return self;
|
||||
}
|
||||
};
|
||||
|
||||
Macros.CAST_OR_CALL(Helper.returnsVoid, true);
|
||||
try testing.expectEqual(true, Helper.last_val);
|
||||
Macros.CAST_OR_CALL(Helper.returnsVoid, false);
|
||||
try testing.expectEqual(false, Helper.last_val);
|
||||
|
||||
try testing.expectEqual(Helper.returnsBool(1), Macros.CAST_OR_CALL(Helper.returnsBool, @as(f32, 1)));
|
||||
try testing.expectEqual(Helper.returnsBool(-1), Macros.CAST_OR_CALL(Helper.returnsBool, @as(f32, -1)));
|
||||
|
||||
try testing.expectEqual(Helper.identity(@as(c_uint, 100)), Macros.CAST_OR_CALL(Helper.identity, @as(c_uint, 100)));
|
||||
}
|
||||
|
||||
@@ -473,10 +473,6 @@ pub const CrossTarget = struct {
|
||||
return self.getOsTag() == .windows;
|
||||
}
|
||||
|
||||
pub fn oFileExt(self: CrossTarget) [:0]const u8 {
|
||||
return Target.oFileExt_os_abi(self.getOsTag(), self.getAbi());
|
||||
}
|
||||
|
||||
pub fn exeFileExt(self: CrossTarget) [:0]const u8 {
|
||||
return Target.exeFileExtSimple(self.getCpuArch(), self.getOsTag());
|
||||
}
|
||||
|
||||
@@ -5009,6 +5009,7 @@ fn ifExpr(
|
||||
const token_name_str = tree.tokenSlice(token_name_index);
|
||||
if (mem.eql(u8, "_", token_name_str))
|
||||
break :s &then_scope.base;
|
||||
try astgen.detectLocalShadowing(&then_scope.base, ident_name, token_name_index);
|
||||
payload_val_scope = .{
|
||||
.parent = &then_scope.base,
|
||||
.gen_zir = &then_scope,
|
||||
@@ -5031,6 +5032,7 @@ fn ifExpr(
|
||||
break :s &then_scope.base;
|
||||
const payload_inst = try then_scope.addUnNode(tag, cond.inst, node);
|
||||
const ident_name = try astgen.identAsString(ident_token);
|
||||
try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token);
|
||||
payload_val_scope = .{
|
||||
.parent = &then_scope.base,
|
||||
.gen_zir = &then_scope,
|
||||
@@ -5072,6 +5074,7 @@ fn ifExpr(
|
||||
const error_token_str = tree.tokenSlice(error_token);
|
||||
if (mem.eql(u8, "_", error_token_str))
|
||||
break :s &else_scope.base;
|
||||
try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token);
|
||||
payload_val_scope = .{
|
||||
.parent = &else_scope.base,
|
||||
.gen_zir = &else_scope,
|
||||
@@ -5265,7 +5268,9 @@ fn whileExpr(
|
||||
const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;
|
||||
if (mem.eql(u8, "_", tree.tokenSlice(ident_token)))
|
||||
break :s &then_scope.base;
|
||||
const ident_name = try astgen.identAsString(payload_token + @boolToInt(payload_is_ref));
|
||||
const payload_name_loc = payload_token + @boolToInt(payload_is_ref);
|
||||
const ident_name = try astgen.identAsString(payload_name_loc);
|
||||
try astgen.detectLocalShadowing(&then_scope.base, ident_name, payload_name_loc);
|
||||
payload_val_scope = .{
|
||||
.parent = &then_scope.base,
|
||||
.gen_zir = &then_scope,
|
||||
@@ -5288,6 +5293,7 @@ fn whileExpr(
|
||||
const ident_name = try astgen.identAsString(ident_token);
|
||||
if (mem.eql(u8, "_", tree.tokenSlice(ident_token)))
|
||||
break :s &then_scope.base;
|
||||
try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token);
|
||||
payload_val_scope = .{
|
||||
.parent = &then_scope.base,
|
||||
.gen_zir = &then_scope,
|
||||
@@ -5345,6 +5351,7 @@ fn whileExpr(
|
||||
const ident_name = try astgen.identAsString(error_token);
|
||||
if (mem.eql(u8, tree.tokenSlice(error_token), "_"))
|
||||
break :s &else_scope.base;
|
||||
try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token);
|
||||
payload_val_scope = .{
|
||||
.parent = &else_scope.base,
|
||||
.gen_zir = &else_scope,
|
||||
@@ -5484,6 +5491,7 @@ fn forExpr(
|
||||
const name_str_index = try astgen.identAsString(ident);
|
||||
const tag: Zir.Inst.Tag = if (is_ptr) .elem_ptr else .elem_val;
|
||||
const payload_inst = try then_scope.addBin(tag, array_ptr, index);
|
||||
try astgen.detectLocalShadowing(&then_scope.base, name_str_index, ident);
|
||||
payload_val_scope = .{
|
||||
.parent = &then_scope.base,
|
||||
.gen_zir = &then_scope,
|
||||
@@ -5507,6 +5515,7 @@ fn forExpr(
|
||||
return astgen.failTok(index_token, "discard of index capture; omit it instead", .{});
|
||||
}
|
||||
const index_name = try astgen.identAsString(index_token);
|
||||
try astgen.detectLocalShadowing(payload_sub_scope, index_name, index_token);
|
||||
index_scope = .{
|
||||
.parent = payload_sub_scope,
|
||||
.gen_zir = &then_scope,
|
||||
|
||||
@@ -143,6 +143,7 @@ debug_compiler_runtime_libs: bool,
|
||||
|
||||
emit_asm: ?EmitLoc,
|
||||
emit_llvm_ir: ?EmitLoc,
|
||||
emit_llvm_bc: ?EmitLoc,
|
||||
emit_analysis: ?EmitLoc,
|
||||
emit_docs: ?EmitLoc,
|
||||
|
||||
@@ -586,6 +587,17 @@ pub const Directory = struct {
|
||||
return std.fs.path.join(allocator, paths);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn joinZ(self: Directory, allocator: *Allocator, paths: []const []const u8) ![:0]u8 {
|
||||
if (self.path) |p| {
|
||||
// TODO clean way to do this with only 1 allocation
|
||||
const part2 = try std.fs.path.join(allocator, paths);
|
||||
defer allocator.free(part2);
|
||||
return std.fs.path.joinZ(allocator, &[_][]const u8{ p, part2 });
|
||||
} else {
|
||||
return std.fs.path.joinZ(allocator, paths);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const EmitLoc = struct {
|
||||
@@ -623,6 +635,8 @@ pub const InitOptions = struct {
|
||||
emit_asm: ?EmitLoc = null,
|
||||
/// `null` means to not emit LLVM IR.
|
||||
emit_llvm_ir: ?EmitLoc = null,
|
||||
/// `null` means to not emit LLVM module bitcode.
|
||||
emit_llvm_bc: ?EmitLoc = null,
|
||||
/// `null` means to not emit semantic analysis JSON.
|
||||
emit_analysis: ?EmitLoc = null,
|
||||
/// `null` means to not emit docs.
|
||||
@@ -812,6 +826,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
const ofmt = options.object_format orelse options.target.getObjectFormat();
|
||||
|
||||
const use_stage1 = options.use_stage1 orelse blk: {
|
||||
// Even though we may have no Zig code to compile (depending on `options.root_pkg`),
|
||||
// we may need to use stage1 for building compiler-rt and other dependencies.
|
||||
|
||||
if (build_options.omit_stage2)
|
||||
break :blk true;
|
||||
if (options.use_llvm) |use_llvm| {
|
||||
@@ -819,6 +836,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
break :blk false;
|
||||
}
|
||||
}
|
||||
|
||||
break :blk build_options.is_stage1;
|
||||
};
|
||||
|
||||
@@ -835,6 +853,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
if (ofmt == .c)
|
||||
break :blk false;
|
||||
|
||||
// If emitting to LLVM bitcode object format, must use LLVM backend.
|
||||
if (options.emit_llvm_ir != null or options.emit_llvm_bc != null)
|
||||
break :blk true;
|
||||
|
||||
// The stage1 compiler depends on the stage1 C++ LLVM backend
|
||||
// to compile zig code.
|
||||
if (use_stage1)
|
||||
@@ -853,6 +875,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
if (options.machine_code_model != .default) {
|
||||
return error.MachineCodeModelNotSupportedWithoutLlvm;
|
||||
}
|
||||
if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) {
|
||||
return error.EmittingLlvmModuleRequiresUsingLlvmBackend;
|
||||
}
|
||||
}
|
||||
|
||||
const tsan = options.want_tsan orelse false;
|
||||
@@ -996,7 +1021,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
break :blk lm;
|
||||
} else default_link_mode;
|
||||
|
||||
const dll_export_fns = if (options.dll_export_fns) |explicit| explicit else is_dyn_lib;
|
||||
const dll_export_fns = if (options.dll_export_fns) |explicit| explicit else is_dyn_lib or options.rdynamic;
|
||||
|
||||
const libc_dirs = try detectLibCIncludeDirs(
|
||||
arena,
|
||||
@@ -1386,6 +1411,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
.bin_file = bin_file,
|
||||
.emit_asm = options.emit_asm,
|
||||
.emit_llvm_ir = options.emit_llvm_ir,
|
||||
.emit_llvm_bc = options.emit_llvm_bc,
|
||||
.emit_analysis = options.emit_analysis,
|
||||
.emit_docs = options.emit_docs,
|
||||
.work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa),
|
||||
@@ -1518,24 +1544,19 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
}
|
||||
|
||||
// The `use_stage1` condition is here only because stage2 cannot yet build compiler-rt.
|
||||
// Once it is capable this condition should be removed.
|
||||
// Once it is capable this condition should be removed. When removing this condition,
|
||||
// also test the use case of `build-obj -fcompiler-rt` with the self-hosted compiler
|
||||
// and make sure the compiler-rt symbols are emitted. Currently this is hooked up for
|
||||
// stage1 but not stage2.
|
||||
if (comp.bin_file.options.use_stage1) {
|
||||
if (comp.bin_file.options.include_compiler_rt) {
|
||||
if (is_exe_or_dyn_lib) {
|
||||
try comp.work_queue.writeItem(.{ .compiler_rt_lib = {} });
|
||||
} else {
|
||||
} else if (options.output_mode != .Obj) {
|
||||
// If build-obj with -fcompiler-rt is requested, that is handled specially
|
||||
// elsewhere. In this case we are making a static library, so we ask
|
||||
// for a compiler-rt object to put in it.
|
||||
try comp.work_queue.writeItem(.{ .compiler_rt_obj = {} });
|
||||
if (comp.bin_file.options.object_format != .elf and
|
||||
comp.bin_file.options.output_mode == .Obj)
|
||||
{
|
||||
// For ELF we can rely on using -r to link multiple objects together into one,
|
||||
// but to truly support `build-obj -fcompiler-rt` will require virtually
|
||||
// injecting `_ = @import("compiler_rt.zig")` into the root source file of
|
||||
// the compilation.
|
||||
fatal("Embedding compiler-rt into {s} objects is not yet implemented.", .{
|
||||
@tagName(comp.bin_file.options.object_format),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (needs_c_symbols) {
|
||||
@@ -2733,7 +2754,10 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
|
||||
comp.bin_file.options.root_name
|
||||
else
|
||||
c_source_basename[0 .. c_source_basename.len - std.fs.path.extension(c_source_basename).len];
|
||||
const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, comp.getTarget().oFileExt() });
|
||||
const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{
|
||||
o_basename_noext,
|
||||
comp.bin_file.options.object_format.fileExt(comp.bin_file.options.target.cpu.arch),
|
||||
});
|
||||
|
||||
const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: {
|
||||
var argv = std.ArrayList([]const u8).init(comp.gpa);
|
||||
@@ -3028,7 +3052,7 @@ pub fn addCCArgs(
|
||||
if (!comp.bin_file.options.strip) {
|
||||
try argv.append("-g");
|
||||
switch (comp.bin_file.options.object_format) {
|
||||
.coff, .pe => try argv.append("-gcodeview"),
|
||||
.coff => try argv.append("-gcodeview"),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
@@ -3954,6 +3978,16 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
|
||||
const id_symlink_basename = "stage1.id";
|
||||
const libs_txt_basename = "libs.txt";
|
||||
|
||||
// The include_compiler_rt stored in the bin file options here means that we need
|
||||
// compiler-rt symbols *somehow*. However, in the context of using the stage1 backend
|
||||
// we need to tell stage1 to include compiler-rt only if stage1 is the place that
|
||||
// needs to provide those symbols. Otherwise the stage2 infrastructure will take care
|
||||
// of it in the linker, by putting compiler_rt.o into a static archive, or linking
|
||||
// compiler_rt.a against an executable. In other words we only want to set this flag
|
||||
// for stage1 if we are using build-obj.
|
||||
const include_compiler_rt = comp.bin_file.options.output_mode == .Obj and
|
||||
comp.bin_file.options.include_compiler_rt;
|
||||
|
||||
// We are about to obtain this lock, so here we give other processes a chance first.
|
||||
comp.releaseStage1Lock();
|
||||
|
||||
@@ -3975,6 +4009,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
|
||||
man.hash.add(target.os.getVersionRange());
|
||||
man.hash.add(comp.bin_file.options.dll_export_fns);
|
||||
man.hash.add(comp.bin_file.options.function_sections);
|
||||
man.hash.add(include_compiler_rt);
|
||||
man.hash.add(comp.bin_file.options.is_test);
|
||||
man.hash.add(comp.bin_file.options.emit != null);
|
||||
man.hash.add(mod.emit_h != null);
|
||||
@@ -3983,6 +4018,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
|
||||
}
|
||||
man.hash.addOptionalEmitLoc(comp.emit_asm);
|
||||
man.hash.addOptionalEmitLoc(comp.emit_llvm_ir);
|
||||
man.hash.addOptionalEmitLoc(comp.emit_llvm_bc);
|
||||
man.hash.addOptionalEmitLoc(comp.emit_analysis);
|
||||
man.hash.addOptionalEmitLoc(comp.emit_docs);
|
||||
man.hash.add(comp.test_evented_io);
|
||||
@@ -4088,13 +4124,14 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
|
||||
) orelse return error.OutOfMemory;
|
||||
|
||||
const emit_bin_path = if (comp.bin_file.options.emit != null) blk: {
|
||||
const bin_basename = try std.zig.binNameAlloc(arena, .{
|
||||
const obj_basename = try std.zig.binNameAlloc(arena, .{
|
||||
.root_name = comp.bin_file.options.root_name,
|
||||
.target = target,
|
||||
.output_mode = .Obj,
|
||||
});
|
||||
break :blk try directory.join(arena, &[_][]const u8{bin_basename});
|
||||
break :blk try directory.join(arena, &[_][]const u8{obj_basename});
|
||||
} else "";
|
||||
|
||||
if (mod.emit_h != null) {
|
||||
log.warn("-femit-h is not available in the stage1 backend; no .h file will be produced", .{});
|
||||
}
|
||||
@@ -4102,6 +4139,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
|
||||
const emit_h_path = try stage1LocPath(arena, emit_h_loc, directory);
|
||||
const emit_asm_path = try stage1LocPath(arena, comp.emit_asm, directory);
|
||||
const emit_llvm_ir_path = try stage1LocPath(arena, comp.emit_llvm_ir, directory);
|
||||
const emit_llvm_bc_path = try stage1LocPath(arena, comp.emit_llvm_bc, directory);
|
||||
const emit_analysis_path = try stage1LocPath(arena, comp.emit_analysis, directory);
|
||||
const emit_docs_path = try stage1LocPath(arena, comp.emit_docs, directory);
|
||||
const stage1_pkg = try createStage1Pkg(arena, "root", mod.root_pkg, null);
|
||||
@@ -4122,6 +4160,8 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
|
||||
.emit_asm_len = emit_asm_path.len,
|
||||
.emit_llvm_ir_ptr = emit_llvm_ir_path.ptr,
|
||||
.emit_llvm_ir_len = emit_llvm_ir_path.len,
|
||||
.emit_bitcode_ptr = emit_llvm_bc_path.ptr,
|
||||
.emit_bitcode_len = emit_llvm_bc_path.len,
|
||||
.emit_analysis_json_ptr = emit_analysis_path.ptr,
|
||||
.emit_analysis_json_len = emit_analysis_path.len,
|
||||
.emit_docs_ptr = emit_docs_path.ptr,
|
||||
@@ -4150,6 +4190,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
|
||||
.valgrind_enabled = comp.bin_file.options.valgrind,
|
||||
.tsan_enabled = comp.bin_file.options.tsan,
|
||||
.function_sections = comp.bin_file.options.function_sections,
|
||||
.include_compiler_rt = include_compiler_rt,
|
||||
.enable_stack_probing = comp.bin_file.options.stack_check,
|
||||
.red_zone = comp.bin_file.options.red_zone,
|
||||
.enable_time_report = comp.time_report,
|
||||
|
||||
@@ -72,9 +72,9 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 {
|
||||
.renderscript32 => "renderscript32",
|
||||
.renderscript64 => "renderscript64",
|
||||
.ve => "ve",
|
||||
.spu_2 => return error.LLVMBackendDoesNotSupportSPUMarkII,
|
||||
.spirv32 => return error.LLVMBackendDoesNotSupportSPIRV,
|
||||
.spirv64 => return error.LLVMBackendDoesNotSupportSPIRV,
|
||||
.spu_2 => return error.@"LLVM backend does not support SPU Mark II",
|
||||
.spirv32 => return error.@"LLVM backend does not support SPIR-V",
|
||||
.spirv64 => return error.@"LLVM backend does not support SPIR-V",
|
||||
};
|
||||
|
||||
const llvm_os = switch (target.os.tag) {
|
||||
@@ -114,11 +114,13 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 {
|
||||
.wasi => "wasi",
|
||||
.emscripten => "emscripten",
|
||||
.uefi => "windows",
|
||||
.opencl => return error.LLVMBackendDoesNotSupportOpenCL,
|
||||
.glsl450 => return error.LLVMBackendDoesNotSupportGLSL450,
|
||||
.vulkan => return error.LLVMBackendDoesNotSupportVulkan,
|
||||
.plan9 => return error.LLVMBackendDoesNotSupportPlan9,
|
||||
.other => "unknown",
|
||||
|
||||
.opencl,
|
||||
.glsl450,
|
||||
.vulkan,
|
||||
.plan9,
|
||||
.other,
|
||||
=> "unknown",
|
||||
};
|
||||
|
||||
const llvm_abi = switch (target.abi) {
|
||||
@@ -152,84 +154,105 @@ pub const Object = struct {
|
||||
llvm_module: *const llvm.Module,
|
||||
context: *const llvm.Context,
|
||||
target_machine: *const llvm.TargetMachine,
|
||||
object_pathZ: [:0]const u8,
|
||||
|
||||
pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Object {
|
||||
_ = sub_path;
|
||||
const self = try allocator.create(Object);
|
||||
errdefer allocator.destroy(self);
|
||||
|
||||
const obj_basename = try std.zig.binNameAlloc(allocator, .{
|
||||
.root_name = options.root_name,
|
||||
.target = options.target,
|
||||
.output_mode = .Obj,
|
||||
});
|
||||
defer allocator.free(obj_basename);
|
||||
|
||||
const o_directory = options.module.?.zig_cache_artifact_directory;
|
||||
const object_path = try o_directory.join(allocator, &[_][]const u8{obj_basename});
|
||||
defer allocator.free(object_path);
|
||||
|
||||
const object_pathZ = try allocator.dupeZ(u8, object_path);
|
||||
errdefer allocator.free(object_pathZ);
|
||||
pub fn create(gpa: *Allocator, options: link.Options) !*Object {
|
||||
const obj = try gpa.create(Object);
|
||||
errdefer gpa.destroy(obj);
|
||||
obj.* = try Object.init(gpa, options);
|
||||
return obj;
|
||||
}
|
||||
|
||||
pub fn init(gpa: *Allocator, options: link.Options) !Object {
|
||||
const context = llvm.Context.create();
|
||||
errdefer context.dispose();
|
||||
|
||||
initializeLLVMTargets();
|
||||
|
||||
const root_nameZ = try allocator.dupeZ(u8, options.root_name);
|
||||
defer allocator.free(root_nameZ);
|
||||
const root_nameZ = try gpa.dupeZ(u8, options.root_name);
|
||||
defer gpa.free(root_nameZ);
|
||||
const llvm_module = llvm.Module.createWithName(root_nameZ.ptr, context);
|
||||
errdefer llvm_module.dispose();
|
||||
|
||||
const llvm_target_triple = try targetTriple(allocator, options.target);
|
||||
defer allocator.free(llvm_target_triple);
|
||||
const llvm_target_triple = try targetTriple(gpa, options.target);
|
||||
defer gpa.free(llvm_target_triple);
|
||||
|
||||
var error_message: [*:0]const u8 = undefined;
|
||||
var target: *const llvm.Target = undefined;
|
||||
if (llvm.Target.getFromTriple(llvm_target_triple.ptr, &target, &error_message).toBool()) {
|
||||
defer llvm.disposeMessage(error_message);
|
||||
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
try stderr.print(
|
||||
\\Zig is expecting LLVM to understand this target: '{s}'
|
||||
\\However LLVM responded with: "{s}"
|
||||
\\
|
||||
,
|
||||
.{ llvm_target_triple, error_message },
|
||||
);
|
||||
return error.InvalidLLVMTriple;
|
||||
log.err("LLVM failed to parse '{s}': {s}", .{ llvm_target_triple, error_message });
|
||||
return error.InvalidLlvmTriple;
|
||||
}
|
||||
|
||||
const opt_level: llvm.CodeGenOptLevel = if (options.optimize_mode == .Debug) .None else .Aggressive;
|
||||
const opt_level: llvm.CodeGenOptLevel = if (options.optimize_mode == .Debug)
|
||||
.None
|
||||
else
|
||||
.Aggressive;
|
||||
|
||||
const reloc_mode: llvm.RelocMode = if (options.pic)
|
||||
.PIC
|
||||
else if (options.link_mode == .Dynamic)
|
||||
llvm.RelocMode.DynamicNoPIC
|
||||
else
|
||||
.Static;
|
||||
|
||||
const code_model: llvm.CodeModel = switch (options.machine_code_model) {
|
||||
.default => .Default,
|
||||
.tiny => .Tiny,
|
||||
.small => .Small,
|
||||
.kernel => .Kernel,
|
||||
.medium => .Medium,
|
||||
.large => .Large,
|
||||
};
|
||||
|
||||
// TODO handle float ABI better- it should depend on the ABI portion of std.Target
|
||||
const float_abi: llvm.ABIType = .Default;
|
||||
|
||||
// TODO a way to override this as part of std.Target ABI?
|
||||
const abi_name: ?[*:0]const u8 = switch (options.target.cpu.arch) {
|
||||
.riscv32 => switch (options.target.os.tag) {
|
||||
.linux => "ilp32d",
|
||||
else => "ilp32",
|
||||
},
|
||||
.riscv64 => switch (options.target.os.tag) {
|
||||
.linux => "lp64d",
|
||||
else => "lp64",
|
||||
},
|
||||
else => null,
|
||||
};
|
||||
|
||||
const target_machine = llvm.TargetMachine.create(
|
||||
target,
|
||||
llvm_target_triple.ptr,
|
||||
"",
|
||||
"",
|
||||
if (options.target.cpu.model.llvm_name) |s| s.ptr else null,
|
||||
options.llvm_cpu_features,
|
||||
opt_level,
|
||||
.Static,
|
||||
.Default,
|
||||
reloc_mode,
|
||||
code_model,
|
||||
options.function_sections,
|
||||
float_abi,
|
||||
abi_name,
|
||||
);
|
||||
errdefer target_machine.dispose();
|
||||
|
||||
self.* = .{
|
||||
return Object{
|
||||
.llvm_module = llvm_module,
|
||||
.context = context,
|
||||
.target_machine = target_machine,
|
||||
.object_pathZ = object_pathZ,
|
||||
};
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Object, allocator: *Allocator) void {
|
||||
pub fn deinit(self: *Object) void {
|
||||
self.target_machine.dispose();
|
||||
self.llvm_module.dispose();
|
||||
self.context.dispose();
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
allocator.free(self.object_pathZ);
|
||||
allocator.destroy(self);
|
||||
pub fn destroy(self: *Object, gpa: *Allocator) void {
|
||||
self.deinit();
|
||||
gpa.destroy(self);
|
||||
}
|
||||
|
||||
fn initializeLLVMTargets() void {
|
||||
@@ -240,38 +263,81 @@ pub const Object = struct {
|
||||
llvm.initializeAllAsmParsers();
|
||||
}
|
||||
|
||||
fn locPath(
|
||||
arena: *Allocator,
|
||||
opt_loc: ?Compilation.EmitLoc,
|
||||
cache_directory: Compilation.Directory,
|
||||
) !?[*:0]u8 {
|
||||
const loc = opt_loc orelse return null;
|
||||
const directory = loc.directory orelse cache_directory;
|
||||
const slice = try directory.joinZ(arena, &[_][]const u8{loc.basename});
|
||||
return slice.ptr;
|
||||
}
|
||||
|
||||
pub fn flushModule(self: *Object, comp: *Compilation) !void {
|
||||
if (comp.verbose_llvm_ir) {
|
||||
const dump = self.llvm_module.printToString();
|
||||
defer llvm.disposeMessage(dump);
|
||||
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
try stderr.writeAll(std.mem.spanZ(dump));
|
||||
self.llvm_module.dump();
|
||||
}
|
||||
|
||||
{
|
||||
if (std.debug.runtime_safety) {
|
||||
var error_message: [*:0]const u8 = undefined;
|
||||
// verifyModule always allocs the error_message even if there is no error
|
||||
defer llvm.disposeMessage(error_message);
|
||||
|
||||
if (self.llvm_module.verify(.ReturnStatus, &error_message).toBool()) {
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
try stderr.print("broken LLVM module found: {s}\nThis is a bug in the Zig compiler.", .{error_message});
|
||||
return error.BrokenLLVMModule;
|
||||
std.debug.print("\n{s}\n", .{error_message});
|
||||
@panic("LLVM module verification failed");
|
||||
}
|
||||
}
|
||||
|
||||
var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
|
||||
defer arena_allocator.deinit();
|
||||
const arena = &arena_allocator.allocator;
|
||||
|
||||
const mod = comp.bin_file.options.module.?;
|
||||
const cache_dir = mod.zig_cache_artifact_directory;
|
||||
|
||||
const emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit != null) blk: {
|
||||
const obj_basename = try std.zig.binNameAlloc(arena, .{
|
||||
.root_name = comp.bin_file.options.root_name,
|
||||
.target = comp.bin_file.options.target,
|
||||
.output_mode = .Obj,
|
||||
});
|
||||
if (cache_dir.joinZ(arena, &[_][]const u8{obj_basename})) |p| {
|
||||
break :blk p.ptr;
|
||||
} else |err| {
|
||||
return err;
|
||||
}
|
||||
} else null;
|
||||
|
||||
const emit_asm_path = try locPath(arena, comp.emit_asm, cache_dir);
|
||||
const emit_llvm_ir_path = try locPath(arena, comp.emit_llvm_ir, cache_dir);
|
||||
const emit_llvm_bc_path = try locPath(arena, comp.emit_llvm_bc, cache_dir);
|
||||
|
||||
var error_message: [*:0]const u8 = undefined;
|
||||
if (self.target_machine.emitToFile(
|
||||
self.llvm_module,
|
||||
self.object_pathZ.ptr,
|
||||
.ObjectFile,
|
||||
&error_message,
|
||||
).toBool()) {
|
||||
comp.bin_file.options.optimize_mode == .Debug,
|
||||
comp.bin_file.options.optimize_mode == .ReleaseSmall,
|
||||
comp.time_report,
|
||||
comp.bin_file.options.tsan,
|
||||
comp.bin_file.options.lto,
|
||||
emit_asm_path,
|
||||
emit_bin_path,
|
||||
emit_llvm_ir_path,
|
||||
emit_llvm_bc_path,
|
||||
)) {
|
||||
defer llvm.disposeMessage(error_message);
|
||||
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
try stderr.print("LLVM failed to emit file: {s}\n", .{error_message});
|
||||
const emit_asm_msg = emit_asm_path orelse "(none)";
|
||||
const emit_bin_msg = emit_bin_path orelse "(none)";
|
||||
const emit_llvm_ir_msg = emit_llvm_ir_path orelse "(none)";
|
||||
const emit_llvm_bc_msg = emit_llvm_bc_path orelse "(none)";
|
||||
log.err("LLVM failed to emit asm={s} bin={s} ir={s} bc={s}: {s}", .{
|
||||
emit_asm_msg, emit_bin_msg, emit_llvm_ir_msg, emit_llvm_bc_msg,
|
||||
error_message,
|
||||
});
|
||||
return error.FailedToEmit;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +123,9 @@ pub const Module = opaque {
|
||||
|
||||
pub const getNamedGlobal = LLVMGetNamedGlobal;
|
||||
extern fn LLVMGetNamedGlobal(M: *const Module, Name: [*:0]const u8) ?*const Value;
|
||||
|
||||
pub const dump = LLVMDumpModule;
|
||||
extern fn LLVMDumpModule(M: *const Module) void;
|
||||
};
|
||||
|
||||
pub const lookupIntrinsicID = LLVMLookupIntrinsicID;
|
||||
@@ -250,31 +253,41 @@ pub const BasicBlock = opaque {
|
||||
};
|
||||
|
||||
pub const TargetMachine = opaque {
|
||||
pub const create = LLVMCreateTargetMachine;
|
||||
extern fn LLVMCreateTargetMachine(
|
||||
pub const create = ZigLLVMCreateTargetMachine;
|
||||
extern fn ZigLLVMCreateTargetMachine(
|
||||
T: *const Target,
|
||||
Triple: [*:0]const u8,
|
||||
CPU: [*:0]const u8,
|
||||
Features: [*:0]const u8,
|
||||
CPU: ?[*:0]const u8,
|
||||
Features: ?[*:0]const u8,
|
||||
Level: CodeGenOptLevel,
|
||||
Reloc: RelocMode,
|
||||
CodeModel: CodeMode,
|
||||
CodeModel: CodeModel,
|
||||
function_sections: bool,
|
||||
float_abi: ABIType,
|
||||
abi_name: ?[*:0]const u8,
|
||||
) *const TargetMachine;
|
||||
|
||||
pub const dispose = LLVMDisposeTargetMachine;
|
||||
extern fn LLVMDisposeTargetMachine(T: *const TargetMachine) void;
|
||||
|
||||
pub const emitToFile = LLVMTargetMachineEmitToFile;
|
||||
extern fn LLVMTargetMachineEmitToFile(
|
||||
*const TargetMachine,
|
||||
pub const emitToFile = ZigLLVMTargetMachineEmitToFile;
|
||||
extern fn ZigLLVMTargetMachineEmitToFile(
|
||||
T: *const TargetMachine,
|
||||
M: *const Module,
|
||||
Filename: [*:0]const u8,
|
||||
codegen: CodeGenFileType,
|
||||
ErrorMessage: *[*:0]const u8,
|
||||
) Bool;
|
||||
is_debug: bool,
|
||||
is_small: bool,
|
||||
time_report: bool,
|
||||
tsan: bool,
|
||||
lto: bool,
|
||||
asm_filename: ?[*:0]const u8,
|
||||
bin_filename: ?[*:0]const u8,
|
||||
llvm_ir_filename: ?[*:0]const u8,
|
||||
bitcode_filename: ?[*:0]const u8,
|
||||
) bool;
|
||||
};
|
||||
|
||||
pub const CodeMode = enum(c_int) {
|
||||
pub const CodeModel = enum(c_int) {
|
||||
Default,
|
||||
JITDefault,
|
||||
Tiny,
|
||||
@@ -295,7 +308,7 @@ pub const RelocMode = enum(c_int) {
|
||||
Default,
|
||||
Static,
|
||||
PIC,
|
||||
DynamicNoPic,
|
||||
DynamicNoPIC,
|
||||
ROPI,
|
||||
RWPI,
|
||||
ROPI_RWPI,
|
||||
@@ -306,6 +319,15 @@ pub const CodeGenFileType = enum(c_int) {
|
||||
ObjectFile,
|
||||
};
|
||||
|
||||
pub const ABIType = enum(c_int) {
|
||||
/// Target-specific (either soft or hard depending on triple, etc).
|
||||
Default,
|
||||
/// Soft float.
|
||||
Soft,
|
||||
// Hard float.
|
||||
Hard,
|
||||
};
|
||||
|
||||
pub const Target = opaque {
|
||||
pub const getFromTriple = LLVMGetTargetFromTriple;
|
||||
extern fn LLVMGetTargetFromTriple(Triple: [*:0]const u8, T: **const Target, ErrorMessage: *[*:0]const u8) Bool;
|
||||
|
||||
16
src/link.zig
16
src/link.zig
@@ -191,7 +191,7 @@ pub const File = struct {
|
||||
const use_stage1 = build_options.is_stage1 and options.use_stage1;
|
||||
if (use_stage1 or options.emit == null) {
|
||||
return switch (options.object_format) {
|
||||
.coff, .pe => &(try Coff.createEmpty(allocator, options)).base,
|
||||
.coff => &(try Coff.createEmpty(allocator, options)).base,
|
||||
.elf => &(try Elf.createEmpty(allocator, options)).base,
|
||||
.macho => &(try MachO.createEmpty(allocator, options)).base,
|
||||
.wasm => &(try Wasm.createEmpty(allocator, options)).base,
|
||||
@@ -206,9 +206,10 @@ pub const File = struct {
|
||||
const use_lld = build_options.have_llvm and options.use_lld; // comptime known false when !have_llvm
|
||||
const sub_path = if (use_lld) blk: {
|
||||
if (options.module == null) {
|
||||
// No point in opening a file, we would not write anything to it. Initialize with empty.
|
||||
// No point in opening a file, we would not write anything to it.
|
||||
// Initialize with empty.
|
||||
return switch (options.object_format) {
|
||||
.coff, .pe => &(try Coff.createEmpty(allocator, options)).base,
|
||||
.coff => &(try Coff.createEmpty(allocator, options)).base,
|
||||
.elf => &(try Elf.createEmpty(allocator, options)).base,
|
||||
.macho => &(try MachO.createEmpty(allocator, options)).base,
|
||||
.plan9 => &(try Plan9.createEmpty(allocator, options)).base,
|
||||
@@ -219,13 +220,16 @@ pub const File = struct {
|
||||
.raw => return error.RawObjectFormatUnimplemented,
|
||||
};
|
||||
}
|
||||
// Open a temporary object file, not the final output file because we want to link with LLD.
|
||||
break :blk try std.fmt.allocPrint(allocator, "{s}{s}", .{ emit.sub_path, options.target.oFileExt() });
|
||||
// Open a temporary object file, not the final output file because we
|
||||
// want to link with LLD.
|
||||
break :blk try std.fmt.allocPrint(allocator, "{s}{s}", .{
|
||||
emit.sub_path, options.object_format.fileExt(options.target.cpu.arch),
|
||||
});
|
||||
} else emit.sub_path;
|
||||
errdefer if (use_lld) allocator.free(sub_path);
|
||||
|
||||
const file: *File = switch (options.object_format) {
|
||||
.coff, .pe => &(try Coff.openPath(allocator, sub_path, options)).base,
|
||||
.coff => &(try Coff.openPath(allocator, sub_path, options)).base,
|
||||
.elf => &(try Elf.openPath(allocator, sub_path, options)).base,
|
||||
.macho => &(try MachO.openPath(allocator, sub_path, options)).base,
|
||||
.plan9 => &(try Plan9.openPath(allocator, sub_path, options)).base,
|
||||
|
||||
@@ -17,9 +17,9 @@ const link = @import("../link.zig");
|
||||
const build_options = @import("build_options");
|
||||
const Cache = @import("../Cache.zig");
|
||||
const mingw = @import("../mingw.zig");
|
||||
const llvm_backend = @import("../codegen/llvm.zig");
|
||||
const Air = @import("../Air.zig");
|
||||
const Liveness = @import("../Liveness.zig");
|
||||
const LlvmObject = @import("../codegen/llvm.zig").Object;
|
||||
|
||||
const allocation_padding = 4 / 3;
|
||||
const minimum_text_block_size = 64 * allocation_padding;
|
||||
@@ -37,7 +37,7 @@ pub const base_tag: link.File.Tag = .coff;
|
||||
const msdos_stub = @embedFile("msdos-stub.bin");
|
||||
|
||||
/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
|
||||
llvm_object: ?*llvm_backend.Object = null,
|
||||
llvm_object: ?*LlvmObject = null,
|
||||
|
||||
base: link.File,
|
||||
ptr_width: PtrWidth,
|
||||
@@ -132,7 +132,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
|
||||
const self = try createEmpty(allocator, options);
|
||||
errdefer self.base.destroy();
|
||||
|
||||
self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);
|
||||
self.llvm_object = try LlvmObject.create(allocator, options);
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -657,10 +657,7 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
|
||||
}
|
||||
|
||||
pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
|
||||
if (build_options.skip_non_native and
|
||||
builtin.object_format != .coff and
|
||||
builtin.object_format != .pe)
|
||||
{
|
||||
if (build_options.skip_non_native and builtin.object_format != .coff) {
|
||||
@panic("Attempted to compile for object format that was disabled by build configuration");
|
||||
}
|
||||
if (build_options.have_llvm) {
|
||||
@@ -697,7 +694,7 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live
|
||||
}
|
||||
|
||||
pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
|
||||
if (build_options.skip_non_native and builtin.object_format != .coff and builtin.object_format != .pe) {
|
||||
if (build_options.skip_non_native and builtin.object_format != .coff) {
|
||||
@panic("Attempted to compile for object format that was disabled by build configuration");
|
||||
}
|
||||
if (build_options.have_llvm) {
|
||||
@@ -823,8 +820,11 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
if (build_options.have_llvm)
|
||||
if (self.llvm_object) |llvm_object| return try llvm_object.flushModule(comp);
|
||||
if (build_options.have_llvm) {
|
||||
if (self.llvm_object) |llvm_object| {
|
||||
return try llvm_object.flushModule(comp);
|
||||
}
|
||||
}
|
||||
|
||||
if (self.text_section_size_dirty) {
|
||||
// Write the new raw size in the .text header
|
||||
@@ -1398,8 +1398,9 @@ pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !v
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Coff) void {
|
||||
if (build_options.have_llvm)
|
||||
if (self.llvm_object) |ir_module| ir_module.deinit(self.base.allocator);
|
||||
if (build_options.have_llvm) {
|
||||
if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
|
||||
}
|
||||
|
||||
self.text_block_free_list.deinit(self.base.allocator);
|
||||
self.offset_table.deinit(self.base.allocator);
|
||||
|
||||
@@ -25,9 +25,9 @@ const target_util = @import("../target.zig");
|
||||
const glibc = @import("../glibc.zig");
|
||||
const musl = @import("../musl.zig");
|
||||
const Cache = @import("../Cache.zig");
|
||||
const llvm_backend = @import("../codegen/llvm.zig");
|
||||
const Air = @import("../Air.zig");
|
||||
const Liveness = @import("../Liveness.zig");
|
||||
const LlvmObject = @import("../codegen/llvm.zig").Object;
|
||||
|
||||
const default_entry_addr = 0x8000000;
|
||||
|
||||
@@ -38,7 +38,7 @@ base: File,
|
||||
ptr_width: PtrWidth,
|
||||
|
||||
/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
|
||||
llvm_object: ?*llvm_backend.Object = null,
|
||||
llvm_object: ?*LlvmObject = null,
|
||||
|
||||
/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
|
||||
/// Same order as in the file.
|
||||
@@ -235,7 +235,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
|
||||
const self = try createEmpty(allocator, options);
|
||||
errdefer self.base.destroy();
|
||||
|
||||
self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);
|
||||
self.llvm_object = try LlvmObject.create(allocator, options);
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -301,9 +301,9 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf {
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Elf) void {
|
||||
if (build_options.have_llvm)
|
||||
if (self.llvm_object) |ir_module|
|
||||
ir_module.deinit(self.base.allocator);
|
||||
if (build_options.have_llvm) {
|
||||
if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
|
||||
}
|
||||
|
||||
self.sections.deinit(self.base.allocator);
|
||||
self.program_headers.deinit(self.base.allocator);
|
||||
@@ -750,8 +750,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
|
||||
if (build_options.have_llvm)
|
||||
if (self.llvm_object) |llvm_object| return try llvm_object.flushModule(comp);
|
||||
|
||||
// TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the
|
||||
// Zig source code.
|
||||
// TODO This linker code currently assumes there is only 1 compilation unit and it
|
||||
// corresponds to the Zig source code.
|
||||
const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
|
||||
|
||||
const target_endian = self.base.options.target.cpu.arch.endian();
|
||||
@@ -1289,6 +1289,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
|
||||
// TODO: remove when stage2 can build compiler_rt.zig
|
||||
if (!build_options.is_stage1) break :blk null;
|
||||
|
||||
// In the case of build-obj we include the compiler-rt symbols directly alongside
|
||||
// the symbols of the root source file, in the same compilation unit.
|
||||
if (is_obj) break :blk null;
|
||||
|
||||
if (is_exe_or_dyn_lib) {
|
||||
break :blk comp.compiler_rt_static_lib.?.full_object_path;
|
||||
} else {
|
||||
|
||||
@@ -29,21 +29,22 @@ const CodeSignature = @import("MachO/CodeSignature.zig");
|
||||
const Compilation = @import("../Compilation.zig");
|
||||
const DebugSymbols = @import("MachO/DebugSymbols.zig");
|
||||
const Dylib = @import("MachO/Dylib.zig");
|
||||
const File = link.File;
|
||||
const Object = @import("MachO/Object.zig");
|
||||
const Liveness = @import("../Liveness.zig");
|
||||
const LlvmObject = @import("../codegen/llvm.zig").Object;
|
||||
const LoadCommand = commands.LoadCommand;
|
||||
const Module = @import("../Module.zig");
|
||||
const File = link.File;
|
||||
const SegmentCommand = commands.SegmentCommand;
|
||||
pub const TextBlock = @import("MachO/TextBlock.zig");
|
||||
const Trie = @import("MachO/Trie.zig");
|
||||
const SegmentCommand = commands.SegmentCommand;
|
||||
|
||||
pub const base_tag: File.Tag = File.Tag.macho;
|
||||
|
||||
base: File,
|
||||
|
||||
/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
|
||||
llvm_object: ?*llvm_backend.Object = null,
|
||||
llvm_object: ?*LlvmObject = null,
|
||||
|
||||
/// Debug symbols bundle (or dSym).
|
||||
d_sym: ?DebugSymbols = null,
|
||||
@@ -333,7 +334,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
|
||||
const self = try createEmpty(allocator, options);
|
||||
errdefer self.base.destroy();
|
||||
|
||||
self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);
|
||||
self.llvm_object = try LlvmObject.create(allocator, options);
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -3305,6 +3306,10 @@ fn writeSymbolTable(self: *MachO) !void {
|
||||
}
|
||||
|
||||
pub fn deinit(self: *MachO) void {
|
||||
if (build_options.have_llvm) {
|
||||
if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
|
||||
}
|
||||
|
||||
if (self.d_sym) |*ds| {
|
||||
ds.deinit(self.base.allocator);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ const build_options = @import("build_options");
|
||||
const wasi_libc = @import("../wasi_libc.zig");
|
||||
const Cache = @import("../Cache.zig");
|
||||
const TypedValue = @import("../TypedValue.zig");
|
||||
const llvm_backend = @import("../codegen/llvm.zig");
|
||||
const LlvmObject = @import("../codegen/llvm.zig").Object;
|
||||
const Air = @import("../Air.zig");
|
||||
const Liveness = @import("../Liveness.zig");
|
||||
|
||||
@@ -27,7 +27,7 @@ pub const base_tag = link.File.Tag.wasm;
|
||||
|
||||
base: link.File,
|
||||
/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
|
||||
llvm_object: ?*llvm_backend.Object = null,
|
||||
llvm_object: ?*LlvmObject = null,
|
||||
/// List of all function Decls to be written to the output file. The index of
|
||||
/// each Decl in this list at the time of writing the binary is used as the
|
||||
/// function index. In the event where ext_funcs' size is not 0, the index of
|
||||
@@ -121,7 +121,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
|
||||
const self = try createEmpty(allocator, options);
|
||||
errdefer self.base.destroy();
|
||||
|
||||
self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);
|
||||
self.llvm_object = try LlvmObject.create(allocator, options);
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -153,6 +153,9 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Wasm {
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Wasm) void {
|
||||
if (build_options.have_llvm) {
|
||||
if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
|
||||
}
|
||||
for (self.symbols.items) |decl| {
|
||||
decl.fn_link.wasm.functype.deinit(self.base.allocator);
|
||||
decl.fn_link.wasm.code.deinit(self.base.allocator);
|
||||
@@ -642,7 +645,9 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
|
||||
break :blk full_obj_path;
|
||||
} else null;
|
||||
|
||||
const compiler_rt_path: ?[]const u8 = if (self.base.options.include_compiler_rt)
|
||||
const is_obj = self.base.options.output_mode == .Obj;
|
||||
|
||||
const compiler_rt_path: ?[]const u8 = if (self.base.options.include_compiler_rt and !is_obj)
|
||||
comp.compiler_rt_static_lib.?.full_object_path
|
||||
else
|
||||
null;
|
||||
|
||||
62
src/main.zig
62
src/main.zig
@@ -287,9 +287,9 @@ const usage_build_generic =
|
||||
\\ .s Target-specific assembly source code
|
||||
\\ .S Assembly with C preprocessor (requires LLVM extensions)
|
||||
\\ .c C source code (requires LLVM extensions)
|
||||
\\ .cpp C++ source code (requires LLVM extensions)
|
||||
\\ Other C++ extensions: .C .cc .cxx
|
||||
\\ .cxx .cc .C .cpp C++ source code (requires LLVM extensions)
|
||||
\\ .m Objective-C source code (requires LLVM extensions)
|
||||
\\ .bc LLVM IR Module (requires LLVM extensions)
|
||||
\\
|
||||
\\General Options:
|
||||
\\ -h, --help Print this help and exit
|
||||
@@ -301,6 +301,8 @@ const usage_build_generic =
|
||||
\\ -fno-emit-asm (default) Do not output .s (assembly code)
|
||||
\\ -femit-llvm-ir[=path] Produce a .ll file with LLVM IR (requires LLVM extensions)
|
||||
\\ -fno-emit-llvm-ir (default) Do not produce a .ll file with LLVM IR
|
||||
\\ -femit-llvm-bc[=path] Produce a LLVM module as a .bc file (requires LLVM extensions)
|
||||
\\ -fno-emit-llvm-bc (default) Do not produce a LLVM module as a .bc file
|
||||
\\ -femit-h[=path] Generate a C header file (.h)
|
||||
\\ -fno-emit-h (default) Do not generate a C header file (.h)
|
||||
\\ -femit-docs[=path] Create a docs/ dir with html documentation
|
||||
@@ -359,15 +361,14 @@ const usage_build_generic =
|
||||
\\ --single-threaded Code assumes it is only used single-threaded
|
||||
\\ -ofmt=[mode] Override target object format
|
||||
\\ elf Executable and Linking Format
|
||||
\\ c Compile to C source code
|
||||
\\ c C source code
|
||||
\\ wasm WebAssembly
|
||||
\\ pe Portable Executable (Windows)
|
||||
\\ coff Common Object File Format (Windows)
|
||||
\\ macho macOS relocatables
|
||||
\\ spirv Standard, Portable Intermediate Representation V (SPIR-V)
|
||||
\\ plan9 Plan 9 from Bell Labs object format
|
||||
\\ hex (planned) Intel IHEX
|
||||
\\ raw (planned) Dump machine code directly
|
||||
\\ hex (planned feature) Intel IHEX
|
||||
\\ raw (planned feature) Dump machine code directly
|
||||
\\ -dirafter [dir] Add directory to AFTER include search path
|
||||
\\ -isystem [dir] Add directory to SYSTEM include search path
|
||||
\\ -I[dir] Add directory to include search path
|
||||
@@ -384,8 +385,8 @@ const usage_build_generic =
|
||||
\\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)
|
||||
\\ --sysroot [path] Set the system root directory (usually /)
|
||||
\\ --version [ver] Dynamic library semver
|
||||
\\ -fsoname[=name] (Linux) Override the default SONAME value
|
||||
\\ -fno-soname (Linux) Disable emitting a SONAME
|
||||
\\ -fsoname[=name] Override the default SONAME value
|
||||
\\ -fno-soname Disable emitting a SONAME
|
||||
\\ -fLLD Force using LLD as the linker
|
||||
\\ -fno-LLD Prevent using LLD as the linker
|
||||
\\ -fcompiler-rt Always include compiler-rt symbols in output
|
||||
@@ -552,6 +553,7 @@ fn buildOutputType(
|
||||
var emit_bin: EmitBin = .yes_default_path;
|
||||
var emit_asm: Emit = .no;
|
||||
var emit_llvm_ir: Emit = .no;
|
||||
var emit_llvm_bc: Emit = .no;
|
||||
var emit_docs: Emit = .no;
|
||||
var emit_analysis: Emit = .no;
|
||||
var target_arch_os_abi: []const u8 = "native";
|
||||
@@ -1011,6 +1013,12 @@ fn buildOutputType(
|
||||
emit_llvm_ir = .{ .yes = arg["-femit-llvm-ir=".len..] };
|
||||
} else if (mem.eql(u8, arg, "-fno-emit-llvm-ir")) {
|
||||
emit_llvm_ir = .no;
|
||||
} else if (mem.eql(u8, arg, "-femit-llvm-bc")) {
|
||||
emit_llvm_bc = .yes_default_path;
|
||||
} else if (mem.startsWith(u8, arg, "-femit-llvm-bc=")) {
|
||||
emit_llvm_bc = .{ .yes = arg["-femit-llvm-bc=".len..] };
|
||||
} else if (mem.eql(u8, arg, "-fno-emit-llvm-bc")) {
|
||||
emit_llvm_bc = .no;
|
||||
} else if (mem.eql(u8, arg, "-femit-docs")) {
|
||||
emit_docs = .yes_default_path;
|
||||
} else if (mem.startsWith(u8, arg, "-femit-docs=")) {
|
||||
@@ -1720,8 +1728,6 @@ fn buildOutputType(
|
||||
break :blk .c;
|
||||
} else if (mem.eql(u8, ofmt, "coff")) {
|
||||
break :blk .coff;
|
||||
} else if (mem.eql(u8, ofmt, "pe")) {
|
||||
break :blk .pe;
|
||||
} else if (mem.eql(u8, ofmt, "macho")) {
|
||||
break :blk .macho;
|
||||
} else if (mem.eql(u8, ofmt, "wasm")) {
|
||||
@@ -1765,7 +1771,7 @@ fn buildOutputType(
|
||||
};
|
||||
|
||||
const a_out_basename = switch (object_format) {
|
||||
.pe, .coff => "a.exe",
|
||||
.coff => "a.exe",
|
||||
else => "a.out",
|
||||
};
|
||||
|
||||
@@ -1830,10 +1836,10 @@ fn buildOutputType(
|
||||
var emit_h_resolved = emit_h.resolve(default_h_basename) catch |err| {
|
||||
switch (emit_h) {
|
||||
.yes => {
|
||||
fatal("unable to open directory from argument 'femit-h', '{s}': {s}", .{ emit_h.yes, @errorName(err) });
|
||||
fatal("unable to open directory from argument '-femit-h', '{s}': {s}", .{ emit_h.yes, @errorName(err) });
|
||||
},
|
||||
.yes_default_path => {
|
||||
fatal("unable to open directory from arguments 'name' or 'soname', '{s}': {s}", .{ default_h_basename, @errorName(err) });
|
||||
fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ default_h_basename, @errorName(err) });
|
||||
},
|
||||
.no => unreachable,
|
||||
}
|
||||
@@ -1844,10 +1850,10 @@ fn buildOutputType(
|
||||
var emit_asm_resolved = emit_asm.resolve(default_asm_basename) catch |err| {
|
||||
switch (emit_asm) {
|
||||
.yes => {
|
||||
fatal("unable to open directory from argument 'femit-asm', '{s}': {s}", .{ emit_asm.yes, @errorName(err) });
|
||||
fatal("unable to open directory from argument '-femit-asm', '{s}': {s}", .{ emit_asm.yes, @errorName(err) });
|
||||
},
|
||||
.yes_default_path => {
|
||||
fatal("unable to open directory from arguments 'name' or 'soname', '{s}': {s}", .{ default_asm_basename, @errorName(err) });
|
||||
fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ default_asm_basename, @errorName(err) });
|
||||
},
|
||||
.no => unreachable,
|
||||
}
|
||||
@@ -1858,16 +1864,30 @@ fn buildOutputType(
|
||||
var emit_llvm_ir_resolved = emit_llvm_ir.resolve(default_llvm_ir_basename) catch |err| {
|
||||
switch (emit_llvm_ir) {
|
||||
.yes => {
|
||||
fatal("unable to open directory from argument 'femit-llvm-ir', '{s}': {s}", .{ emit_llvm_ir.yes, @errorName(err) });
|
||||
fatal("unable to open directory from argument '-femit-llvm-ir', '{s}': {s}", .{ emit_llvm_ir.yes, @errorName(err) });
|
||||
},
|
||||
.yes_default_path => {
|
||||
fatal("unable to open directory from arguments 'name' or 'soname', '{s}': {s}", .{ default_llvm_ir_basename, @errorName(err) });
|
||||
fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ default_llvm_ir_basename, @errorName(err) });
|
||||
},
|
||||
.no => unreachable,
|
||||
}
|
||||
};
|
||||
defer emit_llvm_ir_resolved.deinit();
|
||||
|
||||
const default_llvm_bc_basename = try std.fmt.allocPrint(arena, "{s}.bc", .{root_name});
|
||||
var emit_llvm_bc_resolved = emit_llvm_bc.resolve(default_llvm_bc_basename) catch |err| {
|
||||
switch (emit_llvm_bc) {
|
||||
.yes => {
|
||||
fatal("unable to open directory from argument '-femit-llvm-bc', '{s}': {s}", .{ emit_llvm_bc.yes, @errorName(err) });
|
||||
},
|
||||
.yes_default_path => {
|
||||
fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{ default_llvm_bc_basename, @errorName(err) });
|
||||
},
|
||||
.no => unreachable,
|
||||
}
|
||||
};
|
||||
defer emit_llvm_bc_resolved.deinit();
|
||||
|
||||
const default_analysis_basename = try std.fmt.allocPrint(arena, "{s}-analysis.json", .{root_name});
|
||||
var emit_analysis_resolved = emit_analysis.resolve(default_analysis_basename) catch |err| {
|
||||
switch (emit_analysis) {
|
||||
@@ -2003,6 +2023,7 @@ fn buildOutputType(
|
||||
.emit_h = emit_h_resolved.data,
|
||||
.emit_asm = emit_asm_resolved.data,
|
||||
.emit_llvm_ir = emit_llvm_ir_resolved.data,
|
||||
.emit_llvm_bc = emit_llvm_bc_resolved.data,
|
||||
.emit_docs = emit_docs_resolved.data,
|
||||
.emit_analysis = emit_analysis_resolved.data,
|
||||
.link_mode = link_mode,
|
||||
@@ -2408,11 +2429,8 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, hook: AfterUpdateHook) !voi
|
||||
|
||||
// If a .pdb file is part of the expected output, we must also copy
|
||||
// it into place here.
|
||||
const coff_or_pe = switch (comp.bin_file.options.object_format) {
|
||||
.coff, .pe => true,
|
||||
else => false,
|
||||
};
|
||||
const have_pdb = coff_or_pe and !comp.bin_file.options.strip;
|
||||
const is_coff = comp.bin_file.options.object_format == .coff;
|
||||
const have_pdb = is_coff and !comp.bin_file.options.strip;
|
||||
if (have_pdb) {
|
||||
// Replace `.out` or `.exe` with `.pdb` on both the source and destination
|
||||
const src_bin_ext = fs.path.extension(bin_sub_path);
|
||||
|
||||
@@ -21,7 +21,6 @@ comptime {
|
||||
assert(build_options.is_stage1);
|
||||
assert(build_options.have_llvm);
|
||||
if (!builtin.is_test) {
|
||||
_ = @import("compiler_rt");
|
||||
@export(main, .{ .name = "main" });
|
||||
}
|
||||
}
|
||||
@@ -95,6 +94,8 @@ pub const Module = extern struct {
|
||||
emit_asm_len: usize,
|
||||
emit_llvm_ir_ptr: [*]const u8,
|
||||
emit_llvm_ir_len: usize,
|
||||
emit_bitcode_ptr: [*]const u8,
|
||||
emit_bitcode_len: usize,
|
||||
emit_analysis_json_ptr: [*]const u8,
|
||||
emit_analysis_json_len: usize,
|
||||
emit_docs_ptr: [*]const u8,
|
||||
@@ -124,6 +125,7 @@ pub const Module = extern struct {
|
||||
valgrind_enabled: bool,
|
||||
tsan_enabled: bool,
|
||||
function_sections: bool,
|
||||
include_compiler_rt: bool,
|
||||
enable_stack_probing: bool,
|
||||
red_zone: bool,
|
||||
enable_time_report: bool,
|
||||
|
||||
@@ -2090,6 +2090,7 @@ struct CodeGen {
|
||||
Buf h_file_output_path;
|
||||
Buf asm_file_output_path;
|
||||
Buf llvm_ir_file_output_path;
|
||||
Buf bitcode_file_output_path;
|
||||
Buf analysis_json_output_path;
|
||||
Buf docs_output_path;
|
||||
|
||||
@@ -2149,6 +2150,7 @@ struct CodeGen {
|
||||
bool have_stack_probing;
|
||||
bool red_zone;
|
||||
bool function_sections;
|
||||
bool include_compiler_rt;
|
||||
bool test_is_evented;
|
||||
bool valgrind_enabled;
|
||||
bool tsan_enabled;
|
||||
|
||||
@@ -8506,19 +8506,22 @@ static void zig_llvm_emit_output(CodeGen *g) {
|
||||
const char *asm_filename = nullptr;
|
||||
const char *bin_filename = nullptr;
|
||||
const char *llvm_ir_filename = nullptr;
|
||||
const char *bitcode_filename = nullptr;
|
||||
|
||||
if (buf_len(&g->o_file_output_path) != 0) bin_filename = buf_ptr(&g->o_file_output_path);
|
||||
if (buf_len(&g->asm_file_output_path) != 0) asm_filename = buf_ptr(&g->asm_file_output_path);
|
||||
if (buf_len(&g->llvm_ir_file_output_path) != 0) llvm_ir_filename = buf_ptr(&g->llvm_ir_file_output_path);
|
||||
if (buf_len(&g->bitcode_file_output_path) != 0) bitcode_filename = buf_ptr(&g->bitcode_file_output_path);
|
||||
|
||||
// Unfortunately, LLVM shits the bed when we ask for both binary and assembly. So we call the entire
|
||||
// pipeline multiple times if this is requested.
|
||||
// Unfortunately, LLVM shits the bed when we ask for both binary and assembly.
|
||||
// So we call the entire pipeline multiple times if this is requested.
|
||||
if (asm_filename != nullptr && bin_filename != nullptr) {
|
||||
if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg,
|
||||
g->build_mode == BuildModeDebug, is_small, g->enable_time_report, g->tsan_enabled,
|
||||
g->have_lto, nullptr, bin_filename, llvm_ir_filename))
|
||||
g->have_lto, nullptr, bin_filename, llvm_ir_filename, nullptr))
|
||||
{
|
||||
fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg);
|
||||
fprintf(stderr, "LLVM failed to emit bin=%s, ir=%s: %s\n",
|
||||
bin_filename, llvm_ir_filename, err_msg);
|
||||
exit(1);
|
||||
}
|
||||
bin_filename = nullptr;
|
||||
@@ -8527,9 +8530,11 @@ static void zig_llvm_emit_output(CodeGen *g) {
|
||||
|
||||
if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg,
|
||||
g->build_mode == BuildModeDebug, is_small, g->enable_time_report, g->tsan_enabled,
|
||||
g->have_lto, asm_filename, bin_filename, llvm_ir_filename))
|
||||
g->have_lto, asm_filename, bin_filename, llvm_ir_filename, bitcode_filename))
|
||||
{
|
||||
fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg);
|
||||
fprintf(stderr, "LLVM failed to emit asm=%s, bin=%s, ir=%s, bc=%s: %s\n",
|
||||
asm_filename, bin_filename, llvm_ir_filename, bitcode_filename,
|
||||
err_msg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -9537,6 +9542,22 @@ static void gen_root_source(CodeGen *g) {
|
||||
g->panic_fn = panic_fn_val->data.x_ptr.data.fn.fn_entry;
|
||||
assert(g->panic_fn != nullptr);
|
||||
|
||||
if (g->include_compiler_rt) {
|
||||
Buf *import_target_path;
|
||||
Buf full_path = BUF_INIT;
|
||||
ZigType *compiler_rt_import;
|
||||
if ((err = analyze_import(g, std_import, buf_create_from_str("./special/compiler_rt.zig"),
|
||||
&compiler_rt_import, &import_target_path, &full_path)))
|
||||
{
|
||||
if (err == ErrorFileNotFound) {
|
||||
fprintf(stderr, "unable to find '%s'", buf_ptr(import_target_path));
|
||||
} else {
|
||||
fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(&full_path), err_str(err));
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!g->error_during_imports) {
|
||||
semantic_analyze(g);
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ void zig_stage1_build_object(struct ZigStage1 *stage1) {
|
||||
buf_init_from_mem(&g->h_file_output_path, stage1->emit_h_ptr, stage1->emit_h_len);
|
||||
buf_init_from_mem(&g->asm_file_output_path, stage1->emit_asm_ptr, stage1->emit_asm_len);
|
||||
buf_init_from_mem(&g->llvm_ir_file_output_path, stage1->emit_llvm_ir_ptr, stage1->emit_llvm_ir_len);
|
||||
buf_init_from_mem(&g->bitcode_file_output_path, stage1->emit_bitcode_ptr, stage1->emit_bitcode_len);
|
||||
buf_init_from_mem(&g->analysis_json_output_path, stage1->emit_analysis_json_ptr, stage1->emit_analysis_json_len);
|
||||
buf_init_from_mem(&g->docs_output_path, stage1->emit_docs_ptr, stage1->emit_docs_len);
|
||||
|
||||
@@ -100,6 +101,7 @@ void zig_stage1_build_object(struct ZigStage1 *stage1) {
|
||||
g->link_libc = stage1->link_libc;
|
||||
g->link_libcpp = stage1->link_libcpp;
|
||||
g->function_sections = stage1->function_sections;
|
||||
g->include_compiler_rt = stage1->include_compiler_rt;
|
||||
|
||||
g->subsystem = stage1->subsystem;
|
||||
|
||||
|
||||
@@ -157,6 +157,9 @@ struct ZigStage1 {
|
||||
const char *emit_llvm_ir_ptr;
|
||||
size_t emit_llvm_ir_len;
|
||||
|
||||
const char *emit_bitcode_ptr;
|
||||
size_t emit_bitcode_len;
|
||||
|
||||
const char *emit_analysis_json_ptr;
|
||||
size_t emit_analysis_json_len;
|
||||
|
||||
@@ -193,6 +196,7 @@ struct ZigStage1 {
|
||||
bool valgrind_enabled;
|
||||
bool tsan_enabled;
|
||||
bool function_sections;
|
||||
bool include_compiler_rt;
|
||||
bool enable_stack_probing;
|
||||
bool red_zone;
|
||||
bool enable_time_report;
|
||||
|
||||
@@ -39,6 +39,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
|
||||
" --color [auto|off|on] enable or disable colored error messages\n"
|
||||
" --name [name] override output name\n"
|
||||
" -femit-bin=[path] Output machine code\n"
|
||||
" -fcompiler-rt Always include compiler-rt symbols in output\n"
|
||||
" --pkg-begin [name] [path] make pkg available to import and push current pkg\n"
|
||||
" --pkg-end pop current pkg\n"
|
||||
" -ODebug build with optimizations off and safety on\n"
|
||||
@@ -266,6 +267,7 @@ int main(int argc, char **argv) {
|
||||
const char *mcpu = nullptr;
|
||||
bool single_threaded = false;
|
||||
bool is_test_build = false;
|
||||
bool include_compiler_rt = false;
|
||||
|
||||
for (int i = 1; i < argc; i += 1) {
|
||||
char *arg = argv[i];
|
||||
@@ -334,6 +336,8 @@ int main(int argc, char **argv) {
|
||||
mcpu = arg + strlen("-mcpu=");
|
||||
} else if (str_starts_with(arg, "-femit-bin=")) {
|
||||
emit_bin_path = arg + strlen("-femit-bin=");
|
||||
} else if (strcmp(arg, "-fcompiler-rt") == 0) {
|
||||
include_compiler_rt = true;
|
||||
} else if (i + 1 >= argc) {
|
||||
fprintf(stderr, "Expected another argument after %s\n", arg);
|
||||
return print_error_usage(arg0);
|
||||
@@ -468,6 +472,7 @@ int main(int argc, char **argv) {
|
||||
stage1->subsystem = subsystem;
|
||||
stage1->pic = true;
|
||||
stage1->is_single_threaded = single_threaded;
|
||||
stage1->include_compiler_rt = include_compiler_rt;
|
||||
|
||||
zig_stage1_build_object(stage1);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
//! and stage2.
|
||||
|
||||
const std = @import("std");
|
||||
const testing = std.testing;
|
||||
const assert = std.debug.assert;
|
||||
const clang = @import("clang.zig");
|
||||
const ctok = std.c.tokenizer;
|
||||
@@ -18,6 +19,7 @@ const CallingConvention = std.builtin.CallingConvention;
|
||||
pub const ClangErrMsg = clang.Stage2ErrorMsg;
|
||||
|
||||
pub const Error = std.mem.Allocator.Error;
|
||||
const MacroProcessingError = Error || error{UnexpectedMacroToken};
|
||||
const TypeError = Error || error{UnsupportedType};
|
||||
const TransError = TypeError || error{UnsupportedTranslation};
|
||||
|
||||
@@ -27,6 +29,10 @@ const AliasList = std.ArrayList(struct {
|
||||
name: []const u8,
|
||||
});
|
||||
|
||||
// Maps macro parameter names to token position, for determining if different
|
||||
// identifiers refer to the same positional argument in different macros.
|
||||
const ArgsPositionMap = std.StringArrayHashMapUnmanaged(usize);
|
||||
|
||||
const Scope = struct {
|
||||
id: Id,
|
||||
parent: ?*Scope,
|
||||
@@ -322,6 +328,8 @@ pub const Context = struct {
|
||||
/// up front in a pre-processing step.
|
||||
global_names: std.StringArrayHashMapUnmanaged(void) = .{},
|
||||
|
||||
pattern_list: PatternList,
|
||||
|
||||
fn getMangle(c: *Context) u32 {
|
||||
c.mangle_count += 1;
|
||||
return c.mangle_count;
|
||||
@@ -375,6 +383,7 @@ pub fn translate(
|
||||
.alias_list = AliasList.init(gpa),
|
||||
.global_scope = try arena.allocator.create(Scope.Root),
|
||||
.clang_context = ast_unit.getASTContext(),
|
||||
.pattern_list = try PatternList.init(gpa),
|
||||
};
|
||||
context.global_scope.* = Scope.Root.init(&context);
|
||||
defer {
|
||||
@@ -385,6 +394,7 @@ pub fn translate(
|
||||
context.unnamed_typedefs.deinit(gpa);
|
||||
context.typedefs.deinit(gpa);
|
||||
context.global_scope.deinit();
|
||||
context.pattern_list.deinit(gpa);
|
||||
}
|
||||
|
||||
try context.global_scope.nodes.append(Tag.usingnamespace_builtins.init());
|
||||
@@ -4829,6 +4839,220 @@ fn isZigPrimitiveType(name: []const u8) bool {
|
||||
return @import("AstGen.zig").simple_types.has(name);
|
||||
}
|
||||
|
||||
const PatternList = struct {
|
||||
patterns: []Pattern,
|
||||
|
||||
/// Templates must be function-like macros
|
||||
/// first element is macro source, second element is the name of the function
|
||||
/// in std.lib.zig.c_translation.Macros which implements it
|
||||
const templates = [_][2][]const u8{
|
||||
[2][]const u8{ "f_SUFFIX(X) (X ## f)", "F_SUFFIX" },
|
||||
[2][]const u8{ "F_SUFFIX(X) (X ## F)", "F_SUFFIX" },
|
||||
|
||||
[2][]const u8{ "u_SUFFIX(X) (X ## u)", "U_SUFFIX" },
|
||||
[2][]const u8{ "U_SUFFIX(X) (X ## U)", "U_SUFFIX" },
|
||||
|
||||
[2][]const u8{ "l_SUFFIX(X) (X ## l)", "L_SUFFIX" },
|
||||
[2][]const u8{ "L_SUFFIX(X) (X ## L)", "L_SUFFIX" },
|
||||
|
||||
[2][]const u8{ "ul_SUFFIX(X) (X ## ul)", "UL_SUFFIX" },
|
||||
[2][]const u8{ "uL_SUFFIX(X) (X ## uL)", "UL_SUFFIX" },
|
||||
[2][]const u8{ "Ul_SUFFIX(X) (X ## Ul)", "UL_SUFFIX" },
|
||||
[2][]const u8{ "UL_SUFFIX(X) (X ## UL)", "UL_SUFFIX" },
|
||||
|
||||
[2][]const u8{ "ll_SUFFIX(X) (X ## ll)", "LL_SUFFIX" },
|
||||
[2][]const u8{ "LL_SUFFIX(X) (X ## LL)", "LL_SUFFIX" },
|
||||
|
||||
[2][]const u8{ "ull_SUFFIX(X) (X ## ull)", "ULL_SUFFIX" },
|
||||
[2][]const u8{ "uLL_SUFFIX(X) (X ## uLL)", "ULL_SUFFIX" },
|
||||
[2][]const u8{ "Ull_SUFFIX(X) (X ## Ull)", "ULL_SUFFIX" },
|
||||
[2][]const u8{ "ULL_SUFFIX(X) (X ## ULL)", "ULL_SUFFIX" },
|
||||
|
||||
[2][]const u8{ "CAST_OR_CALL(X, Y) (X)(Y)", "CAST_OR_CALL" },
|
||||
|
||||
[2][]const u8{
|
||||
\\wl_container_of(ptr, sample, member) \
|
||||
\\(__typeof__(sample))((char *)(ptr) - \
|
||||
\\ offsetof(__typeof__(*sample), member))
|
||||
,
|
||||
"WL_CONTAINER_OF",
|
||||
},
|
||||
};
|
||||
|
||||
/// Assumes that `ms` represents a tokenized function-like macro.
|
||||
fn buildArgsHash(allocator: *mem.Allocator, ms: MacroSlicer, hash: *ArgsPositionMap) MacroProcessingError!void {
|
||||
assert(ms.tokens.len > 2);
|
||||
assert(ms.tokens[0].id == .Identifier);
|
||||
assert(ms.tokens[1].id == .LParen);
|
||||
|
||||
var i: usize = 2;
|
||||
while (true) : (i += 1) {
|
||||
const token = ms.tokens[i];
|
||||
switch (token.id) {
|
||||
.RParen => break,
|
||||
.Comma => continue,
|
||||
.Identifier => {
|
||||
const identifier = ms.slice(token);
|
||||
try hash.put(allocator, identifier, i);
|
||||
},
|
||||
else => return error.UnexpectedMacroToken,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Pattern = struct {
|
||||
tokens: []const CToken,
|
||||
source: []const u8,
|
||||
impl: []const u8,
|
||||
args_hash: ArgsPositionMap,
|
||||
|
||||
fn init(self: *Pattern, allocator: *mem.Allocator, template: [2][]const u8) Error!void {
|
||||
const source = template[0];
|
||||
const impl = template[1];
|
||||
|
||||
var tok_list = std.ArrayList(CToken).init(allocator);
|
||||
defer tok_list.deinit();
|
||||
try tokenizeMacro(source, &tok_list);
|
||||
const tokens = try allocator.dupe(CToken, tok_list.items);
|
||||
|
||||
self.* = .{
|
||||
.tokens = tokens,
|
||||
.source = source,
|
||||
.impl = impl,
|
||||
.args_hash = .{},
|
||||
};
|
||||
const ms = MacroSlicer{ .source = source, .tokens = tokens };
|
||||
buildArgsHash(allocator, ms, &self.args_hash) catch |err| switch (err) {
|
||||
error.UnexpectedMacroToken => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
|
||||
fn deinit(self: *Pattern, allocator: *mem.Allocator) void {
|
||||
self.args_hash.deinit(allocator);
|
||||
allocator.free(self.tokens);
|
||||
}
|
||||
|
||||
/// This function assumes that `ms` has already been validated to contain a function-like
|
||||
/// macro, and that the parsed template macro in `self` also contains a function-like
|
||||
/// macro. Please review this logic carefully if changing that assumption. Two
|
||||
/// function-like macros are considered equivalent if and only if they contain the same
|
||||
/// list of tokens, modulo parameter names.
|
||||
fn isEquivalent(self: Pattern, ms: MacroSlicer, args_hash: ArgsPositionMap) bool {
|
||||
if (self.tokens.len != ms.tokens.len) return false;
|
||||
if (args_hash.count() != self.args_hash.count()) return false;
|
||||
|
||||
var i: usize = 2;
|
||||
while (self.tokens[i].id != .RParen) : (i += 1) {}
|
||||
|
||||
const pattern_slicer = MacroSlicer{ .source = self.source, .tokens = self.tokens };
|
||||
while (i < self.tokens.len) : (i += 1) {
|
||||
const pattern_token = self.tokens[i];
|
||||
const macro_token = ms.tokens[i];
|
||||
if (meta.activeTag(pattern_token.id) != meta.activeTag(macro_token.id)) return false;
|
||||
|
||||
const pattern_bytes = pattern_slicer.slice(pattern_token);
|
||||
const macro_bytes = ms.slice(macro_token);
|
||||
switch (pattern_token.id) {
|
||||
.Identifier => {
|
||||
const pattern_arg_index = self.args_hash.get(pattern_bytes);
|
||||
const macro_arg_index = args_hash.get(macro_bytes);
|
||||
|
||||
if (pattern_arg_index == null and macro_arg_index == null) {
|
||||
if (!mem.eql(u8, pattern_bytes, macro_bytes)) return false;
|
||||
} else if (pattern_arg_index != null and macro_arg_index != null) {
|
||||
if (pattern_arg_index.? != macro_arg_index.?) return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
.MacroString, .StringLiteral, .CharLiteral, .IntegerLiteral, .FloatLiteral => {
|
||||
if (!mem.eql(u8, pattern_bytes, macro_bytes)) return false;
|
||||
},
|
||||
else => {
|
||||
// other tags correspond to keywords and operators that do not contain a "payload"
|
||||
// that can vary
|
||||
},
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
fn init(allocator: *mem.Allocator) Error!PatternList {
|
||||
const patterns = try allocator.alloc(Pattern, templates.len);
|
||||
for (templates) |template, i| {
|
||||
try patterns[i].init(allocator, template);
|
||||
}
|
||||
return PatternList{ .patterns = patterns };
|
||||
}
|
||||
|
||||
fn deinit(self: *PatternList, allocator: *mem.Allocator) void {
|
||||
for (self.patterns) |*pattern| pattern.deinit(allocator);
|
||||
allocator.free(self.patterns);
|
||||
}
|
||||
|
||||
fn match(self: PatternList, allocator: *mem.Allocator, ms: MacroSlicer) Error!?Pattern {
|
||||
var args_hash: ArgsPositionMap = .{};
|
||||
defer args_hash.deinit(allocator);
|
||||
|
||||
buildArgsHash(allocator, ms, &args_hash) catch |err| switch (err) {
|
||||
error.UnexpectedMacroToken => return null,
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
for (self.patterns) |pattern| if (pattern.isEquivalent(ms, args_hash)) return pattern;
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const MacroSlicer = struct {
|
||||
source: []const u8,
|
||||
tokens: []const CToken,
|
||||
fn slice(self: MacroSlicer, token: CToken) []const u8 {
|
||||
return self.source[token.start..token.end];
|
||||
}
|
||||
};
|
||||
|
||||
// Testing here instead of test/translate_c.zig allows us to also test that the
|
||||
// mapped function exists in `std.zig.c_translation.Macros`
|
||||
test "Macro matching" {
|
||||
const helper = struct {
|
||||
const MacroFunctions = @import("std").zig.c_translation.Macros;
|
||||
fn checkMacro(allocator: *mem.Allocator, pattern_list: PatternList, source: []const u8, comptime expected_match: ?[]const u8) !void {
|
||||
var tok_list = std.ArrayList(CToken).init(allocator);
|
||||
defer tok_list.deinit();
|
||||
try tokenizeMacro(source, &tok_list);
|
||||
const macro_slicer = MacroSlicer{ .source = source, .tokens = tok_list.items };
|
||||
const matched = try pattern_list.match(allocator, macro_slicer);
|
||||
if (expected_match) |expected| {
|
||||
try testing.expectEqualStrings(expected, matched.?.impl);
|
||||
try testing.expect(@hasDecl(MacroFunctions, expected));
|
||||
} else {
|
||||
try testing.expectEqual(@as(@TypeOf(matched), null), matched);
|
||||
}
|
||||
}
|
||||
};
|
||||
const allocator = std.testing.allocator;
|
||||
var pattern_list = try PatternList.init(allocator);
|
||||
defer pattern_list.deinit(allocator);
|
||||
|
||||
try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## F)", "F_SUFFIX");
|
||||
try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## U)", "U_SUFFIX");
|
||||
try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## L)", "L_SUFFIX");
|
||||
try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## LL)", "LL_SUFFIX");
|
||||
try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## UL)", "UL_SUFFIX");
|
||||
try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## ULL)", "ULL_SUFFIX");
|
||||
try helper.checkMacro(allocator, pattern_list,
|
||||
\\container_of(a, b, c) \
|
||||
\\(__typeof__(b))((char *)(a) - \
|
||||
\\ offsetof(__typeof__(*b), c))
|
||||
, "WL_CONTAINER_OF");
|
||||
|
||||
try helper.checkMacro(allocator, pattern_list, "NO_MATCH(X, Y) (X + Y)", null);
|
||||
try helper.checkMacro(allocator, pattern_list, "CAST_OR_CALL(X, Y) (X)(Y)", "CAST_OR_CALL");
|
||||
}
|
||||
|
||||
const MacroCtx = struct {
|
||||
source: []const u8,
|
||||
list: []const CToken,
|
||||
@@ -4855,8 +5079,30 @@ const MacroCtx = struct {
|
||||
fn fail(self: *MacroCtx, c: *Context, comptime fmt: []const u8, args: anytype) !void {
|
||||
return failDecl(c, self.loc, self.name, fmt, args);
|
||||
}
|
||||
|
||||
fn makeSlicer(self: *const MacroCtx) MacroSlicer {
|
||||
return MacroSlicer{ .source = self.source, .tokens = self.list };
|
||||
}
|
||||
};
|
||||
|
||||
fn tokenizeMacro(source: []const u8, tok_list: *std.ArrayList(CToken)) Error!void {
|
||||
var tokenizer = std.c.Tokenizer{
|
||||
.buffer = source,
|
||||
};
|
||||
while (true) {
|
||||
const tok = tokenizer.next();
|
||||
switch (tok.id) {
|
||||
.Nl, .Eof => {
|
||||
try tok_list.append(tok);
|
||||
break;
|
||||
},
|
||||
.LineComment, .MultiLineComment => continue,
|
||||
else => {},
|
||||
}
|
||||
try tok_list.append(tok);
|
||||
}
|
||||
}
|
||||
|
||||
fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {
|
||||
// TODO if we see #undef, delete it from the table
|
||||
var it = unit.getLocalPreprocessingEntities_begin();
|
||||
@@ -4888,21 +5134,7 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {
|
||||
const slice_len = @ptrToInt(end_c) - @ptrToInt(begin_c);
|
||||
const slice = begin_c[0..slice_len];
|
||||
|
||||
var tokenizer = std.c.Tokenizer{
|
||||
.buffer = slice,
|
||||
};
|
||||
while (true) {
|
||||
const tok = tokenizer.next();
|
||||
switch (tok.id) {
|
||||
.Nl, .Eof => {
|
||||
try tok_list.append(tok);
|
||||
break;
|
||||
},
|
||||
.LineComment, .MultiLineComment => continue,
|
||||
else => {},
|
||||
}
|
||||
try tok_list.append(tok);
|
||||
}
|
||||
try tokenizeMacro(slice, &tok_list);
|
||||
|
||||
var macro_ctx = MacroCtx{
|
||||
.source = slice,
|
||||
@@ -4960,6 +5192,16 @@ fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {
|
||||
}
|
||||
|
||||
fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
|
||||
const macro_slicer = m.makeSlicer();
|
||||
if (try c.pattern_list.match(c.gpa, macro_slicer)) |pattern| {
|
||||
const decl = try Tag.pub_var_simple.create(c.arena, .{
|
||||
.name = m.name,
|
||||
.init = try Tag.helpers_macro.create(c.arena, pattern.impl),
|
||||
});
|
||||
try c.global_scope.macro_table.put(m.name, decl);
|
||||
return;
|
||||
}
|
||||
|
||||
var block_scope = try Scope.Block.init(c, &c.global_scope.base, false);
|
||||
defer block_scope.deinit();
|
||||
const scope = &block_scope.base;
|
||||
|
||||
@@ -193,6 +193,8 @@ pub const Node = extern union {
|
||||
helpers_flexible_array_type,
|
||||
/// @import("std").zig.c_translation.shuffleVectorIndex(lhs, rhs)
|
||||
helpers_shuffle_vector_index,
|
||||
/// @import("std").zig.c_translation.Macro.<operand>
|
||||
helpers_macro,
|
||||
/// @import("std").meta.Vector(lhs, rhs)
|
||||
std_meta_vector,
|
||||
/// @import("std").mem.zeroes(operand)
|
||||
@@ -339,6 +341,7 @@ pub const Node = extern union {
|
||||
.identifier,
|
||||
.warning,
|
||||
.type,
|
||||
.helpers_macro,
|
||||
=> Payload.Value,
|
||||
.discard => Payload.Discard,
|
||||
.@"if" => Payload.If,
|
||||
@@ -1112,6 +1115,16 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
|
||||
.data = undefined,
|
||||
});
|
||||
},
|
||||
.helpers_macro => {
|
||||
const payload = node.castTag(.helpers_macro).?.data;
|
||||
const chain = [_][]const u8{
|
||||
"zig",
|
||||
"c_translation",
|
||||
"Macros",
|
||||
payload,
|
||||
};
|
||||
return renderStdImport(c, &chain);
|
||||
},
|
||||
.string_slice => {
|
||||
const payload = node.castTag(.string_slice).?.data;
|
||||
|
||||
@@ -2310,6 +2323,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
|
||||
.bit_or_assign,
|
||||
.bit_xor_assign,
|
||||
.assign,
|
||||
.helpers_macro,
|
||||
=> {
|
||||
// these should never appear in places where grouping might be needed.
|
||||
unreachable;
|
||||
|
||||
@@ -229,12 +229,14 @@ struct TimeTracerRAII {
|
||||
bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
|
||||
char **error_message, bool is_debug,
|
||||
bool is_small, bool time_report, bool tsan, bool lto,
|
||||
const char *asm_filename, const char *bin_filename, const char *llvm_ir_filename)
|
||||
const char *asm_filename, const char *bin_filename,
|
||||
const char *llvm_ir_filename, const char *bitcode_filename)
|
||||
{
|
||||
TimePassesIsEnabled = time_report;
|
||||
|
||||
raw_fd_ostream *dest_asm_ptr = nullptr;
|
||||
raw_fd_ostream *dest_bin_ptr = nullptr;
|
||||
raw_fd_ostream *dest_bitcode_ptr = nullptr;
|
||||
|
||||
if (asm_filename) {
|
||||
std::error_code EC;
|
||||
@@ -252,9 +254,19 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (bitcode_filename) {
|
||||
std::error_code EC;
|
||||
dest_bitcode_ptr = new(std::nothrow) raw_fd_ostream(bitcode_filename, EC, sys::fs::F_None);
|
||||
if (EC) {
|
||||
*error_message = strdup((const char *)StringRef(EC.message()).bytes_begin());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<raw_fd_ostream> dest_asm(dest_asm_ptr),
|
||||
dest_bin(dest_bin_ptr);
|
||||
dest_bin(dest_bin_ptr),
|
||||
dest_bitcode(dest_bitcode_ptr);
|
||||
|
||||
|
||||
auto PID = sys::Process::getProcessId();
|
||||
std::string ProcName = "zig-";
|
||||
@@ -389,6 +401,9 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
|
||||
if (dest_bin && lto) {
|
||||
WriteBitcodeToFile(module, *dest_bin);
|
||||
}
|
||||
if (dest_bitcode) {
|
||||
WriteBitcodeToFile(module, *dest_bitcode);
|
||||
}
|
||||
|
||||
if (time_report) {
|
||||
TimerGroup::printAll(errs());
|
||||
|
||||
@@ -49,7 +49,8 @@ ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void);
|
||||
ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
|
||||
char **error_message, bool is_debug,
|
||||
bool is_small, bool time_report, bool tsan, bool lto,
|
||||
const char *asm_filename, const char *bin_filename, const char *llvm_ir_filename);
|
||||
const char *asm_filename, const char *bin_filename,
|
||||
const char *llvm_ir_filename, const char *bitcode_filename);
|
||||
|
||||
|
||||
enum ZigLLVMABIType {
|
||||
|
||||
@@ -1065,6 +1065,76 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
":5:19: error: redeclaration of local constant 'c'",
|
||||
":4:19: note: previous declaration here",
|
||||
});
|
||||
case.addError(
|
||||
\\pub fn main() void {
|
||||
\\ var i = 0;
|
||||
\\ for (n) |_, i| {
|
||||
\\ }
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
":3:17: error: redeclaration of local variable 'i'",
|
||||
":2:9: note: previous declaration here",
|
||||
});
|
||||
case.addError(
|
||||
\\pub fn main() void {
|
||||
\\ var i = 0;
|
||||
\\ for (n) |i| {
|
||||
\\ }
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
":3:14: error: redeclaration of local variable 'i'",
|
||||
":2:9: note: previous declaration here",
|
||||
});
|
||||
case.addError(
|
||||
\\pub fn main() void {
|
||||
\\ var i = 0;
|
||||
\\ while (n) |i| {
|
||||
\\ }
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
":3:16: error: redeclaration of local variable 'i'",
|
||||
":2:9: note: previous declaration here",
|
||||
});
|
||||
case.addError(
|
||||
\\pub fn main() void {
|
||||
\\ var i = 0;
|
||||
\\ while (n) |bruh| {
|
||||
\\ _ = bruh;
|
||||
\\ } else |i| {
|
||||
\\
|
||||
\\ }
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
":5:13: error: redeclaration of local variable 'i'",
|
||||
":2:9: note: previous declaration here",
|
||||
});
|
||||
case.addError(
|
||||
\\pub fn main() void {
|
||||
\\ var i = 0;
|
||||
\\ if (true) |i| {}
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
":3:16: error: redeclaration of local variable 'i'",
|
||||
":2:9: note: previous declaration here",
|
||||
});
|
||||
case.addError(
|
||||
\\pub fn main() void {
|
||||
\\ var i = 0;
|
||||
\\ if (true) |i| {} else |e| {}
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
":3:16: error: redeclaration of local variable 'i'",
|
||||
":2:9: note: previous declaration here",
|
||||
});
|
||||
case.addError(
|
||||
\\pub fn main() void {
|
||||
\\ var i = 0;
|
||||
\\ if (true) |_| {} else |i| {}
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
":3:28: error: redeclaration of local variable 'i'",
|
||||
":2:9: note: previous declaration here",
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -3624,4 +3624,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
,
|
||||
\\pub export var @"_": c_int = 42;
|
||||
});
|
||||
|
||||
cases.add("Macro matching",
|
||||
\\#define FOO(X) (X ## U)
|
||||
, &[_][]const u8{
|
||||
\\pub const FOO = @import("std").zig.c_translation.Macros.U_SUFFIX;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user