Supporting sub commands in LLVM command line tools

Hi all,

I’ve written a patch to support subcommands in llvm command line tools. This potentially has broad interest (either positive or negative), so I figured I’d give a heads up here instead of just on llvm-commits.

The motivation for this is that we frequently have tools with incompatible sets of command line options. I ran into this on llvm-pdbdump and was debating breaking off some of the functionality into a separate tool to make the command line interface more sensible, and to prevent people getting confused about which options could be used with which other options.

A better approach to this, in my opinion, is the use of sub commands. This way all the options that can be used together are grouped together, and you simply can’t specify incompatible options at the same time.

There is more information in the patch, including some examples of what it looks like, so if you’re interested in this or have a strong opinion one way or the other, let me know.

Note that this is an opt in feature, and if you continue using cl::opt as you always have, this change should be invisible to you.

Hi,

I haven’t looked at the implementation, but conceptually this looks nice!

We talked internally about an option to build something like a single “llvm” binary that would be symlinked by opt/llc/etc. So that when you invoke opt, it would run the same binary but internally the right subcommand set of options would be used. The downside is that running ninja llvm-mc would depends on every LLVM libraries though.

This is a bit orthogonal to what you’re doing, but I assume your patch would help to build such an option right?

Not sure I follow how this would work. “llvm” is an executable, and “opt” is a symlink to “llvm”? How does llvm then detect that it needs to use the opt set of commands?

That said, in principle sure you could have “llvm opt ” or “llvm llc ”. At some point you’d probably need to extend this to support nested subcommands, which I didn’t attempt here.

Busybox-like: If (argv[0] == “opt”)

Ahh. I don’t think you would necessarily need this change in order to make that work. There’s nothing that says that cl options have to be global, and indeed in this patch I have some examples (mostly in unit tests) of making some options on the stack and then wiping the slate clean later to create some new options. So you could do something like this:

struct OptOptions : public LLVMOptions {
cl::opt<bool> PrintCallgraph("print-callgraph");
};

class LLCOptions : public LLVMOptions {
cl::opt<bool> NoFixup("mno-fixup");
};

std::unique_ptr<LLVMOptions> Options;

int main(int argc, char **argv) {
if (argv[0] == "opt")
Options = llvm::make_unique<OptOptions>();
else if (argv[0] == "llc")
Options = llvm::make_unique<LLCOptions>();
// etc
}

You could, however, use this to make it so that if you just ran the llvm binary by itself, you could invoke any of the subtools as a subcommand, perhaps even combined with the above, so you could do something like:

$ opt -print-callgraph <etc>
$ llc -mno-fixup <etc>
$ llvm opt -print-callgraph <etc>
$ llvm llc -mno-fixup <etc>

Ahh. I don’t think you would necessarily need this change in order to make that work. There’s nothing that says that cl options have to be global, and indeed in this patch I have some examples (mostly in unit tests) of making some options on the stack and then wiping the slate clean later to create some new options. So you could do something like this:

struct OptOptions : public LLVMOptions {
cl::opt<bool> PrintCallgraph("print-callgraph");
};

class LLCOptions : public LLVMOptions {
cl::opt<bool> NoFixup("mno-fixup");
};

std::unique_ptr<LLVMOptions> Options;

int main(int argc, char **argv) {
if (argv[0] == "opt")
Options = llvm::make_unique<OptOptions>();
else if (argv[0] == "llc")
Options = llvm::make_unique<LLCOptions>();
// etc
}

Interesting, I didn’t think about that.

You could, however, use this to make it so that if you just ran the llvm binary by itself, you could invoke any of the subtools as a subcommand, perhaps even combined with the above, so you could do something like:

$ opt -print-callgraph
$ llc -mno-fixup
$ llvm opt -print-callgraph
$ llvm llc -mno-fixup

OK, it is even more orthogonal than I thought!

Thanks.

Mehdi Amini via llvm-dev <llvm-dev@lists.llvm.org> writes:

Hi,

