Skip to content

Commit

Permalink
zig cc: support -S and -emit-llvm CLI parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewrk committed Sep 16, 2021
1 parent 6d37ae9 commit d11f42c
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 28 deletions.
96 changes: 75 additions & 21 deletions src/Compilation.zig
Original file line number Diff line number Diff line change
Expand Up @@ -849,10 +849,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
if (options.use_llvm) |explicit|
break :blk explicit;

// If we have no zig code to compile, no need for LLVM.
if (options.main_pkg == null)
break :blk false;

// If we are outputting .c code we must use Zig backend.
if (ofmt == .c)
break :blk false;
Expand All @@ -861,6 +857,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
if (options.emit_llvm_ir != null or options.emit_llvm_bc != null)
break :blk true;

// If we have no zig code to compile, no need for LLVM.
if (options.main_pkg == null)
break :blk false;

// The stage1 compiler depends on the stage1 C++ LLVM backend
// to compile zig code.
if (use_stage1)
Expand All @@ -876,9 +876,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
if (options.use_llvm == true) {
return error.ZigCompilerNotBuiltWithLLVMExtensions;
}
if (options.machine_code_model != .default) {
return error.MachineCodeModelNotSupportedWithoutLlvm;
}
if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) {
return error.EmittingLlvmModuleRequiresUsingLlvmBackend;
}
Expand Down Expand Up @@ -1793,6 +1790,10 @@ pub fn update(self: *Compilation) !void {
}
}

// Flush takes care of -femit-bin, but we still have -femit-llvm-ir, -femit-llvm-bc, and
// -femit-asm to handle, in the case of C objects.
try self.emitOthers();

// If there are any errors, we anticipate the source files being loaded
// to report error messages. Otherwise we unload all source files to save memory.
// The ZIR needs to stay loaded in memory because (1) Decl objects contain references
Expand All @@ -1808,6 +1809,37 @@ pub fn update(self: *Compilation) !void {
}
}

fn emitOthers(comp: *Compilation) !void {
if (comp.bin_file.options.output_mode != .Obj or comp.bin_file.options.module != null or
comp.c_object_table.count() == 0)
{
return;
}
const obj_path = comp.c_object_table.keys()[0].status.success.object_path;
const cwd = std.fs.cwd();
const ext = std.fs.path.extension(obj_path);
const basename = obj_path[0 .. obj_path.len - ext.len];
// This obj path always ends with the object file extension, but if we change the
// extension to .ll, .bc, or .s, then it will be the path to those things.
const outs = [_]struct {
emit: ?EmitLoc,
ext: []const u8,
}{
.{ .emit = comp.emit_asm, .ext = ".s" },
.{ .emit = comp.emit_llvm_ir, .ext = ".ll" },
.{ .emit = comp.emit_llvm_bc, .ext = ".bc" },
};
for (outs) |out| {
if (out.emit) |loc| {
if (loc.directory) |directory| {
const src_path = try std.fmt.allocPrint(comp.gpa, "{s}{s}", .{ basename, out.ext });
defer comp.gpa.free(src_path);
try cwd.copyFile(src_path, directory.handle, loc.basename, .{});
}
}
}
}

/// Having the file open for writing is problematic as far as executing the
/// binary is concerned. This will remove the write flag, or close the file,
/// or whatever is needed so that it can be executed.
Expand Down Expand Up @@ -2764,6 +2796,9 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
defer man.deinit();

man.hash.add(comp.clang_preprocessor_mode);
man.hash.addOptionalEmitLoc(comp.emit_asm);
man.hash.addOptionalEmitLoc(comp.emit_llvm_ir);
man.hash.addOptionalEmitLoc(comp.emit_llvm_bc);

try man.hashCSource(c_object.src);

Expand All @@ -2787,16 +2822,29 @@ 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.bin_file.options.object_format.fileExt(comp.bin_file.options.target.cpu.arch),
});

const o_ext = 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);
defer argv.deinit();

// We can't know the digest until we do the C compiler invocation, so we need a temporary filename.
// In case we are doing passthrough mode, we need to detect -S and -emit-llvm.
const out_ext = e: {
if (!comp.clang_passthrough_mode)
break :e o_ext;
if (comp.emit_asm != null)
break :e ".s";
if (comp.emit_llvm_ir != null)
break :e ".ll";
if (comp.emit_llvm_bc != null)
break :e ".bc";

break :e o_ext;
};
const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, out_ext });

// We can't know the digest until we do the C compiler invocation,
// so we need a temporary filename.
const out_obj_path = try comp.tmpFilePath(arena, o_basename);
var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{});
defer zig_cache_tmp_dir.close();
Expand All @@ -2810,15 +2858,23 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
try std.fmt.allocPrint(arena, "{s}.d", .{out_obj_path});
try comp.addCCArgs(arena, &argv, ext, out_dep_path);

try argv.ensureCapacity(argv.items.len + 3);
try argv.ensureUnusedCapacity(6 + c_object.src.extra_flags.len);
switch (comp.clang_preprocessor_mode) {
.no => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-c", "-o", out_obj_path }),
.yes => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-E", "-o", out_obj_path }),
.stdout => argv.appendAssumeCapacity("-E"),
}

