Command-line options
This is a full listing of LLPE’s command-line arguments. Note various of these need you to name basic blocks, for which the nameblocks pre-pass will likely be required (see, for example, the conditional specialisation usage example).
Input
-llpe-root fname: start analysis at functionfnameentry point (defaultmain).-llpe-target-stack fname,bbname,index: construct a target stack that constitutes the region of interest for specialisation. For example, I might want to specialise functionhspecifically for the case when it is called via the pathf -> g -> h, and could specify this using the directive-llpe-root f -llpe-target-stack f,1,2 -llpe-target-stack g,3,4, where functionfblock1instruction 2 is a call togandgblock3instruction 4 is a call toh. By contrast with simply using-llpe-root hwe will be able to assume the calling environment implied byfandg. Any path that would miss the target call(s) will lead immediately to unspecialised code. When a target stack is in use, path condition directives (see below) may specify an integer stack index instead of a function name to provide a path condition about a specific specialisation context instead of every invocation of a particular function.-spec-env idx,envfile: load key=value fileenvfileand pass it to the specialisation root argumentidxas anenvironstylechar**.-spec-argv argc_idx,argv_idx,argfile: similarly, load line-delimitedargfileand pass appropriateargcandargvvalues to the specialisation root function.-spec-param idx,value: unconditionally set specialisation root argumentidxtovalue, wherevaluecan be an integer, constant string or function name. No runtime check is produced.-int-spec-stdin filename: specialise assuming that reads fromstdinyield the contents of filefilename.-llpe-use-global-initialisers: Assume all globals have their initialiser values when the specialisation root is entered.-llpe-force-noalias-arg N: Assume that specialisation root function argumentNdoes not alias any global or other pointer argument to the root function.
Output
-integrator-accept-all: Don’t show the GUI; just accept whatever specialisation decisions would have been taken per default.-llpe-graphs-dir DIR: Set where to outputdotandpngfiles for the GUI / for subsequent user reference.-llpe-verbose-overdef: Report on stderr when an analysed instruction causes us to clear symbolic memory, usually because it wrote to an unknown address.-llpe-verbose-path-conditions: Output a program that reports on stderr when specialised code is entered and exited, and for what reason.-llpe-prelude-fn fname: Nominates a particular function to have thelliowd_initcall added that initiates communication with LLPE’s file-watcher daemon, for the case where file contents were read during specialisation. For example, it might be profitable to do this at the top ofmainrather than re-check our connection every time specialised code is entered. Default: the specialisation root.-llpe-prelude-stackidx index: as above, but specify a stack index whenllpe-target-stackis in use instead of naming a function.-llpe-write-llio-conf pathname: Write a configuration file suitable for LLPE’s file-watcher daemon aspathname. Default: write to stdout.-llpe-stats-file pathname: Report summary statistics concerning specialisation topathname.-llpe-omit-checks: Omit path condition checks, checks against thread interference, checks against concurrent modification of files. This effectively makes all specialisation unconditional, and thus unly correct if you can externally verify them before and/or during execution of the specialised code produced.-llpe-omit-malloc-checks: Also omit checks againstmallocet al failing at runtime. The resulting program’s behaviour if allocation does fail is unpredictable.-llpe-force-split fname: When dividing the output program into residual functions, force LLPE to start a new function at any specialisation context concerning functionfname. If no split points are specified, LLPE will try to emit output functions of roughly 10,000 instructions each.-llpe-emit-fake-debug: Write dummy debug information attributing each target instruction to a specialisation context number and basic block name. Useful for tracking back from a faulting specialised program to the context that generated the code.
Specialisation directives
-llpe-always-inline function: Always create a new specialisation context forfunction, even if it appears to be a recursive call. This might produce an infinite specialisation tree if it can in fact recurse indefinitely.-llpe-always-explore function: Always act as if the given function is certain to be entered (i.e. explore child calls and loops in detail).-llpe-optimistic-loop fname,bb1,bb2: If we’re investigating a loop in functionfnameand the edgebb1 -> bb2is shown unreachable, investigate another iteration. In other words, assume that the loop will always exit using that edge, or at least an exit using that edge will terminate specialisation.-llpe-always-iterate fname,header: Always keep investigating iterations for loopheaderin functionfnameuntil the latch edge is shown unreachable.-llpe-assume-edge fname,bb1,bb2: Assume that edgebb1 -> bb2in functionfnamewill be taken for the purposes of deciding whether to explore loops and recursive functions in detail. Normally only loops that are certain to be entered given the specialisation assumptions will be explored in detail; now ifbb1might branch to another targetbb3, the successorbb2will be treated as if it was also in straight-line code.-llpe-ignore-loop fname,header: Don’t create per-iteration specialisation contexts for loopheaderin functionfname, even if it is certain to be entered given the specialisation assumptions. Child loops may be investigated in detail however. May be broken as of LLVM 3.6.-llpe-ignore-loop-children fname,header: as above, but don’t investigate child loops either.-llpe-loop-max N: stop exploring any loop that would iterate more thanNtimes; investigate the general case of the loop body instead in this case.-llpe-malloc-alignment BYTES: Assume anything allocated bymallocis aligned toBYTESgranularity-llpe-stop-after N: Create at most N specialisation contexts.-llpe-special-location fname,size: Mark functionfnameas returning a singlesize-byte object that does not alias any visible global, stack or heap allocation. Effectively the function is treated as an extra global; this may be useful for some implementations oferrno, for example, to avoid having to analyse thread-local storage. Note multiple calls to the annotated function return the same object (i.e. it is not an allocator).-llpe-model-function realf,modelf: Markrealfas modelled bymodelf: whenever a call torealfis encounteredmodelfwill be analysed instead. For example,realfcould be a hard-to-analyse efficient implementation (for example, one that depends on memory alignment) andmodelfcould be a simpler implementation that has the same side-effects.modelfmay callmallocto indicate thatrealfintroduces some new object (i.e. one that doesn’t alias any other visible object).-llpe-allocator-fn mallocname,mallocarg,freename,freearg: Annotate functionmallocname, which takes an allocation size as parametermallocarg, andfreename, which takes a pointer to free as parameterfreearg, to be treated analogous tomallocandfree. For example, functionsmyallocandmyfreewith the same prototypes asmallocandfreecould be specified:-llpe-allocator-fn myalloc,0,myfree,0-llpe-allocator-fn-const allocname,allocsize,freename,freearg,reallocname,reallocptrarg,reallocsizearg: similar to above, butallocnamedoesn’t take a size as a parameter but instead always returns objects of sizeallocsize.reallocnameworks likerealloc, taking a pointer asreallocptrargand a size argumentreallocsizearg.-llpe-never-inline fname: Never create a specialisation context forfname; just treat it as a barrier with unknown side-effects.-int-elim-read-checks: Try to eliminate redundant checks against concurrent file modification, assuming it is only necessary to check again after a thread yield point. The resulting program may be incorrect if another process alters a specialised file.
Concurrency annotations
-llpe-single-threaded: Assume there is only one thread of control, including due to signal handlers, which in this case must not interact with the path under specialisation. Checks that would otherwise be required at apparent yield points are omitted.-llpe-yield-function fname: Markfnameas a function that shouldn’t be analysed, which may alter any memory, but is expected not to. Thus it is treated the same as a thread yield point, after which the specialised program will need to check against possible side-effects, but more optimistically than an undefined function, where we pessimistically drop all knowledge regarding symbolic memory. This could be useful to annotate a mutex-unlock function, for example.-llpe-simple-volatile-load fname,bbname,index: markvolatileload instruction in functionfname/ blockbbname/ positionindexas simple, meaning that thevolatilemodifier only implies that the loaded value itself may be unrepeatable (e.g. for a mapped hardware register), not that general thread interference might have occurred. Unannotated volatiles are treated as thread yield points which may corrupt any memory.-llpe-lock-domain fname,bbname,index,global1[,global2[,global3...]]: mark the call at functionfname/ blockbbname/ positionindexas acquiring a lock that controls access to the given list of globals only (thus only those globals need to be checked for concurrent modification after it is acquired). Unannotated locks may govern access to any memory, so a global recheck becomes necessary.-llpe-pessimistic-lock fname,bbname,index: mark the call at functionfname/ blockbbname/ positionindexas acquiring a lock that is expected to have been held by other threads since it was last acquired. Thus the lock’s domain (or all memory, if no domain is given) is cleared in symbolic memory, rather than just being marked for re-checking as in the default, optimistic case.
Path conditions
Path conditions specify that we are to make some assumption about a particular value or memory location as of some point in the program. A runtime check will be emitted to ensure that this is truly the case (and branch to unspecialised code otherwise), and specialisation will proceed using the assumption.
Their arguments have the general format targetFunction,targetBB,targetIndex,assumption,assumeFunction,assumeBB[,offset], which means: assume that targetFunction block targetBB instructionn #targetIndex takes value assumption as of the top of assumeFunction block assumeBB. The optional offset applies to the target instruction, and only makes sense when it is pointer-typed. For example, llpe-path-condition-str "f,1,0,Hello world!,f,2,16" would mean to assume that if f block 1 begins with %x = alloca [ 128 x i8 ], assume that (char*)(x + 16) contains C-string Hello world! as of f block 2`. The specific arguments affect the nature of the assumption.
The target block can be special value __args__, in which case targetIndex specifies an argument, or __globals__, in which case targetIndex should be replaced with a global name.
-llpe-path-condition-int: target is an integer-typed instruction; assumption is an integer; offset is not applicable.-llpe-path-condition-fptr: target is an function-pointer-typed instruction; assumption is a function name; offset is not applicable.-llpe-path-condition-str: target is an pointer-typed instruction; assumption is a string.-llpe-path-condition-intmem: target is an pointer-to-integer-typed instruction; assumption is an integer (written with width relating to the type of the target).-llpe-path-condition-fptrmem: target is a pointer-typed instruction; assumption is a function name.-llpe-path-condition-stream: target is an instruction with the same type as a file descriptor; assumption is filename containing the data that the file descriptor will yield from this point on.-llpe-path-condition-global-unmodified: target is a global; assumption is ignored; the global will be assumed to retain its initialiser value from the specified assumption point.
The following special path condition is also possible:
-llpe-path-condition-func targetFunction,targetBlock,assumeFunction,verifyFunction[,arg1Function,arg1Block,arg1Index[,arg2Function...]]: at the top oftargetFunctionblocktargetBlock, executeassumeFunctionduring specialisation to make some arbitrary writes to memory or other side-effects expressing an assumption, and thenverifyFunctionat runtime to check that the assumption holds.argNFunction,BlockandIndexspecify values to be passed to both the assume and verify functions. For example, the assume function might set up some complex data structure and then the verify function check that it is as expected.
Sub-passes
-skip-benefit-analysis: disable automatic guessing of which contexts should and shouldn’t be enabled (emitted as specialised code).-skip-llpe-die: skip dead instruction analysis.-skip-check-elim: skip eliminating redundant checks against thread interference.-llpe-enable-sharing: share function specialisation contexts when possible. Almost certainly bit-rotted beyond use at the moment.-llpe-verbose-sharing: report when function sharing happens.-int-skip-post-commit: skip the last optimisation stage, when e.g. basic blocks with a single predecessor and successor are merged. Useful if you want to know the original block name for a particular instruction.