TL;DR: This classical formal framework for abstract interpretation of programs can be applied in extenso to logic programs and is recalled, using a variant of SLD-resolution as the ground operational semantics.
Abstract: interpretation is a theory of semantics approximation that is used for the construction of semantic-based program analysis algorithms (sometimes called “data flow analysis”), the comparison of formal semantics (e.g., construction of a denotational semantics from an operational one), design of proof methods, etc.
Automatic program analysers are used for determining statistically conservative approximations of dynamic properties of programs. Such properties of the run-time behavior of programs are useful for debugging (e.g., type inference), code optimization (e.g., compile-time garbage collection, useless occur-check elimination), program transformation (e.g., partial evaluation, parallelization), and even program correctness proofs (e.g., termination proof).
After a few simple introductory examples, we recall the classical framework for abstract interpretation of programs. Starting from a ground operational semantics formalized as a transition system, classes of program properties are first encapsulated in collecting semantics expressed as fixpoints on partial orders representing concrete program properties. We consider invariance properties characterizing descendants of the initial states (corresponding to top/down or forward analyses), ascendant states of the final states (corresponding to bottom/up or backward analyses) as well as a combination of the two. Then we choose specific approximate abstract properties to be gathered about program behaviors and express them as elements of a poset of abstract properties. The correspondence between concrete and abstract properties is established by a concretization and abstraction function that is a Galois connection formalizing the loss of information. We can then constructively derive the abstract program properties from the collecting semantics by a formal computation leading to a fixpoint expression in terms of abstract operators on the domain of abstract properties. The design of the abstract interpreter then involves the choice of a chaotic iteration strategy to solve this abstract fixpoint equation. We insist on the compositional design of this abstract interpreter, which is formalized by a series of propositions for designing Galois connections (such as Moore families, decomposition by partitioning, reduced product, down-set completion, etc.). Then we recall the convergence acceleration methods using widening and narrowing allowing for the use of very expressive infinite domains of abstract properties.
We show that this classical formal framework can be applied in extenso to logic programs. For simplicity, we use a variant of SLD-resolution as the ground operational semantics. The first example is groundness analysis, which is a variant of Mellish mode analysis. It is extended to a combination of top/down and bottom/up analyses. The second example is the derivation of constraints among argument sizes, which involves an infinite abstract domain requiring the use of convergence accelaration methods. We end up with a short thematic guide to the literature on abstract interpretation of logic programs.
TL;DR: A dynamic technique called PIE (propagation, infection, and execution) is presented for statistically estimating three program characteristics that affect a program's computational behavior that can be used to predict whether faults are likely to be uncovered by software testing.
Abstract: A dynamic technique called PIE (propagation, infection, and execution) is presented for statistically estimating three program characteristics that affect a program's computational behavior: (1) the probability that a particular section of a program is executed, (2) the probability that the particular section affects the data state, and (3) the probability that a data state produced by that section has an effect on program output. These three characteristics can be used to predict whether faults are likely to be uncovered by software testing. >
TL;DR: A language-independent program representation-the program dependence graph- is described and it is discussed how program dependence graphs can provide the basis for powerful programmmg tools that address important software-engineering problems.
Abstract: This paper describes a language-independent program representation-the program dependence graph-and discusses how program dependence graphs, together with operations such as program slicing, can provide the basis for powerful programmmg tools that address important software-engineering problems, such as understanding what an existing program does and how it works, understanding the differences between several versions of a program, and creating new programs by combining pieces of old pro- grams. The paper primarily surveys work in this area that has been carried out at the University of Wisconsin during the past five years.
TL;DR: An approach to automated concept recognition and its application to maintenance-related program transformations are described, with a unique characteristic that transformations of code can be expressed as transformations of abstract concepts.
Abstract: The automated recognition of abstract high-level conceptual information or concepts, which can greatly aid the understanding of programs and therefore support many software maintenance and reengineering activities, is considered. An approach to automated concept recognition and its application to maintenance-related program transformations are described. A unique characteristic of this approach is that transformations of code can be expressed as transformations of abstract concepts. This significantly elevates the level of transformation specifications. >
TL;DR: A new approach is introduced that leads to improved analysis and transformation of programs with recursively-defined pointer data structures by presenting an analysis approach that combines an alias analysis technique, path matrix, with information available from an ADDS declaration.
Abstract: Even though impressive progress has been made in the area of optimizing and parallelizing programs with arrays, the application of similar techniques to programs with pointer data structures has remained difficult. In this paper we introduce a new approach that leads to improved analysis and transformation of programs with recursively-defined pointer data structures.We discuss how an abstract data structure description can improve program analysis by presenting an analysis approach that combines an alias analysis technique, path matrix, with information available from an ADDS declaration. Given this improved alias analysis technique, we provide a concrete example of applying a software pipelining transformation to loops involving pointer data structures.
TL;DR: Comp compiler algorithms for the program analysis for array privatization played a critical role in successful parallelization of several real programs.
Abstract: In recent experiments, array privatization played a critical role in successful parallelization of several real programs. This paper presents compiler algorithms for the program analysis for this transformation. The paper also addresses issues in the implementation.
TL;DR: A program flow analysis framework is proposed for parallelizing compilers where symbolic analysis is used as an abstract interpretation technique to solve many of the flow analysis problems in a unified way to exploitation of parallelism and optimization of the code.
Abstract: A program flow analysis framework is proposed for parallelizing compilers. Within this framework, symbolic analysis is used as an abstract interpretation technique to solve many of the flow analysis problems in a unified way. Some of these problems are constant propagation, global forward substitution, detection of loop invariant computations, and induction variable substitution. The solution space of the above problems is much larger than that handled by existing compiler technology. It covers many of the cases in benchmark codes that other parallelizing compilers can not handle. Employing finite difference methods, the symbolic analyzer derives a functional representation of programs, which is used in dependence analysis. A systematic method for generalized strength reduction based on this representation is also presented. This results in an effective scheme for exploitation of parallelism and optimization of the code. Symbolic analysis also serves as a basis for other code generation optimizations such as elimination of redundant computations and restructuring of arithmetic expressions.
TL;DR: In a computer system, each program has a program attribute list associated with it, for associating different aggregates of program attributes with each of a number of invocation contexts in relation to that program as mentioned in this paper.
Abstract: In a computer system, each program has a program attribute list associated with it, for associating different aggregates of program attributes with each of a number of invocation contexts in relation to that program. In operation, a process is given attributes depending on the current context/program combination, as determined by the program attribute table for that program. The program attributes may, for example, include a set of privileges and the context may include the identity of the user on whose behalf the program is to be run, so that a program can be given different sets of privileges depending on which user runs the program.
TL;DR: The system provides a high-level specification language to let programmers specify what they want to know about their program's execution and automatically generates an augmented program whose execution produces both the results of the original program and answers to the specified questions.
Abstract: Program monitoring and measuring is the activity of collecting information about the execution characteristics of a program. Although this activity is occasionally supported by special-purpose hardware, it is normally done by adding instrumentation code to the program so that it collects interesting data as it runs. Unfortunately, this alteration is itself a difficult task involving all the complexities of programming. Given some questions to be answered, the programmer must determine what data must be collected, determine where in the program those data can be collected, and add code to the program to collect that data and to process it to produce the desired results. The goal of the work described is to automate the process. A high-level program monitoring and measuring system is presented. The system provides a high-level specification language to let programmers specify what they want to know about their program's execution. It automatically generates an augmented program whose execution produces both the results of the original program and answers to the specified questions. >
TL;DR: The dissertation explores a particular definition of program adaptivity and develops some logic for reasoning about adaptive properties of a program, and program composition operators are proposed for the construction of adaptive programs.
Abstract: An adaptive program is one that changes its behavior in response to its environment. For the case of a distributed program in a distributed environment, selection of appropriate behavior for the program's current environment is a problem of distributed control. The dissertation explores a particular definition of program adaptivity. Based on that definition, the dissertation develops some logic for reasoning about adaptive properties of a program. Accompanying the logic, program composition operators are proposed for the construction of adaptive programs. Case studies demonstrate how the composition operators can be applied to construct distributed adaptive programs for mutual exclusion, routing in a communication network, and graph biconnectivity.
The definition of adaptivity is related to the concept of self-stabilization: a self-stabilizing program converges to legitimate behavior from any point in its state-space, including illegitimate states; an adaptive program converges from any point in its state-space to appropriate behavior, where the definition of appropriate behavior depends on the state of the program's environment. This strongly convergent notion of adaptivity means that a distributed adaptive program correctly adapts in spite of an asynchronously changing environment and that the mechanism of adaptivity does not depend on initialization phases or timing protocols; the definition of adaptivity separates the concern of distributed control from the concern of synchronizing environmental change with program computation.
TL;DR: In this paper, a software engineering tool is disclosed which enables the efficiency and performance of a program design to be evaluated prior to the time the program is written into code, and reports are generated which point out the longest paths in the program and sources of potential performance problems.
Abstract: A software engineering tool is disclosed which enables the efficiency and performance of a program design to be evaluated prior to the time the program is written into code. Every possible path that can be followed in the implementation of the program is identified, and its length is measured. From this information, reports are generated which point out the longest paths in the program and sources of potential performance problems. In addition, weights which identify relative complexities or performance timings can be assigned to individual modules in the program, and form the basis of other reports which indicate timing performance. The user is provided with the opportunity to alter the weights assigned to modules, and thereby determine the effect which different weights have on the overall performance of the program.
TL;DR: A tool that bridges the gap between the theory and practice of program analysis specifications is described, which eases the design of analysis algorithms as well as the derivation of correctness proofs.
Abstract: A tool that bridges the gap between the theory and practice of program analysis specifications is described. The tool supports a high-level specification language that enables clear and concise expression of analysis algorithms. The denotational nature of the specifications eases the derivation of formal proofs of correctness for the analysis algorithm. SPARE (structured program analysis refinement environment) is based on a hybrid approach that combines the positive aspects of both the operational and the semantics-driven approach. An extended denotational framework is used to provide specifications in a modular fashion. Several extensions to the traditional denotational specification language have been designed to allow analysis algorithms to be expressed in a clear and concise fashion. This extended framework eases the design of analysis algorithms as well as the derivation of correctness proofs. The tool provides automatic implementation for testing purposes. >
TL;DR: A new approach is presented that leads to the improved analysis and transformation of programs with recursively defined pointer data structures based on a mechanism for the Abstract Description of Data Structures (ADDS).
Abstract: Even though impressive progress has been made in the area of optimizing and parallelizing array-based programs, the application of similar techniques to programs using pointer data structures has remained difficult. Unlike arrays which have a small number of well-defined properties, pointers can be used to implement a wide variety of structures which exhibit a much larger set of properties. The diversity of these structures implies that programs with pointer data structures cannot be effectively analyzed by traditional optimizing and parallelizing compilers.In this paper we present a new approach that leads to the improved analysis and transformation of programs with recursively defined pointer data structures. Our approach is based on a mechanism for the Abstract Description of Data Structures (ADDS). ADDS is a simple extension to existing imperative languages that allows the programmer to explicitly describe the important properties of a large class of data structures. These abstract descriptions may be used by the compiler to achieve more accurate program analysis in the presence of pointers, which in turn enables and improves the application of numerous optimizing and parallelizing transformations. We present ADDS by describing various data structures; we discuss how such descriptions can be used to improve analysis and debugging; and we supply three important transformations enabled by ADDS.
TL;DR: A semantics-based algorithm for multi-procedure integration that makes use of the system dependence graph, interprocedural slicing, and some additional preprocessing of theSystem dependence graph to correctly account for calling context.
Abstract: The magnitude and complexity of existing and future software systems heightens the need for tools that assist programmers with the task of maintaining and developing software. One recurring problem that arises in system development is the need to reconcile multiple divergent lines of program development. When solved by hand, this reconciliation or integration process is often tedious and error prone. A better solution is the use of a program integration tool--a tool that takes as input several variants of a base program, automatically determines the changes in each variant with respect to the base program, and incorporates these changes, along with the portion of the base program preserved in all the variants, into a merged program.
Previous algorithms that solve the program-integration problem include text-based approaches, such as that used by the U scNIX diff3 utility. However, the text-based approach is unsatisfactory since it fails to guarantee any relationship between the behavior of the integrated program and the behaviors of the base program and its variants. In contrast, semantics-based approaches to program integration can exploit knowledge of a language's semantics to provide such a guarantee.
Previous semantics-based algorithms, however, have been limited to single-procedure programs. This dissertation extends one of these algorithms to handle programs that consist of multiple (possibly mutually recursive) procedures. In doing so it makes the following three major contributions.
(1) Definition of the system dependence graph. This graph extends previous dependence representations to incorporate collections of procedures and the interconnections between them.
(2) New algorithms for interprocedural slicing. These algorithms compute several kinds of interprocedural slices (i.e., slices that cross the boundaries between procedures). By solving the calling-context problem the algorithms described in this dissertation produce smaller (i.e., better) slices than previous algorithms.
(3) A semantics-based algorithm for multi-procedure integration. As with slicing, the chief difficulty in multi-procedure integration is correctly accounting for calling context. The algorithm makes use of the system dependence graph, interprocedural slicing, and some additional preprocessing of the system dependence graph to correctly account for calling context.
TL;DR: An efficient algorithm for flow analysis of sequential logic programs is described, an approach to handle parallel executions is extended, and how infinite chains in the analysis domain can be accommodated without compromising termination is described.
Abstract: A framework for efficient dataflow analyses of logic programs is investigated. A number of problems arise in this context: aliasing effects can make analysis computationally expensive for sequential logic programming languages; synchronization issues can complicate the analysis of parallel logic programming languages; and finiteness restrictions to guarantee termination can limit the expressive power of such analyses. Our main result is to give a simple characterization of a family of flow analyses where these issues can be ignored without compromising soundness. This results in algorithms that are simple to verify and implement, and efficient in execution. Based on this approach, we describe an efficient algorithm for flow analysis of sequential logic programs, extend this approach to handle parallel executions, and finally describe how infinite chains in the analysis domain can be accommodated without compromising termination.
TL;DR: This paper argues that the conventional wisdom holds that only offline program specializers, which use binding time annotations, can be specialized into such efficient program generators, and demonstrates that the specialization of a nontrivial online program specializer similar to the original "naive MIX" can indeed yield an efficient program generator.
Abstract: Program specializers improve the speed of programs by performing some of the programs'' reductions at specialization time rather than at runtime. This specialization process can be time-consuming; one common technique for improving the speed of the specialization of a particular program is to specialize the specializer itself on that program, creating a custom specializer, or program generator, for that particular program. Much research has been devoted to the problem of generating efficient program generators, which do not perform reductions at program generation time which could instead have been performed when the program generator was constructed. The conventional wisdom holds that only offline program specializers, which use binding time annotations, can be specialized into such efficient program generators. This paper argues that this is not the case, and demonstrates that the specialization of a nontrivial online program specializer similar to the original "naive MIX" can indeed yield an efficient program generator. The key to our argument is that, while the use of binding time information at program generator generation time is necessary for the construction of an efficient custom specializer, the use of explicit binding time approximation techniques is not. This allows us to distinguish the problem at hand (i.e., the use of binding time information during program generator generation) from particular solutions to that problem (i.e., offline specialization). We show that, given a careful choice of specializer data structures, and sufficiently powerful specialization techniques, binding time information can be inferred and utilized without the use of explicit binding time approximation techniques. This allows the construction of efficient, optimizing program generators from online program specializers.
TL;DR: Although tremendous advances have been made in dependence theory and in the development of a “toolkit” of transformations, parallel systems are used most effectively when the programmer interacts in the optimization process.
Abstract: The exploitation of today's high-performance computer systems requires the effective use of parallelism in many forms and at numerous levels. This survey article discusses program analysis and restructuring techniques that target parallel architectures. We first describe various categories of architectures that are oriented toward parallel computation models: vector architectures, shared-memory multiprocessors, massively parallel machines, message-passing architectures, VLIWs, and multithreaded architectures. We then describe a variety of optimization techniques that can be applied to sequential programs to effectively utilize the vector and parallel processing units. After an overview of basic dependence analysis, we present restructuring transformations on DO loops targeted both to vectorization and to concurrent execution, interprocedural and pointer analysis, task scheduling, instruction-level parallelization, and compiler-assisted data placement. We conclude that although tremendous advances have been made in dependence theory and in the development of a “toolkit” of transformations, parallel systems are used most effectively when the programmer interacts in the optimization process.
TL;DR: In this view, verification is the empirical investigation of: (a) the behavior that programs invoke in a computer system and (b) the larger context in which that behavior occurs.
Abstract: A proof of ‘correctness’ for a mathematical algorithm cannot be relevant to executions of a program based on that algorithm because both the algorithm and the proof are based on assumptions that do not hold for computations carried out by real-world computers. Thus, proving the ‘correctness’ of an algorithm cannot establish the trustworthiness of programs based on that algorithm. Despite the (deceptive) sameness of the notations used to represent them, the transformation of an algorithm into an executable program is a wrenching metamorphosis that changes a mathematical abstraction into a prescription for concrete actions to be taken by real computers. Therefore, it is verification of program executions (processes) that is needed, not of program texts that are merely the scripts for those processes. In this view, verification is the empirical investigation of: (a) the behavior that programs invoke in a computer system and (b) the larger context in which that behavior occurs. Here, deduction can play no more, and no less, a role than it does in the empirical sciences.
TL;DR: This paper provides the theoretical foundations for analyzing parallel programs and illustrates how the theory can be applied to estimate the execution time of a class of parallel programs being executed on a MIMD computer.
Abstract: This paper consists of two parts: the first provides the theoretical foundations for analyzing parallel programs and illustrates how the theory can be applied to estimate the execution time of a class of parallel programs being executed on a MIMD computer. The second part describes a program analysis system, based on the theoretical model, which allows a user to interactively analyze the results of executing (or simulating the execution) of such parallel programs. Several examples illustrating the use of the tool are presented. A novel contribution is the separation (both at the conceptual and the implementation levels) of the machine-independent and the machine-dependent parts of the analysis. This separation enables the users of the system to establish speed-up curves for machines having varying characteristics.
TL;DR: Several tools enabling their user to estimate the efficiency of Pascal or C‐like programs are proposed and described, considerably reduced by the combined usage of Prolog and a symbolic formula manipulation package (Maple).
Abstract: The paper proposes and describes several tools enabling their user to estimate the efficiency of Pascal or C-like programs. The approach consists of generating symbolic formulas expressing the efficiency of the programs being analyzed. The formulas are applicable to a variety of compiler-machine configurations. The actual numeric values of the variables in the symbolic formula are determined using linear programming techniques. The proposed approach reduces considerably the amount of benchmarking needed to analyze programs. Several examples are presented showing the applicability of the tools. The effort necessary to implement them is considerably reduced by the combined usage of Prolog and a symbolic formula manipulation package (Maple).
TL;DR: Correctness properties of object code can be formally derived from corresponding correctness properties of the source program which have been proved using the programming logic, and so can be sure the results do apply to the object code.
Abstract: When developing safety-critical software, it is the correctness of the object code that is paramount. However, it is desirable to perform formal verification on the source program. To ensure that correctness results proved about the source program do apply to the object code, the compiler used can be formally verified. However, care must be taken to ensure that the compiler correctness theorem proved is suitable. We have combined a derived programming logic with a verified compiler for a generic subset of the Vista structured assembly language. We show how correctness properties of object code can be formally derived from corresponding correctness properties of the source program which have been proved using the programming logic. Thus we can be sure the results do apply to the object code. The work described has been performed using the HOL system and so is machine-checked.
TL;DR: A strategy for quickly producing special-purpose tools is described that has been successfully used in the context of the maintenance and reverse engineering of a large, real-time software system used for telephony.
Abstract: A strategy for quickly producing special-purpose tools is described. The strategy combines existing tools including simple off-the-shelf text processing tools; rule-based, language-specific analysis tool; and a commercial CASE (computer-aided software engineering) tool. The strategy has been successfully used in the context of the maintenance and reverse engineering of a large, real-time software system used for telephony. The approach is illustrated by examples that include the generation of cross reference information, calling trees, run-time stack analysis, and code restructuring. >
TL;DR: An algorithm that uses program dependence graphs for static program analysis to validate secure information flow is presented and is able to deal with a wider class of information flow policies than the well-known lattice model of secure Information flow.
TL;DR: This paper addresses a shortcoming of previous work in the area by establishing a more general framework for logics, as is commonly done for progam analysis using abstract interpretation, and there are natural extensions of this work which deal with polymorphic languages.
Abstract: Using logics to express program properties, and deduction systems for proving properties of programs, gives a very elegant way of defining program analysis techniques. This paper addresses a shortcoming of previous work in the area by establishing a more general framework for such logics, as is commonly done for progam analysis using abstract interpretation. Moreover, there are natural extensions of this work which deal with polymorphic languages.
TL;DR: This paper presents a novel approach to automated test data generation that is based on actual execution of the program under test, a run-time scheduler, function-minimization methods, and dynamic dataflow analysis.
Abstract: Test data generation in program testing is the process of identifying a set of test data that satisfies given testing criteria. One of the major problems in dynamic testing of distributed software is reproducible program execution. Repeated executions of a distributed program with the same test data may result in execution of different program paths. Unlike for sequential programs, a test case for a distributed program must contain more than input data; it must also provide appropriate choices for nondeterministic selections. The paper presents a novel approach to automated test data generation that is based on actual execution of the program under test, a run-time scheduler, function-minimization methods, and dynamic dataflow analysis. Test data are developed for the program using actual values of the input variables. When the program is executed, the program execution flow is monitored. If during program execution an undesirable execution flow is observed (e.g., the ‘actual’ path does not correspond to the selected path), then function-minimization search algorithms are used to locate automatically the values of input variables for which the selected path is traversed. In addition, dynamic data-flow analysis is used to determine those input variables responsible for the undesirable program behaviour; this can lead to significant speed-up of the test data generation process.
TL;DR: Opium, the authors' debugger for Prolog, sets up a framework where program sources and traces of program executions can be jointly analysed and gives more flexibility and more power than the hard coded command sets of usual tracers.
Abstract: The data used by program analysis in general is often restricted to the source code of the analysed programs. However, there is a complementary source of information, namely traces of program executions. Usual tracers, which extract this trace information, do not allow for general trace analysis. Opium, our debugger for Prolog, sets up a framework where program sources and traces of program executions can be jointly analysed. As the debugging process is heuristic and not all the debugging strategies have been identified so far, Opium is programmable. In particular, its trace query language gives more flexibility and more power than the hard coded command sets of usual tracers. This trace query language is based on Prolog. Opium is therefore both a helpful tool for Prolog and a nice application of Prolog. The most innovative extensions of Opium compute abstract views of Prolog executions to help users understand the behaviours of programs. In particular they help them understand how error symptoms have been produced. In the following we briefly recall some general information about Opium. A debugging session is then commented in detail. The commands used in the session are described in appendix. A detailed description of Opium can be found in [7].
TL;DR: This program addresses some important analysis problems in cardiovascular research, and allows the user to survey and manipulate cardiovascular waveforms in an intuitive and spontaneous manner.
Abstract: Computerized cardiovascular waveform processing has become a necessary and fundamental tool in the analysis of physiologic data. The availability of numerous commercial data-analysis programs has significantly enhanced the efficiency of waveform analysis. Many such programs come with their own programming language, which enables the researcher to create an application program suited to a specific series of calculations. Once written, an application program can significantly increase the efficiency with which data from a specific experiment can be analyzed. However, creating or modifying a program for each new experimental protocol can be time-consuming, especially in the error-detection and verification stages. Single-purpose programs also prove somewhat inflexible to unexpected changes in experimental formats. These problems suggested the need for a more flexible program, but one that is nevertheless specifically suited to the analysis of cardiovascular signals. This need led to the development of a program designed in collaboration with surgeons and physiologists. This program addresses some important analysis problems in cardiovascular research, and allows the user to survey and manipulate cardiovascular waveforms in an intuitive and spontaneous manner.
TL;DR: This paper proposes a new, message-oriented implementation technique for Moded Flat GHC that compiled unification for data transfer into message passing, and builds an experimental implementation on Sequent Symmetry, which preliminary evaluation shows good parallel speedup and good absolute performance for concurrent operations on binary process trees.
TL;DR: The results suggest that increasing the accuracy of the analysis globally is not a good idea, and that future research should investigate adaptive algorithms that use different amounts of precision on different parts of the problem.
Abstract: Interpretation, a powerful and general framework for performing global program analysis, is being applied to problems whose difficulty far surpasses the traditional "bit-vector'''' dataflow problems for which many of the high-speed abstract interpretation algorithms worked so well. Our experience has been that current methods of large scale abstract interpretation are unacceptably expensive. We studied a typical large-scale abstract interpretation problem: computing the control flow of a higher order program. Researchers have proposed various solutions that are designed primarily to improve the accuracy of the analysis. The cost of the analyses, and its relationship to accuracy, is addressed only cursorily in the literature. Somewhat paradoxically, one can view these strategies as attempts to simultaneously improve the accuracy and reduce the cost. The less accurate strategies explore many spurious control paths because many flowgraph paths represent illegal execution paths. For example, the less accurate strategies violate the LIFO constraints on procedure call and return. More accurate analyses investigate fewer control paths, and therefore may be more efficient despite their increased overhead. We empirically studied this accuracy versus efficiency tradeoff. We implemented two fixpoint algorithms, and four semantics (baseline, baseline + stack reasoning, baseline + contour reasoning, baseline + stack reasoning + contour reasoning) for a total of eight control flow analyzers. Our benchmarks test various programming constructs in isolation --- hence, if a certain algorithm exhibits poor performance, the experiment also yields insight into what kind of program behavior results in that poor performance. The results suggest that strategies that increase accuracy in order to eliminate spurious paths often generate unacceptable overhead in the parts of the analysis that do not benefit from the increased accuracy. Furthermore, we found little evidence that the extra effort significantly improves the accuracy of the final result. This suggests that increasing the accuracy of the analysis globally is not a good idea, and that future research shouldEinvestigate adaptive algorithms that use different amounts of precision on different parts of the problem.