Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimization remarks #15242

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/Compilation.zig
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,8 @@ pub const InitOptions = struct {
emit_docs: ?EmitLoc = null,
/// `null` means to not emit an import lib.
emit_implib: ?EmitLoc = null,
/// `null` means to not emit optimization remarks.
emit_opt_remarks: ?EmitLoc = null,
link_mode: ?std.builtin.LinkMode = null,
dll_export_fns: ?bool = false,
/// Normally when using LLD to link, Zig uses a file named "lld.id" in the
Expand Down Expand Up @@ -1397,6 +1399,30 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
};
};

const opt_remarks_emit: ?link.Emit = blk: {
const emit_opt_remarks = options.emit_opt_remarks orelse break :blk null;

if (emit_opt_remarks.directory) |directory| {
break :blk link.Emit{
.directory = directory,
.sub_path = emit_opt_remarks.basename,
};
}

// This is here for the same reason as in `bin_file_emit` above.
switch (cache_mode) {
.whole => break :blk null,
.incremental => {},
}

// Use the same directory as the bin. The CLI already emits an
// error if -fno-emit-bin is combined with -femit-opt-remarks.
break :blk link.Emit{
.directory = bin_file_emit.?.directory,
.sub_path = emit_opt_remarks.basename,
};
};

// This is so that when doing `CacheMode.whole`, the mechanism in update()
// can use it for communicating the result directory via `bin_file.emit`.
// This is used to distinguish between -fno-emit-bin and -femit-bin
Expand All @@ -1417,6 +1443,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
const bin_file = try link.File.openPath(gpa, .{
.emit = bin_file_emit,
.implib_emit = implib_emit,
.opt_remarks_emit = opt_remarks_emit,
.root_name = root_name,
.module = module,
.target = options.target,
Expand Down
34 changes: 29 additions & 5 deletions src/codegen/llvm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,9 @@ pub const Object = struct {
/// second extern Decl could not be emitted with the correct name due to a
/// name collision.
extern_collisions: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, void),
/// Optimization remarks file handler. When outputting optimization remarks,
/// LLVM expect us to properly close this resource.
opt_remarks: ?*llvm.ToolOutputFile,

pub const TypeMap = std.HashMapUnmanaged(
Type,
Expand Down Expand Up @@ -523,6 +526,19 @@ pub const Object = struct {
context.setOptBisectLimit(std.math.lossyCast(c_int, options.opt_bisect_limit));
}

var opt_remarks: ?*llvm.ToolOutputFile = null;
if (options.opt_remarks_emit) |emit| {
var sub_path = try gpa.dupeZ(u8, emit.sub_path);
var opt_remarks_emit_path = try emit.basenamePath(gpa, sub_path);
defer gpa.free(opt_remarks_emit_path);

opt_remarks = context.openOptimizationRemarks(opt_remarks_emit_path);
if (opt_remarks == null) {
log.err("LLVM failed to open opt remarks file: {s}", .{opt_remarks_emit_path});
return error.FailedToEmit;
}
}

return Object{
.gpa = gpa,
.module = options.module.?,
Expand All @@ -541,6 +557,7 @@ pub const Object = struct {
.di_type_map = .{},
.error_name_table = null,
.extern_collisions = .{},
.opt_remarks = opt_remarks,
};
}

Expand All @@ -559,6 +576,7 @@ pub const Object = struct {
self.type_map.deinit(gpa);
self.type_map_arena.deinit();
self.extern_collisions.deinit(gpa);
if (self.opt_remarks) |remarks_file| remarks_file.close();
self.* = undefined;
}

Expand Down Expand Up @@ -1408,19 +1426,25 @@ pub const Object = struct {
if (gop.found_existing) {
return @ptrCast(*llvm.DIFile, gop.value_ptr.*);
}
// The split between directory and sub_path is actually important because
// the sub_path is the sometimes the only thing that will shown to user.
// For std lib, we want to make clear that files come from std lib and not user code.
const std_lib = std.mem.endsWith(u8, file.pkg.root_src_directory.path orelse "", "lib/zig/std");
const dir_path_z = d: {
var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
const dir_path = file.pkg.root_src_directory.path orelse ".";
const resolved_dir_path = if (std.fs.path.isAbsolute(dir_path))
var resolved_dir_path = if (std.fs.path.isAbsolute(dir_path))
dir_path
else
std.os.realpath(dir_path, &buffer) catch dir_path; // If realpath fails, fallback to whatever dir_path was
break :d try std.fs.path.joinZ(gpa, &.{
resolved_dir_path, std.fs.path.dirname(file.sub_file_path) orelse "",
});
if (std.mem.endsWith(u8, resolved_dir_path, "/zig/std")) {
resolved_dir_path = resolved_dir_path[0 .. resolved_dir_path.len - 8];
}
break :d try gpa.dupeZ(u8, resolved_dir_path);
};
defer gpa.free(dir_path_z);
const sub_file_path_z = try gpa.dupeZ(u8, std.fs.path.basename(file.sub_file_path));

const sub_file_path_z = if (std_lib) try std.fs.path.joinZ(gpa, &.{ "zig/std", file.sub_file_path }) else try gpa.dupeZ(u8, file.sub_file_path);
defer gpa.free(sub_file_path_z);
const di_file = o.di_builder.?.createFile(sub_file_path_z, dir_path_z);
gop.value_ptr.* = di_file.toNode();
Expand Down
8 changes: 8 additions & 0 deletions src/codegen/llvm/bindings.zig
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ pub const Context = opaque {

pub const setOptBisectLimit = ZigLLVMSetOptBisectLimit;
extern fn ZigLLVMSetOptBisectLimit(C: *Context, limit: c_int) void;

pub const openOptimizationRemarks = ZigLLVMOpenOptimizationRemarks;
extern fn ZigLLVMOpenOptimizationRemarks(C: *Context, RemarksFilename: [*:0]const u8) *ToolOutputFile;
};

pub const ToolOutputFile = opaque {
pub const close = ZigLLVMCloseToolOutputFile;
extern fn ZigLLVMCloseToolOutputFile(file: *ToolOutputFile) void;
};

pub const Value = opaque {
Expand Down
1 change: 1 addition & 0 deletions src/link.zig
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ pub const Options = struct {
emit: ?Emit,
/// This is `null` not building a Windows DLL, or when `-fno-emit-implib` is used.
implib_emit: ?Emit,
opt_remarks_emit: ?Emit,
target: std.Target,
output_mode: std.builtin.OutputMode,
link_mode: std.builtin.LinkMode,
Expand Down
28 changes: 28 additions & 0 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,8 @@ const usage_build_generic =
\\ -fno-emit-analysis (default) Do not write analysis JSON file with type information
\\ -femit-implib[=path] (default) Produce an import .lib when building a Windows DLL
\\ -fno-emit-implib Do not produce an import .lib when building a Windows DLL
\\ -femit-opt-remarks[=path] Write optimization remarks in a YAML file.
\\ -fno-emit-opt-remarks (default) Do not write optimization remarks in a YAML file.
\\ --show-builtin Output the source of @import("builtin") then exit
\\ --cache-dir [path] Override the local cache directory
\\ --global-cache-dir [path] Override the global cache directory
Expand Down Expand Up @@ -720,6 +722,7 @@ fn buildOutputType(
var emit_analysis: Emit = .no;
var emit_implib: Emit = .yes_default_path;
var emit_implib_arg_provided = false;
var emit_opt_remarks: Emit = .no;
var target_arch_os_abi: []const u8 = "native";
var target_mcpu: ?[]const u8 = null;
var target_dynamic_linker: ?[]const u8 = null;
Expand Down Expand Up @@ -1336,6 +1339,12 @@ fn buildOutputType(
} else if (mem.eql(u8, arg, "-fno-emit-implib")) {
emit_implib = .no;
emit_implib_arg_provided = true;
} else if (mem.eql(u8, arg, "-femit-opt-remarks")) {
emit_opt_remarks = .yes_default_path;
} else if (mem.startsWith(u8, arg, "-femit-opt-remarks=")) {
emit_opt_remarks = .{ .yes = arg["-femit-opt-remarks=".len..] };
} else if (mem.eql(u8, arg, "-fno-emit-opt-remarks")) {
emit_opt_remarks = .no;
} else if (mem.eql(u8, arg, "-dynamic")) {
link_mode = .Dynamic;
} else if (mem.eql(u8, arg, "-static")) {
Expand Down Expand Up @@ -2963,6 +2972,24 @@ fn buildOutputType(
};
defer emit_implib_resolved.deinit();

const default_opt_remarks_basename = try std.fmt.allocPrintZ(arena, "{s}.opt.yaml", .{root_name});
var emit_opt_remarks_resolved = switch (emit_opt_remarks) {
.no => Emit.Resolved{ .data = null, .dir = null },
.yes => |p| emit_opt_remarks.resolve(default_opt_remarks_basename) catch |err| {
fatal("unable to open directory from argument '-femit-opt-remarks', '{s}': {s}", .{
p, @errorName(err),
});
},
.yes_default_path => Emit.Resolved{
.data = Compilation.EmitLoc{
.directory = emit_bin_loc.?.directory,
.basename = default_opt_remarks_basename,
},
.dir = null,
},
};
defer emit_opt_remarks_resolved.deinit();

const main_pkg: ?*Package = if (root_src_file) |unresolved_src_path| blk: {
const src_path = try introspect.resolvePath(arena, unresolved_src_path);
if (main_pkg_path) |unresolved_main_pkg_path| {
Expand Down Expand Up @@ -3144,6 +3171,7 @@ fn buildOutputType(
.emit_docs = emit_docs_resolved.data,
.emit_analysis = emit_analysis_resolved.data,
.emit_implib = emit_implib_resolved.data,
.emit_opt_remarks = emit_opt_remarks_resolved.data,
.link_mode = link_mode,
.dll_export_fns = dll_export_fns,
.optimize_mode = optimize_mode,
Expand Down
21 changes: 20 additions & 1 deletion src/zig_llvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <llvm/IR/InlineAsm.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/IR/LLVMRemarkStreamer.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/OptBisect.h>
#include <llvm/IR/PassManager.h>
Expand All @@ -53,6 +54,7 @@
#include <llvm/Support/TargetParser.h>
#include <llvm/Support/TimeProfiler.h>
#include <llvm/Support/Timer.h>
#include <llvm/Support/ToolOutputFile.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/Target/CodeGenCWrappers.h>
Expand All @@ -65,7 +67,6 @@
#include <llvm/Transforms/Utils/AddDiscriminators.h>
#include <llvm/Transforms/Utils/CanonicalizeAliases.h>
#include <llvm/Transforms/Utils/NameAnonGlobals.h>

#include <lld/Common/Driver.h>

#if __GNUC__ >= 9
Expand Down Expand Up @@ -425,6 +426,24 @@ ZIG_EXTERN_C void ZigLLVMSetOptBisectLimit(LLVMContextRef context_ref, int limit
// unwrap(context_ref)->setOptPassGate(_opt_bisector);
}

ZIG_EXTERN_C ZigLLVMToolOutputFile* ZigLLVMOpenOptimizationRemarks(LLVMContextRef context_ref, const char *RemarksFilename) {
auto remarks_err = llvm::setupLLVMOptimizationRemarks(*unwrap(context_ref), RemarksFilename, ".*", "yaml", false, 0);
if (Error E = remarks_err.takeError())
return nullptr;

std::unique_ptr<ToolOutputFile> remarks_file = std::move(*remarks_err);
if (remarks_file) {
remarks_file->keep();
return reinterpret_cast<ZigLLVMToolOutputFile *>(remarks_file.release());
}
return nullptr;
}

ZIG_EXTERN_C void ZigLLVMCloseToolOutputFile(ZigLLVMToolOutputFile *remarks_file) {
ToolOutputFile* file = reinterpret_cast<ToolOutputFile*>(remarks_file);
delete file;
}

LLVMValueRef ZigLLVMAddFunctionInAddressSpace(LLVMModuleRef M, const char *Name, LLVMTypeRef FunctionTy, unsigned AddressSpace) {
Function* func = Function::Create(unwrap<FunctionType>(FunctionTy), GlobalValue::ExternalLinkage, AddressSpace, Name, unwrap(M));
return wrap(func);
Expand Down
5 changes: 5 additions & 0 deletions src/zig_llvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ struct ZigLLVMDIEnumerator;
struct ZigLLVMInsertionPoint;
struct ZigLLVMDINode;
struct ZigLLVMMDString;
struct ZigLLVMToolOutputFile;


ZIG_EXTERN_C void ZigLLVMInitializeLoopStrengthReducePass(LLVMPassRegistryRef R);
ZIG_EXTERN_C void ZigLLVMInitializeLowerIntrinsicsPass(LLVMPassRegistryRef R);
Expand Down Expand Up @@ -69,6 +71,9 @@ ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref);

ZIG_EXTERN_C void ZigLLVMSetOptBisectLimit(LLVMContextRef context_ref, int limit);

ZIG_EXTERN_C ZigLLVMToolOutputFile* ZigLLVMOpenOptimizationRemarks(LLVMContextRef context_ref, const char *RemarksFilename);
ZIG_EXTERN_C void ZigLLVMCloseToolOutputFile(ZigLLVMToolOutputFile *remarks_file);

ZIG_EXTERN_C LLVMValueRef ZigLLVMAddFunctionInAddressSpace(LLVMModuleRef M, const char *Name,
LLVMTypeRef FunctionTy, unsigned AddressSpace);

Expand Down