try argv.append(c_object.src.src_path);
try argv.appendSlice(c_object.src.extra_flags);
if (comp.clang_passthrough_mode) {
if (comp.emit_asm != null) {
argv.appendAssumeCapacity("-S");
} else if (comp.emit_llvm_ir != null) {
argv.appendSliceAssumeCapacity(&[_][]const u8{ "-emit-llvm", "-S" });
} else if (comp.emit_llvm_bc != null) {
argv.appendAssumeCapacity("-emit-llvm");
}
}
argv.appendAssumeCapacity(c_object.src.src_path);
argv.appendSliceAssumeCapacity(c_object.src.extra_flags);

if (comp.verbose_cc) {
dump_argv(argv.items);
Expand All @@ -2838,8 +2894,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
switch (term) {
.Exited => |code| {
if (code != 0) {
// TODO https://github.com/ziglang/zig/issues/6342
std.process.exit(1);
std.process.exit(code);
}
if (comp.clang_preprocessor_mode == .stdout)
std.process.exit(0);
Expand All @@ -2855,9 +2910,6 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P

const stderr_reader = child.stderr.?.reader();

// TODO https://github.com/ziglang/zig/issues/6343
// Please uncomment and use stdout once this issue is fixed
// const stdout = try stdout_reader.readAllAlloc(arena, std.math.maxInt(u32));
const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);

const term = child.wait() catch |err| {
Expand Down Expand Up @@ -2907,6 +2959,8 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
break :blk digest;
};

const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, o_ext });

c_object.status = .{
.success = .{
.object_path = try comp.local_cache_directory.join(comp.gpa, &[_][]const u8{
Expand Down
9 changes: 8 additions & 1 deletion src/clang_options_data.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2434,7 +2434,14 @@ flagpd1("emit-codegen-only"),
flagpd1("emit-header-module"),
flagpd1("emit-html"),
flagpd1("emit-interface-stubs"),
flagpd1("emit-llvm"),
.{
.name = "emit-llvm",
.syntax = .flag,
.zig_equivalent = .emit_llvm,
.pd1 = true,
.pd2 = false,
.psl = false,
},
flagpd1("emit-llvm-bc"),
flagpd1("emit-llvm-only"),
flagpd1("emit-llvm-uselists"),
Expand Down
35 changes: 29 additions & 6 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1151,6 +1151,7 @@ fn buildOutputType(
var is_shared_lib = false;
var linker_args = std.ArrayList([]const u8).init(arena);
var it = ClangArgIterator.init(arena, all_args);
var emit_llvm = false;
while (it.has_next) {
it.next() catch |err| {
fatal("unable to parse command line parameters: {s}", .{@errorName(err)});
Expand All @@ -1161,6 +1162,7 @@ fn buildOutputType(
.c => c_out_mode = .object, // -c
.asm_only => c_out_mode = .assembly, // -S
.preprocess_only => c_out_mode = .preprocessor, // -E
.emit_llvm => emit_llvm = true,
.other => {
try clang_argv.appendSlice(it.other_args);
},
Expand Down Expand Up @@ -1518,22 +1520,42 @@ fn buildOutputType(
output_mode = if (is_shared_lib) .Lib else .Exe;
emit_bin = if (out_path) |p| .{ .yes = p } else EmitBin.yes_a_out;
enable_cache = true;
if (emit_llvm) {
fatal("-emit-llvm cannot be used when linking", .{});
}
},
.object => {
output_mode = .Obj;
if (out_path) |p| {
emit_bin = .{ .yes = p };
if (emit_llvm) {
emit_bin = .no;
if (out_path) |p| {
emit_llvm_bc = .{ .yes = p };
} else {
emit_llvm_bc = .yes_default_path;
}
} else {
emit_bin = .yes_default_path;
if (out_path) |p| {
emit_bin = .{ .yes = p };
} else {
emit_bin = .yes_default_path;
}
}
},
.assembly => {
output_mode = .Obj;
emit_bin = .no;
if (out_path) |p| {
emit_asm = .{ .yes = p };
if (emit_llvm) {
if (out_path) |p| {
emit_llvm_ir = .{ .yes = p };
} else {
emit_llvm_ir = .yes_default_path;
}
} else {
emit_asm = .yes_default_path;
if (out_path) |p| {
emit_asm = .{ .yes = p };
} else {
emit_asm = .yes_default_path;
}
}
},
.preprocessor => {
Expand Down Expand Up @@ -3663,6 +3685,7 @@ pub const ClangArgIterator = struct {
no_red_zone,
strip,
exec_model,
emit_llvm,
};

const Args = struct {
Expand Down
4 changes: 4 additions & 0 deletions tools/update_clang_options.zig
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,10 @@ const known_options = [_]KnownOpt{
.name = "mexec-model",
.ident = "exec_model",
},
.{
.name = "emit-llvm",
.ident = "emit_llvm",
},
};

const blacklisted_options = [_][]const u8{};
Expand Down

0 comments on commit d11f42c

Please sign in to comment.