In this recipe, we will look at some more transformational passes, which are more like of utility passes. We will look at the strip-debug-symbols
pass and the prune-eh
pass.
$ cat teststripdebug.ll @x = common global i32 0 ; <i32*> [#uses=0] define void @foo() nounwind readnone optsize ssp { entry: tail call void @llvm.dbg.value(metadata i32 0, i64 0, metadata !5, metadata !{}), !dbg !10 ret void, !dbg !11 } declare void @llvm.dbg.value(metadata, i64, metadata, metadata) nounwind readnone !llvm.dbg.cu = !{!2} !llvm.module.flags = !{!13} !llvm.dbg.sp = !{!0} !llvm.dbg.lv.foo = !{!5} !llvm.dbg.gv = !{!8} !0 = !MDSubprogram(name: "foo", linkageName: "foo", line: 2, isLocal: false, isDefinition: true, virtualIndex: 6, isOptimized: true, file: !12, scope: !1, type: !3, function: void ()* @foo) !1 = !MDFile(filename: "b.c", directory: "/tmp") !2 = !MDCompileUnit(language: DW_LANG_C89, producer: "4.2.1 (Based on Apple Inc. build 5658) (LLVM build)", isOptimized: true, emissionKind: 0, file: !12, enums: !4, retainedTypes: !4) !3 = !MDSubroutineType(types: !4) !4 = !{null} !5 = !MDLocalVariable(tag: DW_TAG_auto_variable, name: "y", line: 3, scope: !6, file: !1, type: !7) !6 = distinct !MDLexicalBlock(line: 2, column: 0, file: !12, scope: !0) !7 = !MDBasicType(tag: DW_TAG_base_type, name: "int", size: 32, align: 32, encoding: DW_ATE_signed) !8 = !MDGlobalVariable(name: "x", line: 1, isLocal: false, isDefinition: true, scope: !1, file: !1, type: !7, variable: i32* @x) !9 = !{i32 0} !10 = !MDLocation(line: 3, scope: !6) !11 = !MDLocation(line: 4, scope: !6) !12 = !MDFile(filename: "b.c", directory: "/tmp") !13 = !{i32 1, !"Debug Info Version", i32 3}
strip-debug-symbols
pass by passing the –strip-debug
command-line option to the opt
tool:$ opt -strip-debug teststripdebug.ll -S ; ModuleID = ' teststripdebug.ll' @x = common global i32 0 ; Function Attrs: nounwind optsize readnone ssp define void @foo() #0 { entry: ret void } attributes #0 = { nounwind optsize readnone ssp } !llvm.module.flags = !{!0} !0 = metadata !{i32 1, metadata !"Debug Info Version", i32 2}
prune-eh
pass:$ cat simpletest.ll declare void @nounwind() nounwind define internal void @foo() { call void @nounwind() ret void } define i32 @caller() { invoke void @foo( ) to label %Normal unwind label %Except Normal: ; preds = %0 ret i32 0 Except: ; preds = %0 landingpad { i8*, i32 } personality i32 (...)* @__gxx_personality_v0 catch i8* null ret i32 1 } declare i32 @__gxx_personality_v0(...)
–prune-eh
command-line option to the opt tool:$ opt -prune-eh -S simpletest.ll ; ModuleID = 'simpletest.ll' ; Function Attrs: nounwind declare void @nounwind() #0 ; Function Attrs: nounwind define internal void @foo() #0 { call void @nounwind() ret void } ; Function Attrs: nounwind define i32 @caller() #0 { call void @foo() br label %Normal Normal: ; preds = %0 ret i32 0 } declare i32 @__gxx_personality_v0(...) attributes #0 = { nounwind }
In the first case, where we are running the strip-debug
pass, it removes the debug information from the code, and we can get compact code. This pass must be used only when we are looking for compact code, as it can delete the names of virtual registers and the symbols for internal global variables and functions, thus making the source code less readable and making it difficult to reverse engineer the code.
The part of code that handles this transformation is located in the llvm/lib/Transforms/IPO/StripSymbols.cpp
file, where the StripDeadDebugInfo::runOnModule
function is responsible for stripping the debug information.
The second test is for removing unused exception information using the prune-eh
pass, which implements an interprocedural pass. This walks the call-graph, turning invoke instructions into call instructions only if the callee cannot throw an exception, and marking functions as nounwind
if they cannot throw the exceptions.
13.58.5.57