I haven't looked at the implementation, but conceptually this looks nice!

We talked internally about an option to build something like a single "llvm"
binary that would be symlinked by opt/llc/etc. So that when you invoke `opt`,
it would run the same binary but internally the right subcommand set of
options would be used. The downside is that running `ninja llvm-mc` would
depends on every LLVM libraries though.

Why would you want that? I build specific tools an order of magnitude
more often than building all of them, so personally I would consider
this downside really critical.

Mehdi Amini via llvm-dev <llvm-dev@lists.llvm.org> writes:

Hi,

I haven’t looked at the implementation, but conceptually this looks nice!

We talked internally about an option to build something like a single “llvm”
binary that would be symlinked by opt/llc/etc. So that when you invoke opt,
it would run the same binary but internally the right subcommand set of
options would be used. The downside is that running ninja llvm-mc would
depends on every LLVM libraries though.

Why would you want that?

I can see three reasons right now:

  1. Disk space: the “bin” directory for just llvm (no clang, libc++, etc.) after running dsymutil takes 2.8GB right now.
  2. running dsymutil on every binary is taking quite a long time
  3. LTO build time would be significantly faster :wink:

I build specific tools an order of magnitude
more often than building all of them, so personally I would consider
this downside really critical.

This would be a build settings, you wouldn’t lose anything if you don’t opt-in.

I haven't looked at the implementation, but conceptually this looks nice!

Indeed, really nice!

We talked internally about an option to build something like a single "llvm"
binary that would be symlinked by opt/llc/etc. So that when you invoke
`opt`, it would run the same binary but internally the right subcommand set
of options would be used. The downside is that running `ninja llvm-mc` would
depends on every LLVM libraries though.

I think this would muddle things, but it would also help merge all
command line options (ex. -mtriple vs. -triple vs -target).

The only way I know we could solve the dependency, though, is if there
was a binary called "llvm-mc" from "llvm-mc.cpp" which provided its
own view of the "llvm-mc" functionality (ie. just a shell), and "llvm"
would be also just a shell to all of them together.

So, "ninja llvm" would build all tools, while "ninja llvm-mc" would
only build one of them. Now, how do you then transform from a binary
called "llvm-mc" to a symlink to "llvm", it'd probably happen at
install time if you choose to install "developer tools", which I find
quite useful from say, an "llvm-dev" package.

This is a bit orthogonal to what you’re doing, but I assume your patch would
help to build such an option right?

Unless you're proposing to move from "llvm-mv" to "llvm mc", I don't
see how this could...

cheers,
--renato

From: llvm-dev [mailto:llvm-dev-bounces@lists.llvm.org] On Behalf Of
Renato Golin via llvm-dev
Sent: Saturday, June 18, 2016 4:02 AM
To: Mehdi Amini
Cc: llvm-dev
Subject: Re: [llvm-dev] Supporting sub commands in LLVM command line tools

> I haven't looked at the implementation, but conceptually this looks
nice!

Indeed, really nice!

> We talked internally about an option to build something like a single
"llvm"
> binary that would be symlinked by opt/llc/etc. So that when you invoke
> `opt`, it would run the same binary but internally the right subcommand
set
> of options would be used. The downside is that running `ninja llvm-mc`
would
> depends on every LLVM libraries though.

I think this would muddle things, but it would also help merge all
command line options (ex. -mtriple vs. -triple vs -target).

We get some of that with include/llvm/CodeGen/CommandFlags.h, which
mostly has flags to package up TargetOptions but also a few other
common things. But getting more consistency across the tools, with
or without commonizing the tool itself, seems like it could only be
a good thing.
--paulr

Is anyone willing to / interested in reviewing the patch? Or perhaps suggesting a good person to add to the reviewer list?

Oh, there was a patch?! :slight_smile:

I have no idea who would be the best people to review scripts. Some
people there seem interested, so I think it's going well, for now.

I've added the people in this thread, just in case. :slight_smile:

But "people in this thread", feel free to add more people if you know
they are interested.

cheers,
--renato