首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何避免LLVM支持CommandLine泄漏库参数?

如何避免LLVM支持CommandLine泄漏库参数?
EN

Stack Overflow用户
提问于 2015-12-08 03:27:50
回答 3查看 1.7K关注 0票数 11

我一直在为我的一种语言编写编译器,并希望利用LLVM支持库CommandLine来处理参数解析。

我只添加了两个简单的声明:

代码语言:javascript
复制
static cl::opt<std::string>
OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename"));

static cl::list<std::string> 
InputFilenames("i", cl::desc("Input files"), cl::value_desc("filenames"), cl::OneOrMore);

然后,我在main中添加了通常的调用:

代码语言:javascript
复制
int main(int argc, char *argv[])
{
    cl::ParseCommandLineOptions(argc, argv, " My compiler\n");

...

当将-help传递给我的程序时,问题非常明显:

代码语言:javascript
复制
General options:

  -aarch64-neon-syntax             - Choose style of NEON code to emit from AArch64 backend:
    =generic                       -   Emit generic NEON assembly
    =apple                         -   Emit Apple-style NEON assembly
  -cppfname=<function name>        - Specify the name of the generated function
  -cppfor=<string>                 - Specify the name of the thing to generate
  -cppgen                          - Choose what kind of output to generate
    =program                       -   Generate a complete program
    =module                        -   Generate a module definition
    =contents                      -   Generate contents of a module
    =function                      -   Generate a function definition
    =functions                     -   Generate all function definitions
    =inline                        -   Generate an inline function
    =variable                      -   Generate a variable definition
    =type                          -   Generate a type definition
  -debugger-tune                   - Tune debug info for a particular debugger
    =gdb                           -   gdb
    =lldb                          -   lldb
    =sce                           -   SCE targets (e.g. PS4)
  -disable-spill-fusing            - Disable fusing of spill code into instructions
  -enable-implicit-null-checks     - Fold null checks into faulting memory operations
  -enable-load-pre                 - 
  -enable-objc-arc-opts            - enable/disable all ARC Optimizations
  -enable-scoped-noalias           - 
  -enable-tbaa                     - 
  -exhaustive-register-search      - Exhaustive Search for registers bypassing the depth and interference cutoffs of last chance recoloring
  -gpsize=<uint>                   - Global Pointer Addressing Size.  The default size is 8.
  -i=<filenames>                   - Input files
  -imp-null-check-page-size=<uint> - The page size of the target in bytes
  -join-liveintervals              - Coalesce copies (default=true)
  -limit-float-precision=<uint>    - Generate low-precision inline sequences for some float libcalls
  -merror-missing-parenthesis      - Error for missing parenthesis around predicate registers
  -merror-noncontigious-register   - Error for register names that aren't contigious
  -mfuture-regs                    - Enable future registers
  -mips16-constant-islands         - Enable mips16 constant islands.
  -mips16-hard-float               - Enable mips16 hard float.
  -mno-compound                    - Disable looking for compound instructions for Hexagon
  -mno-ldc1-sdc1                   - Expand double precision loads and stores to their single precision counterparts
  -mno-pairing                     - Disable looking for duplex instructions for Hexagon
  -mwarn-missing-parenthesis       - Warn for missing parenthesis around predicate registers
  -mwarn-noncontigious-register    - Warn for register names that arent contigious
  -mwarn-sign-mismatch             - Warn for mismatching a signed and unsigned value
  -nvptx-sched4reg                 - NVPTX Specific: schedule for register pressue
  -o=<filename>                    - Output filename
  -print-after-all                 - Print IR after each pass
  -print-before-all                - Print IR before each pass
  -print-machineinstrs=<pass-name> - Print machine instrs
  -regalloc                        - Register allocator to use
    =default                       -   pick register allocator based on -O option
    =fast                          -   fast register allocator
    =greedy                        -   greedy register allocator
    =pbqp                          -   PBQP register allocator
  -rewrite-map-file=<filename>     - Symbol Rewrite Map
  -rng-seed=<seed>                 - Seed for the random number generator
  -stackmap-version=<int>          - Specify the stackmap encoding version (default = 1)
  -stats                           - Enable statistics output from program (available with Asserts)
  -time-passes                     - Time each pass, printing elapsed time for each on exit
  -verify-debug-info               - 
  -verify-dom-info                 - Verify dominator info (time consuming)
  -verify-loop-info                - Verify loop info (time consuming)
  -verify-regalloc                 - Verify during register allocation
  -verify-region-info              - Verify region info (time consuming)
  -verify-scev                     - Verify ScalarEvolution's backedge taken counts (slow)
  -x86-asm-syntax                  - Choose style of code to emit from X86 backend:
    =att                           -   Emit AT&T-style assembly
    =intel                         -   Emit Intel-style assembly

Generic Options:

  -help                            - Display available options (-help-hidden for more)
  -help-list                       - Display list of available options (-help-list-hidden for more)
  -version                         - Display the version of this program

我显然想要减少噪音,只显示相关的命令行选项,我已经暴露了。

我意识到CommandLine实用程序正在使用全局变量和模板元编程,我的问题可能是因为静态地链接到LLVM。

我已经找到了几个似乎涉及到这个问题的链接,但是除了可能动态链接到LLVM之外,没有任何具体的解决方案。

我正在运行OS Capitan版本10.11.1

我按照以下方式安装了llvm:

代码语言:javascript
复制
git clone http://llvm.org/git/llvm.git
git clone http://llvm.org/git/clang.git llvm/tools/clang
git clone http://llvm.org/git/clang-tools-extra.git llvm/tools/clang/tools/extra
git clone http://llvm.org/git/compiler-rt.git llvm/projects/compiler-rt
git clone http://llvm.org/git/libcxx.git llvm/projects/libcxx
git clone http://llvm.org/git/libcxxabi.git llvm/projects/libcxxabi

mkdir build_llvm
cd build_llvm && cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=prefix=/usr/local/llvm ../llvm
make

我的Makefile的相关部分:

代码语言:javascript
复制
LLVMCONFIG = llvm-config
CPPFLAGS = `$(LLVMCONFIG) --cxxflags` -std=c++11
LDFLAGS = `$(LLVMCONFIG) --ldflags` -lpthread -ldl -lz -lncurses -rdynamic
LIBS = `$(LLVMCONFIG) --libs`

%.o: %.cpp
    clang++ -I /usr/local/llvm/include -c $(CPPFLAGS) $< -o $@

mycompiler: $(OBJECTS)
    clang++ -I /usr/local/llvm/include -g $^ $(LIBS) $(LDFLAGS) -o $@
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-12-09 04:40:55

据我所见,最好的方法是将所有选项放入特定的类别中,并使用void::cl::HideUnrelatedOptions(ArrayRef类别) (如果只有一个类别,则使用cl::HideUnrelatedOptions(cl::OptionCategory &cl) )。文档表明,clang使用它来获得对命令行选项的控制,尽管我还没有在clang源代码中签入,以确定这是否真的正确。

票数 6
EN

Stack Overflow用户

发布于 2015-12-11 02:04:33

杰夫·瑞迪的回答对我非常有用,所以我接受了他的回答,不过,我想给出更多关于你如何设置它的细节,以及我发现的一些其他信息。

与以前一样,我在main函数的上方有声明形式的选项,但是,我添加了一个声明性OptionCategory,并将其包含在该类别中的每个选项的声明中:

代码语言:javascript
复制
cl::OptionCategory 
CompilerCategory("Compiler Options", "Options for controlling the compilation process.");

static cl::opt<std::string>
OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename"), cl::cat(CompilerCategory));

static cl::list<std::string> 
InputFilenames("i", cl::desc("Input files"), cl::value_desc("filenames"), cl::OneOrMore, cl::cat(CompilerCategory));

然后,在HideUnrelatedOptions之前,在main中调用ParseCommandLineOptions

代码语言:javascript
复制
int main(int argc, char *argv[])
{
    cl::HideUnrelatedOptions( CompilerCategory );
    cl::ParseCommandLineOptions(argc, argv, " My compiler\n");

... 

现在,我的OPTIONS输出看起来好多了:

代码语言:javascript
复制
OPTIONS:

Compiler Options:
Options for controlling the compilation process.

  -i=<filenames> - Input files
  -o=<filename>  - Output filename

Generic Options:

  -help          - Display available options (-help-hidden for more)
  -help-list     - Display list of available options (-help-list-hidden for more)
  -version       - Display the version of this program

这基本上是将所有选项标记为cl::ReallyHidden,这样它们甚至不会出现在-help-hidden :)。

我发现的另一个有用的信息是在CommandLine图书馆手册上。

我知道LLVM是不断变化的,所以为了防止页面在将来消失,这里是一个例子:

使用命名空间llvm;int main(int argc,char **argv) {cl:options(“Some options");StringMap Map;cl::getRegisteredOptions(Map);//Unhide有用选项,并将其放入另一个类别断言(Map.count(”打印-所有选项“)> 0);Map"print-all-options"->setCategory(AnotherCategory);//隐藏我们不希望看到的断言选项( Map"enable-no-infs-fp-math"->setHiddenFlag(cl::Hidden);(“Map.count(”Map.count-no-infs-fp-math“)> 0);Map.count//更改-版本到-显示版本断言(Map.count(”版本“)> 0);地图版本”->setArgStr(“显示-版本”);//更改-帮助描述断言(Map.count(“帮助”)> 0);映射“帮助”->setDescription(“显示帮助”);cl::ParseCommandLineOptions(argc,argv,“这是一个演示LLVM CommandLine API的小程序”);.}

这基本上显示了系统的力量,并演示了如何修改特定的选项,无论您愿意。

票数 11
EN

Stack Overflow用户

发布于 2022-06-20 02:15:08

很抱歉让这条线从很久以前恢复过来。然而,我也面临着同样的问题,发现现有的两个答案是无价的。谢谢你,杰夫-雷迪和马修-桑德斯!当我遵循现有答案的建议时,我遇到了一个相当大的问题:

如果我用-help-hidden调用程序,即使没有显示其中包含的选项,类别标题(即使它们是无关的)仍然被打印出来。它产生了非常奇怪的输出。

所以,我研究了一些,并提出了一个替代的解决方案,基于令人敬畏的,现有的答案。为了子孙后代,我会把它张贴在这里,以防它帮助别人(可能是我的未来版本)!

与其声明命令行值(我希望程序接受这些值为文件作用域的全局变量(使用CommandLine库的典型建议),我必须将它们创建为某个作用域的局部变量。这在本质上推迟了在库的实现中声明的GlobalParser对象中选项的添加,直到详细说明了该范围。然后,在详细说明该范围之前,我调用了llvm::cl::ResetCommandLineParser。这从GlobalParser中删除了所有泄漏的命令行选项。然后,当遇到我的选项的构造函数时,它们是GlobalParser中唯一可用的命令行选项。

最后,我指定了一个“丢弃”llvm::raw_ostream (llvm::raw_null_ostream)来传递给调用llvm::cl::ParseCommandLineOptionsErrs参数。在对Errs的调用中存在llvm::cl::ParseCommandLineOptions参数会迫使库在解析参数时不终止程序。我指定了一个空输出流,因为我想要生成自己的错误消息,但是指定一个真正的流会给llvm::cl::ParseCommandLineOptions提供相同的信号,并允许您打印解析器-为用户生成错误消息。

当然,还有一些问题要解决。最值得注意的是,这种方法意味着我必须自己指定和处理-help (或-help-hidden)。使用llvm::cl::PrintHelpMessage()函数可以相对容易地做到这一点。接下来,我必须处理用户没有正确指定llvm::cl::Required选项的情况。处理这个问题也很简单--当解析命令行参数时出现错误时,cl::llvm::ParseCommandLineOptions将返回false

在全局范围内声明命令行选项变量的好处之一是其他函数可以轻松地访问它们。使用我所采用的方法,此功能不再可用。虽然我在编写的代码中没有处理这个问题,但可能会编写一组助手函数,从静态的函数本地存储中检索命令行选项变量的值。

作为参考,下面是我最后编写的代码:

代码语言:javascript
复制
[[noreturn]] void usage() {
  llvm::cl::PrintHelpMessage();
  exit(EXIT_FAILURE);
}

int main(int argc, char **argv) {  
  llvm::raw_null_ostream raw_discard{};
  llvm::cl::ResetCommandLineParser();

  llvm::cl::OptionCategory SeePlusPlusCategory(
      "See Plus Plus Options",
      "Options for controlling the behavior of See Plus Plus.");
  llvm::cl::opt<std::string> SourceCodeFilename(
      llvm::cl::Positional, llvm::cl::desc("<source file>"), llvm::cl::Required,
      llvm::cl::cat(SeePlusPlusCategory));
  llvm::cl::opt<bool> SuppressLineNumbers(
      "nl", llvm::cl::desc("Suppress line numbers in output."),
      llvm::cl::cat(SeePlusPlusCategory));
  llvm::cl::opt<bool> Help(
      "help", llvm::cl::desc("Display available options"),
      llvm::cl::ValueDisallowed, llvm::cl::cat(SeePlusPlusCategory));

  llvm::cl::HideUnrelatedOptions(SeePlusPlusCategory);
  if (!llvm::cl::ParseCommandLineOptions(argc, argv, "", &raw_discard)) {
    usage();
  }

  if (Help) {
    usage();
  }

  // Program continues from here.
}

再次感谢马修和杰夫的伟大回答。我希望我上面所写的帮助别人在未来的帮助,就像他们的答案帮助我一样!

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34147464

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档