diff options
431 files changed, 74977 insertions, 34 deletions
diff --git a/LICENSE.TXT b/LICENSE.TXT index 20b7da58..ce3677c5 100644 --- a/LICENSE.TXT +++ b/LICENSE.TXT @@ -11,8 +11,7 @@ Developed by: klee Team - Stanford Checking Group: Daniel Dunbar, Cristian Cadar, Peter - Pawlowki, Dawson Engler. + Stanford Checking Group http://klee.llvm.org @@ -30,10 +29,10 @@ so, subject to the following conditions: this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution. - * Neither the names of the LLVM Team, University of Illinois at - Urbana-Champaign, nor the names of its contributors may be used to - endorse or promote products derived from this Software without specific - prior written permission. + * Neither the names of the klee Team, Stanford University, nor the + names of its contributors may be used to endorse or promote + products derived from this Software without specific prior + written permission. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS @@ -50,10 +49,9 @@ This file will describe the copyrights, license, and restrictions which apply to that code. The disclaimer of warranty in the University of Illinois Open Source License -applies to all code in the klee Distribution, and nothing in any of the -other licenses gives permission to use the names of the klee Team or the -University of Illinois to endorse or promote products derived from this -Software. +applies to all code in the klee Distribution, and nothing in any of the other +licenses gives permission to use the names of the klee Team or Stanford +University to endorse or promote products derived from this Software. The following pieces of software have additional or alternate copyrights, licenses, and/or restrictions: @@ -61,4 +59,5 @@ licenses, and/or restrictions: Program Directory ------- --------- STP klee/stp +klee-libc runtime/klee-libc diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..ec9c7cca --- /dev/null +++ b/Makefile @@ -0,0 +1,57 @@ +#===-- klee/Makefile ---------------------------------------*- Makefile -*--===# +# +# The KLEE Symbolic Virtual Machine +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +# +# Indicates our relative path to the top of the project's root directory. +# +LEVEL = . + +DIRS = stp lib tools runtime +EXTRA_DIST = include + +# Only build support directories when building unittests. +ifeq ($(MAKECMDGOALS),unittests) + DIRS := $(filter-out tools runtime, $(DIRS)) unittests + OPTIONAL_DIRS := +endif + +# +# Include the Master Makefile that knows how to build all. +# +include $(LEVEL)/Makefile.common + +.PHONY: doxygen +doxygen: + doxygen docs/doxygen.cfg + +.PHONY: cscope.files +cscope.files: + find \ + lib include stp tools runtime examples unittests \ + -name Makefile -or \ + -name \*.in -or \ + -name \*.c -or \ + -name \*.cpp -or \ + -name \*.exp -or \ + -name \*.inc -or \ + -name \*.h | sort > cscope.files + +test:: + -(cd test/ && make) + +.PHONY: klee-cov +klee-cov: + rm -rf klee-cov + zcov-scan --look-up-dirs=1 klee.zcov . + zcov-genhtml --root $$(pwd) klee.zcov klee-cov + +clean:: + $(MAKE) -C test clean + $(MAKE) -C unittests clean + rm -rf docs/doxygen diff --git a/Makefile.common b/Makefile.common new file mode 100644 index 00000000..b8b352aa --- /dev/null +++ b/Makefile.common @@ -0,0 +1,26 @@ +# -*- Makefile -*- + +include $(LEVEL)/Makefile.config + +# Include LLVM's Master Makefile config and rules. +include $(LLVM_OBJ_ROOT)/Makefile.config + +ifeq ($(BYTECODE_LIBRARY), 1) +# +# Override make variables based on the runtime configuration. We want +# to override whatever the user may have said on the command line, +# hence the use of override. +# + +override ENABLE_OPTIMIZED := $(RUNTIME_ENABLE_OPTIMIZED) +override DISABLE_ASSERTIONS := $(RUNTIME_DISABLE_ASSERTIONS) +override ENABLE_PROFILING := $(RUNTIME_ENABLE_PROFILING) +override ENABLE_COVERAGE := $(RUNTIME_ENABLE_COVERAGE) +endif + +include $(LLVM_OBJ_ROOT)/Makefile.rules + +LD.Flags += -L$(PROJ_SRC_ROOT)/stp/lib +CXX.Flags += -DLLVM_23 +CXX.Flags += -I$(PROJ_SRC_ROOT)/stp/include +CXX.Flags += -DKLEE_DIR=\"$(PROJ_SRC_ROOT)\" diff --git a/Makefile.config.in b/Makefile.config.in new file mode 100644 index 00000000..0b858831 --- /dev/null +++ b/Makefile.config.in @@ -0,0 +1,45 @@ +# -*- Makefile -*- + +# Set the name of the project here +PROJECT_NAME := klee +PROJ_VERSION := 0.01 + +# Set this variable to the top of the LLVM source tree. +LLVM_SRC_ROOT = @LLVM_SRC@ + +# Set this variable to the top level directory where LLVM was built +# (this is *not* the same as OBJ_ROOT as defined in LLVM's Makefile.config). +LLVM_OBJ_ROOT = @LLVM_OBJ@ + +# Set the directory root of this project's source files +PROJ_SRC_ROOT := $(subst //,/,@abs_top_srcdir@) + +# Set the root directory of this project's object files +PROJ_OBJ_ROOT := $(subst //,/,@abs_top_objdir@) + +# Set the root directory of this project's install prefix +PROJ_INSTALL_ROOT := @prefix@ + +ENABLE_POSIX_RUNTIME := @ENABLE_POSIX_RUNTIME@ +ENABLE_STPLOG := @ENABLE_STPLOG@ +ENABLE_UCLIBC := @ENABLE_UCLIBC@ + +HAVE_SELINUX := @HAVE_SELINUX@ + +RUNTIME_ENABLE_OPTIMIZED := @RUNTIME_ENABLE_OPTIMIZED@ +RUNTIME_DISABLE_ASSERTIONS := +RUNTIME_ENABLE_COVERAGE := +RUNTIME_ENABLE_PROFILING := + +# A list of "features" which tests can check for in XFAIL: +TEST_FEATURE_LIST := + +ifeq ($(HAVE_SELINUX_SELINUX_H),1) + TEST_FEATURE_LIST += have-selinux +else + TEST_FEATURE_LIST += no-selinux +endif + +CFLAGS := @CFLAGS@ +CXXFLAGS := @CXXFLAGS@ +LDFLAGS := @LDFLAGS@ diff --git a/README.txt b/README.txt index 0c1a79e9..96d60a9e 100644 --- a/README.txt +++ b/README.txt @@ -1,24 +1,24 @@ //===----------------------------------------------------------------------===// // Klee Symbolic Virtual Machine //===----------------------------------------------------------------------===// - Daniel Dunbar klee is a symbolic virtual machine built on top of the LLVM compiler -infrastructure. Currently, there are two primary components. +infrastructure. Currently, there are two primary components: -1. The core symbolic virtual machine engine; this is responsible for -executing LLVM bitcode modules with support for symbolic values. This -is comprised of the code in lib/. + 1. The core symbolic virtual machine engine; this is responsible for + executing LLVM bitcode modules with support for symbolic + values. This is comprised of the code in lib/. -2. An emulation layer for the Linux system call interface, with -additional support for making parts of the operating environment -symbolic. This is found in models/simple. + 2. A POSIX/Linux emulation layer oriented towards supporting uClibc, + with additional support for making parts of the operating system + environment symbolic. -Additionally, there is a simple library in runtime/ which supports -replaying computed inputs on native code. There is a more complicated -library in replay/ which supports running inputs computed as part of -the system call emulation layer natively -- setting up files, pipes, -etc. on the native system to match the inputs that the emulation layer -provided. +Additionally, there is a simple library for replaying computed inputs +on native code (for closed programs). There is also a more complicated +infrastructure for replaying the inputs generated for the POSIX/Linux +emulation layer, which handles running native programs in an +environment that matches a computed test input, including setting up +files, pipes, environment variables, and passing command line +arguments. -For further information, see the docs in www/. +For further information, see the webpage or docs in www/. diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 00000000..bbdd4c86 --- /dev/null +++ b/TODO.txt @@ -0,0 +1,59 @@ +TODO +-- + +Build System / Configure / Release Cleanups +-- + o Rename .bout to .ktest (klee test) + + o Rename .pc to .kquery (kleaver query) + + o Configure doesn't check for bison / flex, we don't really use these + for anything important (just the command line STP tool), it would + be nice if they weren't required. + + o Need a way to hide LLVM options in "klee --help". + +Klee Internal +-- + o Make sure that namespaces and .cpp locations match with reorganized + include locations. + + o Add replay framework for POSIX model tests. + +Kleaver Internal +-- + o We need to fix the constants-in-exprs problem, this makes + separating out a Kleaver expr library much more difficult. There + are two parts: + + 1. Pull fast (pure constant) path operations out of Expr.cpp, + into Executor.cpp. + + 2. Lift constants-are-immediate optimization out of ref<Expr> + into Cell. Expressions in memory already have the concrete + cache, so we get that part for free. + + We will need a way to distinguish if a cell has an expr or a + constant. Incidentally, this gives us an extra sentinel value + (is-expr == true and Expr* == null) we can use to mark + uninitialized-value of a register. + + It may be worth sinking Expr construction into a Builder class + while we are at it. + + There is a also a nice cleanup/perf win where we can work with + registers (Cells) directly, now that we build the constant table, + it might be worth doing this at the same time. This exposes a win + for IVC where it can write back a constant value into a register, + which needs to be done with care but would be a big improvement for + IVC. + + o The stpArray field of an UpdateNode needs to die. This isn't as + easy as dropping it from the map, because we also need a + notification to free it. I think probably what we should do is + introduce an ExprContext can be used to deal with such things. + o The ExprContext could also have the default builder, for + example, which would make it easy to swap in an optimizing + builder. + + diff --git a/autoconf/AutoRegen.sh b/autoconf/AutoRegen.sh new file mode 100755 index 00000000..4a34f8c0 --- /dev/null +++ b/autoconf/AutoRegen.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +if ([ "$#" != 1 ] || + [ ! -d "$1" ] || + [ ! -d "$1/autoconf/m4" ]); then + echo "usage: $0 <llvmsrc-dir>" 1>& 2 + exit 1 +fi + +llvm_src_root=$(cd $1; pwd) +llvm_m4=$llvm_src_root/autoconf/m4 +die () { + echo "$@" 1>&2 + exit 1 +} +test -d autoconf && test -f autoconf/configure.ac && cd autoconf +test -f configure.ac || die "Can't find 'autoconf' dir; please cd into it first" +autoconf --version | egrep '2\.60' > /dev/null +if test $? -ne 0 ; then + die "Your autoconf was not detected as being 2.60" +fi +# Patch LLVM_SRC_ROOT in configure.ac +sed -e "s#^LLVM_SRC_ROOT=.*#LLVM_SRC_ROOT=\"$llvm_src_root\"#" \ + configure.ac > configure.tmp.ac +echo "Regenerating aclocal.m4 with aclocal" +rm -f aclocal.m4 +echo aclocal -I $llvm_m4 -I "$llvm_m4/.." || die "aclocal failed" +aclocal -I $llvm_m4 -I "$llvm_m4/.." || die "aclocal failed" +echo "Regenerating configure with autoconf 2.60" +echo autoconf --warnings=all -o ../configure configure.tmp.ac || die "autoconf failed" +autoconf --warnings=all -o ../configure configure.tmp.ac || die "autoconf failed" +cp ../configure ../configure.bak +sed -e "s#^LLVM_SRC_ROOT=.*#LLVM_SRC_ROOT=\".\"#" \ + ../configure.bak > ../configure +cd .. +echo "Regenerating config.h.in with autoheader" +autoheader --warnings=all \ + -I autoconf -I autoconf/m4 \ + autoconf/configure.tmp.ac || die "autoheader failed" +rm -f autoconf/configure.tmp.ac configure.bak +exit 0 diff --git a/autoconf/config.guess b/autoconf/config.guess new file mode 100755 index 00000000..7d0185e0 --- /dev/null +++ b/autoconf/config.guess @@ -0,0 +1,1447 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + +timestamp='2004-09-07' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Per Bothner <per@bothner.com>. +# Please send patches to <config-patches@gnu.org>. Submit a context +# diff and a properly formatted ChangeLog entry. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit build system type. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to <config-patches@gnu.org>." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep __ELF__ >/dev/null + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit 0 ;; + amd64:OpenBSD:*:*) + echo x86_64-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + amiga:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + cats:OpenBSD:*:*) + echo arm-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + hp300:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + luna88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + macppc:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvmeppc:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sgi:OpenBSD:*:*) + echo mips64-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sun3:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:OpenBSD:*:*) + echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit 0 ;; + macppc:MirBSD:*:*) + echo powerppc-unknown-mirbsd${UNAME_RELEASE} + exit 0 ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit 0 ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit 0 ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit 0 ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit 0 ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit 0;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit 0 ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit 0 ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit 0 ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit 0 ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit 0;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit 0;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit 0 ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit 0 ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit 0 ;; + DRS?6000:UNIX_SV:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7 && exit 0 ;; + esac ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit 0 ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit 0 ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit 0 ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit 0 ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit 0 ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit 0 ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit 0 ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit 0 ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit 0 ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit 0 ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit 0 ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include <stdio.h> /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c \ + && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ + && exit 0 + echo mips-mips-riscos${UNAME_RELEASE} + exit 0 ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit 0 ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit 0 ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit 0 ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit 0 ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit 0 ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit 0 ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit 0 ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit 0 ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit 0 ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit 0 ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit 0 ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit 0 ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit 0 ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit 0 ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include <sys/systemcfg.h> + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 + echo rs6000-ibm-aix3.2.5 + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit 0 ;; + *:AIX:*:[45]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit 0 ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit 0 ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit 0 ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit 0 ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit 0 ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit 0 ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit 0 ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include <stdlib.h> + #include <unistd.h> + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + # avoid double evaluation of $set_cc_for_build + test -n "$CC_FOR_BUILD" || eval $set_cc_for_build + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit 0 ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit 0 ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include <unistd.h> + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 + echo unknown-hitachi-hiuxwe2 + exit 0 ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit 0 ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit 0 ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit 0 ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit 0 ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit 0 ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit 0 ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit 0 ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit 0 ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit 0 ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit 0 ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit 0 ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit 0 ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:FreeBSD:*:*) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit 0 ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit 0 ;; + i*:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit 0 ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit 0 ;; + x86:Interix*:[34]*) + echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//' + exit 0 ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit 0 ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit 0 ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit 0 ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit 0 ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit 0 ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu + exit 0 ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit 0 ;; + arm*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + cris:Linux:*:*) + echo cris-axis-linux-gnu + exit 0 ;; + crisv32:Linux:*:*) + echo crisv32-axis-linux-gnu + exit 0 ;; + frv:Linux:*:*) + echo frv-unknown-linux-gnu + exit 0 ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + mips:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips + #undef mipsel + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mipsel + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` + test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 + ;; + mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips64 + #undef mips64el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mips64el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips64 + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` + test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 + ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit 0 ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit 0 ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit 0 ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit 0 ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit 0 ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit 0 ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit 0 ;; + i*86:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + # Set LC_ALL=C to ensure ld outputs messages in English. + ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ + | sed -ne '/supported targets:/!d + s/[ ][ ]*/ /g + s/.*supported targets: *// + s/ .*// + p'` + case "$ld_supported_targets" in + elf32-i386) + TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" + ;; + a.out-i386-linux) + echo "${UNAME_MACHINE}-pc-linux-gnuaout" + exit 0 ;; + coff-i386) + echo "${UNAME_MACHINE}-pc-linux-gnucoff" + exit 0 ;; + "") + # Either a pre-BFD a.out linker (linux-gnuoldld) or + # one that does not give us useful --help. + echo "${UNAME_MACHINE}-pc-linux-gnuoldld" + exit 0 ;; + esac + # Determine whether the default compiler is a.out or elf + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include <features.h> + #ifdef __ELF__ + # ifdef __GLIBC__ + # if __GLIBC__ >= 2 + LIBC=gnu + # else + LIBC=gnulibc1 + # endif + # else + LIBC=gnulibc1 + # endif + #else + #ifdef __INTEL_COMPILER + LIBC=gnu + #else + LIBC=gnuaout + #endif + #endif + #ifdef __dietlibc__ + LIBC=dietlibc + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=` + test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0 + test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0 + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit 0 ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit 0 ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit 0 ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit 0 ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit 0 ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit 0 ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit 0 ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit 0 ;; + i*86:*:5:[78]*) + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit 0 ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name` + echo ${UNAME_MACHINE}-pc-isc$UNAME_REL + elif /bin/uname -X 2>/dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit 0 ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i386. + echo i386-pc-msdosdjgpp + exit 0 ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit 0 ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit 0 ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit 0 ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit 0 ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit 0 ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit 0 ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4.3${OS_REL} && exit 0 + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4 && exit 0 ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit 0 ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit 0 ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit 0 ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says <Richard.M.Bartel@ccMail.Census.GOV> + echo i586-unisys-sysv4 + exit 0 ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes <hewes@openmarket.com>. + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit 0 ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit 0 ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit 0 ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit 0 ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit 0 ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit 0 ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit 0 ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit 0 ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit 0 ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit 0 ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + case $UNAME_PROCESSOR in + *86) UNAME_PROCESSOR=i686 ;; + unknown) UNAME_PROCESSOR=powerpc ;; + esac + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit 0 ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit 0 ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit 0 ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit 0 ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit 0 ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit 0 ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit 0 ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit 0 ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit 0 ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit 0 ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit 0 ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit 0 ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit 0 ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit 0 ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit 0 ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit 0 ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms && exit 0 ;; + I*) echo ia64-dec-vms && exit 0 ;; + V*) echo vax-dec-vms && exit 0 ;; + esac +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c <<EOF +#ifdef _SEQUENT_ +# include <sys/types.h> +# include <sys/utsname.h> +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include <sys/param.h> + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include <sys/param.h> +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0 + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit 0 ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + c34*) + echo c34-convex-bsd + exit 0 ;; + c38*) + echo c38-convex-bsd + exit 0 ;; + c4*) + echo c4-convex-bsd + exit 0 ;; + esac +fi + +cat >&2 <<EOF +$0: unable to guess system type + +This script, last modified $timestamp, has failed to recognize +the operating system you are using. It is advised that you +download the most up to date version of the config scripts from + + ftp://ftp.gnu.org/pub/gnu/config/ + +If the version you run ($0) is already up to date, please +send the following data and any information you think might be +pertinent to <config-patches@gnu.org> in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/autoconf/config.sub b/autoconf/config.sub new file mode 100755 index 00000000..edb6b663 --- /dev/null +++ b/autoconf/config.sub @@ -0,0 +1,1555 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + +timestamp='2004-08-29' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Please send patches to <config-patches@gnu.org>. Submit a context +# diff and a properly formatted ChangeLog entry. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to <config-patches@gnu.org>." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit 0;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \ + kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray) + os= + basic_machine=$1 + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ + | c4x | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | m32r | m32rle | m68000 | m68k | m88k | mcore \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64vr | mips64vrel \ + | mips64orion | mips64orionel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | msp430 \ + | ns16k | ns32k \ + | openrisc | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv8 | sparcv9 | sparcv9b \ + | strongarm \ + | tahoe | thumb | tic4x | tic80 | tron \ + | v850 | v850e \ + | we32k \ + | x86 | xscale | xstormy16 | xtensa \ + | z8k) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* \ + | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ + | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | mcore-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | msp430-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* \ + | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \ + | tahoe-* | thumb-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tron-* \ + | v850-* | v850e-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xps100-* | xscale-* | xstormy16-* \ + | xtensa-* \ + | ymp-* \ + | z8k-*) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16c) + basic_machine=cr16c-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + or32 | or32-*) + basic_machine=or32-unknown + os=-coff + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tic55x | c55x*) + basic_machine=tic55x-unknown + os=-coff + ;; + tic6x | c6x*) + basic_machine=tic6x-unknown + os=-coff + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -kaos*) + os=-kaos + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/autoconf/configure.ac b/autoconf/configure.ac new file mode 100644 index 00000000..9c24a1ef --- /dev/null +++ b/autoconf/configure.ac @@ -0,0 +1,253 @@ +dnl ************************************************************************** +dnl * Initialize +dnl ************************************************************************** +AC_INIT([[KLEE]],[[0.01]],[daniel@minormatter.com]) + +dnl Identify where LLVM source tree is (this is patched by +dnl AutoRegen.sh) +LLVM_SRC_ROOT=XXX + +dnl Tell autoconf that the auxilliary files are actually located in +dnl the LLVM autoconf directory, not here. +AC_CONFIG_AUX_DIR($LLVM_SRC_ROOT/autoconf) + +dnl Tell autoconf that this is an LLVM project being configured +dnl This provides the --with-llvmsrc and --with-llvmobj options +LLVM_CONFIG_PROJECT("","") + +dnl Verify that the source directory is valid +AC_CONFIG_SRCDIR(["Makefile.config.in"]) + +dnl Configure a common Makefile +AC_CONFIG_FILES(Makefile.config) +AC_CONFIG_FILES(stp/Makefile.common) + +dnl Configure project makefiles +dnl List every Makefile that exists within your source tree +AC_CONFIG_HEADERS([include/klee/Config/config.h]) + +dnl FIXME: Make out of tree builds work. + +AC_LANG([C++]) + +dnl ************************************************************************** +dnl Find the host + +AC_CANONICAL_TARGET + +dnl Determine the platform type and cache its value. This helps us configure +dnl the System library to the correct build platform. +AC_CACHE_CHECK([type of operating system we're going to host on], + [klee_cv_os_type], +[case $host in + *-*-linux*) + host_supports_posix_runtime=yes ;; + *) + host_supports_posix_runtime=no ;; +esac]) + +dnl ************************************************************************** +dnl Verify that we can find llvm + +dnl --with-llvm is a shortcut for setting srcdir and objdir. +AC_ARG_WITH(llvm, + AS_HELP_STRING([--with-llvm], + [Location of LLVM Source and Object code]),,) + +AC_MSG_CHECKING([llvm source dir]) + +if test X${with_llvm} != X; then + dnl Verify that --with-llvm{src,obj} were not given. + if test X${with_llvmsrc} != X; then + AC_MSG_ERROR([--with-llvmsrc cannot be specified when using --with-llvm]) + fi + if test X${with_llvmobj} != X; then + AC_MSG_ERROR([--with-llvmobj cannot be specified when using --with-llvm]) + fi + with_llvmsrc=$with_llvm + with_llvmobj=$with_llvm +fi + +dnl If one of with_llvmsrc or with_llvmobj was given, we must have both. +if (test X${with_llvmsrc} != X || test X${with_llvmobj} != X); then + dnl Verify that with_llvmobj was given as well. + if test X${with_llvmsrc} = X; then + AC_MSG_ERROR([--with-llvmsrc must be specified when using --with-llvmobj]) + fi + if test X${with_llvmobj} = X; then + AC_MSG_ERROR([--with-llvmobj must be specified when using --with-llvmsrc]) + fi +else + dnl Otherwise try and use llvm-config to find. + llvm_version=`llvm-config --version` + if test X${llvm_version} = X; then + AC_MSG_ERROR([unable to find llvm, use --with-llvmsrc and --with-llvmobj]) + fi + + with_llvmsrc=`llvm-config --src-root` + with_llvmobj=`llvm-config --obj-root` +fi + +dnl Try to validate directories +if test ! -f ${with_llvmsrc}/Makefile.rules; then + AC_MSG_ERROR([invalid llvmsrc directory: ${with_llvmsrc}]) +fi +if test ! -f ${with_llvmobj}/Makefile.config; then + AC_MSG_ERROR([invalid llvmobj directory: ${with_llvmobj}]) +fi + +dnl Make the paths absolute +llvm_src=`cd $with_llvmsrc 2> /dev/null; pwd` +llvm_obj=`cd $with_llvmobj 2> /dev/null; pwd` + +AC_MSG_RESULT([$llvm_src]) + +dnl Report obj dir as well. +AC_MSG_CHECKING([llvm obj dir]) +AC_MSG_RESULT([$llvm_obj]) + +AC_SUBST(LLVM_SRC,$llvm_src) +AC_SUBST(LLVM_OBJ,$llvm_obj) + +dnl ************************************************************************** +dnl User option to enable uClibc support. + +AC_ARG_WITH(uclibc, + AS_HELP_STRING([--with-uclibc], + [Enable use of the klee uclibc at the given path]),,) + +dnl If uclibc wasn't given, check for a uclibc in the current +dnl directory. +if (test X${with_uclibc} = X && test -d uclibc); then + with_uclibc=uclibc +fi + +dnl Validate uclibc if given. + +AC_MSG_CHECKING([uclibc]) +if (test X${with_uclibc} != X); then + if test ! -d ${with_uclibc}; then + AC_MSG_ERROR([invalid uclibc directory: ${with_uclibc}]) + fi + + dnl Make the path absolute + with_uclibc=`cd $with_uclibc 2> /dev/null; pwd` + + AC_MSG_RESULT([$with_uclibc]) +else + AC_MSG_RESULT([no]) +fi + +AC_DEFINE_UNQUOTED(KLEE_UCLIBC, "$with_uclibc", [Path to KLEE's uClibc]) +AC_SUBST(KLEE_UCLIBC) + +if test X${with_uclibc} != X ; then + AC_SUBST(ENABLE_UCLIBC,[[1]]) +else + AC_SUBST(ENABLE_UCLIBC,[[0]]) +fi + +dnl ************************************************************************** +dnl User option to enable the POSIX runtime + +AC_ARG_ENABLE(posix-runtime, + AS_HELP_STRING([--enable-posix-runtime], + [Enable the POSIX runtime]), + ,enableval=default) + +AC_MSG_CHECKING([POSIX runtime]) +if test ${enableval} = "default" ; then + if test X${with_uclibc} != X; then + enableval=$host_supports_posix_runtime + if test ${enableval} = "yes"; then + AC_MSG_RESULT([default (enabled)]) + else + AC_MSG_RESULT([default (disabled, unsupported target)]) + fi + else + enableval="no" + AC_MSG_RESULT([default (disabled, no uclibc)]) + fi +else + if test ${enableval} = "yes" ; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi +fi + +if test ${enableval} = "yes" ; then + AC_SUBST(ENABLE_POSIX_RUNTIME,[[1]]) +else + AC_SUBST(ENABLE_POSIX_RUNTIME,[[0]]) +fi + +dnl ************************************************************************** +dnl User option to select runtime version + +AC_ARG_WITH(runtime, + AS_HELP_STRING([--with-runtime], + [Select build configuration for runtime libraries (default [Release])]),, + withval=default) + +if test X"${withval}" = Xdefault; then + with_runtime=Release +fi + +AC_MSG_CHECKING([runtime configuration]) +if test X${with_runtime} = XRelease; then + AC_MSG_RESULT([Release]) + AC_SUBST(RUNTIME_ENABLE_OPTIMIZED,[[1]]) +elif test X${with_runtime} = XDebug; then + AC_MSG_RESULT([Debug]) + AC_SUBST(RUNTIME_ENABLE_OPTIMIZED,[[0]]) +else + AC_MSG_ERROR([invalid configuration: ${with_runtime}]) +fi + +AC_DEFINE_UNQUOTED(RUNTIME_CONFIGURATION, "$with_runtime", [Configuration for runtime libraries]) +AC_SUBST(RUNTIME_CONFIGURATION) + +dnl ************************************************************************** +dnl See if we should support __ctype_b_loc externals. + +dnl FIXME: Do the proper test if we continue to need this. +case $host in + *-*-linux*) + AC_DEFINE_UNQUOTED(HAVE_CTYPE_EXTERNALS, 1, [Does the platform use __ctype_b_loc, etc.]) +esac + +dnl ************************************************************************** +dnl Checks for header files. + +dnl NOTE: This is mostly just to force autoconf to make CFLAGS defines +dnl for us. +AC_LANG_PUSH([C]) + +AC_CHECK_HEADERS([sys/acl.h]) + +AC_LANG_POP([C]) + +AC_CHECK_HEADERS([selinux/selinux.h], + AC_SUBST(HAVE_SELINUX, 1), + AC_SUBST(HAVE_SELINUX, 0)) + +dnl User option to use stplog. + +AC_ARG_ENABLE(stplog, + AS_HELP_STRING([--enable-stplog], + [Compile with the stplog library [[disabled]]]), + ,enableval=no) +if test ${enableval} = "yes" ; then + AC_SUBST(ENABLE_STPLOG,[[1]]) +else + AC_SUBST(ENABLE_STPLOG,[[0]]) +fi +AC_DEFINE_UNQUOTED([ENABLE_STPLOG],$ENABLE_STPLOG,[Define if stplog enabled]) + +dnl ************************************************************************** +dnl * Create the output files +dnl ************************************************************************** + +dnl This must be last +AC_OUTPUT diff --git a/autoconf/install-sh b/autoconf/install-sh new file mode 100755 index 00000000..dd97db7a --- /dev/null +++ b/autoconf/install-sh @@ -0,0 +1,322 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2004-09-10.20 + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +chmodcmd="$chmodprog 0755" +chowncmd= +chgrpcmd= +stripcmd= +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src= +dst= +dir_arg= +dstarg= +no_target_directory= + +usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: +-c (ignored) +-d create directories instead of installing files. +-g GROUP $chgrpprog installed files to GROUP. +-m MODE $chmodprog installed files to MODE. +-o USER $chownprog installed files to USER. +-s $stripprog installed files. +-t DIRECTORY install into DIRECTORY. +-T report an error if DSTFILE is a directory. +--help display this help and exit. +--version display version info and exit. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG +" + +while test -n "$1"; do + case $1 in + -c) shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + --help) echo "$usage"; exit 0;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -s) stripcmd=$stripprog + shift + continue;; + + -t) dstarg=$2 + shift + shift + continue;; + + -T) no_target_directory=true + shift + continue;; + + --version) echo "$0 $scriptversion"; exit 0;; + + *) # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + test -n "$dir_arg$dstarg" && break + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dstarg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dstarg" + shift # fnord + fi + shift # arg + dstarg=$arg + done + break;; + esac +done + +if test -z "$1"; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call `install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +for src +do + # Protect names starting with `-'. + case $src in + -*) src=./$src ;; + esac + + if test -n "$dir_arg"; then + dst=$src + src= + + if test -d "$dst"; then + mkdircmd=: + chmodcmd= + else + mkdircmd=$mkdirprog + fi + else + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dstarg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + + dst=$dstarg + # Protect names starting with `-'. + case $dst in + -*) dst=./$dst ;; + esac + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dstarg: Is a directory" >&2 + exit 1 + fi + dst=$dst/`basename "$src"` + fi + fi + + # This sed command emulates the dirname command. + dstdir=`echo "$dst" | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + + # Make sure that the destination directory exists. + + # Skip lots of stat calls in the usual case. + if test ! -d "$dstdir"; then + defaultIFS=' + ' + IFS="${IFS-$defaultIFS}" + + oIFS=$IFS + # Some sh's can't handle IFS=/ for some reason. + IFS='%' + set - `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'` + IFS=$oIFS + + pathcomp= + + while test $# -ne 0 ; do + pathcomp=$pathcomp$1 + shift + if test ! -d "$pathcomp"; then + $mkdirprog "$pathcomp" + # mkdir can fail with a `File exist' error in case several + # install-sh are creating the directory concurrently. This + # is OK. + test -d "$pathcomp" || exit + fi + pathcomp=$pathcomp/ + done + fi + + if test -n "$dir_arg"; then + $doit $mkdircmd "$dst" \ + && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; } + + else + dstfile=`basename "$dst"` + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + trap '(exit $?); exit' 1 2 13 15 + + # Copy the file name to the temp name. + $doit $cpprog "$src" "$dsttmp" && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } && + + # Now rename the file to the real destination. + { $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \ + || { + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + if test -f "$dstdir/$dstfile"; then + $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \ + || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \ + || { + echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2 + (exit 1); exit + } + else + : + fi + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dstdir/$dstfile" + } + } + fi || { (exit 1); exit; } +done + +# The final little trick to "correctly" pass the exit status to the exit trap. +{ + (exit 0); exit +} + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/configure b/configure new file mode 100755 index 00000000..9ca86611 --- /dev/null +++ b/configure @@ -0,0 +1,6039 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.60 for KLEE 0.01. +# +# Report bugs to <daniel@minormatter.com>. +# +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +as_nl=' +' +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + +if test "x$CONFIG_SHELL" = x; then + if (eval ":") 2>/dev/null; then + as_have_required=yes +else + as_have_required=no +fi + + if test $as_have_required = yes && (eval ": +(as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=\$LINENO + as_lineno_2=\$LINENO + test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" && + test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; } +") 2> /dev/null; then + : +else + as_candidate_shells= + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /usr/bin/posix$PATH_SEPARATOR/bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + case $as_dir in + /*) + for as_base in sh bash ksh sh5; do + as_candidate_shells="$as_candidate_shells $as_dir/$as_base" + done;; + esac +done +IFS=$as_save_IFS + + + for as_shell in $as_candidate_shells $SHELL; do + # Try only shells that exist, to save several forks. + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { ("$as_shell") 2> /dev/null <<\_ASEOF +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +: +_ASEOF +}; then + CONFIG_SHELL=$as_shell + as_have_required=yes + if { "$as_shell" 2> /dev/null <<\_ASEOF +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +: +(as_func_return () { + (exit $1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = "$1" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test $exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; } + +_ASEOF +}; then + break +fi + +fi + + done + + if test "x$CONFIG_SHELL" != x; then + for as_var in BASH_ENV ENV + do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + done + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} +fi + + + if test $as_have_required = no; then + echo This script requires a shell more modern than all the + echo shells that I found on your system. Please install a + echo modern shell, or manually run the script under such a + echo shell if you do have one. + { (exit 1); exit 1; } +fi + + +fi + +fi + + + +(eval "as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0") || { + echo No shell found that supports shell functions. + echo Please tell autoconf@gnu.org about your system, + echo including any error possibly output before this + echo message +} + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir +fi +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +# Find out whether ``test -x'' works. Don't use a zero-byte file, as +# systems may use methods other than mode bits to determine executability. +cat >conf$$.file <<_ASEOF +#! /bin/sh +exit 0 +_ASEOF +chmod +x conf$$.file +if test -x conf$$.file >/dev/null 2>&1; then + as_executable_p="test -x" +else + as_executable_p=: +fi +rm -f conf$$.file + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + + +exec 7<&0 </dev/null 6>&1 + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Identity of this package. +PACKAGE_NAME='KLEE' +PACKAGE_TARNAME='-klee-' +PACKAGE_VERSION='0.01' +PACKAGE_STRING='KLEE 0.01' +PACKAGE_BUGREPORT='daniel@minormatter.com' + +ac_unique_file=""Makefile.config.in"" +# Factoring default headers for most tests. +ac_includes_default="\ +#include <stdio.h> +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#if HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#if STDC_HEADERS +# include <stdlib.h> +# include <stddef.h> +#else +# if HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include <memory.h> +# endif +# include <string.h> +#endif +#if HAVE_STRINGS_H +# include <strings.h> +#endif +#if HAVE_INTTYPES_H +# include <inttypes.h> +#endif +#if HAVE_STDINT_H +# include <stdint.h> +#endif +#if HAVE_UNISTD_H +# include <unistd.h> +#endif" + +ac_subst_vars='SHELL +PATH_SEPARATOR +PACKAGE_NAME +PACKAGE_TARNAME +PACKAGE_VERSION +PACKAGE_STRING +PACKAGE_BUGREPORT +exec_prefix +prefix +program_transform_name +bindir +sbindir +libexecdir +datarootdir +datadir +sysconfdir +sharedstatedir +localstatedir +includedir +oldincludedir +docdir +infodir +htmldir +dvidir +pdfdir +psdir +libdir +localedir +mandir +DEFS +ECHO_C +ECHO_N +ECHO_T +LIBS +build_alias +host_alias +target_alias +LLVM_SRC +LLVM_OBJ +build +build_cpu +build_vendor +build_os +host +host_cpu +host_vendor +host_os +target +target_cpu +target_vendor +target_os +KLEE_UCLIBC +ENABLE_UCLIBC +ENABLE_POSIX_RUNTIME +RUNTIME_ENABLE_OPTIMIZED +RUNTIME_CONFIGURATION +CC +CFLAGS +LDFLAGS +CPPFLAGS +ac_ct_CC +EXEEXT +OBJEXT +CPP +GREP +EGREP +HAVE_SELINUX +CXX +CXXFLAGS +ac_ct_CXX +CXXCPP +ENABLE_STPLOG +LIBOBJS +LTLIBOBJS' +ac_subst_files='' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +CPPFLAGS +CPP +CXX +CXXFLAGS +CCC +CXXCPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval enable_$ac_feature=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval enable_$ac_feature=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval with_$ac_package=\$ac_optarg ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval with_$ac_package=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute directory names. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; } +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + { echo "$as_me: error: Working directory cannot be determined" >&2 + { (exit 1); exit 1; }; } +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + { echo "$as_me: error: pwd does not report name of working directory" >&2 + { (exit 1); exit 1; }; } + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$0" || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || { echo "$as_me: error: $ac_msg" >&2 + { (exit 1); exit 1; }; } + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures KLEE 0.01 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/-klee-] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] + --target=TARGET configure for building compilers for TARGET [HOST] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of KLEE 0.01:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-posix-runtime Enable the POSIX runtime + --enable-stplog Compile with the stplog library [disabled] + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-llvmsrc Location of LLVM Source Code + --with-llvmobj Location of LLVM Object Code + --with-llvm Location of LLVM Source and Object code + --with-uclibc Enable use of the klee uclibc at the given path + --with-runtime Select build configuration for runtime libraries + (default Release) + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a + nonstandard directory <lib dir> + CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I<include dir> if + you have headers in a nonstandard directory <include dir> + CPP C preprocessor + CXX C++ compiler command + CXXFLAGS C++ compiler flags + CXXCPP C++ preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to <daniel@minormatter.com>. +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +KLEE configure 0.01 +generated by GNU Autoconf 2.60 + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by KLEE $as_me 0.01, which was +generated by GNU Autoconf 2.60. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args '$ac_arg'" + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 +echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------------- ## +## File substitutions. ## +## ------------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -n "$CONFIG_SITE"; then + set x "$CONFIG_SITE" +elif test "x$prefix" != xNONE; then + set x "$prefix/share/config.site" "$prefix/etc/config.site" +else + set x "$ac_default_prefix/share/config.site" \ + "$ac_default_prefix/etc/config.site" +fi +shift +for ac_site_file +do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + + + + + + + + + + + + + + + + + + + + + + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +LLVM_SRC_ROOT="." + +ac_aux_dir= +for ac_dir in $LLVM_SRC_ROOT/autoconf "$srcdir"/$LLVM_SRC_ROOT/autoconf; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $LLVM_SRC_ROOT/autoconf \"$srcdir\"/$LLVM_SRC_ROOT/autoconf" >&5 +echo "$as_me: error: cannot find install-sh or install.sh in $LLVM_SRC_ROOT/autoconf \"$srcdir\"/$LLVM_SRC_ROOT/autoconf" >&2;} + { (exit 1); exit 1; }; } +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + + + +# Check whether --with-llvmsrc was given. +if test "${with_llvmsrc+set}" = set; then + withval=$with_llvmsrc; llvm_src="$withval" +else + llvm_src="""" +fi + + LLVM_SRC=$llvm_src + + +# Check whether --with-llvmobj was given. +if test "${with_llvmobj+set}" = set; then + withval=$with_llvmobj; llvm_obj="$withval" +else + llvm_obj="""" +fi + + LLVM_OBJ=$llvm_obj + + ac_config_commands="$ac_config_commands setup" + + + + + +ac_config_files="$ac_config_files Makefile.config" + +ac_config_files="$ac_config_files stp/Makefile.common" + + +ac_config_headers="$ac_config_headers include/klee/Config/config.h" + + + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + + +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + { { echo "$as_me:$LINENO: error: cannot run $SHELL $ac_aux_dir/config.sub" >&5 +echo "$as_me: error: cannot run $SHELL $ac_aux_dir/config.sub" >&2;} + { (exit 1); exit 1; }; } + +{ echo "$as_me:$LINENO: checking build system type" >&5 +echo $ECHO_N "checking build system type... $ECHO_C" >&6; } +if test "${ac_cv_build+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5 +echo "$as_me: error: cannot guess build type; you must specify one" >&2;} + { (exit 1); exit 1; }; } +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + { { echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&5 +echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_build" >&5 +echo "${ECHO_T}$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) { { echo "$as_me:$LINENO: error: invalid value of canonical build" >&5 +echo "$as_me: error: invalid value of canonical build" >&2;} + { (exit 1); exit 1; }; };; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ echo "$as_me:$LINENO: checking host system type" >&5 +echo $ECHO_N "checking host system type... $ECHO_C" >&6; } +if test "${ac_cv_host+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + { { echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&5 +echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&2;} + { (exit 1); exit 1; }; } +fi + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_host" >&5 +echo "${ECHO_T}$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) { { echo "$as_me:$LINENO: error: invalid value of canonical host" >&5 +echo "$as_me: error: invalid value of canonical host" >&2;} + { (exit 1); exit 1; }; };; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + +{ echo "$as_me:$LINENO: checking target system type" >&5 +echo $ECHO_N "checking target system type... $ECHO_C" >&6; } +if test "${ac_cv_target+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "x$target_alias" = x; then + ac_cv_target=$ac_cv_host +else + ac_cv_target=`$SHELL "$ac_aux_dir/config.sub" $target_alias` || + { { echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $target_alias failed" >&5 +echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $target_alias failed" >&2;} + { (exit 1); exit 1; }; } +fi + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_target" >&5 +echo "${ECHO_T}$ac_cv_target" >&6; } +case $ac_cv_target in +*-*-*) ;; +*) { { echo "$as_me:$LINENO: error: invalid value of canonical target" >&5 +echo "$as_me: error: invalid value of canonical target" >&2;} + { (exit 1); exit 1; }; };; +esac +target=$ac_cv_target +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_target +shift +target_cpu=$1 +target_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +target_os=$* +IFS=$ac_save_IFS +case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac + + +# The aliases save the names the user supplied, while $host etc. +# will get canonicalized. +test -n "$target_alias" && + test "$program_prefix$program_suffix$program_transform_name" = \ + NONENONEs,x,x, && + program_prefix=${target_alias}- + +{ echo "$as_me:$LINENO: checking type of operating system we're going to host on" >&5 +echo $ECHO_N "checking type of operating system we're going to host on... $ECHO_C" >&6; } +if test "${klee_cv_os_type+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $host in + *-*-linux*) + host_supports_posix_runtime=yes ;; + *) + host_supports_posix_runtime=no ;; +esac +fi +{ echo "$as_me:$LINENO: result: $klee_cv_os_type" >&5 +echo "${ECHO_T}$klee_cv_os_type" >&6; } + + + +# Check whether --with-llvm was given. +if test "${with_llvm+set}" = set; then + withval=$with_llvm; +fi + + +{ echo "$as_me:$LINENO: checking llvm source dir" >&5 +echo $ECHO_N "checking llvm source dir... $ECHO_C" >&6; } + +if test X${with_llvm} != X; then + if test X${with_llvmsrc} != X; then + { { echo "$as_me:$LINENO: error: --with-llvmsrc cannot be specified when using --with-llvm" >&5 +echo "$as_me: error: --with-llvmsrc cannot be specified when using --with-llvm" >&2;} + { (exit 1); exit 1; }; } + fi + if test X${with_llvmobj} != X; then + { { echo "$as_me:$LINENO: error: --with-llvmobj cannot be specified when using --with-llvm" >&5 +echo "$as_me: error: --with-llvmobj cannot be specified when using --with-llvm" >&2;} + { (exit 1); exit 1; }; } + fi + with_llvmsrc=$with_llvm + with_llvmobj=$with_llvm +fi + +if (test X${with_llvmsrc} != X || test X${with_llvmobj} != X); then + if test X${with_llvmsrc} = X; then + { { echo "$as_me:$LINENO: error: --with-llvmsrc must be specified when using --with-llvmobj" >&5 +echo "$as_me: error: --with-llvmsrc must be specified when using --with-llvmobj" >&2;} + { (exit 1); exit 1; }; } + fi + if test X${with_llvmobj} = X; then + { { echo "$as_me:$LINENO: error: --with-llvmobj must be specified when using --with-llvmsrc" >&5 +echo "$as_me: error: --with-llvmobj must be specified when using --with-llvmsrc" >&2;} + { (exit 1); exit 1; }; } + fi +else + llvm_version=`llvm-config --version` + if test X${llvm_version} = X; then + { { echo "$as_me:$LINENO: error: unable to find llvm, use --with-llvmsrc and --with-llvmobj" >&5 +echo "$as_me: error: unable to find llvm, use --with-llvmsrc and --with-llvmobj" >&2;} + { (exit 1); exit 1; }; } + fi + + with_llvmsrc=`llvm-config --src-root` + with_llvmobj=`llvm-config --obj-root` +fi + +if test ! -f ${with_llvmsrc}/Makefile.rules; then + { { echo "$as_me:$LINENO: error: invalid llvmsrc directory: ${with_llvmsrc}" >&5 +echo "$as_me: error: invalid llvmsrc directory: ${with_llvmsrc}" >&2;} + { (exit 1); exit 1; }; } +fi +if test ! -f ${with_llvmobj}/Makefile.config; then + { { echo "$as_me:$LINENO: error: invalid llvmobj directory: ${with_llvmobj}" >&5 +echo "$as_me: error: invalid llvmobj directory: ${with_llvmobj}" >&2;} + { (exit 1); exit 1; }; } +fi + +llvm_src=`cd $with_llvmsrc 2> /dev/null; pwd` +llvm_obj=`cd $with_llvmobj 2> /dev/null; pwd` + +{ echo "$as_me:$LINENO: result: $llvm_src" >&5 +echo "${ECHO_T}$llvm_src" >&6; } + +{ echo "$as_me:$LINENO: checking llvm obj dir" >&5 +echo $ECHO_N "checking llvm obj dir... $ECHO_C" >&6; } +{ echo "$as_me:$LINENO: result: $llvm_obj" >&5 +echo "${ECHO_T}$llvm_obj" >&6; } + +LLVM_SRC=$llvm_src + +LLVM_OBJ=$llvm_obj + + + + +# Check whether --with-uclibc was given. +if test "${with_uclibc+set}" = set; then + withval=$with_uclibc; +fi + + +if (test X${with_uclibc} = X && test -d uclibc); then + with_uclibc=uclibc +fi + + +{ echo "$as_me:$LINENO: checking uclibc" >&5 +echo $ECHO_N "checking uclibc... $ECHO_C" >&6; } +if (test X${with_uclibc} != X); then + if test ! -d ${with_uclibc}; then + { { echo "$as_me:$LINENO: error: invalid uclibc directory: ${with_uclibc}" >&5 +echo "$as_me: error: invalid uclibc directory: ${with_uclibc}" >&2;} + { (exit 1); exit 1; }; } + fi + + with_uclibc=`cd $with_uclibc 2> /dev/null; pwd` + + { echo "$as_me:$LINENO: result: $with_uclibc" >&5 +echo "${ECHO_T}$with_uclibc" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + +cat >>confdefs.h <<_ACEOF +#define KLEE_UCLIBC "$with_uclibc" +_ACEOF + + + +if test X${with_uclibc} != X ; then + ENABLE_UCLIBC=1 + +else + ENABLE_UCLIBC=0 + +fi + + +# Check whether --enable-posix-runtime was given. +if test "${enable_posix_runtime+set}" = set; then + enableval=$enable_posix_runtime; +else + enableval=default +fi + + +{ echo "$as_me:$LINENO: checking POSIX runtime" >&5 +echo $ECHO_N "checking POSIX runtime... $ECHO_C" >&6; } +if test ${enableval} = "default" ; then + if test X${with_uclibc} != X; then + enableval=$host_supports_posix_runtime + if test ${enableval} = "yes"; then + { echo "$as_me:$LINENO: result: default (enabled)" >&5 +echo "${ECHO_T}default (enabled)" >&6; } + else + { echo "$as_me:$LINENO: result: default (disabled, unsupported target)" >&5 +echo "${ECHO_T}default (disabled, unsupported target)" >&6; } + fi + else + enableval="no" + { echo "$as_me:$LINENO: result: default (disabled, no uclibc)" >&5 +echo "${ECHO_T}default (disabled, no uclibc)" >&6; } + fi +else + if test ${enableval} = "yes" ; then + { echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } + else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } + fi +fi + +if test ${enableval} = "yes" ; then + ENABLE_POSIX_RUNTIME=1 + +else + ENABLE_POSIX_RUNTIME=0 + +fi + + + +# Check whether --with-runtime was given. +if test "${with_runtime+set}" = set; then + withval=$with_runtime; +else + withval=default +fi + + +if test X"${withval}" = Xdefault; then + with_runtime=Release +fi + +{ echo "$as_me:$LINENO: checking runtime configuration" >&5 +echo $ECHO_N "checking runtime configuration... $ECHO_C" >&6; } +if test X${with_runtime} = XRelease; then + { echo "$as_me:$LINENO: result: Release" >&5 +echo "${ECHO_T}Release" >&6; } + RUNTIME_ENABLE_OPTIMIZED=1 + +elif test X${with_runtime} = XDebug; then + { echo "$as_me:$LINENO: result: Debug" >&5 +echo "${ECHO_T}Debug" >&6; } + RUNTIME_ENABLE_OPTIMIZED=0 + +else + { { echo "$as_me:$LINENO: error: invalid configuration: ${with_runtime}" >&5 +echo "$as_me: error: invalid configuration: ${with_runtime}" >&2;} + { (exit 1); exit 1; }; } +fi + + +cat >>confdefs.h <<_ACEOF +#define RUNTIME_CONFIGURATION "$with_runtime" +_ACEOF + + + + +case $host in + *-*-linux*) + +cat >>confdefs.h <<_ACEOF +#define HAVE_CTYPE_EXTERNALS 1 +_ACEOF + +esac + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO: checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (ac_try="$ac_compiler --version >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler --version >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -v >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler -v >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -V >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler -V >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6; } +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +# +# List of possible output files, starting from the most likely. +# The algorithm is not robust to junk in `.', hence go to wildcards (a.*) +# only as a last resort. b.out is created by i960 compilers. +ac_files='a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out' +# +# The IRIX 6 linker writes into existing files which may not be +# executable, retaining their permissions. Remove them first so a +# subsequent execution test works. +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { (ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +{ echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6; } + +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6; } +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +{ echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6; } +{ echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6; } + +{ echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6; } +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +{ echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +{ echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6; } +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6; } +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_compiler_gnu=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6; } +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6; } +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + CFLAGS="" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 +echo $ECHO_N "checking for $CC option to accept ISO C89... $ECHO_C" >&6; } +if test "${ac_cv_prog_cc_c89+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_c89=$ac_arg +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6; } ;; + xno) + { echo "$as_me:$LINENO: result: unsupported" >&5 +echo "${ECHO_T}unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_c89" >&6; } ;; +esac + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ echo "$as_me:$LINENO: result: $CPP" >&5 +echo "${ECHO_T}$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5 +echo $ECHO_N "checking for grep that handles long lines and -e... $ECHO_C" >&6; } +if test "${ac_cv_path_GREP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Extract the first word of "grep ggrep" to use in msg output +if test -z "$GREP"; then +set dummy grep ggrep; ac_prog_name=$2 +if test "${ac_cv_path_GREP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_path_GREP_found=false +# Loop through the user's path and test for each of PROGNAME-LIST +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_GREP" && $as_executable_p "$ac_path_GREP"; } || continue + # Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + ac_count=`expr $ac_count + 1` + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + + $ac_path_GREP_found && break 3 + done +done + +done +IFS=$as_save_IFS + + +fi + +GREP="$ac_cv_path_GREP" +if test -z "$GREP"; then + { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 +echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} + { (exit 1); exit 1; }; } +fi + +else + ac_cv_path_GREP=$GREP +fi + + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5 +echo "${ECHO_T}$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ echo "$as_me:$LINENO: checking for egrep" >&5 +echo $ECHO_N "checking for egrep... $ECHO_C" >&6; } +if test "${ac_cv_path_EGREP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + # Extract the first word of "egrep" to use in msg output +if test -z "$EGREP"; then +set dummy egrep; ac_prog_name=$2 +if test "${ac_cv_path_EGREP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_path_EGREP_found=false +# Loop through the user's path and test for each of PROGNAME-LIST +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_EGREP" && $as_executable_p "$ac_path_EGREP"; } || continue + # Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + ac_count=`expr $ac_count + 1` + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + + $ac_path_EGREP_found && break 3 + done +done + +done +IFS=$as_save_IFS + + +fi + +EGREP="$ac_cv_path_EGREP" +if test -z "$EGREP"; then + { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 +echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} + { (exit 1); exit 1; }; } +fi + +else + ac_cv_path_EGREP=$EGREP +fi + + + fi +fi +{ echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5 +echo "${ECHO_T}$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6; } +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_stdc=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <string.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ctype.h> +#include <stdlib.h> +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +fi +{ echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +{ echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_Header=no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + +for ac_header in sys/acl.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + { echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +else + # Is the header compilable? +{ echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6; } + +# Is the header present? +{ echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( cat <<\_ASBOX +## ------------------------------------- ## +## Report this to daniel@minormatter.com ## +## ------------------------------------- ## +_ASBOX + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +{ echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +if test -z "$CXX"; then + if test -n "$CCC"; then + CXX=$CCC + else + if test -n "$ac_tool_prefix"; then + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + { echo "$as_me:$LINENO: result: $CXX" >&5 +echo "${ECHO_T}$CXX" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CXX="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + { echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5 +echo "${ECHO_T}$ac_ct_CXX" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + test -n "$ac_ct_CXX" && break +done + + if test "x$ac_ct_CXX" = x; then + CXX="g++" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} +ac_tool_warned=yes ;; +esac + CXX=$ac_ct_CXX + fi +fi + + fi +fi +# Provide some information about the compiler. +echo "$as_me:$LINENO: checking for C++ compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (ac_try="$ac_compiler --version >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler --version >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -v >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler -v >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -V >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler -V >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +{ echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6; } +if test "${ac_cv_cxx_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_compiler_gnu=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6; } +GXX=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +{ echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5 +echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6; } +if test "${ac_cv_prog_cxx_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_save_cxx_werror_flag=$ac_cxx_werror_flag + ac_cxx_werror_flag=yes + ac_cv_prog_cxx_g=no + CXXFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cxx_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + CXXFLAGS="" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cxx_werror_flag=$ac_save_cxx_werror_flag + CXXFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cxx_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cxx_werror_flag=$ac_save_cxx_werror_flag +fi +{ echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6; } +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +{ echo "$as_me:$LINENO: checking how to run the C++ preprocessor" >&5 +echo $ECHO_N "checking how to run the C++ preprocessor... $ECHO_C" >&6; } +if test -z "$CXXCPP"; then + if test "${ac_cv_prog_CXXCPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CXXCPP needs to be expanded + for CXXCPP in "$CXX -E" "/lib/cpp" + do + ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CXXCPP=$CXXCPP + +fi + CXXCPP=$ac_cv_prog_CXXCPP +else + ac_cv_prog_CXXCPP=$CXXCPP +fi +{ echo "$as_me:$LINENO: result: $CXXCPP" >&5 +echo "${ECHO_T}$CXXCPP" >&6; } +ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + + +for ac_header in selinux/selinux.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + { echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +else + # Is the header compilable? +{ echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6; } + +# Is the header present? +{ echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( cat <<\_ASBOX +## ------------------------------------- ## +## Report this to daniel@minormatter.com ## +## ------------------------------------- ## +_ASBOX + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +{ echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + HAVE_SELINUX=1 + +else + HAVE_SELINUX=0 + +fi + +done + + + +# Check whether --enable-stplog was given. +if test "${enable_stplog+set}" = set; then + enableval=$enable_stplog; +else + enableval=no +fi + +if test ${enableval} = "yes" ; then + ENABLE_STPLOG=1 + +else + ENABLE_STPLOG=0 + +fi + +cat >>confdefs.h <<_ACEOF +#define ENABLE_STPLOG $ENABLE_STPLOG +_ACEOF + + + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 +echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + test "x$cache_file" != "x/dev/null" && + { echo "$as_me:$LINENO: updating cache $cache_file" >&5 +echo "$as_me: updating cache $cache_file" >&6;} + cat confcache >$cache_file + else + { echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5 +echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +as_nl=' +' +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir +fi +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +# Find out whether ``test -x'' works. Don't use a zero-byte file, as +# systems may use methods other than mode bits to determine executability. +cat >conf$$.file <<_ASEOF +#! /bin/sh +exit 0 +_ASEOF +chmod +x conf$$.file +if test -x conf$$.file >/dev/null 2>&1; then + as_executable_p="test -x" +else + as_executable_p=: +fi +rm -f conf$$.file + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 + +# Save the log message, to keep $[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by KLEE $as_me 0.01, which was +generated by GNU Autoconf 2.60. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" +config_commands="$ac_config_commands" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to <bug-autoconf@gnu.org>." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +KLEE config.status 0.01 +configured by $0, generated by GNU Autoconf 2.60, + with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2006 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + echo "$ac_cs_version"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + { echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +if \$ac_cs_recheck; then + echo "running CONFIG_SHELL=$SHELL $SHELL $0 "$ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + CONFIG_SHELL=$SHELL + export CONFIG_SHELL + exec $SHELL "$0"$ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +# +# INIT-COMMANDS +# +llvm_src="${LLVM_SRC}" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "setup") CONFIG_COMMANDS="$CONFIG_COMMANDS setup" ;; + "Makefile.config") CONFIG_FILES="$CONFIG_FILES Makefile.config" ;; + "stp/Makefile.common") CONFIG_FILES="$CONFIG_FILES stp/Makefile.common" ;; + "include/klee/Config/config.h") CONFIG_HEADERS="$CONFIG_HEADERS include/klee/Config/config.h" ;; + + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= + trap 'exit_status=$? + { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status +' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +# +# Set up the sed scripts for CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "$CONFIG_FILES"; then + +_ACEOF + + + +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + cat >conf$$subs.sed <<_ACEOF +SHELL!$SHELL$ac_delim +PATH_SEPARATOR!$PATH_SEPARATOR$ac_delim +PACKAGE_NAME!$PACKAGE_NAME$ac_delim +PACKAGE_TARNAME!$PACKAGE_TARNAME$ac_delim +PACKAGE_VERSION!$PACKAGE_VERSION$ac_delim +PACKAGE_STRING!$PACKAGE_STRING$ac_delim +PACKAGE_BUGREPORT!$PACKAGE_BUGREPORT$ac_delim +exec_prefix!$exec_prefix$ac_delim +prefix!$prefix$ac_delim +program_transform_name!$program_transform_name$ac_delim +bindir!$bindir$ac_delim +sbindir!$sbindir$ac_delim +libexecdir!$libexecdir$ac_delim +datarootdir!$datarootdir$ac_delim +datadir!$datadir$ac_delim +sysconfdir!$sysconfdir$ac_delim +sharedstatedir!$sharedstatedir$ac_delim +localstatedir!$localstatedir$ac_delim +includedir!$includedir$ac_delim +oldincludedir!$oldincludedir$ac_delim +docdir!$docdir$ac_delim +infodir!$infodir$ac_delim +htmldir!$htmldir$ac_delim +dvidir!$dvidir$ac_delim +pdfdir!$pdfdir$ac_delim +psdir!$psdir$ac_delim +libdir!$libdir$ac_delim +localedir!$localedir$ac_delim +mandir!$mandir$ac_delim +DEFS!$DEFS$ac_delim +ECHO_C!$ECHO_C$ac_delim +ECHO_N!$ECHO_N$ac_delim +ECHO_T!$ECHO_T$ac_delim +LIBS!$LIBS$ac_delim +build_alias!$build_alias$ac_delim +host_alias!$host_alias$ac_delim +target_alias!$target_alias$ac_delim +LLVM_SRC!$LLVM_SRC$ac_delim +LLVM_OBJ!$LLVM_OBJ$ac_delim +build!$build$ac_delim +build_cpu!$build_cpu$ac_delim +build_vendor!$build_vendor$ac_delim +build_os!$build_os$ac_delim +host!$host$ac_delim +host_cpu!$host_cpu$ac_delim +host_vendor!$host_vendor$ac_delim +host_os!$host_os$ac_delim +target!$target$ac_delim +target_cpu!$target_cpu$ac_delim +target_vendor!$target_vendor$ac_delim +target_os!$target_os$ac_delim +KLEE_UCLIBC!$KLEE_UCLIBC$ac_delim +ENABLE_UCLIBC!$ENABLE_UCLIBC$ac_delim +ENABLE_POSIX_RUNTIME!$ENABLE_POSIX_RUNTIME$ac_delim +RUNTIME_ENABLE_OPTIMIZED!$RUNTIME_ENABLE_OPTIMIZED$ac_delim +RUNTIME_CONFIGURATION!$RUNTIME_CONFIGURATION$ac_delim +CC!$CC$ac_delim +CFLAGS!$CFLAGS$ac_delim +LDFLAGS!$LDFLAGS$ac_delim +CPPFLAGS!$CPPFLAGS$ac_delim +ac_ct_CC!$ac_ct_CC$ac_delim +EXEEXT!$EXEEXT$ac_delim +OBJEXT!$OBJEXT$ac_delim +CPP!$CPP$ac_delim +GREP!$GREP$ac_delim +EGREP!$EGREP$ac_delim +HAVE_SELINUX!$HAVE_SELINUX$ac_delim +CXX!$CXX$ac_delim +CXXFLAGS!$CXXFLAGS$ac_delim +ac_ct_CXX!$ac_ct_CXX$ac_delim +CXXCPP!$CXXCPP$ac_delim +ENABLE_STPLOG!$ENABLE_STPLOG$ac_delim +LIBOBJS!$LIBOBJS$ac_delim +LTLIBOBJS!$LTLIBOBJS$ac_delim +_ACEOF + + if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 74; then + break + elif $ac_last_try; then + { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed` +if test -n "$ac_eof"; then + ac_eof=`echo "$ac_eof" | sort -nru | sed 1q` + ac_eof=`expr $ac_eof + 1` +fi + +cat >>$CONFIG_STATUS <<_ACEOF +cat >"\$tmp/subs-1.sed" <<\CEOF$ac_eof +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b end +_ACEOF +sed ' +s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g +s/^/s,@/; s/!/@,|#_!!_#|/ +:n +t n +s/'"$ac_delim"'$/,g/; t +s/$/\\/; p +N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n +' >>$CONFIG_STATUS <conf$$subs.sed +rm -f conf$$subs.sed +cat >>$CONFIG_STATUS <<_ACEOF +:end +s/|#_!!_#|//g +CEOF$ac_eof +_ACEOF + + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/ +s/:*\${srcdir}:*/:/ +s/:*@srcdir@:*/:/ +s/^\([^=]*=[ ]*\):*/\1/ +s/:*$// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF +fi # test -n "$CONFIG_FILES" + + +for ac_tag in :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) { { echo "$as_me:$LINENO: error: Invalid tag $ac_tag." >&5 +echo "$as_me: error: Invalid tag $ac_tag." >&2;} + { (exit 1); exit 1; }; };; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + { { echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5 +echo "$as_me: error: cannot find input file: $ac_f" >&2;} + { (exit 1); exit 1; }; };; + esac + ac_file_inputs="$ac_file_inputs $ac_f" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input="Generated from "`IFS=: + echo $* | sed 's|^[^:]*/||;s|:[^:]*/|, |g'`" by configure." + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + fi + + case $ac_tag in + *:-:* | *:-) cat >"$tmp/stdin";; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + { as_dir="$ac_dir" + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 +echo "$as_me: error: cannot create directory $as_dir" >&2;} + { (exit 1); exit 1; }; }; } + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= + +case `sed -n '/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p +' $ac_file_inputs` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s&@configure_input@&$configure_input&;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +$ac_datarootdir_hack +" $ac_file_inputs | sed -f "$tmp/subs-1.sed" >$tmp/out + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && + { echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&5 +echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&2;} + + rm -f "$tmp/stdin" + case $ac_file in + -) cat "$tmp/out"; rm -f "$tmp/out";; + *) rm -f "$ac_file"; mv "$tmp/out" $ac_file;; + esac + ;; + :H) + # + # CONFIG_HEADER + # +_ACEOF + +# Transform confdefs.h into a sed script `conftest.defines', that +# substitutes the proper values into config.h.in to produce config.h. +rm -f conftest.defines conftest.tail +# First, append a space to every undef/define line, to ease matching. +echo 's/$/ /' >conftest.defines +# Then, protect against being on the right side of a sed subst, or in +# an unquoted here document, in config.status. If some macros were +# called several times there might be several #defines for the same +# symbol, which is useless. But do not sort them, since the last +# AC_DEFINE must be honored. +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +# These sed commands are passed to sed as "A NAME B PARAMS C VALUE D", where +# NAME is the cpp macro being defined, VALUE is the value it is being given. +# PARAMS is the parameter list in the macro definition--in most cases, it's +# just an empty string. +ac_dA='s,^\\([ #]*\\)[^ ]*\\([ ]*' +ac_dB='\\)[ (].*,\\1define\\2' +ac_dC=' ' +ac_dD=' ,' + +uniq confdefs.h | + sed -n ' + t rset + :rset + s/^[ ]*#[ ]*define[ ][ ]*// + t ok + d + :ok + s/[\\&,]/\\&/g + s/^\('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/ '"$ac_dA"'\1'"$ac_dB"'\2'"${ac_dC}"'\3'"$ac_dD"'/p + s/^\('"$ac_word_re"'\)[ ]*\(.*\)/'"$ac_dA"'\1'"$ac_dB$ac_dC"'\2'"$ac_dD"'/p + ' >>conftest.defines + +# Remove the space that was appended to ease matching. +# Then replace #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +# (The regexp can be short, since the line contains either #define or #undef.) +echo 's/ $// +s,^[ #]*u.*,/* & */,' >>conftest.defines + +# Break up conftest.defines: +ac_max_sed_lines=50 + +# First sed command is: sed -f defines.sed $ac_file_inputs >"$tmp/out1" +# Second one is: sed -f defines.sed "$tmp/out1" >"$tmp/out2" +# Third one will be: sed -f defines.sed "$tmp/out2" >"$tmp/out1" +# et cetera. +ac_in='$ac_file_inputs' +ac_out='"$tmp/out1"' +ac_nxt='"$tmp/out2"' + +while : +do + # Write a here document: + cat >>$CONFIG_STATUS <<_ACEOF + # First, check the format of the line: + cat >"\$tmp/defines.sed" <<\\CEOF +/^[ ]*#[ ]*undef[ ][ ]*$ac_word_re[ ]*\$/b def +/^[ ]*#[ ]*define[ ][ ]*$ac_word_re[( ]/b def +b +:def +_ACEOF + sed ${ac_max_sed_lines}q conftest.defines >>$CONFIG_STATUS + echo 'CEOF + sed -f "$tmp/defines.sed"' "$ac_in >$ac_out" >>$CONFIG_STATUS + ac_in=$ac_out; ac_out=$ac_nxt; ac_nxt=$ac_in + sed 1,${ac_max_sed_lines}d conftest.defines >conftest.tail + grep . conftest.tail >/dev/null || break + rm -f conftest.defines + mv conftest.tail conftest.defines +done +rm -f conftest.defines conftest.tail + +echo "ac_result=$ac_in" >>$CONFIG_STATUS +cat >>$CONFIG_STATUS <<\_ACEOF + if test x"$ac_file" != x-; then + echo "/* $configure_input */" >"$tmp/config.h" + cat "$ac_result" >>"$tmp/config.h" + if diff $ac_file "$tmp/config.h" >/dev/null 2>&1; then + { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 +echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f $ac_file + mv "$tmp/config.h" $ac_file + fi + else + echo "/* $configure_input */" + cat "$ac_result" + fi + rm -f "$tmp/out12" + ;; + + :C) { echo "$as_me:$LINENO: executing $ac_file commands" >&5 +echo "$as_me: executing $ac_file commands" >&6;} + ;; + esac + +done # for ac_tag + + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + diff --git a/docs/SMT-COMP/BitVector_ArraysEx.smt b/docs/SMT-COMP/BitVector_ArraysEx.smt new file mode 100644 index 00000000..a60413ca --- /dev/null +++ b/docs/SMT-COMP/BitVector_ArraysEx.smt @@ -0,0 +1,74 @@ +(theory BitVector_ArraysEx + + :written_by {Clark Barrett} + :date {May 7, 2007} + +:sorts_description { + All sort symbols of the form BitVec[m], + where m is a numeral greater than 0. + + All sort symbols of the form Array[m:n], + where m and n are numerals with m > 0 and n > 0. +} + +:funs_description { + All functions from the theory Fixed_Size_Bitvectors. +} + +:funs_description { + All function symbols with arity of the form + + (select Array[m:n] BitVec[m] BitVec[n]) + + where + - m,n are numerals + - m > 0, n > 0 +} + +:funs_description { + All function symbols with arity of the form + + (store Array[m:n] BitVec[m] BitVec[n] Array[m:n]) + + where + - m,n are numerals + - m > 0, n > 0 +} + +:preds_description { + All predicates from the theory Fixed_Size_Bitvectors. +} + + + :definition + "This is a theory containing an infinite number of copies of the theory of + functional arrays with extensionality: one for each pair of bitvector sorts. + It can be formally defined as the union of the SMT-LIB theory + Fixed_Size_Bitvectors and an infinite number of variants of the SMT-LIB + theory ArraysEx: one for each distinct signature morphism mapping the sort Index + to BitVec[m] and the sort Element to Bitvec[n] where m and n range over all + positive numerals. In each of the copies of ArraysEx, the sort Array is + renamed to Array[m:n] and each copy of ArraysEx contributes exactly one select + function and one store function to the infinite polymorphic family of select + and store functions described above. + " + + :notes + "As in the theory Fixed_Size_Bitvectors, this theory does not + provide a value for the formal attributes :sorts, :funs, and :preds because + there are an infinite number of them. See the notes in theory + Fixed_Size_Bitvectors for details. + + If for i=1,2, T_i is an SMT-LIB theory with sorts S_i, function symbols F_i, + predicate symbols P_i, and axioms A_i, by \"union\" of T_1 and T_2 + we mean the theory T with sorts S_1 U S_2, function symbols F_1 U F_2, + predicate symbols P_1 U P_2, and axioms A_1 U A_2 (where U stands for set + theoretic union). + + The theory T is a well-defined SMT-LIB theory whenever S_1, S_2, F_1, F_2, + P_1, P_2 are all pairwise disjoint, as is the case for the component theories + considered here. + " +) + + diff --git a/docs/SMT-COMP/BitVectors.smt b/docs/SMT-COMP/BitVectors.smt new file mode 100644 index 00000000..f2cde1b3 --- /dev/null +++ b/docs/SMT-COMP/BitVectors.smt @@ -0,0 +1,187 @@ +(theory Fixed_Size_BitVectors + +:written_by {Silvio Ranise, Cesare Tinelli, and Clark Barrett} + +:date {May 7, 2007} + +:notes + "Against the requirements of the current SMT-LIB standard this theory does + not provide a value for the formal attributes :sorts, :funs, and :preds. + The reason is that the theory has an infinite number of sort, function, and + predicate symbols, and so they cannot be specified formally in the current + SMT-LIB language. While extending SMT-LIB's type system with dependent + types would allow a finitary formal specification of all the symbols in + this theory's signature, such an extension does not seem to be worth the + trouble at the moment. As a temporary ad-hoc solution, this theory + declaration specifies the signature, in English, in the user-defined + attributes :sorts_description, :funs_description, and :preds_description. + " + +:sorts_description { + All sort symbols of the form BitVec[m], + where m is a numeral greater than 0. +} + +:funs_description { + Constant symbols bit0 and bit1 of sort BitVec[1] +} + +:funs_description { + All function symbols with arity of the form + + (concat BitVec[i] BitVec[j] BitVec[m]) + + where + - i,j,m are numerals + - i,j > 0 + - i + j = m +} + +:funs_description { + All function symbols with arity of the form + + (extract[i:j] BitVec[m] BitVec[n]) + + where + - i,j,m,n are numerals + - m > i >= j >= 0, + - n = i-j+1. +} + +:funs_description { + All function symbols with arity of the form + + (op1 BitVec[m] BitVec[m]) + or + (op2 BitVec[m] BitVec[m] BitVec[m]) + + where + - op1 is from {bvnot, bvneg} + - op2 is from {bvand, bvor, bvadd, bvmul, bvudiv, bvurem, bvshl, bvlshr} + - m is a numeral greater than 0 +} + +:preds_description { + All predicate symbols with arity of the form + + (pred BitVec[m] BitVec[m]) + + where + - pred is from {bvult} + - m is a numeral greater than 0 +} + +:definition + "This is a core theory for fixed-size bitvectors where the operations + of concatenation and extraction of bitvectors as well as the usual + logical and arithmetic operations are overloaded. + The theory is defined semantically as follows. + + The sort BitVec[m] (for m > 0) is the set of finite functions + whose domain is the initial segment of the naturals [0...m), meaning + that 0 is included and m is excluded, and the co-domain is {0,1}. + + The semantic interpretation [[_]] of well-sorted BitVec-terms is + inductively defined as follows. + + - Variables + + If v is a variable of sort BitVec[m] with 0 < m, then + [[v]] is some element of [{0,...,m-1} -> {0,1}], the set of total + functions from {0,...,m-1} to {0,1}. + + - Constant symbols bit0 and bit1 of sort BitVec[1] + + [[bit0]] := \lambda x : [0,1). 0 + [[bit1]] := \lambda x : [0,1). 1 + + - Function symbols for concatenation + + [[(concat s t)]] := \lambda x : [0...n+m). + if (x<m) then [[t]](x) else [[s]](x-m) + where + s and t are terms of sort BitVec[n] and BitVec[m], respectively, + 0 < n, 0 < m. + + - Function symbols for extraction + + [[(extract[i:j] s)]] := \lambda x : [0...i-j+1). [[s]](j+x) + where s is of sort BitVec[l], 0 <= j <= i < l. + + - Bit-wise operations + + [[(bvnot s)]] := \lambda x : [0...m). if [[s]](x) = 0 then 1 else 0 + + [[(bvand s t)]] := \lambda x : [0...m). + if [[s]](x) = 0 then 0 else [[t]](x) + + [[(bvor s t)]] := \lambda x : [0...m). + if [[s]](x) = 1 then 1 else [[t]](x) + + where s and t are both of sort BitVec[m] and 0 < m. + + - Arithmetic operations + + To define the semantics of the bitvector arithmetic operators, we first + introduce some additional definitions: + + o (x div y) where x and y are integers with x >= 0 and y > 0 returns the + integer part of x divided by y (i.e., truncated integer division). + + o (x rem y) where x and y are integers with x >= 0 and y > 0 returns the + remainder when x is divided by y. Note that we always have the following + equivalence (for y > 0): (x div y) * y + (x rem y) = x. + + o bv2nat which takes a bitvector b: [0...m) --> {0,1} + with 0 < m, and returns an integer in the range [0...2^m), + and is defined as follows: + + bv2nat(b) := b(m-1)*2^{m-1} + b(m-2)*2^{m-2} + ... + b(0)*2^0 + + o nat2bv[m], with 0 < m, which takes a non-negative integer + n and returns the (unique) bitvector b: [0,...,m) -> {0,1} + such that + + b(m-1)*2^{m-1} + ... + b(0)*2^0 = n rem 2^m + + Now, we can define the following operations. Suppose s and t are both terms + of sort BitVec[m], m > 0. + + [[(bvneg s)]] := nat2bv[m](2^m - bv2nat([[s]])) + + [[(bvadd s t)]] := nat2bv[m](bv2nat([[s]]) + bv2nat([[t]])) + + [[(bvmul s t)]] := nat2bv[m](bv2nat([[s]]) * bv2nat([[t]])) + + [[(bvudiv s t)]] := if bv2nat([[t]]) != 0 then + nat2bv[m](bv2nat([[s]]) div bv2nat([[t]])) + + [[(bvurem s t)]] := if bv2nat([[t]]) != 0 then + nat2bv[m](bv2nat([[s]]) rem bv2nat([[t]])) + + - Shift operations + + Suppose s and t are both terms of sort BitVec[m], m > 0. We make use of the + definitions given for the arithmetic operations, above. + + [[(bvshl s t)]] := nat2bv[m](bv2nat([[s]]) * 2^(bv2nat([[t]]))) + + [[(bvlshr s t)]] := nat2bv[m](bv2nat([[s]]) div 2^(bv2nat([[t]]))) + + Finally, we can define the binary predicate bvult: + + (bvult s t) is interpreted to be true iff bv2nat([[s]]) < bv2nat([[t]]) + + Note that the semantic interpretation above is underspecified because it + does not specify the meaning of (bvudiv s t) or (bvurem s t) in case + bv2nat([[t]]) is 0. Since the semantics of SMT-LIB's underlying logic + associates *total* functions to function symbols, we then consider as models + of this theory *any* interpretation conforming to the specifications above + (and defining bvudiv and bvurem arbitrarily when the second argument + evaluates to 0). Benchmarks using this theory should only include a + :status sat or :status unsat attribute if the status is independent of + the particular choice of model for the theory. + + " + +) diff --git a/docs/SMT-COMP/QF_AUFBV.smt b/docs/SMT-COMP/QF_AUFBV.smt new file mode 100644 index 00000000..bc055c35 --- /dev/null +++ b/docs/SMT-COMP/QF_AUFBV.smt @@ -0,0 +1,19 @@ +(logic QF_AUFBV + + :written_by {Clark Barrett} + :date {May 7, 2007} + + :theory BV_ArraysEx + + :language + "Closed quantifier-free formulas built over an arbitrary expansion of the + BV_ArraysEx signature with free function and predicate symbols over + the sorts of BV_ArraysEx. Formulas in ite terms must satisfy the same + restriction as well, with the exception that they need not be closed (because + they may be in the scope of a let expression). + " + :extensions + "As in the logic QF_BV." +) + + diff --git a/docs/SMT-COMP/QF_BV.smt b/docs/SMT-COMP/QF_BV.smt new file mode 100644 index 00000000..edf02241 --- /dev/null +++ b/docs/SMT-COMP/QF_BV.smt @@ -0,0 +1,261 @@ +(logic QF_BV + +:written_by {Silvio Ranise, Cesare Tinelli, and Clark Barrett} +:date {May 7, 2007} + +:theory Fixed_Size_BitVectors + +:language + + "Closed quantifier-free formulas built over an arbitrary expansion of the + Fixed_Size_BitVectors signature with free constant symbols over the sorts + BitVec[m] for 0 < m. Formulas in ite terms must satisfy the same restriction + as well, with the exception that they need not be closed (because they may be + in the scope of a let expression). + " + +:notes + "For quick reference, the following is a brief and informal summary of the + legal symbols in this logic and their meaning (formal definitions are found + either in the Fixed_Size_Bitvectors theory, or in the extensions below). + + Defined in theory Fixed_Size_Bitvectors: + + Functions/Constants: + + (bit0 BitVec[1]) + - the constant consisting of a single bit with value 0 + (bit1 BitVec[1]) + - the constant consisting of a single bit with value 1 + (concat BitVec[i] BitVec[j] BitVec[m]) + - concatenation of bitvectors of size i and j to get a new bitvector of + size m, where m = i + j + (extract[i:j] BitVec[m] BitVec[n]) + - extraction of bits i down to j from a bitvector of size m to yield a + new bitvector of size n, where n = i - j + 1 + (bvnot BitVec[m] BitVec[m]) + - bitwise negation + (bvand BitVec[m] BitVec[m] BitVec[m]) + - bitwise and + (bvor BitVec[m] BitVec[m] BitVec[m]) + - bitwise or + (bvneg BitVec[m] BitVec[m]) + - 2's complement unary minus + (bvadd BitVec[m] BitVec[m] BitVec[m]) + - addition modulo 2^m + (bvmul BitVec[m] BitVec[m] BitVec[m]) + - multiplication modulo 2^m + (bvudiv BitVec[m] BitVec[m] BitVec[m]) + - unsigned division, truncating towards 0 (undefined if divisor is 0) + (bvurem BitVec[m] BitVec[m] BitVec[m]) + - unsigned remainder from truncating division (undefined if divisor is 0) + (bvshl BitVec[m] BitVec[m] BitVec[m]) + - shift left (equivalent to multiplication by 2^x where x is the value of + the second argument) + (bvlshr BitVec[m] BitVec[m] BitVec[m]) + - logical shift right (equivalent to unsigned division by 2^x where x is + the value of the second argument) + + Predicates: + + (bvult BitVec[m] BitVec[m]) + - binary predicate for unsigned less than + + Defined below: + + Functions/Constants: + + Bitvector constants: + - bvX[m] where X is a numeral in base 10 defines the bitvector constant + with numeric value X of size m. + - bvbinX where X is a binary numeral of length m defines the + bitvector constant with value X and size m. + - bvhexX where X is a hexadecimal numeral of length m defines the + bitvector constant with value X and size 4*m. + (bvnand BitVec[m] BitVec[m] BitVec[m]) + - bitwise nand (negation of and) + (bvnor BitVec[m] BitVec[m] BitVec[m]) + - bitwise nor (negation of or) + (bvxor BitVec[m] BitVec[m] BitVec[m]) + - bitwise exclusive or + (bvxnor BitVec[m] BitVec[m] BitVec[m]) + - bitwise equivalence (equivalently, negation of bitwise exclusive or) + (bvcomp BitVec[m] BitVec[m] BitVec[1]) + - bit comparator: equals bit1 iff all bits are equal + (bvsub BitVec[m] BitVec[m] BitVec[m]) + - 2's complement subtraction modulo 2^m + (bvsdiv BitVec[m] BitVec[m] BitVec[m]) + - 2's complement signed division + (bvsrem BitVec[m] BitVec[m] BitVec[m]) + - 2's complement signed remainder (sign follows dividend) + (bvsmod BitVec[m] BitVec[m] BitVec[m]) + - 2's complement signed remainder (sign follows divisor) + (bvashr BitVec[m] BitVec[m] BitVec[m]) + - Arithmetic shift right, like logical shift right except that the most + significant bits of the result always copy the most significant + bit of the first argument. + + The following symbols are parameterized by the numeral i, where i >= 0. + + (repeat[i] BitVec[m] BitVec[i*m]) + - (repeat[i] x) means concatenate i copies of x + (zero_extend[i] BitVec[m] BitVec[m+i]) + - (zero_extend[i] x) means extend x with zeroes to the (unsigned) + equivalent bitvector of size m+i + (sign_extend[i] BitVec[m] BitVec[m+i]) + - (sign_extend[i] x) means extend x to the (signed) equivalent bitvector + of size m+i + (rotate_left[i] BitVec[m] BitVec[m]) + - (rotate_left[i] x) means rotate bits of x to the left i times + (rotate_right[i] BitVec[m] BitVec[m]) + - (rotate_right[i] x) means rotate bits of x to the right y times + + Predicates: + + (bvule BitVec[m] BitVec[m]) + - binary predicate for unsigned less than or equal + (bvugt BitVec[m] BitVec[m]) + - binary predicate for unsigned greater than + (bvuge BitVec[m] BitVec[m]) + - binary predicate for unsigned greater than or equal + (bvslt BitVec[m] BitVec[m]) + - binary predicate for signed less than + (bvsle BitVec[m] BitVec[m]) + - binary predicate for signed less than or equal + (bvsgt BitVec[m] BitVec[m]) + - binary predicate for signed greater than + (bvsge BitVec[m] BitVec[m]) + - binary predicate for signed greater than or equal + + " + +:extensions + "Below, let |exp| denote the integer resulting from the evaluation + of the arithmetic expression exp. + + - Bitvector Constants: + The string bv followed by the numeral n and a size [m] (as in bv13[32]) + abbreviates any term t of sort BitVec[m] built only out of the symbols in + {concat, bit0, bit1} such that + + [[t]] = nat2bv[m](n) for n=0, ..., 2^m - 1. + + See the specification of the theory's semantics for a definition + of the functions [[_]] and nat2bv. Note that this convention implicitly + considers the numeral n as a number written in base 10. + + For backward compatibility, if the size [m] is omitted, then the size is + assumed to be 32. + + The string bvbin followed by a sequence of 0's and 1's abbreviates the + concatenation of a similar sequence of bit0 and bit1 terms. Thus, + if n is the numeral represented in base 2 by the sequence of 0's and 1's + and m is the length of the sequence, then the term represents + nat2bv[m](n). For example bvbin0101 is equivalent to bv5[4]. + + The string bvhex followed by a sequence of digits and/or letters from A to + F is interpreted similarly as a concatenation of bit0 and bit1 as follows. + If n is the numeral represented in hexadecimal (base 16) by the sequence of + digits and letters from A to F and m is four times the length of the + sequence, then the term represents nat2bv[m](n). For example, bvbinFF is + equivalent to bv255[8]. Letters in the hexadecimal sequence may be in + either upper or lower case. + + - Bitwise operators + + For all terms s,t of sort BitVec[m], where 0 < m, + + (bvnand s t) abbreviates (bvnot (bvand s t)) + (bvnor s t) abbreviates (bvnot (bvor s t)) + (bvxor s t) abbreviates (bvor (bvand s (bvnot t)) (bvand (bvnot s) t)) + (bvxnor s t) abbreviates (bvor (bvand s t) (bvand (bvnot s) (bvnot t))) + (bvcomp s t) abbreviates (bvxnor s t) if m = 1, and + (bvand (bvxnor (extract[|m-1|:|m-1|] s) (extract[|m-1|:|m-1|] t)) + (bvcomp (extract[|m-2|:0] s) (extract[|m-2|:0] t))) otherwise + + - Arithmetic operators + + For all terms s,t of sort BitVec[m], where 0 < m, + + (bvsub s t) abbreviates (bvadd s (bvneg t)) + (bvsdiv s t) abbreviates + (let (?msb_s (extract[|m-1|:|m-1|] s)) + (let (?msb_t (extract[|m-1|:|m-1|] t)) + (ite (and (= ?msb_s bit0) (= ?msb_t bit0)) + (bvudiv s t) + (ite (and (= ?msb_s bit1) (= ?msb_t bit0)) + (bvneg (bvudiv (bvneg s) t)) + (ite (and (= ?msb_s bit0) (= ?msb_t bit1)) + (bvneg (bvudiv s (bvneg t))) + (bvudiv (bvneg s) (bvneg t))))))) + (bvsrem s t) abbreviates + (let (?msb_s (extract[|m-1|:|m-1|] s)) + (let (?msb_t (extract[|m-1|:|m-1|] t)) + (ite (and (= ?msb_s bit0) (= ?msb_t bit0)) + (bvurem s t) + (ite (and (= ?msb_s bit1) (= ?msb_t bit0)) + (bvneg (bvurem (bvneg s) t)) + (ite (and (= ?msb_s bit0) (= ?msb_t bit1)) + (bvurem s (bvneg t))) + (bvneg (bvurem (bvneg s) (bvneg t))))))) + (bvsmod s t) abbreviates + (let (?msb_s (extract[|m-1|:|m-1|] s)) + (let (?msb_t (extract[|m-1|:|m-1|] t)) + (ite (and (= ?msb_s bit0) (= ?msb_t bit0)) + (bvurem s t) + (ite (and (= ?msb_s bit1) (= ?msb_t bit0)) + (bvadd (bvneg (bvurem (bvneg s) t)) t) + (ite (and (= ?msb_s bit0) (= ?msb_t bit1)) + (bvadd (bvurem s (bvneg t)) t) + (bvneg (bvurem (bvneg s) (bvneg t))))))) + (bvule s t) abbreviates (or (bvult s t) (= s t)) + (bvugt s t) abbreviates (bvult t s) + (bvuge s t) abbreviates (or (bvult t s) (= s t)) + (bvslt s t) abbreviates: + (or (and (= (extract[|m-1|:|m-1|] s) bit1) + (= (extract[|m-1|:|m-1|] t) bit0)) + (and (= (extract[|m-1|:|m-1|] s) (extract[|m-1|:|m-1|] t)) + (bvult s t))) + (bvsle s t) abbreviates: + (or (and (= (extract[|m-1|:|m-1|] s) bit1) + (= (extract[|m-1|:|m-1|] t) bit0)) + (and (= (extract[|m-1|:|m-1|] s) (extract[|m-1|:|m-1|] t)) + (bvule s t))) + (bvsgt s t) abbreviates (bvslt t s) + (bvsge s t) abbreviates (bvsle t s) + + - Other operations + + For all numerals j > 1 and 0 < m, and all terms of s and t of + sort BitVec[m], + + (bvashr s t) abbreviates + (ite (= (extract[|m-1|:|m-1|] s) bit0) + (bvlshr s t) + (bvnot (bvlshr (bvnot s) t))) + + (repeat[1] t) stands for t + (repeat[j] t) abbreviates (concat t (repeat[|j-1|] t)) + + (zero_extend[0] t) stands for t + (zero_extend[j] t) abbreviates (concat (repeat[j] bit0) t) + + (sign_extend[0] t) stands for t + (sign_extend[j] t) abbreviates + (concat (repeat[j] (extract[|m-1|:|m-1|] t)) t) + + (rotate_left[0] t) stands for t + (rotate_left[j] t) abbreviates t if m = 1, and + (rotate_left[|j-1|] + (concat (extract[|m-2|:0] t) (extract[|m-1|:|m-1|] t)) + otherwise + + (rotate_right[0] t) stands for t + (rotate_right[j] t) abbreviates t if m = 1, and + (rotate_right[|j-1|] + (concat (extract[0:0] t) (extract[|m-1|:1] t))) + otherwise + + " +) + diff --git a/docs/doxygen.cfg b/docs/doxygen.cfg new file mode 100644 index 00000000..b13a65ec --- /dev/null +++ b/docs/doxygen.cfg @@ -0,0 +1,1291 @@ +# Doxyfile 1.5.3 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file that +# follow. The default is UTF-8 which is also the encoding used for all text before +# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into +# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of +# possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = klee + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = docs/doxygen + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, +# Italian, Japanese, Japanese-en (Japanese with English messages), Korean, +# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, +# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to +# include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be extracted +# and appear in the documentation as a namespace called 'anonymous_namespace{file}', +# where file will be replaced with the base name of the file that contains the anonymous +# namespace. By default anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = YES + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command <command> <input-file>, where <command> is the value of +# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = tools/ \ + lib/ \ + include/klee \ + docs/intro \ + docs/overview + +# This tag can be used to specify the character encoding of the source files that +# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default +# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding. +# See http://www.gnu.org/software/libiconv for the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = */.svn* */Debug* + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the output. +# The symbol name can be a fully qualified name, a word, or if the wildcard * is used, +# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH +# then you must also enable this option. If you don't then doxygen will produce +# a warning and turn it on anyway + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentstion. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = YES + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to +# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to +# specify the directory where the mscgen tool resides. If left empty the tool is assumed to +# be found in the default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = YES + +# If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will +# generate a caller dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. + +CALLER_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the number +# of direct children of the root node in a graph is already larger than +# MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = YES diff --git a/docs/intro b/docs/intro new file mode 100644 index 00000000..c3451789 --- /dev/null +++ b/docs/intro @@ -0,0 +1,10 @@ +/// @mainpage KLEE +/// +/// @section main_intro Introduction +/// Welcome to KLEE. KLEE is a symbolic execution engine that works on LLVM +/// bitcode. +/// +/// @section Documentation +/// The documentation of KLEE is composed of the Doxygen documentation +/// of the code as well as the following documents: +/// - @subpage overview diff --git a/docs/overview b/docs/overview new file mode 100644 index 00000000..666a23d9 --- /dev/null +++ b/docs/overview @@ -0,0 +1,104 @@ +/// @page overview High level overview of KLEE. +/// This document contains a high level overview of the inner workings of KLEE. +/// +/// KLEE implements symbolic execution by interpreting LLVM bitcode. Symbolic +/// memory is defined by inserting special calls to KLEE (namely +/// klee_make_symbolic) +/// During execution, KLEE tracks all uses of symbolic memory. Constraints +/// on symbolic memory usage are collected. Memory +/// that is defined using previously declared symbolic memory become +/// symbolic as well. +/// Whenever a branch refering to symbolic memory is encountered, KLEE forks +/// the entire states and explores each side of the branch for which a possible +/// solution to the symbolic constraints can be found. +/// KLEE makes queries to STP to solve symbolic constraints. +/// +/// The rest of this document describes some of the important components of KLEE +/// +/// @section executor Interpreter +/// klee::Interpreter is the main abstract class defining the interface of the +/// bitcode interpreter. klee::Executor is the main concrete instance of this +/// class. +/// Application states (i.e. memory, registers and PC) are stored in instances of +/// class klee::ExecutionState. There is one such instance for each path beeing +/// executed (except when some states are merged together). +/// On a branch, if condition is symbolic, klee::Executor::fork returns a +/// klee::ExecutionState::StatePair which is a pair of ExecutionState to be +/// executed. +/// +/// @section memory Memory model +/// MemoryObject's represent allocation sites in the program (calls to malloc, stack +/// objects, global variables) +/// and, at least conceptually, can be thought of as the unique name for the object +/// allocated at that site. +/// ObjectState's are used to store the actual contents of a MemoryObject in a +/// particular ExecutionState (but +/// can be shared). I need better names for these two things. +/// +/// Each ExecutionState stores a mapping of MemoryObjects -> ObjectState using the +/// AddressSpace data +/// structure (implemented as an immutable tree so that copying is cheap and the +/// shared structure is exploited). +/// Each AddressSpace may "own" some subset of the ObjectStates in the mapping. When +/// an AddressSpace +/// is duplicated it loses ownership of the ObjectState in the map. Any subsequent +/// write to an ObjectState will +/// create a copy of the object (AddressSpace::getWriteable). This is the COW +/// mechanism (which gets used +/// for all objects, not just globals). +/// +/// From the point of view of the state and this mapping there is no distinction +/// between stack, heap, and global +/// objects. The only special handling for stack objects is that the MemoryObject is +/// marked as isLocal and the +/// MemoryObject is stored in the StackFrame alloca list. When the StackFrame is +/// popped these objects are +/// then unbound so that the state can no longer access the memory directly +/// (references to the memory object +/// may still remain in ReadExprs, but conceptually the actual memory is no longer +/// addressable). +/// +/// It is also important that the AddressSpace mapping is ordered. We use this when +/// we need to resolve a symbolic +/// address to an ObjectState by first getting a particular value for the symbolic +/// address, and using that value to start +/// looking for objects that the pointer can fall within. +/// Difference betweens MemoryObjects and ObjectStates ? +/// +/// @section expression Expressions +/// The various Expr classes mostly model the llvm instruction set. ref<Expr> is +/// used to maintain the reference count +/// but also embeds any constant expressions. In fact in the current code base +/// ConstantExprs should almost never be +/// created. Most of the Expr's are straightforward. Some of the most important ones +/// are Concat?Expr, which join +/// some number of bytes into a larger type, ExtractExpr which extracts smaller +/// types from larger ones, and ReadExpr +/// which is a symbolic array access. +/// +/// The way memory is implemented all accesses are broken down into byte level +/// operations. This means that the +/// memory system (by which I mean the ObjectState data structure) tends to use a +/// lot of ExtractExpr and Concat?Expr, +/// so it is very important that these expressions fold their operands when +/// possible. +/// +/// The ReadExpr is probably the most important one. Conceptually it is simply an +/// index and a list of (index, value) +/// updates (writes). The ReadExpr evaluates to all the values for which the two +/// indices can be equal. The ObjectState +/// structure uses a cache for concrete writes and for symbolic writes at concrete +/// indices, but for writes at symbolic +/// indices it must construct a list of such updates. These are stored in the +/// UpdateList and UpdateNode structures +/// which are again immutable data structures so that copy is cheap and the sharing +/// is exploited. +/// +/// @section searcher Searcher +/// Base classe: klee::Searcher. The Executor uses a Searcher to select the next +/// state (i.e. program instance following a single path) for which an +/// instruction +/// will be executed. There are multiple implementations of Searcher in klee, +/// implementing different search policies. klee::RandomSearcher selects the next state randomly. +/// klee::DFSSearcher uses a depth first approach. klee::MergingSearcher tries +/// to merge states ? diff --git a/examples/regexp/Regexp.c b/examples/regexp/Regexp.c new file mode 100644 index 00000000..f3e751f9 --- /dev/null +++ b/examples/regexp/Regexp.c @@ -0,0 +1,62 @@ +/* + * Simple regular expression matching. + * + * From: + * The Practice of Programming + * Brian W. Kernighan, Rob Pike + * + */ + +#include <klee/klee.h> + +static int matchhere(char*,char*); + +static int matchstar(int c, char *re, char *text) { + do { + if (matchhere(re, text)) + return 1; + } while (*text != '\0' && (*text++ == c || c== '.')); + return 0; +} + +static int matchhere(char *re, char *text) { + if (re[0] == '\0') + return 0; + if (re[1] == '*') + return matchstar(re[0], re+2, text); + if (re[0] == '$' && re[1]=='\0') + return *text == '\0'; + if (*text!='\0' && (re[0]=='.' || re[0]==*text)) + return matchhere(re+1, text+1); + return 0; +} + +int match(char *re, char *text) { + if (re[0] == '^') + return matchhere(re+1, text); + do { + if (matchhere(re, text)) + return 1; + } while (*text++ != '\0'); + return 0; +} + +/* + * Harness for testing with KLEE. + */ + +// The size of the buffer to test with. +#define SIZE 7 + +int main() { + // The input regular expression. + char re[SIZE]; + + // Make the input symbolic. + klee_make_symbolic_name(re, sizeof re, "re"); + + // Try to match against a constant string "hello". + match(re, "hello"); + + return 0; +} diff --git a/examples/regexp/notes.txt b/examples/regexp/notes.txt new file mode 100644 index 00000000..7d25585c --- /dev/null +++ b/examples/regexp/notes.txt @@ -0,0 +1,26 @@ +clang -m32 -I ~/private/klee/include -c -emit-llvm Regexp.c + +klee Regexp.o +klee --only-output-states-covering-new Regexp.o +ls -l klee-out-0 +ls -l klee-out-1 +ls -l klee-last + +cd klee-last +klee-bout-tool *.bout +klee-bout-tool --trim-zeros *.bout + +Stuff to show: +Adding klee_prefer_cex + +PrintStats.py klee-last + +PrintStats.py klee-last +Why not 100% coverage? + +clang -g -m32 -I ~/private/klee/include -c -emit-llvm Regexp.c + +KCachegrind? + +Disable klee_assume, show coverage again (why is klee-check-div getting pulled +in?) diff --git a/examples/sort/sort.c b/examples/sort/sort.c new file mode 100644 index 00000000..a1629c6b --- /dev/null +++ b/examples/sort/sort.c @@ -0,0 +1,78 @@ +#include <klee/klee.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> + +static void insert_ordered(int *array, unsigned nelem, int item) { + unsigned i = 0; + + for (; i != nelem; ++i) { + if (item < array[i]) { + memmove(&array[i+1], &array[i], sizeof(*array) * (nelem - i)); + break; + } + } + + array[i] = item; +} + +void bubble_sort(int *array, unsigned nelem) { + for (;;) { + int done = 1; + + for (unsigned i = 0; i + 1 < nelem; ++i) { + if (array[i+1] < array[i]) { + int t = array[i + 1]; + array[i + 1] = array[i]; + array[i] = t; + done = 0; + } + } + + break; + } +} + +void insertion_sort(int *array, unsigned nelem) { + int *temp = malloc(sizeof(*temp) * nelem); + + for (unsigned i = 0; i != nelem; ++i) + insert_ordered(temp, i, array[i]); + + memcpy(array, temp, sizeof(*array) * nelem); + free(temp); +} + +void test(int *array, unsigned nelem) { + int *temp1 = malloc(sizeof(*array) * nelem); + int *temp2 = malloc(sizeof(*array) * nelem); + + printf("input: [%d, %d, %d, %d]\n", + array[0], array[1], array[2], array[3]); + + memcpy(temp1, array, sizeof(*array) * 4); + memcpy(temp2, array, sizeof(*array) * 4); + + insertion_sort(temp1, 4); + bubble_sort(temp2, 4); + + printf("insertion_sort: [%d, %d, %d, %d]\n", + temp1[0], temp1[1], temp1[2], temp1[3]); + + printf("bubble_sort : [%d, %d, %d, %d]\n", + temp2[0], temp2[1], temp2[2], temp2[3]); + + for (unsigned i = 0; i != nelem; ++i) + assert(temp1[i] == temp2[i]); +} + +int main() { + int input[4] = { 4, 3, 2, 1}; + + klee_make_symbolic(&input, sizeof(input)); + test(input, 4); + + return 0; +} diff --git a/include/expr/Lexer.h b/include/expr/Lexer.h new file mode 100644 index 00000000..4ae760a0 --- /dev/null +++ b/include/expr/Lexer.h @@ -0,0 +1,114 @@ +//===-- Lexer.h -------------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_EXPR_LEXER_H +#define KLEE_EXPR_LEXER_H + +#include <string> + +namespace llvm { + class MemoryBuffer; +} + +namespace klee { +namespace expr { + struct Token { + enum Kind { + At, /// '@' + Arrow, /// '->' + Colon, /// ':' + Comma, /// ',' + Comment, /// #[^\n]+ + EndOfFile, /// <end of file> + Equals, /// ' = ' + Identifier, /// [a-zA-Z_][a-zA-Z0-9._]* + KWFalse, /// 'false' + KWQuery, /// 'query' + KWReserved, /// fp[0-9]+([.].*)?, i[0-9]+ + KWTrue, /// 'true' + KWWidth, /// w[0-9]+ + LBrace, /// '{' + LParen, /// '(' + LSquare, /// '[' + Number, /// [+-]?[0-9][a-zA-Z0-9_]+ + RBrace, /// '}' + RParen, /// ')' + RSquare, /// ']' + Semicolon, /// ';' + Unknown /// <other> + }; + + Kind kind; /// The token kind. + const char *start; /// The beginning of the token string. + unsigned length; /// The length of the token. + unsigned line; /// The line number of the start of this token. + unsigned column; /// The column number at the start of + /// this token. + + /// getKindName - The name of this token's kind. + const char *getKindName() const; + + /// getString - The string spanned by this token. This is not + /// particularly efficient, use start and length when reasonable. + std::string getString() const { return std::string(start, length); } + + /// isKeyword - True if this token is a keyword. + bool isKeyword() const { + return kind >= KWFalse && kind <= KWTrue; + } + + // dump - Dump the token to stderr. + void dump(); + }; + + /// Lexer - Interface for lexing tokens from a .pc language file. + class Lexer { + const char *BufferPos; /// The current lexer position. + const char *BufferEnd; /// The buffer end position. + unsigned LineNumber; /// The current line. + unsigned ColumnNumber; /// The current column. + + /// GetNextChar - Eat a character or -1 from the stream. + int GetNextChar(); + + /// PeekNextChar - Return the next character without consuming it + /// from the stream. This does not perform newline + /// canonicalization. + int PeekNextChar(); + + /// SetTokenKind - Set the token kind and length (using the + /// token's start pointer, which must have been initialized). + Token &SetTokenKind(Token &Result, Token::Kind k); + + /// SetTokenKind - Set an identifiers token kind. This has the + /// same requirements as SetTokenKind and additionally takes care + /// of keyword recognition. + Token &SetIdentifierTokenKind(Token &Result); + + void SkipToEndOfLine(); + + /// LexNumber - Lex a number which does not have a base specifier. + Token &LexNumber(Token &Result); + + /// LexIdentifier - Lex an identifier. + Token &LexIdentifier(Token &Result); + + public: + explicit Lexer(const llvm::MemoryBuffer *_buf); + ~Lexer(); + + /// Lex - Return the next token from the file or EOF continually + /// when the end of the file is reached. The input argument is + /// used as the result, for convenience. + Token &Lex(Token &Result); + }; +} +} + +#endif diff --git a/include/expr/Parser.h b/include/expr/Parser.h new file mode 100644 index 00000000..7634d66a --- /dev/null +++ b/include/expr/Parser.h @@ -0,0 +1,178 @@ +//===-- Parser.h ------------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_EXPR_PARSER_H +#define KLEE_EXPR_PARSER_H + +#include "klee/Expr.h" + +#include <vector> +#include <string> + +namespace llvm { + class MemoryBuffer; +} + +namespace klee { +namespace expr { + // These are the language types we manipulate. + typedef ref<Expr> ExprHandle; + typedef UpdateList VersionHandle; + + /// Identifier - Wrapper for a uniqued string. + struct Identifier { + const std::string Name; + + public: + Identifier(const std::string _Name) : Name(_Name) {} + }; + + // FIXME: Do we have a use for tracking source locations? + + /// Decl - Base class for top level declarations. + class Decl { + public: + Decl(); + virtual ~Decl() {} + + /// dump - Dump the AST node to stderr. + virtual void dump() = 0; + }; + + /// ArrayDecl - Array declarations. + /// + /// For example: + /// array obj : 32 -> 8 = symbolic + /// array obj[32] : 32 -> 8 = { ... } + class ArrayDecl : public Decl { + public: + /// Name - The name of this array. + const Identifier *Name; + + /// Size - The maximum array size (or 0 if unspecified). Concrete + /// arrays always are specified with a size. + const unsigned Size; + + /// Domain - The width of indices. + const unsigned Domain; + + /// Range - The width of array contents. + const unsigned Range; + + /// Contents - The initial contents of the array. The array is + /// symbolic if no contents are specified. The contained + /// expressions are guaranteed to be constants. + const std::vector<ExprHandle> Contents; + + public: + template<typename InputIterator> + ArrayDecl(const Identifier *_Name, unsigned _Size, + unsigned _Domain, unsigned _Range, + InputIterator ContentsBegin=InputIterator(), + InputIterator ContentsEnd=InputIterator()) + : Name(_Name), Size(_Size), Domain(_Domain), Range(_Range), + Contents(ContentsBegin, ContentsEnd) {} + }; + + /// VarDecl - Variable declarations, used to associate names to + /// expressions or array versions outside of expressions. + /// + /// For example: + // FIXME: What syntax are we going to use for this? We need it. + class VarDecl : public Decl { + public: + const Identifier *Name; + }; + + /// ExprVarDecl - Expression variable declarations. + class ExprVarDecl : public VarDecl { + public: + ExprHandle Value; + }; + + /// VersionVarDecl - Array version variable declarations. + class VersionVarDecl : public VarDecl { + public: + VersionHandle Value; + }; + + /// CommandDecl - Base class for language commands. + class CommandDecl : public Decl { + public: + const Identifier *Name; + }; + + /// QueryCommand - Query commands. + /// + /// (query [ ... constraints ... ] expression) + /// (query [ ... constraints ... ] expression values) + /// (query [ ... constraints ... ] expression values objects) + class QueryCommand : public CommandDecl { + public: + // FIXME: One issue with STP... should we require the FE to + // guarantee that these are consistent? That is a cornerstone of + // being able to do independence. We may want this as a flag, if + // we are to interface with things like SMT. + + /// Constraints - The list of constraints to assume for this + /// expression. + const std::vector<ExprHandle> Constraints; + + /// Query - The expression being queried. + ExprHandle Query; + + /// Values - The expressions for which counterexamples should be + /// given if the query is invalid. + const std::vector<ExprHandle> Values; + + /// Objects - Symbolic arrays whose initial contents should be + /// given if the query is invalid. + const std::vector<ArrayDecl> Objects; + + public: + template<typename InputIterator> + QueryCommand(InputIterator ConstraintsBegin, + InputIterator ConstraintsEnd, + ExprHandle _Query) + : Constraints(ConstraintsBegin, ConstraintsEnd), + Query(_Query) {} + + virtual void dump(); + }; + + /// Parser - Public interface for parsing a .pc language file. + class Parser { + protected: + Parser(); + public: + virtual ~Parser(); + + /// SetMaxErrors - Suppress anything beyond the first N errors. + virtual void SetMaxErrors(unsigned N) = 0; + + /// GetNumErrors - Return the number of encountered errors. + virtual unsigned GetNumErrors() const = 0; + + /// ParseTopLevelDecl - Parse and return a top level declaration, + /// which the caller assumes ownership of. + /// + /// \return NULL indicates the end of the file has been reached. + virtual Decl *ParseTopLevelDecl() = 0; + + /// CreateParser - Create a parser implementation for the given + /// MemoryBuffer. + /// + /// \arg Name - The name to use in diagnostic messages. + static Parser *Create(const std::string Name, + const llvm::MemoryBuffer *MB); + }; +} +} + +#endif diff --git a/include/klee/Config/config.h.in b/include/klee/Config/config.h.in new file mode 100644 index 00000000..ef41d786 --- /dev/null +++ b/include/klee/Config/config.h.in @@ -0,0 +1,64 @@ +/* include/klee/Config/config.h.in. Generated from autoconf/configure.tmp.ac by autoheader. */ + +/* Define if stplog enabled */ +#undef ENABLE_STPLOG + +/* Does the platform use __ctype_b_loc, etc. */ +#undef HAVE_CTYPE_EXTERNALS + +/* Define to 1 if you have the <inttypes.h> header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the <memory.h> header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the <selinux/selinux.h> header file. */ +#undef HAVE_SELINUX_SELINUX_H + +/* Define to 1 if you have the <stdint.h> header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the <strings.h> header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the <sys/acl.h> header file. */ +#undef HAVE_SYS_ACL_H + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* Path to KLEE's uClibc */ +#undef KLEE_UCLIBC + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Configuration for runtime libraries */ +#undef RUNTIME_CONFIGURATION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS diff --git a/include/klee/Constraints.h b/include/klee/Constraints.h new file mode 100644 index 00000000..87069f89 --- /dev/null +++ b/include/klee/Constraints.h @@ -0,0 +1,79 @@ +//===-- Constraints.h -------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_CONSTRAINTS_H +#define KLEE_CONSTRAINTS_H + +#include "klee/Expr.h" + +// FIXME: Currently we use ConstraintManager for two things: to pass +// sets of constraints around, and to optimize constraints. We should +// move the first usage into a separate data structure +// (ConstraintSet?) which ConstraintManager could embed if it likes. +namespace klee { + +class ExprVisitor; + +class ConstraintManager { +public: + typedef std::vector< ref<Expr> > constraints_ty; + typedef constraints_ty::iterator iterator; + typedef constraints_ty::const_iterator const_iterator; + + ConstraintManager() {} + + // create from constraints with no optimization + explicit + ConstraintManager(const std::vector< ref<Expr> > &_constraints) : + constraints(_constraints) {} + + ConstraintManager(const ConstraintManager &cs) : constraints(cs.constraints) {} + + typedef std::vector< ref<Expr> >::const_iterator constraint_iterator; + + // given a constraint which is known to be valid, attempt to + // simplify the existing constraint set + void simplifyForValidConstraint(ref<Expr> e); + + ref<Expr> simplifyExpr(ref<Expr> e) const; + + void addConstraint(ref<Expr> e); + + bool empty() const { + return constraints.empty(); + } + ref<Expr> back() const { + return constraints.back(); + } + constraint_iterator begin() const { + return constraints.begin(); + } + constraint_iterator end() const { + return constraints.end(); + } + size_t size() const { + return constraints.size(); + } + + bool operator==(const ConstraintManager &other) const { + return constraints == other.constraints; + } + +private: + std::vector< ref<Expr> > constraints; + + // returns true iff the constraints were modified + bool rewriteConstraints(ExprVisitor &visitor); + + void addConstraintInternal(ref<Expr> e); +}; + +} + +#endif /* KLEE_CONSTRAINTS_H */ diff --git a/include/klee/ExecutionState.h b/include/klee/ExecutionState.h new file mode 100644 index 00000000..09ce2a4b --- /dev/null +++ b/include/klee/ExecutionState.h @@ -0,0 +1,250 @@ +//===-- ExecutionState.h ----------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_EXECUTIONSTATE_H +#define KLEE_EXECUTIONSTATE_H + +#include "klee/Constraints.h" +#include "klee/Expr.h" +#include "klee/Internal/ADT/TreeStream.h" + +// FIXME: We do not want to be exposing these? :( +#include "../../lib/Core/AddressSpace.h" +#include "klee/Internal/Module/KInstIterator.h" + +#include <map> +#include <set> +#include <vector> + +namespace klee { + class CallPathNode; + class Cell; + class KFunction; + class KInstruction; + class MemoryObject; + class PTreeNode; + class InstructionInfo; + class ExecutionTraceEvent; + +std::ostream &operator<<(std::ostream &os, const MemoryMap &mm); + +struct StackFrame { + KInstIterator caller; + KFunction *kf; + CallPathNode *callPathNode; + + std::vector<const MemoryObject*> allocas; + Cell *locals; + + /// Minimum distance to an uncovered instruction once the function + /// returns. This is not a good place for this but is used to + /// quickly compute the context sensitive minimum distance to an + /// uncovered instruction. This value is updated by the StatsTracker + /// periodically. + unsigned minDistToUncoveredOnReturn; + + // For vararg functions: arguments not passed via parameter are + // stored (packed tightly) in a local (alloca) memory object. This + // is setup to match the way the front-end generates vaarg code (it + // does not pass vaarg through as expected). VACopy is lowered inside + // of intrinsic lowering. + MemoryObject *varargs; + + StackFrame(KInstIterator caller, KFunction *kf); + StackFrame(const StackFrame &s); + ~StackFrame(); +}; + +// FIXME: Redo execution trace stuff to use a TreeStream, there is no +// need to keep this stuff in memory as far as I can tell. + +// each state should have only one of these guys ... +class ExecutionTraceManager { +public: + ExecutionTraceManager() : hasSeenUserMain(false) {} + + void addEvent(ExecutionTraceEvent* evt); + void printAllEvents(std::ostream &os) const; + +private: + // have we seen a call to __user_main() yet? + // don't bother tracing anything until we see this, + // or else we'll get junky prologue shit + bool hasSeenUserMain; + + // ugh C++ only supports polymorphic calls thru pointers + // + // WARNING: these are NEVER FREED, because they are shared + // across different states (when states fork), so we have + // an *intentional* memory leak, but oh wellz ;) + std::vector<ExecutionTraceEvent*> events; +}; + + +class ExecutionState { +public: + typedef std::vector<StackFrame> stack_ty; + +private: + // unsupported, use copy constructor + ExecutionState &operator=(const ExecutionState&); + std::map< std::string, std::string > fnAliases; + +public: + bool fakeState; + // Are we currently underconstrained? Hack: value is size to make fake + // objects. + unsigned underConstrained; + unsigned depth; + + // pc - pointer to current instruction stream + KInstIterator pc, prevPC; + stack_ty stack; + ConstraintManager constraints; + mutable double queryCost; + double weight; + AddressSpace addressSpace; + TreeOStream pathOS, symPathOS; + unsigned instsSinceCovNew; + bool coveredNew; + + // for printing execution traces when this state terminates + ExecutionTraceManager exeTraceMgr; + + /// Disables forking, set by user code. + bool forkDisabled; + + std::map<const std::string*, std::set<unsigned> > coveredLines; + PTreeNode *ptreeNode; + + /// ordered list of symbolics: used to generate test cases. + // + // FIXME: Move to a shared list structure (not critical). + std::vector<const MemoryObject*> symbolics; + + // Used by the checkpoint/rollback methods for fake objects. + // FIXME: not freeing things on branch deletion. + MemoryMap shadowObjects; + + unsigned incomingBBIndex; + + std::string getFnAlias(std::string fn); + void addFnAlias(std::string old_fn, std::string new_fn); + void removeFnAlias(std::string fn); + +private: + ExecutionState() : fakeState(false), underConstrained(0), ptreeNode(0) {}; + +public: + ExecutionState(KFunction *kf); + + // XXX total hack, just used to make a state so solver can + // use on structure + ExecutionState(const std::vector<ref<Expr> > &assumptions); + + ~ExecutionState(); + + ExecutionState *branch(); + + void pushFrame(KInstIterator caller, KFunction *kf); + void popFrame(); + + void addSymbolic(const MemoryObject *mo) { + symbolics.push_back(mo); + } + void addConstraint(ref<Expr> e) { + constraints.addConstraint(e); + } + + // Used for checkpoint/rollback of fake objects created during tainting. + ObjectState *cloneObject(ObjectState *os, MemoryObject *mo); + + // + + bool merge(const ExecutionState &b); +}; + + +// for producing abbreviated execution traces to help visualize +// paths and diagnose bugs + +class ExecutionTraceEvent { +public: + // the location of the instruction: + std::string file; + unsigned line; + std::string funcName; + unsigned stackDepth; + + unsigned consecutiveCount; // init to 1, increase for CONSECUTIVE + // repetitions of the SAME event + + ExecutionTraceEvent() + : file("global"), line(0), funcName("global_def"), + consecutiveCount(1) {} + + ExecutionTraceEvent(ExecutionState& state, KInstruction* ki); + + virtual ~ExecutionTraceEvent() {} + + void print(std::ostream &os) const; + + // return true if it shouldn't be added to ExecutionTraceManager + // + virtual bool ignoreMe() const; + +private: + virtual void printDetails(std::ostream &os) const = 0; +}; + + +class FunctionCallTraceEvent : public ExecutionTraceEvent { +public: + std::string calleeFuncName; + + FunctionCallTraceEvent(ExecutionState& state, KInstruction* ki, + const std::string& _calleeFuncName) + : ExecutionTraceEvent(state, ki), calleeFuncName(_calleeFuncName) {} + +private: + virtual void printDetails(std::ostream &os) const { + os << "CALL " << calleeFuncName; + } + +}; + +class FunctionReturnTraceEvent : public ExecutionTraceEvent { +public: + FunctionReturnTraceEvent(ExecutionState& state, KInstruction* ki) + : ExecutionTraceEvent(state, ki) {} + +private: + virtual void printDetails(std::ostream &os) const { + os << "RETURN"; + } +}; + +class BranchTraceEvent : public ExecutionTraceEvent { +public: + bool trueTaken; // which side taken? + bool canForkGoBothWays; + + BranchTraceEvent(ExecutionState& state, KInstruction* ki, + bool _trueTaken, bool _isTwoWay) + : ExecutionTraceEvent(state, ki), + trueTaken(_trueTaken), + canForkGoBothWays(_isTwoWay) {} + +private: + virtual void printDetails(std::ostream &os) const; +}; + +} + +#endif diff --git a/include/klee/Expr.h b/include/klee/Expr.h new file mode 100644 index 00000000..d16a09bf --- /dev/null +++ b/include/klee/Expr.h @@ -0,0 +1,808 @@ +//===-- Expr.h --------------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_EXPR_H +#define KLEE_EXPR_H + +#include "Machine.h" +#include "klee/util/Bits.h" + +#include "llvm/Support/Streams.h" +#include "llvm/ADT/SmallVector.h" + +#include <set> +#include <vector> + +namespace llvm { + class Type; +} + +namespace klee { + +class Array; +class ConstantExpr; +class ObjectState; +class MemoryObject; + +template<class T> class ref; + + +/// Class representing symbolic expressions. +/** + +<b>Expression canonicalization</b>: we define certain rules for +canonicalization rules for Exprs in order to simplify code that +pattern matches Exprs (since the number of forms are reduced), to open +up further chances for optimization, and to increase similarity for +caching and other purposes. + +The general rules are: +<ol> +<li> No Expr has all constant arguments.</li> + +<li> Booleans: + <ol type="a"> + <li> Boolean not is written as <tt>(false == ?)</tt> </li> + <li> \c Ne, \c Ugt, \c Uge, \c Sgt, \c Sge are not used </li> + <li> The only acceptable operations with boolean arguments are \c And, + \c Or, \c Xor, \c Eq, as well as \c SExt, \c ZExt, + \c Select and \c NotOptimized. </li> + <li> The only boolean operation which may involve a constant is boolean not (<tt>== false</tt>). </li> + </ol> +</li> + +<li> Linear Formulas: + <ol type="a"> + <li> For any subtree representing a linear formula, a constant + term must be on the LHS of the root node of the subtree. In particular, + in a BinaryExpr a constant must always be on the LHS. For example, subtraction + by a constant c is written as <tt>add(-c, ?)</tt>. </li> + </ol> +</li> + + +<li> Chains are unbalanced to the right </li> + +</ol> + + +<b>Steps required for adding an expr</b>: + -# Add case to printKind + -# Add to ExprVisitor + -# Add to IVC (implied value concretization) if possible + +Todo: Shouldn't bool \c Xor just be written as not equal? + +*/ + +class Expr { +public: + static unsigned count; + static const unsigned MAGIC_HASH_CONSTANT = 39; + + /// The type of an expression is simply its width, in bits. + typedef unsigned Width; + + static const Width InvalidWidth = 0; + static const Width Bool = 1; + static const Width Int8 = 8; + static const Width Int16 = 16; + static const Width Int32 = 32; + static const Width Int64 = 64; + + + enum Kind { + InvalidKind = -1, + + // Primitive + + Constant = 0, + + // Special + + /// Prevents optimization below the given expression. Used for + /// testing: make equality constraints that KLEE will not use to + /// optimize to concretes. + NotOptimized, + + //// Skip old varexpr, just for deserialization, purge at some point + Read=NotOptimized+2, + Select, + Concat, + Extract, + + // Casting, + + ZExt, + SExt, + + // Arithmetic + Add, + Sub, + Mul, + UDiv, + SDiv, + URem, + SRem, + + // Bit + And, + Or, + Xor, + Shl, + LShr, + AShr, + + // Compare + Eq, + Ne, /// Not used in canonical form + Ult, + Ule, + Ugt, /// Not used in canonical form + Uge, /// Not used in canonical form + Slt, + Sle, + Sgt, /// Not used in canonical form + Sge, /// Not used in canonical form + + LastKind=Sge + }; + + unsigned refCount; + +protected: + unsigned hashValue; + +public: + Expr() : refCount(0) { Expr::count++; } + virtual ~Expr() { Expr::count--; } + + virtual Kind getKind() const = 0; + virtual Width getWidth() const = 0; + + virtual unsigned getNumKids() const = 0; + virtual ref<Expr> getKid(unsigned i) const = 0; + + virtual void print(std::ostream &os) const; + + /// Returns the pre-computed hash of the current expression + virtual unsigned hash() const { return hashValue; } + + /// (Re)computes the hash of the current expression. + /// Returns the hash value. + virtual unsigned computeHash(); + + static unsigned hashConstant(uint64_t val, Width w) { + return val ^ (w * MAGIC_HASH_CONSTANT); + } + + /// Returns 0 iff b is structuraly equivalent to *this + int compare(const Expr &b) const; + virtual int compareContents(const Expr &b) const { return 0; } + + // Given an array of new kids return a copy of the expression + // but using those children. + virtual ref<Expr> rebuild(ref<Expr> kids[/* getNumKids() */]) const = 0; + + /// + + uint64_t getConstantValue() const; + + /* Static utility methods */ + + static void printKind(std::ostream &os, Kind k); + static void printWidth(std::ostream &os, Expr::Width w); + static Width getWidthForLLVMType(const llvm::Type *type); + + /// returns the smallest number of bytes in which the given width fits + static inline unsigned getMinBytesForWidth(Width w) { + return (w + 7) / 8; + } + + /* Kind utilities */ + + /* Utility creation functions */ + static ref<Expr> createCoerceToPointerType(ref<Expr> e); + static ref<Expr> createNot(ref<Expr> e); + static ref<Expr> createImplies(ref<Expr> hyp, ref<Expr> conc); + static ref<Expr> createIsZero(ref<Expr> e); + + /// Create a little endian read of the given type at offset 0 of the + /// given object. + static ref<Expr> createTempRead(const Array *array, Expr::Width w); + + static ref<Expr> createPointer(uint64_t v); + + // do not use + static Expr *createConstant(uint64_t val, Width w); + + struct CreateArg; + static ref<Expr> createFromKind(Kind k, std::vector<CreateArg> args); + + static bool isValidKidWidth(unsigned kid, Width w) { return true; } + static bool needsResultType() { return false; } +}; +// END class Expr + + + +#include "klee/util/Ref.h" + +struct Expr::CreateArg { + ref<Expr> expr; + Width width; + + CreateArg(Width w = Bool) : expr(0, Expr::Bool), width(w) {} + CreateArg(ref<Expr> e) : expr(e), width(Expr::InvalidWidth) {} + + bool isExpr() { return !isWidth(); } + bool isWidth() { return width != Expr::InvalidWidth; } +}; + +// Comparison operators + +inline bool operator==(const Expr &lhs, const Expr &rhs) { + return lhs.compare(rhs) == 0; +} + +inline bool operator<(const Expr &lhs, const Expr &rhs) { + return lhs.compare(rhs) < 0; +} + +inline bool operator!=(const Expr &lhs, const Expr &rhs) { + return !(lhs == rhs); +} + +inline bool operator>(const Expr &lhs, const Expr &rhs) { + return rhs < lhs; +} + +inline bool operator<=(const Expr &lhs, const Expr &rhs) { + return !(lhs > rhs); +} + +inline bool operator>=(const Expr &lhs, const Expr &rhs) { + return !(lhs < rhs); +} + +// Printing operators + +inline std::ostream &operator<<(std::ostream &os, const Expr &e) { + e.print(os); + return os; +} + +inline std::ostream &operator<<(std::ostream &os, const Expr::Kind kind) { + Expr::printKind(os, kind); + return os; +} + +// Terminal Exprs + +class ConstantExpr : public Expr { +public: + static const Kind kind = Constant; + static const unsigned numKids = 0; + +public: + union { + uint64_t asUInt64; + }; + Width width; + +public: + ~ConstantExpr() {}; + // should change the code to make this private + ConstantExpr(uint64_t v, Width w) : asUInt64(v), width(w) {} + + Width getWidth() const { return width; } + Kind getKind() const { return Constant; } + + unsigned getNumKids() const { return 0; } + ref<Expr> getKid(unsigned i) const { return 0; } + + int compareContents(const Expr &b) const { + const ConstantExpr &cb = static_cast<const ConstantExpr&>(b); + if (width != cb.width) return width < cb.width ? -1 : 1; + if (asUInt64 < cb.asUInt64) { + return -1; + } else if (asUInt64 > cb.asUInt64) { + return 1; + } else { + return 0; + } + } + + virtual ref<Expr> rebuild(ref<Expr> kids[]) const { + assert(0 && "rebuild() on ConstantExpr"); + } + + virtual unsigned computeHash(); + + static ref<ConstantExpr> fromMemory(void *address, Width w); + void toMemory(void *address); + + static ref<ConstantExpr> alloc(uint64_t v, Width w) { + // constructs an "optimized" ConstantExpr + return ref<ConstantExpr>(v, w); + } + + static ref<ConstantExpr> create(uint64_t v, Width w) { + assert(v == bits64::truncateToNBits(v, w) && + "invalid constant"); + return alloc(v, w); + } +}; + + +// Utility classes + +class BinaryExpr : public Expr { +public: + ref<Expr> left, right; + +public: + unsigned getNumKids() const { return 2; } + ref<Expr> getKid(unsigned i) const { + if(i == 0) + return left; + if(i == 1) + return right; + return 0; + } + +protected: + BinaryExpr(const ref<Expr> &l, const ref<Expr> &r) : left(l), right(r) {} +}; + + +class CmpExpr : public BinaryExpr { + +protected: + CmpExpr(ref<Expr> l, ref<Expr> r) : BinaryExpr(l,r) {} + +public: + Width getWidth() const { return Bool; } +}; + +// Special + +class NotOptimizedExpr : public Expr { +public: + static const Kind kind = NotOptimized; + static const unsigned numKids = 1; + ref<Expr> src; + + static ref<Expr> alloc(const ref<Expr> &src) { + ref<Expr> r(new NotOptimizedExpr(src)); + r.computeHash(); + return r; + } + + static ref<Expr> create(ref<Expr> src); + + Width getWidth() const { return src.getWidth(); } + Kind getKind() const { return NotOptimized; } + + unsigned getNumKids() const { return 1; } + ref<Expr> getKid(unsigned i) const { return src; } + + virtual ref<Expr> rebuild(ref<Expr> kids[]) const { return create(kids[0]); } + +private: + NotOptimizedExpr(const ref<Expr> &_src) : src(_src) {} +}; + + +/// Class representing a byte update of an array. +class UpdateNode { + friend class UpdateList; + friend class STPBuilder; // for setting STPArray + + mutable unsigned refCount; + // gross + mutable void *stpArray; + // cache instead of recalc + unsigned hashValue; + +public: + const UpdateNode *next; + ref<Expr> index, value; + +private: + /// size of this update sequence, including this update + unsigned size; + +public: + UpdateNode(const UpdateNode *_next, + const ref<Expr> &_index, + const ref<Expr> &_value); + + unsigned getSize() const { return size; } + + int compare(const UpdateNode &b) const; + unsigned hash() const { return hashValue; } + +private: + UpdateNode() : refCount(0), stpArray(0) {} + ~UpdateNode(); + + unsigned computeHash(); +}; + +class Array { +public: + const MemoryObject *object; + unsigned id; + unsigned size; + + // FIXME: This does not belong here. + mutable void *stpInitialArray; + +public: + // NOTE: id's ***MUST*** be unique to ensure sanity w.r.t. STP, + // which hashes different arrays with the same id to the same + // object! We should probably use the pointer for talking to STP, as + // long as we can guarantee that it won't be a "stale" reference + // once we have freed it. + Array(const MemoryObject *_object, unsigned _id, uint64_t _size) + : object(_object), id(_id), size(_size), stpInitialArray(0) {} + ~Array() { + // FIXME: This relies on caller to delete the STP array. + assert(!stpInitialArray && "Array must be deleted by caller!"); + } +}; + +/// Class representing a complete list of updates into an array. +/** The main trick is the isRooted bit, which enables important optimizations. + ... + */ +class UpdateList { + friend class ReadExpr; // for default constructor + +public: + const Array *root; + + /// pointer to the most recent update node + const UpdateNode *head; + + // shouldn't this be part of the ReadExpr? + bool isRooted; + +public: + UpdateList(const Array *_root, bool isRooted, const UpdateNode *_head); + UpdateList(const UpdateList &b); + ~UpdateList(); + + UpdateList &operator=(const UpdateList &b); + + /// size of this update list + unsigned getSize() const { return (head ? head->getSize() : 0); } + + void extend(const ref<Expr> &index, const ref<Expr> &value); + + int compare(const UpdateList &b) const; + unsigned hash() const; +}; + +/// Class representing a one byte read from an array. +class ReadExpr : public Expr { +public: + static const Kind kind = Read; + static const unsigned numKids = 1; + +public: + UpdateList updates; + ref<Expr> index; + +public: + static ref<Expr> alloc(const UpdateList &updates, const ref<Expr> &index) { + ref<Expr> r(new ReadExpr(updates, index)); + r.computeHash(); + return r; + } + + static ref<Expr> create(const UpdateList &updates, ref<Expr> i); + + Width getWidth() const { return Expr::Int8; } + Kind getKind() const { return Read; } + + unsigned getNumKids() const { return numKids; } + ref<Expr> getKid(unsigned i) const { return !i ? index : 0; } + + int compareContents(const Expr &b) const; + + virtual ref<Expr> rebuild(ref<Expr> kids[]) const { + return create(updates, kids[0]); + } + + virtual unsigned computeHash(); + +private: + ReadExpr(const UpdateList &_updates, const ref<Expr> &_index) : + updates(_updates), index(_index) {} +}; + + +/// Class representing an if-then-else expression. +class SelectExpr : public Expr { +public: + static const Kind kind = Select; + static const unsigned numKids = 3; + +public: + ref<Expr> cond, trueExpr, falseExpr; + +public: + static ref<Expr> alloc(const ref<Expr> &c, const ref<Expr> &t, const ref<Expr> &f) { + ref<Expr> r(new SelectExpr(c, t, f)); + r.computeHash(); + return r; + } + + static ref<Expr> create(ref<Expr> c, ref<Expr> t, ref<Expr> f); + + Width getWidth() const { return trueExpr.getWidth(); } + Kind getKind() const { return Select; } + + unsigned getNumKids() const { return numKids; } + ref<Expr> getKid(unsigned i) const { + switch(i) { + case 0: return cond; + case 1: return trueExpr; + case 2: return falseExpr; + default: return 0; + } + } + + static bool isValidKidWidth(unsigned kid, Width w) { + if (kid == 0) + return w == Bool; + else + return true; + } + + virtual ref<Expr> rebuild(ref<Expr> kids[]) const { + return create(kids[0], kids[1], kids[2]); + } + +private: + SelectExpr(const ref<Expr> &c, const ref<Expr> &t, const ref<Expr> &f) + : cond(c), trueExpr(t), falseExpr(f) {} +}; + + +/** Children of a concat expression can have arbitrary widths. + Kid 0 is the left kid, kid 1 is the right kid. +*/ +class ConcatExpr : public Expr { +public: + static const Kind kind = Concat; + static const unsigned numKids = 2; + +private: + Width width; + ref<Expr> left, right; + +public: + static ref<Expr> alloc(const ref<Expr> &l, const ref<Expr> &r) { + ref<Expr> c(new ConcatExpr(l, r)); + c.computeHash(); + return c; + } + + static ref<Expr> create(const ref<Expr> &l, const ref<Expr> &r); + + Width getWidth() const { return width; } + Kind getKind() const { return kind; } + ref<Expr> getLeft() const { return left; } + ref<Expr> getRight() const { return right; } + + unsigned getNumKids() const { return numKids; } + ref<Expr> getKid(unsigned i) const { + if (i == 0) return left; + else if (i == 1) return right; + else return NULL; + } + + /// Shortcuts to create larger concats. The chain returned is unbalanced to the right + static ref<Expr> createN(unsigned nKids, const ref<Expr> kids[]); + static ref<Expr> create4(const ref<Expr> &kid1, const ref<Expr> &kid2, + const ref<Expr> &kid3, const ref<Expr> &kid4); + static ref<Expr> create8(const ref<Expr> &kid1, const ref<Expr> &kid2, + const ref<Expr> &kid3, const ref<Expr> &kid4, + const ref<Expr> &kid5, const ref<Expr> &kid6, + const ref<Expr> &kid7, const ref<Expr> &kid8); + + virtual ref<Expr> rebuild(ref<Expr> kids[]) const { return create(kids[0], kids[1]); } + + + /* These will be eliminated */ + bool is2ByteConcat() const { return false; } + bool is4ByteConcat() const { return false; } + bool is8ByteConcat() const { return false; } + +private: + ConcatExpr(const ref<Expr> &l, const ref<Expr> &r) : left(l), right(r) { + width = l.getWidth() + r.getWidth(); + } +}; + + +/** This class represents an extract from expression {\tt expr}, at + bit offset {\tt offset} of width {\tt width}. Bit 0 is the right most + bit of the expression. + */ +class ExtractExpr : public Expr { +public: + static const Kind kind = Extract; + static const unsigned numKids = 1; + +public: + ref<Expr> expr; + unsigned offset; + Width width; + +public: + static ref<Expr> alloc(const ref<Expr> &e, unsigned o, Width w) { + ref<Expr> r(new ExtractExpr(e, o, w)); + r.computeHash(); + return r; + } + + /// Creates an ExtractExpr with the given bit offset and width + static ref<Expr> create(ref<Expr> e, unsigned bitOff, Width w); + + /// Creates an ExtractExpr with the given byte offset and width + static ref<Expr> createByteOff(ref<Expr> e, unsigned byteOff, Width w=Expr::Int8); + + Width getWidth() const { return width; } + Kind getKind() const { return Extract; } + + unsigned getNumKids() const { return numKids; } + ref<Expr> getKid(unsigned i) const { return expr; } + + int compareContents(const Expr &b) const { + const ExtractExpr &eb = static_cast<const ExtractExpr&>(b); + if (offset != eb.offset) return offset < eb.offset ? -1 : 1; + if (width != eb.width) return width < eb.width ? -1 : 1; + return 0; + } + + virtual ref<Expr> rebuild(ref<Expr> kids[]) const { + return create(kids[0], offset, width); + } + + virtual unsigned computeHash(); + +private: + ExtractExpr(const ref<Expr> &e, unsigned b, Width w) + : expr(e),offset(b),width(w) {} +}; + + +// Casting + +class CastExpr : public Expr { +public: + ref<Expr> src; + Width width; + +public: + CastExpr(const ref<Expr> &e, Width w) : src(e), width(w) {} + + Width getWidth() const { return width; } + + unsigned getNumKids() const { return 1; } + ref<Expr> getKid(unsigned i) const { return (i==0) ? src : 0; } + + static bool needsResultType() { return true; } + + int compareContents(const Expr &b) const { + const CastExpr &eb = static_cast<const CastExpr&>(b); + if (width != eb.width) return width < eb.width ? -1 : 1; + return 0; + } + + virtual unsigned computeHash(); +}; + +#define CAST_EXPR_CLASS(_class_kind) \ +class _class_kind ## Expr : public CastExpr { \ +public: \ + static const Kind kind = _class_kind; \ + static const unsigned numKids = 1; \ +public: \ + _class_kind ## Expr(ref<Expr> e, Width w) : CastExpr(e,w) {} \ + static ref<Expr> alloc(const ref<Expr> &e, Width w) { \ + ref<Expr> r(new _class_kind ## Expr(e, w)); \ + r.computeHash(); \ + return r; \ + } \ + static ref<Expr> create(const ref<Expr> &e, Width w); \ + Kind getKind() const { return _class_kind; } \ + virtual ref<Expr> rebuild(ref<Expr> kids[]) const { \ + return create(kids[0], width); \ + } \ +}; \ + +CAST_EXPR_CLASS(SExt) +CAST_EXPR_CLASS(ZExt) + +// Arithmetic/Bit Exprs + +#define ARITHMETIC_EXPR_CLASS(_class_kind) \ +class _class_kind ## Expr : public BinaryExpr { \ +public: \ + static const Kind kind = _class_kind; \ + static const unsigned numKids = 2; \ +public: \ + _class_kind ## Expr(const ref<Expr> &l, const ref<Expr> &r) : BinaryExpr(l,r) {} \ + static ref<Expr> alloc(const ref<Expr> &l, const ref<Expr> &r) { \ + ref<Expr> res(new _class_kind ## Expr (l, r)); \ + res.computeHash(); \ + return res; \ + } \ + static ref<Expr> create(const ref<Expr> &l, const ref<Expr> &r); \ + Width getWidth() const { return left.getWidth(); } \ + Kind getKind() const { return _class_kind; } \ + virtual ref<Expr> rebuild(ref<Expr> kids[]) const { \ + return create(kids[0], kids[1]); \ + } \ +}; \ + +ARITHMETIC_EXPR_CLASS(Add) +ARITHMETIC_EXPR_CLASS(Sub) +ARITHMETIC_EXPR_CLASS(Mul) +ARITHMETIC_EXPR_CLASS(UDiv) +ARITHMETIC_EXPR_CLASS(SDiv) +ARITHMETIC_EXPR_CLASS(URem) +ARITHMETIC_EXPR_CLASS(SRem) +ARITHMETIC_EXPR_CLASS(And) +ARITHMETIC_EXPR_CLASS(Or) +ARITHMETIC_EXPR_CLASS(Xor) +ARITHMETIC_EXPR_CLASS(Shl) +ARITHMETIC_EXPR_CLASS(LShr) +ARITHMETIC_EXPR_CLASS(AShr) + +// Comparison Exprs + +#define COMPARISON_EXPR_CLASS(_class_kind) \ +class _class_kind ## Expr : public CmpExpr { \ +public: \ + static const Kind kind = _class_kind; \ + static const unsigned numKids = 2; \ +public: \ + _class_kind ## Expr(const ref<Expr> &l, const ref<Expr> &r) : CmpExpr(l,r) {} \ + static ref<Expr> alloc(const ref<Expr> &l, const ref<Expr> &r) { \ + ref<Expr> res(new _class_kind ## Expr (l, r)); \ + res.computeHash(); \ + return res; \ + } \ + static ref<Expr> create(const ref<Expr> &l, const ref<Expr> &r); \ + Kind getKind() const { return _class_kind; } \ + virtual ref<Expr> rebuild(ref<Expr> kids[]) const { \ + return create(kids[0], kids[1]); \ + } \ +}; \ + +COMPARISON_EXPR_CLASS(Eq) +COMPARISON_EXPR_CLASS(Ne) +COMPARISON_EXPR_CLASS(Ult) +COMPARISON_EXPR_CLASS(Ule) +COMPARISON_EXPR_CLASS(Ugt) +COMPARISON_EXPR_CLASS(Uge) +COMPARISON_EXPR_CLASS(Slt) +COMPARISON_EXPR_CLASS(Sle) +COMPARISON_EXPR_CLASS(Sgt) +COMPARISON_EXPR_CLASS(Sge) + +} // End klee namespace + +#endif diff --git a/include/klee/IncompleteSolver.h b/include/klee/IncompleteSolver.h new file mode 100644 index 00000000..f72607b5 --- /dev/null +++ b/include/klee/IncompleteSolver.h @@ -0,0 +1,108 @@ +//===-- IncompleteSolver.h --------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_INCOMPLETESOLVER_H +#define KLEE_INCOMPLETESOLVER_H + +#include "klee/Solver.h" +#include "klee/SolverImpl.h" + +namespace klee { + +/// IncompleteSolver - Base class for incomplete solver +/// implementations. +/// +/// Incomplete solvers are useful for implementing optimizations which +/// may quickly compute an answer, but cannot always compute the +/// correct answer. They can be used with the StagedSolver to provide +/// a complete Solver implementation. +class IncompleteSolver { +public: + /// PartialValidity - Represent a possibility incomplete query + /// validity. + enum PartialValidity { + /// The query is provably true. + MustBeTrue = 1, + + /// The query is provably false. + MustBeFalse = -1, + + /// The query is not provably false (a true assignment is known to + /// exist). + MayBeTrue = 2, + + /// The query is not provably true (a false assignment is known to + /// exist). + MayBeFalse = -2, + + /// The query is known to have both true and false assignments. + TrueOrFalse = 0, + + /// The validity of the query is unknown. + None = 3 + }; + + static PartialValidity negatePartialValidity(PartialValidity pv); + +public: + IncompleteSolver() {}; + virtual ~IncompleteSolver() {}; + + /// computeValidity - Compute a partial validity for the given query. + /// + /// The passed expression is non-constant with bool type. + /// + /// The IncompleteSolver class provides an implementation of + /// computeValidity using computeTruth. Sub-classes may override + /// this if a more efficient implementation is available. + virtual IncompleteSolver::PartialValidity computeValidity(const Query&); + + /// computeValidity - Compute a partial validity for the given query. + /// + /// The passed expression is non-constant with bool type. + virtual IncompleteSolver::PartialValidity computeTruth(const Query&) = 0; + + /// computeValue - Attempt to compute a value for the given expression. + virtual bool computeValue(const Query&, ref<Expr> &result) = 0; + + /// computeInitialValues - Attempt to compute the constant values + /// for the initial state of each given object. If a correct result + /// is not found, then the values array must be unmodified. + virtual bool computeInitialValues(const Query&, + const std::vector<const Array*> + &objects, + std::vector< std::vector<unsigned char> > + &values, + bool &hasSolution) = 0; +}; + +/// StagedSolver - Adapter class for staging an incomplete solver with +/// a complete secondary solver, to form an (optimized) complete +/// solver. +class StagedSolverImpl : public SolverImpl { +private: + IncompleteSolver *primary; + Solver *secondary; + +public: + StagedSolverImpl(IncompleteSolver *_primary, Solver *_secondary); + ~StagedSolverImpl(); + + bool computeTruth(const Query&, bool &isValid); + bool computeValidity(const Query&, Solver::Validity &result); + bool computeValue(const Query&, ref<Expr> &result); + bool computeInitialValues(const Query&, + const std::vector<const Array*> &objects, + std::vector< std::vector<unsigned char> > &values, + bool &hasSolution); +}; + +} + +#endif diff --git a/include/klee/Internal/ADT/BOut.h b/include/klee/Internal/ADT/BOut.h new file mode 100644 index 00000000..14aeb714 --- /dev/null +++ b/include/klee/Internal/ADT/BOut.h @@ -0,0 +1,62 @@ +//===-- BOut.h --------------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __COMMON_BOUT_H__ +#define __COMMON_BOUT_H__ + + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct BOutObject BOutObject; + struct BOutObject { + char *name; + unsigned numBytes; + unsigned char *bytes; + }; + + typedef struct BOut BOut; + struct BOut { + /* file format version */ + unsigned version; + + unsigned numArgs; + char **args; + + unsigned symArgvs; + unsigned symArgvLen; + + unsigned numObjects; + BOutObject *objects; + }; + + + /* returns the current .bout file format version */ + unsigned bOut_getCurrentVersion(); + + /* return true iff file at path matches BOut header */ + int bOut_isBOutFile(const char *path); + + /* returns NULL on (unspecified) error */ + BOut* bOut_fromFile(const char *path); + + /* returns 1 on success, 0 on (unspecified) error */ + int bOut_toFile(BOut *, const char *path); + + /* returns total number of object bytes */ + unsigned bOut_numBytes(BOut *); + + void bOut_free(BOut *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/klee/Internal/ADT/DiscretePDF.h b/include/klee/Internal/ADT/DiscretePDF.h new file mode 100644 index 00000000..bda851fa --- /dev/null +++ b/include/klee/Internal/ADT/DiscretePDF.h @@ -0,0 +1,47 @@ +//===-- DiscretePDF.h -------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +namespace klee { + template <class T> + class DiscretePDF { + // not perfectly parameterized, but float/double/int should work ok, + // although it would be better to have choose argument range from 0 + // to queryable max. + typedef double weight_type; + + public: + DiscretePDF(); + ~DiscretePDF(); + + bool empty() const; + void insert(T item, weight_type weight); + void update(T item, weight_type newWeight); + void remove(T item); + bool inTree(T item); + weight_type getWeight(T item); + + /* pick a tree element according to its + * weight. p should be in [0,1). + */ + T choose(double p); + + private: + class Node; + Node *m_root; + + Node **lookup(T item, Node **parent_out); + void split(Node *node); + void rotate(Node *node); + void lengthen(Node *node); + void propogateSumsUp(Node *n); + }; + +} + +#include "DiscretePDF.inc" diff --git a/include/klee/Internal/ADT/DiscretePDF.inc b/include/klee/Internal/ADT/DiscretePDF.inc new file mode 100644 index 00000000..1eb23629 --- /dev/null +++ b/include/klee/Internal/ADT/DiscretePDF.inc @@ -0,0 +1,342 @@ +//===- DiscretePDF.inc - --*- C++ -*-===// + +// + +namespace klee { + +template <class T> +class DiscretePDF<T>::Node +{ +private: + bool m_mark; + +public: + Node *parent, *left, *right; + T key; + weight_type weight, sumWeights; + +public: + Node(T key_, weight_type weight_, Node *parent_); + ~Node(); + + Node *sibling() { return this==parent->left?parent->right:parent->left; } + + void markRed() { m_mark = true; } + void markBlack() { m_mark = false; } + bool isBlack() { return !m_mark; } + bool leftIsBlack() { return !left || left->isBlack(); } + bool rightIsBlack() { return !right || right->isBlack(); } + void setSum() { + sumWeights = weight; + if (left) sumWeights += left->sumWeights; + if (right) sumWeights += right->sumWeights; + } +}; + + /// + +template <class T> +DiscretePDF<T>::Node::Node(T key_, weight_type weight_, Node *parent_) { + m_mark = false; + + key = key_; + weight = weight_; + sumWeights = 0; + left = right = 0; + parent = parent_; +} + +template <class T> +DiscretePDF<T>::Node::~Node() { + if (left) delete left; + if (right) delete right; +} + +// + +template <class T> +DiscretePDF<T>::DiscretePDF() { + m_root = 0; +} + +template <class T> +DiscretePDF<T>::~DiscretePDF() { + if (m_root) delete m_root; +} + +template <class T> +bool DiscretePDF<T>::empty() const { + return m_root == 0; +} + +template <class T> +void DiscretePDF<T>::insert(T item, weight_type weight) { + Node *p=0, *n=m_root; + + while (n) { + if (!n->leftIsBlack() && !n->rightIsBlack()) + split(n); + + p = n; + if (n->key==item) { + assert(0 && "insert: argument(item) already in tree"); + } else { + n = (item<n->key)?n->left:n->right; + } + } + + n = new Node(item, weight, p); + + if (!p) { + m_root = n; + } else { + if (item<p->key) { + p->left = n; + } else { + p->right = n; + } + + split(n); + } + + propogateSumsUp(n); +} + +template <class T> +void DiscretePDF<T>::remove(T item) { + Node **np = lookup(item, 0); + Node *child, *n = *np; + + if (!n) { + assert(0 && "remove: argument(item) not in tree"); + } else { + if (n->left) { + Node **leftMaxp = &n->left; + + while ((*leftMaxp)->right) + leftMaxp = &(*leftMaxp)->right; + + n->key = (*leftMaxp)->key; + n->weight = (*leftMaxp)->weight; + + np = leftMaxp; + n = *np; + } + + // node now has at most one child + + child = n->left?n->left:n->right; + *np = child; + + if (child) { + child->parent = n->parent; + + if (n->isBlack()) { + lengthen(child); + } + } + + propogateSumsUp(n->parent); + + n->left = n->right = 0; + delete n; + } +} + +template <class T> +void DiscretePDF<T>::update(T item, weight_type weight) { + Node *n = *lookup(item, 0); + + if (!n) { + assert(0 && "update: argument(item) not in tree"); + } else { + n->weight = weight; + propogateSumsUp(n); + } +} + +template <class T> +T DiscretePDF<T>::choose(double p) { + if (p<0.0 || p>=1.0) { + assert(0 && "choose: argument(p) outside valid range"); + } else if (!m_root) { + assert(0 && "choose: choose() called on empty tree"); + } else { + weight_type w = (weight_type) (m_root->sumWeights * p); + Node *n = m_root; + + while (1) { + if (n->left) { + if (w<n->left->sumWeights) { + n = n->left; + continue; + } else { + w -= n->left->sumWeights; + } + } + if (w<n->weight || !n->right) { + break; // !n->right condition shouldn't be necessary, just sanity check + } + w -= n->weight; + n = n->right; + } + + return n->key; + } +} + +template <class T> +bool DiscretePDF<T>::inTree(T item) { + Node *n = *lookup(item, 0); + + return !!n; +} + +template <class T> +typename DiscretePDF<T>::weight_type DiscretePDF<T>::getWeight(T item) { + Node *n = *lookup(item, 0); + assert(n); + return n->weight; +} + +// + +template <class T> +typename DiscretePDF<T>::Node ** +DiscretePDF<T>::lookup(T item, Node **parent_out) { + Node *n, *p=0, **np=&m_root; + + while ((n = *np)) { + if (n->key==item) { + break; + } else { + p = n; + if (item<n->key) { + np = &n->left; + } else { + np = &n->right; + } + } + } + + if (parent_out) + *parent_out = p; + return np; +} + +template <class T> +void DiscretePDF<T>::split(Node *n) { + if (n->left) n->left->markBlack(); + if (n->right) n->right->markBlack(); + + if (n->parent) { + Node *p = n->parent; + + n->markRed(); + + if (!p->isBlack()) { + p->parent->markRed(); + + // not same direction + if (!((n==p->left && p==p->parent->left) || + (n==p->right && p==p->parent->right))) { + rotate(n); + p = n; + } + + rotate(p); + p->markBlack(); + } + } +} + +template <class T> +void DiscretePDF<T>::rotate(Node *n) { + Node *p=n->parent, *pp=p->parent; + + n->parent = pp; + p->parent = n; + + if (n==p->left) { + p->left = n->right; + n->right = p; + if (p->left) p->left->parent = p; + } else { + p->right = n->left; + n->left = p; + if (p->right) p->right->parent = p; + } + + n->setSum(); + p->setSum(); + + if (!pp) { + m_root = n; + } else { + if (p==pp->left) { + pp->left = n; + } else { + pp->right = n; + } + } +} + +template <class T> +void DiscretePDF<T>::lengthen(Node *n) { + if (!n->isBlack()) { + n->markBlack(); + } else if (n->parent) { + Node *sibling = n->sibling(); + + if (sibling && !sibling->isBlack()) { + n->parent->markRed(); + sibling->markBlack(); + + rotate(sibling); // node sibling is now old sibling child, must be black + sibling = n->sibling(); + } + + // sibling is black + + if (!sibling) { + lengthen(n->parent); + } else if (sibling->leftIsBlack() && sibling->rightIsBlack()) { + if (n->parent->isBlack()) { + sibling->markRed(); + lengthen(n->parent); + } else { + sibling->markRed(); + n->parent->markBlack(); + } + } else { + if (n==n->parent->left && sibling->rightIsBlack()) { + rotate(sibling->left); // sibling->left must be red + sibling->markRed(); + sibling->parent->markBlack(); + sibling = sibling->parent; + } else if (n==n->parent->right && sibling->leftIsBlack()) { + rotate(sibling->right); // sibling->right must be red + sibling->markRed(); + sibling->parent->markBlack(); + sibling = sibling->parent; + } + + // sibling is black, and sibling's far child is red + + rotate(sibling); + if (!n->parent->isBlack()) + sibling->markRed(); + sibling->left->markBlack(); + sibling->right->markBlack(); + } + } +} + +template <class T> +void DiscretePDF<T>::propogateSumsUp(Node *n) { + for (; n; n=n->parent) + n->setSum(); +} + +} + diff --git a/include/klee/Internal/ADT/ImmutableMap.h b/include/klee/Internal/ADT/ImmutableMap.h new file mode 100644 index 00000000..c7af3786 --- /dev/null +++ b/include/klee/Internal/ADT/ImmutableMap.h @@ -0,0 +1,104 @@ +//===-- ImmutableMap.h ------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __UTIL_IMMUTABLEMAP_H__ +#define __UTIL_IMMUTABLEMAP_H__ + +#include <functional> + +#include "ImmutableTree.h" + +namespace klee { + template<class V, class D> + struct _Select1st { + D &operator()(V &a) const { return a.first; } + const D &operator()(const V &a) const { return a.first; } + }; + + template<class K, class D, class CMP=std::less<K> > + class ImmutableMap { + public: + typedef K key_type; + typedef std::pair<K,D> value_type; + + typedef ImmutableTree<K, value_type, _Select1st<value_type,key_type>, CMP> Tree; + typedef typename Tree::iterator iterator; + + private: + Tree elts; + + ImmutableMap(const Tree &b): elts(b) {} + + public: + ImmutableMap() {} + ImmutableMap(const ImmutableMap &b) : elts(b.elts) {} + ~ImmutableMap() {} + + ImmutableMap &operator=(const ImmutableMap &b) { elts = b.elts; return *this; } + + bool empty() const { + return elts.empty(); + } + unsigned count(const key_type &key) const { + return elts.count(key); + } + const value_type *lookup(const key_type &key) const { + return elts.lookup(key); + } + const value_type *lookup_previous(const key_type &key) const { + return elts.lookup_previous(key); + } + const value_type &min() const { + return elts.min(); + } + const value_type &max() const { + return elts.max(); + } + unsigned size() const { + return elts.size(); + } + + ImmutableMap insert(const value_type &value) const { + return elts.insert(value); + } + ImmutableMap replace(const value_type &value) const { + return elts.replace(value); + } + ImmutableMap remove(const key_type &key) const { + return elts.remove(key); + } + ImmutableMap popMin(const value_type &valueOut) const { + return elts.popMin(valueOut); + } + ImmutableMap popMax(const value_type &valueOut) const { + return elts.popMax(valueOut); + } + + iterator begin() const { + return elts.begin(); + } + iterator end() const { + return elts.end(); + } + iterator find(const key_type &key) const { + return elts.find(key); + } + iterator lower_bound(const key_type &key) const { + return elts.lower_bound(key); + } + iterator upper_bound(const key_type &key) const { + return elts.upper_bound(key); + } + + static unsigned getAllocated() { return Tree::allocated; } + }; + +} + +#endif diff --git a/include/klee/Internal/ADT/ImmutableSet.h b/include/klee/Internal/ADT/ImmutableSet.h new file mode 100644 index 00000000..0c79eb9c --- /dev/null +++ b/include/klee/Internal/ADT/ImmutableSet.h @@ -0,0 +1,101 @@ +//===-- ImmutableSet.h ------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __UTIL_IMMUTABLESET_H__ +#define __UTIL_IMMUTABLESET_H__ + +#include <functional> + +#include "ImmutableTree.h" + +namespace klee { + template<class T> + struct _Identity { + T &operator()(T &a) const { return a; } + const T &operator()(const T &a) const { return a; } + }; + + template<class T, class CMP=std::less<T> > + class ImmutableSet { + public: + typedef T key_type; + typedef T value_type; + + typedef ImmutableTree<T, T, _Identity<T>, CMP> Tree; + typedef typename Tree::iterator iterator; + + private: + Tree elts; + + ImmutableSet(const Tree &b): elts(b) {} + + public: + ImmutableSet() {} + ImmutableSet(const ImmutableSet &b) : elts(b.elts) {} + ~ImmutableSet() {} + + ImmutableSet &operator=(const ImmutableSet &b) { elts = b.elts; return *this; } + + bool empty() const { + return elts.empty(); + } + unsigned count(const key_type &key) const { + return elts.count(key); + } + const value_type *lookup(const key_type &key) const { + return elts.lookup(key); + } + const value_type &min() const { + return elts.min(); + } + const value_type &max() const { + return elts.max(); + } + unsigned size() { + return elts.size(); + } + + ImmutableSet insert(const value_type &value) const { + return elts.insert(value); + } + ImmutableSet replace(const value_type &value) const { + return elts.replace(value); + } + ImmutableSet remove(const key_type &key) const { + return elts.remove(key); + } + ImmutableSet popMin(const value_type &valueOut) const { + return elts.popMin(valueOut); + } + ImmutableSet popMax(const value_type &valueOut) const { + return elts.popMax(valueOut); + } + + iterator begin() const { + return elts.begin(); + } + iterator end() const { + return elts.end(); + } + iterator find(const key_type &key) const { + return elts.find(key); + } + iterator lower_bound(const key_type &key) const { + return elts.lower_bound(key); + } + iterator upper_bound(const key_type &key) const { + return elts.upper_bound(key); + } + + static unsigned getAllocated() { return Tree::allocated; } + }; + +} + +#endif diff --git a/include/klee/Internal/ADT/ImmutableTree.h b/include/klee/Internal/ADT/ImmutableTree.h new file mode 100644 index 00000000..2f294077 --- /dev/null +++ b/include/klee/Internal/ADT/ImmutableTree.h @@ -0,0 +1,619 @@ +//===-- ImmutableTree.h -----------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __UTIL_IMMUTABLETREE_H__ +#define __UTIL_IMMUTABLETREE_H__ + +#include <cassert> +#include <vector> + +namespace klee { + template<class K, class V, class KOV, class CMP> + class ImmutableTree { + public: + static unsigned allocated; + class iterator; + + typedef K key_type; + typedef V value_type; + typedef KOV key_of_value; + typedef CMP key_compare; + + public: + ImmutableTree(); + ImmutableTree(const ImmutableTree &s); + ~ImmutableTree(); + + ImmutableTree &operator=(const ImmutableTree &s); + + bool empty() const; + + unsigned count(const key_type &key) const; // always 0 or 1 + const value_type *lookup(const key_type &key) const; + + // find the last value less than or equal to key, or null if + // no such value exists + const value_type *lookup_previous(const key_type &key) const; + + const value_type &min() const; + const value_type &max() const; + unsigned size() const; + + ImmutableTree insert(const value_type &value) const; + ImmutableTree replace(const value_type &value) const; + ImmutableTree remove(const key_type &key) const; + ImmutableTree popMin(value_type &valueOut) const; + ImmutableTree popMax(value_type &valueOut) const; + + iterator begin() const; + iterator end() const; + iterator find(const key_type &key) const; + iterator lower_bound(const key_type &key) const; + iterator upper_bound(const key_type &key) const; + + static unsigned getAllocated() { return allocated; } + + private: + class Node; + + Node *node; + ImmutableTree(Node *_node); + }; + + /***/ + + template<class K, class V, class KOV, class CMP> + class ImmutableTree<K,V,KOV,CMP>::Node { + public: + static Node terminator; + Node *left, *right; + value_type value; + unsigned height, references; + + protected: + Node(); // solely for creating the terminator node + static Node *balance(Node *left, const value_type &value, Node *right); + + public: + + Node(Node *_left, Node *_right, const value_type &_value); + ~Node(); + + void decref(); + Node *incref(); + + bool isTerminator(); + + unsigned size(); + Node *popMin(value_type &valueOut); + Node *popMax(value_type &valueOut); + Node *insert(const value_type &v); + Node *replace(const value_type &v); + Node *remove(const key_type &k); + }; + + // Should live somewhere else, this is a simple stack with maximum size. + template<typename T> + class FixedStack { + unsigned pos, max; + T *elts; + + public: + FixedStack(unsigned _max) : pos(0), + max(_max), + elts(new T[max]) {} + FixedStack(const FixedStack &b) : pos(b.pos), + max(b.max), + elts(new T[b.max]) { + std::copy(b.elts, b.elts+pos, elts); + } + ~FixedStack() { delete[] elts; } + + void push_back(const T &elt) { elts[pos++] = elt; } + void pop_back() { --pos; } + bool empty() { return pos==0; } + T &back() { return elts[pos-1]; } + + + FixedStack &operator=(const FixedStack &b) { + assert(max == b.max); + pos = b.pos; + std::copy(b.elts, b.elts+pos, elts); + return *this; + } + + bool operator==(const FixedStack &b) { + return (pos == b.pos && + std::equal(elts, elts+pos, b.elts)); + } + bool operator!=(const FixedStack &b) { return !(*this==b); } + }; + + template<class K, class V, class KOV, class CMP> + class ImmutableTree<K,V,KOV,CMP>::iterator { + friend class ImmutableTree<K,V,KOV,CMP>; + private: + Node *root; // so can back up from end + FixedStack<Node*> stack; + + public: + iterator(Node *_root, bool atBeginning) : root(_root->incref()), + stack(root->height) { + if (atBeginning) { + for (Node *n=root; !n->isTerminator(); n=n->left) + stack.push_back(n); + } + } + iterator(const iterator &i) : root(i.root->incref()), + stack(i.stack) { + } + ~iterator() { + root->decref(); + } + + iterator &operator=(const iterator &b) { + b.root->incref(); + root->decref(); + root = b.root; + stack = b.stack; + return *this; + } + + const value_type &operator*() { + Node *n = stack.back(); + return n->value; + } + + const value_type *operator->() { + Node *n = stack.back(); + return &n->value; + } + + bool operator==(const iterator &b) { + return stack==b.stack; + } + bool operator!=(const iterator &b) { + return stack!=b.stack; + } + + iterator &operator--() { + if (stack.empty()) { + for (Node *n=root; !n->isTerminator(); n=n->right) + stack.push_back(n); + } else { + Node *n = stack.back(); + if (n->left->isTerminator()) { + for (;;) { + Node *prev = n; + stack.pop_back(); + if (stack.empty()) { + break; + } else { + n = stack.back(); + if (prev==n->right) + break; + } + } + } else { + stack.push_back(n->left); + for (n=n->left->right; !n->isTerminator(); n=n->right) + stack.push_back(n); + } + } + return *this; + } + + iterator &operator++() { + assert(!stack.empty()); + Node *n = stack.back(); + if (n->right->isTerminator()) { + for (;;) { + Node *prev = n; + stack.pop_back(); + if (stack.empty()) { + break; + } else { + n = stack.back(); + if (prev==n->left) + break; + } + } + } else { + stack.push_back(n->right); + for (n=n->right->left; !n->isTerminator(); n=n->left) + stack.push_back(n); + } + return *this; + } + }; + + /***/ + + template<class K, class V, class KOV, class CMP> + typename ImmutableTree<K,V,KOV,CMP>::Node + ImmutableTree<K,V,KOV,CMP>::Node::terminator; + + template<class K, class V, class KOV, class CMP> + unsigned ImmutableTree<K,V,KOV,CMP>::allocated = 0; + + template<class K, class V, class KOV, class CMP> + ImmutableTree<K,V,KOV,CMP>::Node::Node() + : left(&terminator), + right(&terminator), + height(0), + references(3) { + assert(this==&terminator); + } + + template<class K, class V, class KOV, class CMP> + ImmutableTree<K,V,KOV,CMP>::Node::Node(Node *_left, Node *_right, const value_type &_value) + : left(_left), + right(_right), + value(_value), + height(std::max(left->height, right->height) + 1), + references(1) + { + ++allocated; + } + + template<class K, class V, class KOV, class CMP> + ImmutableTree<K,V,KOV,CMP>::Node::~Node() { + left->decref(); + right->decref(); + --allocated; + } + + template<class K, class V, class KOV, class CMP> + inline void ImmutableTree<K,V,KOV,CMP>::Node::decref() { + --references; + if (references==0) delete this; + } + + template<class K, class V, class KOV, class CMP> + inline typename ImmutableTree<K,V,KOV,CMP>::Node *ImmutableTree<K,V,KOV,CMP>::Node::incref() { + ++references; + return this; + } + + template<class K, class V, class KOV, class CMP> + inline bool ImmutableTree<K,V,KOV,CMP>::Node::isTerminator() { + return this==&terminator; + } + + /***/ + + template<class K, class V, class KOV, class CMP> + typename ImmutableTree<K,V,KOV,CMP>::Node * + ImmutableTree<K,V,KOV,CMP>::Node::balance(Node *left, const value_type &value, Node *right) { + if (left->height > right->height + 2) { + Node *ll = left->left; + Node *lr = left->right; + if (ll->height >= lr->height) { + Node *nlr = new Node(lr->incref(), right, value); + Node *res = new Node(ll->incref(), nlr, left->value); + left->decref(); + return res; + } else { + Node *lrl = lr->left; + Node *lrr = lr->right; + Node *nll = new Node(ll->incref(), lrl->incref(), left->value); + Node *nlr = new Node(lrr->incref(), right, value); + Node *res = new Node(nll, nlr, lr->value); + left->decref(); + return res; + } + } else if (right->height > left->height + 2) { + Node *rl = right->left; + Node *rr = right->right; + if (rr->height >= rl->height) { + Node *nrl = new Node(left, rl->incref(), value); + Node *res = new Node(nrl, rr->incref(), right->value); + right->decref(); + return res; + } else { + Node *rll = rl->left; + Node *rlr = rl->right; + Node *nrl = new Node(left, rll->incref(), value); + Node *nrr = new Node(rlr->incref(), rr->incref(), right->value); + Node *res = new Node(nrl, nrr, rl->value); + right->decref(); + return res; + } + } else { + return new Node(left, right, value); + } + } + + template<class K, class V, class KOV, class CMP> + unsigned ImmutableTree<K,V,KOV,CMP>::Node::size() { + if (isTerminator()) { + return 0; + } else { + return left->size() + 1 + right->size(); + } + } + + template<class K, class V, class KOV, class CMP> + typename ImmutableTree<K,V,KOV,CMP>::Node * + ImmutableTree<K,V,KOV,CMP>::Node::popMin(value_type &valueOut) { + if (left->isTerminator()) { + valueOut = value; + return right->incref(); + } else { + return balance(left->popMin(valueOut), value, right->incref()); + } + } + + template<class K, class V, class KOV, class CMP> + typename ImmutableTree<K,V,KOV,CMP>::Node * + ImmutableTree<K,V,KOV,CMP>::Node::popMax(value_type &valueOut) { + if (right->isTerminator()) { + valueOut = value; + return left->incref(); + } else { + return balance(left->incref(), value, right->popMax(valueOut)); + } + } + + template<class K, class V, class KOV, class CMP> + typename ImmutableTree<K,V,KOV,CMP>::Node * + ImmutableTree<K,V,KOV,CMP>::Node::insert(const value_type &v) { + if (isTerminator()) { + return new Node(terminator.incref(), terminator.incref(), v); + } else { + if (key_compare()(key_of_value()(v), key_of_value()(value))) { + return balance(left->insert(v), value, right->incref()); + } else if (key_compare()(key_of_value()(value), key_of_value()(v))) { + return balance(left->incref(), value, right->insert(v)); + } else { + return incref(); + } + } + } + + template<class K, class V, class KOV, class CMP> + typename ImmutableTree<K,V,KOV,CMP>::Node * + ImmutableTree<K,V,KOV,CMP>::Node::replace(const value_type &v) { + if (isTerminator()) { + return new Node(terminator.incref(), terminator.incref(), v); + } else { + if (key_compare()(key_of_value()(v), key_of_value()(value))) { + return balance(left->replace(v), value, right->incref()); + } else if (key_compare()(key_of_value()(value), key_of_value()(v))) { + return balance(left->incref(), value, right->replace(v)); + } else { + return new Node(left->incref(), right->incref(), v); + } + } + } + + template<class K, class V, class KOV, class CMP> + typename ImmutableTree<K,V,KOV,CMP>::Node * + ImmutableTree<K,V,KOV,CMP>::Node::remove(const key_type &k) { + if (isTerminator()) { + return incref(); + } else { + if (key_compare()(k, key_of_value()(value))) { + return balance(left->remove(k), value, right->incref()); + } else if (key_compare()(key_of_value()(value), k)) { + return balance(left->incref(), value, right->remove(k)); + } else { + if (left->isTerminator()) { + return right->incref(); + } else if (right->isTerminator()) { + return left->incref(); + } else { + value_type min; + Node *nr = right->popMin(min); + return balance(left->incref(), min, nr); + } + } + } + } + + /***/ + + template<class K, class V, class KOV, class CMP> + ImmutableTree<K,V,KOV,CMP>::ImmutableTree() + : node(Node::terminator.incref()) { + } + + template<class K, class V, class KOV, class CMP> + ImmutableTree<K,V,KOV,CMP>::ImmutableTree(Node *_node) + : node(_node) { + } + + template<class K, class V, class KOV, class CMP> + ImmutableTree<K,V,KOV,CMP>::ImmutableTree(const ImmutableTree &s) + : node(s.node->incref()) { + } + + template<class K, class V, class KOV, class CMP> + ImmutableTree<K,V,KOV,CMP>::~ImmutableTree() { + node->decref(); + } + + template<class K, class V, class KOV, class CMP> + ImmutableTree<K,V,KOV,CMP> &ImmutableTree<K,V,KOV,CMP>::operator=(const ImmutableTree &s) { + Node *n = s.node->incref(); + node->decref(); + node = n; + return *this; + } + + template<class K, class V, class KOV, class CMP> + bool ImmutableTree<K,V,KOV,CMP>::empty() const { + return node->isTerminator(); + } + + template<class K, class V, class KOV, class CMP> + unsigned ImmutableTree<K,V,KOV,CMP>::count(const key_type &k) const { + Node *n = node; + while (!n->isTerminator()) { + key_type key = key_of_value()(n->value); + if (key_compare()(k, key)) { + n = n->left; + } else if (key_compare()(key, k)) { + n = n->right; + } else { + return 1; + } + } + return 0; + } + + template<class K, class V, class KOV, class CMP> + const typename ImmutableTree<K,V,KOV,CMP>::value_type * + ImmutableTree<K,V,KOV,CMP>::lookup(const key_type &k) const { + Node *n = node; + while (!n->isTerminator()) { + key_type key = key_of_value()(n->value); + if (key_compare()(k, key)) { + n = n->left; + } else if (key_compare()(key, k)) { + n = n->right; + } else { + return &n->value; + } + } + return 0; + } + + template<class K, class V, class KOV, class CMP> + const typename ImmutableTree<K,V,KOV,CMP>::value_type * + ImmutableTree<K,V,KOV,CMP>::lookup_previous(const key_type &k) const { + Node *n = node; + Node *result = 0; + while (!n->isTerminator()) { + key_type key = key_of_value()(n->value); + if (key_compare()(k, key)) { + n = n->left; + } else if (key_compare()(key, k)) { + result = n; + n = n->right; + } else { + return &n->value; + } + } + return result ? &result->value : 0; + } + + template<class K, class V, class KOV, class CMP> + const typename ImmutableTree<K,V,KOV,CMP>::value_type & + ImmutableTree<K,V,KOV,CMP>::min() const { + Node *n = node; + assert(!n->isTerminator()); + while (!n->left->isTerminator()) n = n->left; + return n->value; + } + + template<class K, class V, class KOV, class CMP> + const typename ImmutableTree<K,V,KOV,CMP>::value_type & + ImmutableTree<K,V,KOV,CMP>::max() const { + Node *n = node; + assert(!n->isTerminator()); + while (!n->right->isTerminator()) n = n->right; + return n->value; + } + + template<class K, class V, class KOV, class CMP> + unsigned ImmutableTree<K,V,KOV,CMP>::size() const { + return node->size(); + } + + template<class K, class V, class KOV, class CMP> + ImmutableTree<K,V,KOV,CMP> + ImmutableTree<K,V,KOV,CMP>::insert(const value_type &value) const { + return ImmutableTree(node->insert(value)); + } + + template<class K, class V, class KOV, class CMP> + ImmutableTree<K,V,KOV,CMP> + ImmutableTree<K,V,KOV,CMP>::replace(const value_type &value) const { + return ImmutableTree(node->replace(value)); + } + + template<class K, class V, class KOV, class CMP> + ImmutableTree<K,V,KOV,CMP> + ImmutableTree<K,V,KOV,CMP>::remove(const key_type &key) const { + return ImmutableTree(node->remove(key)); + } + + template<class K, class V, class KOV, class CMP> + ImmutableTree<K,V,KOV,CMP> + ImmutableTree<K,V,KOV,CMP>::popMin(value_type &valueOut) const { + return ImmutableTree(node->popMin(valueOut)); + } + + template<class K, class V, class KOV, class CMP> + ImmutableTree<K,V,KOV,CMP> + ImmutableTree<K,V,KOV,CMP>::popMax(value_type &valueOut) const { + return ImmutableTree(node->popMax(valueOut)); + } + + template<class K, class V, class KOV, class CMP> + inline typename ImmutableTree<K,V,KOV,CMP>::iterator + ImmutableTree<K,V,KOV,CMP>::begin() const { + return iterator(node, true); + } + + template<class K, class V, class KOV, class CMP> + inline typename ImmutableTree<K,V,KOV,CMP>::iterator + ImmutableTree<K,V,KOV,CMP>::end() const { + return iterator(node, false); + } + + template<class K, class V, class KOV, class CMP> + inline typename ImmutableTree<K,V,KOV,CMP>::iterator + ImmutableTree<K,V,KOV,CMP>::find(const key_type &key) const { + iterator end(node,false), it = lower_bound(key); + if (it==end || key_compare()(key,key_of_value()(*it))) { + return end; + } else { + return it; + } + } + + template<class K, class V, class KOV, class CMP> + inline typename ImmutableTree<K,V,KOV,CMP>::iterator + ImmutableTree<K,V,KOV,CMP>::lower_bound(const key_type &k) const { + // XXX ugh this doesn't have to be so ugly does it? + iterator it(node,false); + for (Node *root=node; !root->isTerminator();) { + it.stack.push_back(root); + if (key_compare()(k, key_of_value()(root->value))) { + root = root->left; + } else if (key_compare()(key_of_value()(root->value), k)) { + root = root->right; + } else { + return it; + } + } + // it is now beginning or first element < k + if (!it.stack.empty()) { + Node *last = it.stack.back(); + if (key_compare()(key_of_value()(last->value), k)) + ++it; + } + return it; + } + + template<class K, class V, class KOV, class CMP> + typename ImmutableTree<K,V,KOV,CMP>::iterator + ImmutableTree<K,V,KOV,CMP>::upper_bound(const key_type &key) const { + iterator end(node,false),it = lower_bound(key); + if (it!=end && + !key_compare()(key,key_of_value()(*it))) // no need to loop, no duplicates + ++it; + return it; + } + +} + +#endif diff --git a/include/klee/Internal/ADT/MapOfSets.h b/include/klee/Internal/ADT/MapOfSets.h new file mode 100644 index 00000000..25c5e2b9 --- /dev/null +++ b/include/klee/Internal/ADT/MapOfSets.h @@ -0,0 +1,385 @@ +//===-- MapOfSets.h ---------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __UTIL_MAPOFSETS_H__ +#define __UTIL_MAPOFSETS_H__ + +#include <cassert> +#include <vector> +#include <set> +#include <map> + +// This should really be broken down into TreeOfSets on top of which +// SetOfSets and MapOfSets are easily implemeted. It should also be +// parameterized on the underlying set type. Neither are hard to do, +// just not worth it at the moment. +// +// I also broke some STLish conventions because I was bored. + +namespace klee { + + /** This implements the UBTree data structure (see Hoffmann and + Koehler, "A New Method to Index and Query Sets", IJCAI 1999) */ + template<class K, class V> + class MapOfSets { + public: + class iterator; + + public: + MapOfSets(); + + void clear(); + + void insert(const std::set<K> &set, const V &value); + + V *lookup(const std::set<K> &set); + + iterator begin(); + iterator end(); + + void subsets(const std::set<K> &set, + std::vector< std::pair<std::set<K>, V> > &resultOut); + void supersets(const std::set<K> &set, + std::vector< std::pair<std::set<K>, V> > &resultOut); + + template<class Predicate> + V *findSuperset(const std::set<K> &set, const Predicate &p); + template<class Predicate> + V *findSubset(const std::set<K> &set, const Predicate &p); + + private: + class Node; + + Node root; + + template<class Iterator, class Vector> + void findSubsets(Node *n, + const std::set<K> &accum, + Iterator begin, + Iterator end, + Vector &resultsOut); + template<class Iterator, class Vector> + void findSupersets(Node *n, + const std::set<K> &accum, + Iterator begin, + Iterator end, + Vector &resultsOut); + template<class Predicate> + V *findSuperset(Node *n, + typename std::set<K>::iterator begin, + typename std::set<K>::iterator end, + const Predicate &p); + template<class Predicate> + V *findSubset(Node *n, + typename std::set<K>::iterator begin, + typename std::set<K>::iterator end, + const Predicate &p); + }; + + /***/ + + template<class K, class V> + class MapOfSets<K,V>::Node { + friend class MapOfSets<K,V>; + friend class MapOfSets<K,V>::iterator; + + public: + typedef std::map<K, Node> children_ty; + + V value; + + private: + bool isEndOfSet; + std::map<K, Node> children; + + public: + Node() : isEndOfSet(false) {} + }; + + template<class K, class V> + class MapOfSets<K,V>::iterator { + typedef std::vector< typename std::map<K, Node>::iterator > stack_ty; + friend class MapOfSets<K,V>; + private: + Node *root; + bool onEntry; + stack_ty stack; + + void step() { + if (onEntry) { + onEntry = false; + Node *n = stack.empty() ? root : &stack.back()->second; + while (!n->children.empty()) { + stack.push_back(n->children.begin()); + n = &stack.back()->second; + if (n->isEndOfSet) { + onEntry = true; + return; + } + } + } + + while (!stack.empty()) { + unsigned size = stack.size(); + Node *at = size==1 ? root : &stack[size-2]->second; + typename std::map<K,Node>::iterator &cur = stack.back(); + ++cur; + if (cur==at->children.end()) { + stack.pop_back(); + } else { + Node *n = &cur->second; + + while (!n->isEndOfSet) { + assert(!n->children.empty()); + stack.push_back(n->children.begin()); + n = &stack.back()->second; + } + + onEntry = true; + break; + } + } + } + + public: + // end() + iterator() : onEntry(false) {} + // begin() + iterator(Node *_n) : root(_n), onEntry(true) { + if (!root->isEndOfSet) + step(); + } + + const std::pair<const std::set<K>, const V> operator*() { + assert(onEntry || !stack.empty()); + std::set<K> s; + for (typename stack_ty::iterator it = stack.begin(), ie = stack.end(); + it != ie; ++it) + s.insert((*it)->first); + Node *n = stack.empty() ? root : &stack.back()->second; + return std::make_pair(s, n->value); + } + + bool operator==(const iterator &b) { + return onEntry==b.onEntry && stack==b.stack; + } + bool operator!=(const iterator &b) { + return !(*this==b); + } + + iterator &operator++() { + step(); + return *this; + } + }; + + /***/ + + template<class K, class V> + MapOfSets<K,V>::MapOfSets() {} + + template<class K, class V> + void MapOfSets<K,V>::insert(const std::set<K> &set, const V &value) { + Node *n = &root; + for (typename std::set<K>::const_iterator it = set.begin(), ie = set.end(); + it != ie; ++it) + n = &n->children.insert(std::make_pair(*it, Node())).first->second; + n->isEndOfSet = true; + n->value = value; + } + + template<class K, class V> + V *MapOfSets<K,V>::lookup(const std::set<K> &set) { + Node *n = &root; + for (typename std::set<K>::const_iterator it = set.begin(), ie = set.end(); + it != ie; ++it) { + typename Node::children_ty::iterator kit = n->children.find(*it); + if (kit==n->children.end()) { + return 0; + } else { + n = &kit->second; + } + } + if (n->isEndOfSet) { + return &n->value; + } else { + return 0; + } + } + + template<class K, class V> + typename MapOfSets<K,V>::iterator + MapOfSets<K,V>::begin() { return iterator(&root); } + + template<class K, class V> + typename MapOfSets<K,V>::iterator + MapOfSets<K,V>::end() { return iterator(); } + + template<class K, class V> + template<class Iterator, class Vector> + void MapOfSets<K,V>::findSubsets(Node *n, + const std::set<K> &accum, + Iterator begin, + Iterator end, + Vector &resultsOut) { + if (n->isEndOfSet) { + resultsOut.push_back(std::make_pair(accum, n->value)); + } + + for (Iterator it=begin; it!=end;) { + K elt = *it; + typename Node::children_ty::iterator kit = n->children.find(elt); + it++; + if (kit!=n->children.end()) { + std::set<K> nacc = accum; + nacc.insert(elt); + findSubsets(&kit->second, nacc, it, end, resultsOut); + } + } + } + + template<class K, class V> + void MapOfSets<K,V>::subsets(const std::set<K> &set, + std::vector< std::pair<std::set<K>, + V> > &resultOut) { + findSubsets(&root, std::set<K>(), set.begin(), set.end(), resultOut); + } + + template<class K, class V> + template<class Iterator, class Vector> + void MapOfSets<K,V>::findSupersets(Node *n, + const std::set<K> &accum, + Iterator begin, + Iterator end, + Vector &resultsOut) { + if (begin==end) { + if (n->isEndOfSet) + resultsOut.push_back(std::make_pair(accum, n->value)); + for (typename Node::children_ty::iterator it = n->children.begin(), + ie = n->children.end(); it != ie; ++it) { + std::set<K> nacc = accum; + nacc.insert(it->first); + findSupersets(&it->second, nacc, begin, end, resultsOut); + } + } else { + K elt = *begin; + Iterator next = begin; + ++next; + for (typename Node::children_ty::iterator it = n->children.begin(), + ie = n->children.end(); it != ie; ++it) { + std::set<K> nacc = accum; + nacc.insert(it->first); + if (elt==it->first) { + findSupersets(&it->second, nacc, next, end, resultsOut); + } else if (it->first<elt) { + findSupersets(&it->second, nacc, begin, end, resultsOut); + } else { + break; + } + } + } + } + + template<class K, class V> + void MapOfSets<K,V>::supersets(const std::set<K> &set, + std::vector< std::pair<std::set<K>, V> > &resultOut) { + findSupersets(&root, std::set<K>(), set.begin(), set.end(), resultOut); + } + + template<class K, class V> + template<class Predicate> + V *MapOfSets<K,V>::findSubset(Node *n, + typename std::set<K>::iterator begin, + typename std::set<K>::iterator end, + const Predicate &p) { + if (n->isEndOfSet && p(n->value)) { + return &n->value; + } else if (begin==end) { + return 0; + } else { + typename Node::children_ty::iterator kend = n->children.end(); + typename Node::children_ty::iterator + kbegin = n->children.lower_bound(*begin); + typename std::set<K>::iterator it = begin; + if (kbegin==kend) + return 0; + for (;;) { // kbegin!=kend && *it <= kbegin->first + while (*it < kbegin->first) { + ++it; + if (it==end) + return 0; + } + if (*it == kbegin->first) { + ++it; + V *res = findSubset(&kbegin->second, it, end, p); + if (res || it==end) + return res; + } + while (kbegin->first < *it) { + ++kbegin; + if (kbegin==kend) + return 0; + } + } + } + } + + template<class K, class V> + template<class Predicate> + V *MapOfSets<K,V>::findSuperset(Node *n, + typename std::set<K>::iterator begin, + typename std::set<K>::iterator end, + const Predicate &p) { + if (begin==end) { + if (n->isEndOfSet && p(n->value)) + return &n->value; + for (typename Node::children_ty::iterator it = n->children.begin(), + ie = n->children.end(); it != ie; ++it) { + V *res = findSuperset(&it->second, begin, end, p); + if (res) return res; + } + } else { + typename Node::children_ty::iterator kbegin = n->children.begin(); + typename Node::children_ty::iterator kmid = + n->children.lower_bound(*begin); + for (typename Node::children_ty::iterator it = n->children.begin(), + ie = n->children.end(); it != ie; ++it) { + V *res = findSuperset(&it->second, begin, end, p); + if (res) return res; + } + if (kmid!=n->children.end() && *begin==kmid->first) { + V *res = findSuperset(&kmid->second, ++begin, end, p); + if (res) return res; + } + } + return 0; + } + + template<class K, class V> + template<class Predicate> + V *MapOfSets<K,V>::findSuperset(const std::set<K> &set, const Predicate &p) { + return findSuperset(&root, set.begin(), set.end(), p); + } + + template<class K, class V> + template<class Predicate> + V *MapOfSets<K,V>::findSubset(const std::set<K> &set, const Predicate &p) { + return findSubset(&root, set.begin(), set.end(), p); + } + + template<class K, class V> + void MapOfSets<K,V>::clear() { + root.isEndOfSet = false; + root.value = V(); + root.children.clear(); + } + +} + +#endif diff --git a/include/klee/Internal/ADT/RNG.h b/include/klee/Internal/ADT/RNG.h new file mode 100644 index 00000000..b7f4906b --- /dev/null +++ b/include/klee/Internal/ADT/RNG.h @@ -0,0 +1,50 @@ +//===-- RNG.h ---------------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_UTIL_RNG_H +#define KLEE_UTIL_RNG_H + +namespace klee { + class RNG { + private: + /* Period parameters */ + static const int N = 624; + static const int M = 397; + static const unsigned int MATRIX_A = 0x9908b0dfUL; /* constant vector a */ + static const unsigned int UPPER_MASK = 0x80000000UL; /* most significant w-r bits */ + static const unsigned int LOWER_MASK = 0x7fffffffUL; /* least significant r bits */ + + private: + unsigned int mt[N]; /* the array for the state vector */ + int mti; + + public: + RNG(unsigned int seed=5489UL); + + void seed(unsigned int seed); + + /* generates a random number on [0,0xffffffff]-interval */ + unsigned int getInt32(); + /* generates a random number on [0,0x7fffffff]-interval */ + int getInt31(); + /* generates a random number on [0,1]-real-interval */ + double getDoubleLR(); + float getFloatLR(); + /* generates a random number on [0,1)-real-interval */ + double getDoubleL(); + float getFloatL(); + /* generates a random number on (0,1)-real-interval */ + double getDouble(); + float getFloat(); + /* generators a random flop */ + bool getBool(); + }; +} + +#endif diff --git a/include/klee/Internal/ADT/TreeStream.h b/include/klee/Internal/ADT/TreeStream.h new file mode 100644 index 00000000..63e49dbb --- /dev/null +++ b/include/klee/Internal/ADT/TreeStream.h @@ -0,0 +1,77 @@ +//===-- TreeStream.h --------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __UTIL_TREESTREAM_H__ +#define __UTIL_TREESTREAM_H__ + +#include <string> +#include <iostream> +#include <vector> + +namespace klee { + + typedef unsigned TreeStreamID; + class TreeOStream; + + class TreeStreamWriter { + static const unsigned bufferSize = 4*4096; + + friend class TreeOStream; + + private: + char buffer[bufferSize]; + unsigned lastID, bufferCount; + + std::string path; + std::ofstream *output; + unsigned ids; + + void write(TreeOStream &os, const char *s, unsigned size); + void flushBuffer(); + + public: + TreeStreamWriter(const std::string &_path); + ~TreeStreamWriter(); + + bool good(); + + TreeOStream open(); + TreeOStream open(const TreeOStream &node); + + void flush(); + + // hack, to be replace by proper stream capabilities + void readStream(TreeStreamID id, + std::vector<unsigned char> &out); + }; + + class TreeOStream { + friend class TreeStreamWriter; + + private: + TreeStreamWriter *writer; + unsigned id; + + TreeOStream(TreeStreamWriter &_writer, unsigned _id); + + public: + TreeOStream(); + ~TreeOStream(); + + unsigned getID() const; + + void write(const char *buffer, unsigned size); + + TreeOStream &operator<<(const std::string &s); + + void flush(); + }; +} + +#endif diff --git a/include/klee/Internal/Module/Cell.h b/include/klee/Internal/Module/Cell.h new file mode 100644 index 00000000..f54cd6eb --- /dev/null +++ b/include/klee/Internal/Module/Cell.h @@ -0,0 +1,23 @@ +//===-- Cell.h --------------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_CELL_H +#define KLEE_CELL_H + +#include <klee/Expr.h> + +namespace klee { + class MemoryObject; + + struct Cell { + ref<Expr> value; + }; +} + +#endif diff --git a/include/klee/Internal/Module/InstructionInfoTable.h b/include/klee/Internal/Module/InstructionInfoTable.h new file mode 100644 index 00000000..c93f5ddb --- /dev/null +++ b/include/klee/Internal/Module/InstructionInfoTable.h @@ -0,0 +1,70 @@ +//===-- InstructionInfoTable.h ----------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_LIB_INSTRUCTIONINFOTABLE_H +#define KLEE_LIB_INSTRUCTIONINFOTABLE_H + +#include <map> +#include <string> +#include <set> + +namespace llvm { + class Function; + class Instruction; + class Module; +} + +namespace klee { + + /* Stores debug information for a KInstruction */ + struct InstructionInfo { + unsigned id; + const std::string &file; + unsigned line; + unsigned assemblyLine; + + public: + InstructionInfo(unsigned _id, + const std::string &_file, + unsigned _line, + unsigned _assemblyLine) + : id(_id), + file(_file), + line(_line), + assemblyLine(_assemblyLine) { + } + }; + + class InstructionInfoTable { + struct ltstr { + bool operator()(const std::string *a, const std::string *b) const { + return *a<*b; + } + }; + + std::string dummyString; + InstructionInfo dummyInfo; + std::map<const llvm::Instruction*, InstructionInfo> infos; + std::set<const std::string *, ltstr> internedStrings; + + private: + const std::string *internString(std::string s); + + public: + InstructionInfoTable(llvm::Module *m); + ~InstructionInfoTable(); + + unsigned getMaxID() const; + const InstructionInfo &getInfo(const llvm::Instruction*) const; + const InstructionInfo &getFunctionInfo(const llvm::Function*) const; + }; + +} + +#endif diff --git a/include/klee/Internal/Module/KInstIterator.h b/include/klee/Internal/Module/KInstIterator.h new file mode 100644 index 00000000..3890cc65 --- /dev/null +++ b/include/klee/Internal/Module/KInstIterator.h @@ -0,0 +1,49 @@ +//===-- KInstIterator.h -----------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_KINSTITERATOR_H +#define KLEE_KINSTITERATOR_H + +namespace klee { + class KInstruction; + + class KInstIterator { + KInstruction **it; + + public: + KInstIterator() : it(0) {} + KInstIterator(KInstruction **_it) : it(_it) {} + KInstIterator(const KInstIterator &b) : it(b.it) {} + ~KInstIterator() {} + + KInstIterator &operator=(const KInstIterator &b) { + it = b.it; + return *this; + } + + bool operator==(const KInstIterator &b) const { + return it==b.it; + } + bool operator!=(const KInstIterator &b) const { + return !(*this == b); + } + + KInstIterator &operator++() { + ++it; + return *this; + } + + operator KInstruction*() const { return it ? *it : 0;} + operator bool() const { return it != 0; } + + KInstruction *operator ->() const { return *it; } + }; +} // End klee namespace + +#endif diff --git a/include/klee/Internal/Module/KInstruction.h b/include/klee/Internal/Module/KInstruction.h new file mode 100644 index 00000000..c91d37ab --- /dev/null +++ b/include/klee/Internal/Module/KInstruction.h @@ -0,0 +1,50 @@ +//===-- KInstruction.h ------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_KINSTRUCTION_H +#define KLEE_KINSTRUCTION_H + +#include <vector> + +namespace llvm { + class Instruction; +} + +namespace klee { + class Executor; + struct InstructionInfo; + class KModule; + + + /// KInstruction - Intermediate instruction representation used + /// during execution. + struct KInstruction { + llvm::Instruction *inst; + const InstructionInfo *info; + + /// Value numbers for each operand. -1 is an invalid value, + /// otherwise negative numbers are indices (negated and offset by + /// 2) into the module constant table and positive numbers are + /// register indices. + int *operands; + /// Destination register index. + unsigned dest; + + public: + virtual ~KInstruction(); + }; + + struct KGEPInstruction : KInstruction { + std::vector< std::pair<unsigned, unsigned> > indices; + unsigned offset; + }; +} + +#endif + diff --git a/include/klee/Internal/Module/KModule.h b/include/klee/Internal/Module/KModule.h new file mode 100644 index 00000000..690f079d --- /dev/null +++ b/include/klee/Internal/Module/KModule.h @@ -0,0 +1,119 @@ +//===-- KModule.h -----------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_KMODULE_H +#define KLEE_KMODULE_H + +#include "klee/Interpreter.h" + +#include <map> +#include <set> +#include <vector> + +namespace llvm { + class BasicBlock; + class Constant; + class Function; + class Instruction; + class Module; + class TargetData; +} + +namespace klee { + class Cell; + class Executor; + class Expr; + class InterpreterHandler; + class InstructionInfoTable; + class KInstruction; + class KModule; + template<class T> class ref; + + struct KFunction { + llvm::Function *function; + + unsigned numArgs, numRegisters; + + unsigned numInstructions; + KInstruction **instructions; + + std::map<llvm::BasicBlock*, unsigned> basicBlockEntry; + + /// Whether instructions in this function should count as + /// "coverable" for statistics and search heuristics. + bool trackCoverage; + + private: + KFunction(const KFunction&); + KFunction &operator=(const KFunction&); + + public: + explicit KFunction(llvm::Function*, KModule *); + ~KFunction(); + + unsigned getArgRegister(unsigned index) { return index; } + }; + + + class KConstant { + public: + /// Actual LLVM constant this represents. + llvm::Constant* ct; + + /// The constant ID. + unsigned id; + + /// First instruction where this constant was encountered, or NULL + /// if not applicable/unavailable. + KInstruction *ki; + + KConstant(llvm::Constant*, unsigned, KInstruction*); + }; + + + class KModule { + public: + llvm::Module *module; + llvm::TargetData *targetData; + + // Some useful functions to know the address of + llvm::Function *dbgStopPointFn, *kleeMergeFn; + + // Our shadow versions of LLVM structures. + std::vector<KFunction*> functions; + std::map<llvm::Function*, KFunction*> functionMap; + + // Functions which escape (may be called indirectly) + // XXX change to KFunction + std::set<llvm::Function*> escapingFunctions; + + InstructionInfoTable *infos; + + std::vector<llvm::Constant*> constants; + std::map<llvm::Constant*, KConstant*> constantMap; + KConstant* getKConstant(llvm::Constant *c); + + Cell *constantTable; + + public: + KModule(llvm::Module *_module); + ~KModule(); + + /// Initialize local data structures. + // + // FIXME: ihandler should not be here + void prepare(const Interpreter::ModuleOptions &opts, + InterpreterHandler *ihandler); + + /// Return an id for the given constant, creating a new one if necessary. + unsigned getConstantID(llvm::Constant *c, KInstruction* ki); + }; +} // End klee namespace + +#endif diff --git a/include/klee/Internal/README.txt b/include/klee/Internal/README.txt new file mode 100644 index 00000000..9cedb653 --- /dev/null +++ b/include/klee/Internal/README.txt @@ -0,0 +1,3 @@ +This directory holds header files for things which are exposed as part +of the internal API of a library, but shouldn't be exposed to +externally. diff --git a/include/klee/Internal/Support/FloatEvaluation.h b/include/klee/Internal/Support/FloatEvaluation.h new file mode 100644 index 00000000..1d305374 --- /dev/null +++ b/include/klee/Internal/Support/FloatEvaluation.h @@ -0,0 +1,258 @@ +//===-- FloatEvaluation.h ---------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// FIXME: Ditch this and use APFloat. + +#ifndef KLEE_UTIL_FLOATS_H +#define KLEE_UTIL_FLOATS_H + +#include "klee/util/Bits.h" //bits64::truncateToNBits +#include "IntEvaluation.h" //ints::sext + +#include "llvm/Support/MathExtras.h" + +namespace klee { +namespace floats { + +// ********************************** // +// *** Pack uint64_t into FP types ** // +// ********************************** // + +// interpret the 64 bits as a double instead of a uint64_t +inline double UInt64AsDouble( uint64_t bits ) { + double ret; + assert( sizeof(bits) == sizeof(ret) ); + memcpy( &ret, &bits, sizeof bits ); + return ret; +} + +// interpret the first 32 bits as a float instead of a uint64_t +inline float UInt64AsFloat( uint64_t bits ) { + uint32_t tmp = bits; // ensure that we read correct bytes + float ret; + assert( sizeof(tmp) == sizeof(ret) ); + memcpy( &ret, &tmp, sizeof tmp ); + return ret; +} + + +// ********************************** // +// *** Pack FP types into uint64_t ** // +// ********************************** // + +// interpret the 64 bits as a uint64_t instead of a double +inline uint64_t DoubleAsUInt64( double d ) { + uint64_t ret; + assert( sizeof(d) == sizeof(ret) ); + memcpy( &ret, &d, sizeof d ); + return ret; +} + +// interpret the 32 bits as a uint64_t instead of as a float (add 32 0s) +inline uint64_t FloatAsUInt64( float f ) { + uint32_t tmp; + assert( sizeof(tmp) == sizeof(f) ); + memcpy( &tmp, &f, sizeof f ); + return (uint64_t)tmp; +} + + +// ********************************** // +// ************ Constants *********** // +// ********************************** // + +const unsigned FLT_BITS = 32; +const unsigned DBL_BITS = 64; + + + +// ********************************** // +// ***** LLVM Binary Operations ***** // +// ********************************** // + +// add of l and r +inline uint64_t add(uint64_t l, uint64_t r, unsigned inWidth) { + switch( inWidth ) { + case FLT_BITS: return bits64::truncateToNBits(FloatAsUInt64(UInt64AsFloat(l) + UInt64AsFloat(r)), FLT_BITS); + case DBL_BITS: return bits64::truncateToNBits(DoubleAsUInt64(UInt64AsDouble(l) + UInt64AsDouble(r)), DBL_BITS); + default: assert(0 && "invalid floating point width"); + } +} + +// difference of l and r +inline uint64_t sub(uint64_t l, uint64_t r, unsigned inWidth) { + switch( inWidth ) { + case FLT_BITS: return bits64::truncateToNBits(FloatAsUInt64(UInt64AsFloat(l) - UInt64AsFloat(r)), FLT_BITS); + case DBL_BITS: return bits64::truncateToNBits(DoubleAsUInt64(UInt64AsDouble(l) - UInt64AsDouble(r)), DBL_BITS); + default: assert(0 && "invalid floating point width"); + } +} + +// product of l and r +inline uint64_t mul(uint64_t l, uint64_t r, unsigned inWidth) { + switch( inWidth ) { + case FLT_BITS: return bits64::truncateToNBits(FloatAsUInt64(UInt64AsFloat(l) * UInt64AsFloat(r)), FLT_BITS); + case DBL_BITS: return bits64::truncateToNBits(DoubleAsUInt64(UInt64AsDouble(l) * UInt64AsDouble(r)), DBL_BITS); + default: assert(0 && "invalid floating point width"); + } +} + +// signed divide of l by r +inline uint64_t div(uint64_t l, uint64_t r, unsigned inWidth) { + switch( inWidth ) { + case FLT_BITS: return bits64::truncateToNBits(FloatAsUInt64(UInt64AsFloat(l) / UInt64AsFloat(r)), FLT_BITS); + case DBL_BITS: return bits64::truncateToNBits(DoubleAsUInt64(UInt64AsDouble(l) / UInt64AsDouble(r)), DBL_BITS); + default: assert(0 && "invalid floating point width"); + } +} + +// signed modulo of l by r +inline uint64_t mod(uint64_t l, uint64_t r, unsigned inWidth) { + switch( inWidth ) { + case FLT_BITS: return bits64::truncateToNBits(FloatAsUInt64( fmod(UInt64AsFloat(l), UInt64AsFloat(r)) ), FLT_BITS); + case DBL_BITS: return bits64::truncateToNBits(DoubleAsUInt64( fmod(UInt64AsDouble(l), UInt64AsDouble(r)) ), DBL_BITS); + default: assert(0 && "invalid floating point width"); + } +} + + +// ********************************** // +// *** LLVM Comparison Operations *** // +// ********************************** // + +// determine if l represents NaN +inline bool isNaN(uint64_t l, unsigned inWidth) { + switch( inWidth ) { + case FLT_BITS: return llvm::IsNAN( UInt64AsFloat(l) ); + case DBL_BITS: return llvm::IsNAN( UInt64AsDouble(l) ); + default: assert(0 && "invalid floating point width"); + } +} + +inline uint64_t eq(uint64_t l, uint64_t r, unsigned inWidth) { + switch( inWidth ) { + case FLT_BITS: return UInt64AsFloat(l) == UInt64AsFloat(r); + case DBL_BITS: return UInt64AsDouble(l) == UInt64AsDouble(r); + default: assert(0 && "invalid floating point width"); + } +} + +inline uint64_t ne(uint64_t l, uint64_t r, unsigned inWidth) { + switch( inWidth ) { + case FLT_BITS: return UInt64AsFloat(l) != UInt64AsFloat(r); + case DBL_BITS: return UInt64AsDouble(l) != UInt64AsDouble(r); + default: assert(0 && "invalid floating point width"); + } +} + +inline uint64_t lt(uint64_t l, uint64_t r, unsigned inWidth) { + switch( inWidth ) { + case FLT_BITS: return UInt64AsFloat(l) < UInt64AsFloat(r); + case DBL_BITS: return UInt64AsDouble(l) < UInt64AsDouble(r); + default: assert(0 && "invalid floating point width"); + } +} + +inline uint64_t le(uint64_t l, uint64_t r, unsigned inWidth) { + switch( inWidth ) { + case FLT_BITS: return UInt64AsFloat(l) <= UInt64AsFloat(r); + case DBL_BITS: return UInt64AsDouble(l) <= UInt64AsDouble(r); + default: assert(0 && "invalid floating point width"); + } +} + +inline uint64_t gt(uint64_t l, uint64_t r, unsigned inWidth) { + switch( inWidth ) { + case FLT_BITS: return UInt64AsFloat(l) > UInt64AsFloat(r); + case DBL_BITS: return UInt64AsDouble(l) > UInt64AsDouble(r); + default: assert(0 && "invalid floating point width"); + } +} + +inline uint64_t ge(uint64_t l, uint64_t r, unsigned inWidth) { + switch( inWidth ) { + case FLT_BITS: return UInt64AsFloat(l) >= UInt64AsFloat(r); + case DBL_BITS: return UInt64AsDouble(l) >= UInt64AsDouble(r); + default: assert(0 && "invalid floating point width"); + } +} + + +// ********************************** // +// *** LLVM Conversion Operations *** // +// ********************************** // + +// truncation of l (which must be a double) to float (casts a double to a float) +inline uint64_t trunc(uint64_t l, unsigned outWidth, unsigned inWidth) { + // FIXME: Investigate this, should this not happen? Was a quick + // patch for busybox. + if (inWidth==64 && outWidth==64) { + return l; + } else { + assert(inWidth==64 && "can only truncate from a 64-bit double"); + assert(outWidth==32 && "can only truncate to a 32-bit float"); + float trunc = (float)UInt64AsDouble(l); + return bits64::truncateToNBits(FloatAsUInt64(trunc), outWidth); + } +} + +// extension of l (which must be a float) to double (casts a float to a double) +inline uint64_t ext(uint64_t l, unsigned outWidth, unsigned inWidth) { + // FIXME: Investigate this, should this not happen? Was a quick + // patch for busybox. + if (inWidth==64 && outWidth==64) { + return l; + } else { + assert(inWidth==32 && "can only extend from a 32-bit float"); + assert(outWidth==64 && "can only extend to a 64-bit double"); + double ext = (double)UInt64AsFloat(l); + return bits64::truncateToNBits(DoubleAsUInt64(ext), outWidth); + } +} + +// conversion of l to an unsigned integer, rounding towards zero +inline uint64_t toUnsignedInt( uint64_t l, unsigned outWidth, unsigned inWidth ) { + switch( inWidth ) { + case FLT_BITS: return bits64::truncateToNBits((uint64_t)UInt64AsFloat(l), outWidth ); + case DBL_BITS: return bits64::truncateToNBits((uint64_t)UInt64AsDouble(l), outWidth ); + default: assert(0 && "invalid floating point width"); + } +} + +// conversion of l to a signed integer, rounding towards zero +inline uint64_t toSignedInt( uint64_t l, unsigned outWidth, unsigned inWidth ) { + switch( inWidth ) { + case FLT_BITS: return bits64::truncateToNBits((int64_t)UInt64AsFloat(l), outWidth); + case DBL_BITS: return bits64::truncateToNBits((int64_t)UInt64AsDouble(l), outWidth); + default: assert(0 && "invalid floating point width"); + } +} + +// conversion of l, interpreted as an unsigned int, to a floating point number +inline uint64_t UnsignedIntToFP( uint64_t l, unsigned outWidth ) { + switch( outWidth ) { + case FLT_BITS: return bits64::truncateToNBits(FloatAsUInt64((float)l), outWidth); + case DBL_BITS: return bits64::truncateToNBits(DoubleAsUInt64((double)l), outWidth); + default: assert(0 && "invalid floating point width"); + } +} + +// conversion of l, interpreted as a signed int, to a floating point number +inline uint64_t SignedIntToFP( uint64_t l, unsigned outWidth, unsigned inWidth ) { + switch( outWidth ) { + case FLT_BITS: return bits64::truncateToNBits(FloatAsUInt64((float)(int64_t)ints::sext(l, 64, inWidth)), outWidth); + case DBL_BITS: return bits64::truncateToNBits(DoubleAsUInt64((double)(int64_t)ints::sext(l,64, inWidth)), outWidth); + default: assert(0 && "invalid floating point width"); + } +} + +} // end namespace ints +} // end namespace klee + +#endif //KLEE_UTIL_FLOATS_H diff --git a/include/klee/Internal/Support/IntEvaluation.h b/include/klee/Internal/Support/IntEvaluation.h new file mode 100644 index 00000000..ed3ffc23 --- /dev/null +++ b/include/klee/Internal/Support/IntEvaluation.h @@ -0,0 +1,164 @@ +//===-- IntEvaluation.h -----------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_UTIL_INTEVALUATION_H +#define KLEE_UTIL_INTEVALUATION_H + +#include "klee/util/Bits.h" + +#define MAX_BITS (sizeof(uint64_t) * 8) + +// ASSUMPTION: invalid bits in each uint64_t are 0. the trade-off here is +// between making trunc/zext/sext fast and making operations that depend +// on the invalid bits being 0 fast. + +namespace klee { +namespace ints { + +// add of l and r +inline uint64_t add(uint64_t l, uint64_t r, unsigned inWidth) { + return bits64::truncateToNBits(l + r, inWidth); +} + +// difference of l and r +inline uint64_t sub(uint64_t l, uint64_t r, unsigned inWidth) { + return bits64::truncateToNBits(l - r, inWidth); +} + +// product of l and r +inline uint64_t mul(uint64_t l, uint64_t r, unsigned inWidth) { + return bits64::truncateToNBits(l * r, inWidth); +} + +// truncation of l to outWidth bits +inline uint64_t trunc(uint64_t l, unsigned outWidth, unsigned inWidth) { + return bits64::truncateToNBits(l, outWidth); +} + +// zero-extension of l from inWidth to outWidth bits +inline uint64_t zext(uint64_t l, unsigned outWidth, unsigned inWidth) { + return l; +} + +// sign-extension of l from inWidth to outWidth bits +inline uint64_t sext(uint64_t l, unsigned outWidth, unsigned inWidth) { + uint32_t numInvalidBits = MAX_BITS - inWidth; + return bits64::truncateToNBits(((int64_t)(l << numInvalidBits)) >> numInvalidBits, outWidth); +} + +// unsigned divide of l by r +inline uint64_t udiv(uint64_t l, uint64_t r, unsigned inWidth) { + return bits64::truncateToNBits(l / r, inWidth); +} + +// unsigned mod of l by r +inline uint64_t urem(uint64_t l, uint64_t r, unsigned inWidth) { + return bits64::truncateToNBits(l % r, inWidth); +} + +// signed divide of l by r +inline uint64_t sdiv(uint64_t l, uint64_t r, unsigned inWidth) { + // sign extend operands so that signed operation on 64-bits works correctly + int64_t sl = sext(l, MAX_BITS, inWidth); + int64_t sr = sext(r, MAX_BITS, inWidth); + return bits64::truncateToNBits(sl / sr, inWidth); +} + +// signed mod of l by r +inline uint64_t srem(uint64_t l, uint64_t r, unsigned inWidth) { + // sign extend operands so that signed operation on 64-bits works correctly + int64_t sl = sext(l, MAX_BITS, inWidth); + int64_t sr = sext(r, MAX_BITS, inWidth); + return bits64::truncateToNBits(sl % sr, inWidth); +} + +// arithmetic shift right of l by r bits +inline uint64_t ashr(uint64_t l, uint64_t shift, unsigned inWidth) { + int64_t sl = sext(l, MAX_BITS, inWidth); + return bits64::truncateToNBits(sl >> shift, inWidth); +} + +// logical shift right of l by r bits +inline uint64_t lshr(uint64_t l, uint64_t shift, unsigned inWidth) { + return l >> shift; +} + +// shift left of l by r bits +inline uint64_t shl(uint64_t l, uint64_t shift, unsigned inWidth) { + return bits64::truncateToNBits(l << shift, inWidth); +} + +// logical AND of l and r +inline uint64_t land(uint64_t l, uint64_t r, unsigned inWidth) { + return l & r; +} + +// logical OR of l and r +inline uint64_t lor(uint64_t l, uint64_t r, unsigned inWidth) { + return l | r; +} + +// logical XOR of l and r +inline uint64_t lxor(uint64_t l, uint64_t r, unsigned inWidth) { + return l ^ r; +} + +// comparison operations +inline uint64_t eq(uint64_t l, uint64_t r, unsigned inWidth) { + return l == r; +} + +inline uint64_t ne(uint64_t l, uint64_t r, unsigned inWidth) { + return l != r; +} + +inline uint64_t ult(uint64_t l, uint64_t r, unsigned inWidth) { + return l < r; +} + +inline uint64_t ule(uint64_t l, uint64_t r, unsigned inWidth) { + return l <= r; +} + +inline uint64_t ugt(uint64_t l, uint64_t r, unsigned inWidth) { + return l > r; +} + +inline uint64_t uge(uint64_t l, uint64_t r, unsigned inWidth) { + return l >= r; +} + +inline uint64_t slt(uint64_t l, uint64_t r, unsigned inWidth) { + int64_t sl = sext(l, MAX_BITS, inWidth); + int64_t sr = sext(r, MAX_BITS, inWidth); + return sl < sr; +} + +inline uint64_t sle(uint64_t l, uint64_t r, unsigned inWidth) { + int64_t sl = sext(l, MAX_BITS, inWidth); + int64_t sr = sext(r, MAX_BITS, inWidth); + return sl <= sr; +} + +inline uint64_t sgt(uint64_t l, uint64_t r, unsigned inWidth) { + int64_t sl = sext(l, MAX_BITS, inWidth); + int64_t sr = sext(r, MAX_BITS, inWidth); + return sl > sr; +} + +inline uint64_t sge(uint64_t l, uint64_t r, unsigned inWidth) { + int64_t sl = sext(l, MAX_BITS, inWidth); + int64_t sr = sext(r, MAX_BITS, inWidth); + return sl >= sr; +} + +} // end namespace ints +} // end namespace klee + +#endif diff --git a/include/klee/Internal/Support/ModuleUtil.h b/include/klee/Internal/Support/ModuleUtil.h new file mode 100644 index 00000000..6cf5fbee --- /dev/null +++ b/include/klee/Internal/Support/ModuleUtil.h @@ -0,0 +1,40 @@ +//===-- ModuleUtil.h --------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_TRANSFORM_UTIL_H +#define KLEE_TRANSFORM_UTIL_H + +#include <string> + +namespace llvm { + class Function; + class Instruction; + class Module; +} + +namespace klee { + + /// Link a module with a specified bitcode archive. + llvm::Module *linkWithLibrary(llvm::Module *module, + const std::string &libraryName); + + /// Return the Function* target of a Call or Invoke instruction, or + /// null if it cannot be determined (should be only for indirect + /// calls, although complicated constant expressions might be + /// another possibility). + llvm::Function *getDirectCallTarget(const llvm::Instruction*); + + /// Return true iff the given Function value is used in something + /// other than a direct call (or a constant expression that + /// terminates in a direct call). + bool functionEscapes(const llvm::Function *f); + +} + +#endif diff --git a/include/klee/Internal/Support/QueryLog.h b/include/klee/Internal/Support/QueryLog.h new file mode 100644 index 00000000..b090d2d9 --- /dev/null +++ b/include/klee/Internal/Support/QueryLog.h @@ -0,0 +1,63 @@ +//===-- QueryLog.h ----------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_OPT_LOGGINGSOLVER_H +#define KLEE_OPT_LOGGINGSOLVER_H + +#include "klee/Expr.h" + +#include <vector> + +namespace klee { + struct Query; + + class QueryLogEntry { + public: + enum Type { + Validity, + Truth, + Value, + Cex + }; + + typedef std::vector< ref<Expr> > exprs_ty; + exprs_ty exprs; + + Type type; + ref<Expr> query; + unsigned instruction; + std::vector<const Array*> objects; + + public: + QueryLogEntry() : query(0,Expr::Bool) {} + QueryLogEntry(const QueryLogEntry &b); + QueryLogEntry(const Query &_query, + Type _type, + const std::vector<const Array*> *objects = 0); + }; + + class QueryLogResult { + public: + uint64_t result; + double time; + + public: + QueryLogResult() {} + QueryLogResult(bool _success, uint64_t _result, double _time) + : result(_result), time(_time) { + if (!_success) { // la la la + result = 0; + time = -1; + } + } + }; + +} + +#endif diff --git a/include/klee/Internal/Support/Timer.h b/include/klee/Internal/Support/Timer.h new file mode 100644 index 00000000..a422abd0 --- /dev/null +++ b/include/klee/Internal/Support/Timer.h @@ -0,0 +1,28 @@ +//===-- Timer.h -------------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_TIMER_H +#define KLEE_TIMER_H + +#include <stdint.h> + +namespace klee { + class WallTimer { + uint64_t startMicroseconds; + + public: + WallTimer(); + + /// check - Return the delta since the timer was created, in microseconds. + uint64_t check(); + }; +} + +#endif + diff --git a/include/klee/Internal/System/Time.h b/include/klee/Internal/System/Time.h new file mode 100644 index 00000000..5229c8c9 --- /dev/null +++ b/include/klee/Internal/System/Time.h @@ -0,0 +1,20 @@ +//===-- Time.h --------------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_UTIL_TIME_H +#define KLEE_UTIL_TIME_H + +namespace klee { + namespace util { + double getUserTime(); + double getWallTime(); + } +} + +#endif diff --git a/include/klee/Interpreter.h b/include/klee/Interpreter.h new file mode 100644 index 00000000..eca6e59e --- /dev/null +++ b/include/klee/Interpreter.h @@ -0,0 +1,149 @@ +//===-- Interpreter.h - Abstract Execution Engine Interface -----*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +//===----------------------------------------------------------------------===// + +#ifndef KLEE_INTERPRETER_H +#define KLEE_INTERPRETER_H + +#include <vector> +#include <string> +#include <map> +#include <set> + +struct BOut; + +namespace llvm { +class Function; +class Module; +} + +namespace klee { +class ExecutionState; +class Interpreter; +class TreeStreamWriter; + +class InterpreterHandler { +public: + InterpreterHandler() {} + virtual ~InterpreterHandler() {}; + + virtual std::ostream &getInfoStream() const = 0; + + virtual std::string getOutputFilename(const std::string &filename) = 0; + virtual std::ostream *openOutputFile(const std::string &filename) = 0; + + virtual void incPathsExplored() = 0; + + virtual void processTestCase(const ExecutionState &state, + const char *err, + const char *suffix) = 0; +}; + +class Interpreter { +public: + /// ModuleOptions - Module level options which can be set when + /// registering a module with the interpreter. + struct ModuleOptions { + std::string LibraryDir; + bool Optimize; + bool CheckDivZero; + + ModuleOptions(const std::string& _LibraryDir, + bool _Optimize, bool _CheckDivZero) + : LibraryDir(_LibraryDir), Optimize(_Optimize), + CheckDivZero(_CheckDivZero) {} + }; + + /// InterpreterOptions - Options varying the runtime behavior during + /// interpretation. + struct InterpreterOptions { + /// A frequency at which to make concrete reads return constrained + /// symbolic values. This is used to test the correctness of the + /// symbolic execution on concrete programs. + unsigned MakeConcreteSymbolic; + + InterpreterOptions() + : MakeConcreteSymbolic(false) + {} + }; + +protected: + const InterpreterOptions interpreterOpts; + + Interpreter(const InterpreterOptions &_interpreterOpts) + : interpreterOpts(_interpreterOpts) + {}; + +public: + virtual ~Interpreter() {}; + + static Interpreter *create(const InterpreterOptions &_interpreterOpts, + InterpreterHandler *ih); + + /// Register the module to be executed. + /// + /// \return The final module after it has been optimized, checks + /// inserted, and modified for interpretation. + virtual const llvm::Module * + setModule(llvm::Module *module, + const ModuleOptions &opts) = 0; + + // supply a tree stream writer which the interpreter will use + // to record the concrete path (as a stream of '0' and '1' bytes). + virtual void setPathWriter(TreeStreamWriter *tsw) = 0; + + // supply a tree stream writer which the interpreter will use + // to record the symbolic path (as a stream of '0' and '1' bytes). + virtual void setSymbolicPathWriter(TreeStreamWriter *tsw) = 0; + + // supply a test case to replay from. this can be used to drive the + // interpretation down a user specified path. use null to reset. + virtual void setReplayOut(const struct BOut *out) = 0; + + // supply a list of branch decisions specifying which direction to + // take on forks. this can be used to drive the interpretation down + // a user specified path. use null to reset. + virtual void setReplayPath(const std::vector<bool> *path) = 0; + + // supply a set of symbolic bindings that will be used as "seeds" + // for the search. use null to reset. + virtual void useSeeds(const std::vector<struct BOut *> *seeds) = 0; + + virtual void runFunctionAsMain(llvm::Function *f, + int argc, + char **argv, + char **envp) = 0; + + /*** Runtime options ***/ + + virtual void setHaltExecution(bool value) = 0; + + virtual void setInhibitForking(bool value) = 0; + + /*** State accessor methods ***/ + + virtual unsigned getPathStreamID(const ExecutionState &state) = 0; + + virtual unsigned getSymbolicPathStreamID(const ExecutionState &state) = 0; + + virtual void getConstraintLog(const ExecutionState &state, + std::string &res, + bool asCVC = false) = 0; + + virtual bool getSymbolicSolution(const ExecutionState &state, + std::vector< + std::pair<std::string, + std::vector<unsigned char> > > + &res) = 0; + + virtual void getCoveredLines(const ExecutionState &state, + std::map<const std::string*, std::set<unsigned> > &res) = 0; +}; + +} // End klee namespace + +#endif diff --git a/include/klee/Machine.h b/include/klee/Machine.h new file mode 100644 index 00000000..b8a9e9ac --- /dev/null +++ b/include/klee/Machine.h @@ -0,0 +1,28 @@ +//===-- Machine.h -----------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __KLEE_MACHINE_H__ +#define __KLEE_MACHINE_H__ + +#include "klee/Expr.h" + +namespace klee { + namespace machine { + enum ByteOrder { + LSB = 0, + MSB = 1 + }; + } +} + +#define kMachineByteOrder klee::machine::LSB +#define kMachinePointerType Expr::Int32 +#define kMachinePointerSize 4 + +#endif /* __KLEE_MACHINE_H__ */ diff --git a/include/klee/Solver.h b/include/klee/Solver.h new file mode 100644 index 00000000..2d4fa044 --- /dev/null +++ b/include/klee/Solver.h @@ -0,0 +1,216 @@ +//===-- Solver.h ------------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_SOLVER_H +#define KLEE_SOLVER_H + +#include "klee/Expr.h" + +#include <vector> + +namespace klee { + class ConstraintManager; + class Expr; + class SolverImpl; + + struct Query { + public: + const ConstraintManager &constraints; + ref<Expr> expr; + + Query(const ConstraintManager& _constraints, ref<Expr> _expr) + : constraints(_constraints), expr(_expr) { + } + + /// withExpr - Return a copy of the query with the given expression. + Query withExpr(ref<Expr> _expr) const { + return Query(constraints, _expr); + } + + /// withFalse - Return a copy of the query with a false expression. + Query withFalse() const { + return Query(constraints, ref<Expr>(0, Expr::Bool)); + } + + /// negateExpr - Return a copy of the query with the expression negated. + Query negateExpr() const { + return withExpr(Expr::createNot(expr)); + } + }; + + class Solver { + // DO NOT IMPLEMENT. + Solver(const Solver&); + void operator=(const Solver&); + + public: + enum Validity { + True = 1, + False = -1, + Unknown = 0 + }; + + public: + /// validity_to_str - Return the name of given Validity enum value. + static const char *validity_to_str(Validity v); + + public: + SolverImpl *impl; + + public: + Solver(SolverImpl *_impl) : impl(_impl) {}; + ~Solver(); + + /// evaluate - Determine the full validity of an expression in particular + /// state. + //// + /// \param [out] result - The validity of the given expression (provably + /// true, provably false, or neither). + /// + /// \return True on success. + bool evaluate(const Query&, Validity &result); + + /// mustBeTrue - Determine if the expression is provably true. + /// + /// \param [out] result - On success, true iff the expresssion is provably + /// false. + /// + /// \return True on success. + bool mustBeTrue(const Query&, bool &result); + + /// mustBeFalse - Determine if the expression is provably false. + /// + /// \param [out] result - On success, true iff the expresssion is provably + /// false. + /// + /// \return True on success. + bool mustBeFalse(const Query&, bool &result); + + /// mayBeTrue - Determine if there is a valid assignment for the given state + /// in which the expression evaluates to false. + /// + /// \param [out] result - On success, true iff the expresssion is true for + /// some satisfying assignment. + /// + /// \return True on success. + bool mayBeTrue(const Query&, bool &result); + + /// mayBeFalse - Determine if there is a valid assignment for the given + /// state in which the expression evaluates to false. + /// + /// \param [out] result - On success, true iff the expresssion is false for + /// some satisfying assignment. + /// + /// \return True on success. + bool mayBeFalse(const Query&, bool &result); + + /// getValue - Compute one possible value for the given expression. + /// + /// \param [out] result - On success, a value for the expression in some + /// satisying assignment. + /// + /// \return True on success. + bool getValue(const Query&, ref<Expr> &result); + + /// getInitialValues - Compute the initial values for a list of objects. + /// + /// \param [out] result - On success, this vector will be filled in with an + /// array of bytes for each given object (with length matching the object + /// size). The bytes correspond to the initial values for the objects for + /// some satisying assignment. + /// + /// \return True on success. + /// + /// NOTE: This function returns failure if there is no satisfying + /// assignment. + // + // FIXME: This API is lame. We should probably just provide an API which + // returns an Assignment object, then clients can get out whatever values + // they want. This also allows us to optimize the representation. + bool getInitialValues(const Query&, + const std::vector<const Array*> &objects, + std::vector< std::vector<unsigned char> > &result); + + /// getRange - Compute a tight range of possible values for a given + /// expression. + /// + /// \return - A pair with (min, max) values for the expression. + /// + /// \post(mustBeTrue(min <= e <= max) && + /// mayBeTrue(min == e) && + /// mayBeTrue(max == e)) + // + // FIXME: This should go into a helper class, and should handle failure. + virtual std::pair< ref<Expr>, ref<Expr> > getRange(const Query&); + }; + + /// STPSolver - A complete solver based on STP. + class STPSolver : public Solver { + public: + /// STPSolver - Construct a new STPSolver. + /// + /// \param useForkedSTP - Whether STP should be run in a separate process + /// (required for using timeouts). + STPSolver(bool useForkedSTP); + + /// getConstraintLog - Return the constraint log for the given state in CVC + /// format. + char *getConstraintLog(const Query&); + + /// setTimeout - Set constraint solver timeout delay to the given value; 0 + /// is off. + void setTimeout(double timeout); + }; + + /* *** */ + + /// createValidatingSolver - Create a solver which will validate all query + /// results against an oracle, used for testing that an optimized solver has + /// the same results as an unoptimized one. This solver will assert on any + /// mismatches. + /// + /// \param s - The primary underlying solver to use. + /// \param oracle - The solver to check query results against. + Solver *createValidatingSolver(Solver *s, Solver *oracle); + + /// createCachingSolver - Create a solver which will cache the queries in + /// memory (without eviction). + /// + /// \param s - The underlying solver to use. + Solver *createCachingSolver(Solver *s); + + /// createCexCachingSolver - Create a counterexample caching solver. This is a + /// more sophisticated cache which records counterexamples for a constraint + /// set and uses subset/superset relations among constraints to try and + /// quickly find satisfying assignments. + /// + /// \param s - The underlying solver to use. + Solver *createCexCachingSolver(Solver *s); + + /// createFastCexSolver - Create a "fast counterexample solver", which tries + /// to quickly compute a satisfying assignment for a constraint set using + /// value propogation and range analysis. + /// + /// \param s - The underlying solver to use. + Solver *createFastCexSolver(Solver *s); + + /// createIndependentSolver - Create a solver which will eliminate any + /// unnecessary constraints before propogating the query to the underlying + /// solver. + /// + /// \param s - The underlying solver to use. + Solver *createIndependentSolver(Solver *s); + + /// createPCLoggingSolver - Create a solver which will forward all queries + /// after writing them to the given path in .pc format. + Solver *createPCLoggingSolver(Solver *s, std::string path); + +} + +#endif diff --git a/include/klee/SolverImpl.h b/include/klee/SolverImpl.h new file mode 100644 index 00000000..29c356be --- /dev/null +++ b/include/klee/SolverImpl.h @@ -0,0 +1,63 @@ +//===-- SolverImpl.h --------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_SOLVERIMPL_H +#define KLEE_SOLVERIMPL_H + +#include <vector> + +namespace klee { + class Array; + class ExecutionState; + class Expr; + struct Query; + + /// SolverImpl - Abstract base clase for solver implementations. + class SolverImpl { + // DO NOT IMPLEMENT. + SolverImpl(const SolverImpl&); + void operator=(const SolverImpl&); + + public: + SolverImpl() {} + virtual ~SolverImpl(); + + /// computeValidity - Compute a full validity result for the + /// query. + /// + /// The query expression is guaranteed to be non-constant and have + /// bool type. + /// + /// SolverImpl provides a default implementation which uses + /// computeTruth. Clients should override this if a more efficient + /// implementation is available. + virtual bool computeValidity(const Query& query, Solver::Validity &result); + + /// computeTruth - Determine whether the given query is provable. + /// + /// The query expression is guaranteed to be non-constant and have + /// bool type. + virtual bool computeTruth(const Query& query, bool &isValid) = 0; + + /// computeValue - Compute a feasible value for the expression. + /// + /// The query expression is guaranteed to be non-constant. + virtual bool computeValue(const Query& query, ref<Expr> &result) = 0; + + virtual bool computeInitialValues(const Query& query, + const std::vector<const Array*> + &objects, + std::vector< std::vector<unsigned char> > + &values, + bool &hasSolution) = 0; +}; + +} + +#endif diff --git a/include/klee/Statistic.h b/include/klee/Statistic.h new file mode 100644 index 00000000..e5aef6b1 --- /dev/null +++ b/include/klee/Statistic.h @@ -0,0 +1,65 @@ +//===-- Statistic.h ---------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_STATISTIC_H +#define KLEE_STATISTIC_H + +#include <string> + +namespace klee { + class Statistic; + class StatisticManager; + class StatisticRecord; + + /// Statistic - A named statistic instance. + /// + /// The Statistic class holds information about the statistic, but + /// not the actual values. Values are managed by the global + /// StatisticManager to enable transparent support for instruction + /// level and call path level statistics. + class Statistic { + friend class StatisticManager; + friend class StatisticRecord; + + private: + unsigned id; + const std::string name; + const std::string shortName; + + public: + Statistic(const std::string &_name, + const std::string &_shortName); + ~Statistic(); + + /// getID - Get the unique statistic ID. + unsigned getID() { return id; } + + /// getName - Get the statistic name. + const std::string &getName() const { return name; } + + /// getShortName - Get the "short" statistic name, used in + /// callgrind output for example. + const std::string &getShortName() const { return shortName; } + + /// getValue - Get the current primary statistic value. + uint64_t getValue() const; + + /// operator uint64_t - Get the current primary statistic value. + operator uint64_t () const { return getValue(); } + + /// operator++ - Increment the statistic by 1. + Statistic &operator ++() { return (*this += 1); } + + /// operator+= - Increment the statistic by \arg addend. + Statistic &operator +=(const uint64_t addend); + }; +} + +#endif + diff --git a/include/klee/Statistics.h b/include/klee/Statistics.h new file mode 100644 index 00000000..20ab2528 --- /dev/null +++ b/include/klee/Statistics.h @@ -0,0 +1,154 @@ +//===-- Statistics.h --------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_STATISTICS_H +#define KLEE_STATISTICS_H + +#include "Statistic.h" + +#include <vector> +#include <string> +#include <string.h> + +namespace klee { + class Statistic; + class StatisticRecord { + friend class StatisticManager; + + private: + uint64_t *data; + + public: + StatisticRecord(); + StatisticRecord(const StatisticRecord &s); + ~StatisticRecord() { delete[] data; } + + void zero(); + + uint64_t getValue(const Statistic &s) const; + void incrementValue(const Statistic &s, uint64_t addend) const; + StatisticRecord &operator =(const StatisticRecord &s); + StatisticRecord &operator +=(const StatisticRecord &sr); + }; + + class StatisticManager { + private: + bool enabled; + std::vector<Statistic*> stats; + uint64_t *globalStats; + uint64_t *indexedStats; + StatisticRecord *contextStats; + unsigned index; + + public: + StatisticManager(); + ~StatisticManager(); + + void useIndexedStats(unsigned totalIndices); + + StatisticRecord *getContext(); + void setContext(StatisticRecord *sr); /* null to reset */ + + void setIndex(unsigned i) { index = i; } + unsigned getIndex() { return index; } + unsigned getNumStatistics() { return stats.size(); } + Statistic &getStatistic(unsigned i) { return *stats[i]; } + + void registerStatistic(Statistic &s); + void incrementStatistic(Statistic &s, uint64_t addend); + uint64_t getValue(const Statistic &s) const; + void incrementIndexedValue(const Statistic &s, unsigned index, + uint64_t addend) const; + uint64_t getIndexedValue(const Statistic &s, unsigned index) const; + void setIndexedValue(const Statistic &s, unsigned index, uint64_t value); + int getStatisticID(const std::string &name) const; + Statistic *getStatisticByName(const std::string &name) const; + }; + + extern StatisticManager *theStatisticManager; + + inline void StatisticManager::incrementStatistic(Statistic &s, + uint64_t addend) { + if (enabled) { + globalStats[s.id] += addend; + if (indexedStats) { + indexedStats[index*stats.size() + s.id] += addend; + if (contextStats) + contextStats->data[s.id] += addend; + } + } + } + + inline StatisticRecord *StatisticManager::getContext() { + return contextStats; + } + inline void StatisticManager::setContext(StatisticRecord *sr) { + contextStats = sr; + } + + inline void StatisticRecord::zero() { + ::memset(data, 0, sizeof(*data)*theStatisticManager->getNumStatistics()); + } + + inline StatisticRecord::StatisticRecord() + : data(new uint64_t[theStatisticManager->getNumStatistics()]) { + zero(); + } + + inline StatisticRecord::StatisticRecord(const StatisticRecord &s) + : data(new uint64_t[theStatisticManager->getNumStatistics()]) { + ::memcpy(data, s.data, + sizeof(*data)*theStatisticManager->getNumStatistics()); + } + + inline StatisticRecord &StatisticRecord::operator=(const StatisticRecord &s) { + ::memcpy(data, s.data, + sizeof(*data)*theStatisticManager->getNumStatistics()); + return *this; + } + + inline void StatisticRecord::incrementValue(const Statistic &s, + uint64_t addend) const { + data[s.id] += addend; + } + inline uint64_t StatisticRecord::getValue(const Statistic &s) const { + return data[s.id]; + } + + inline StatisticRecord & + StatisticRecord::operator +=(const StatisticRecord &sr) { + unsigned nStats = theStatisticManager->getNumStatistics(); + for (unsigned i=0; i<nStats; i++) + data[i] += sr.data[i]; + return *this; + } + + inline uint64_t StatisticManager::getValue(const Statistic &s) const { + return globalStats[s.id]; + } + + inline void StatisticManager::incrementIndexedValue(const Statistic &s, + unsigned index, + uint64_t addend) const { + indexedStats[index*stats.size() + s.id] += addend; + } + + inline uint64_t StatisticManager::getIndexedValue(const Statistic &s, + unsigned index) const { + return indexedStats[index*stats.size() + s.id]; + } + + inline void StatisticManager::setIndexedValue(const Statistic &s, + unsigned index, + uint64_t value) { + indexedStats[index*stats.size() + s.id] = value; + } +} + +#endif diff --git a/include/klee/TimerStatIncrementer.h b/include/klee/TimerStatIncrementer.h new file mode 100644 index 00000000..35495d8b --- /dev/null +++ b/include/klee/TimerStatIncrementer.h @@ -0,0 +1,32 @@ +//===-- TimerStatIncrementer.h ----------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_TIMERSTATINCREMENTER_H +#define KLEE_TIMERSTATINCREMENTER_H + +#include "klee/Statistics.h" +#include "klee/Internal/Support/Timer.h" + +namespace klee { + class TimerStatIncrementer { + private: + WallTimer timer; + Statistic &statistic; + + public: + TimerStatIncrementer(Statistic &_statistic) : statistic(_statistic) {} + ~TimerStatIncrementer() { + statistic += timer.check(); + }; + + uint64_t check() { return timer.check(); } + }; +} + +#endif diff --git a/include/klee/klee.h b/include/klee/klee.h new file mode 100644 index 00000000..698a61c4 --- /dev/null +++ b/include/klee/klee.h @@ -0,0 +1,120 @@ +//===-- klee.h --------------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __KLEE_H__ +#define __KLEE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + /* Add an accesible memory object at a user specified location. It + is the users responsibility to make sure that these memory + objects do not overlap. These memory objects will also + (obviously) not correctly interact with external function + calls. */ + void klee_define_fixed_object(void *addr, unsigned nbytes); + + /* make the contents of the object pointed to by addr symbolic. addr + * should always be the start of the object and nbytes must be its + * entire size. name is an optional name (can be the empty string) + */ + void klee_make_symbolic_name(void *addr, unsigned nbytes, + const char *name); + + void klee_make_symbolic(void *addr, unsigned nbytes); + + /* Return symbolic value in the (signed) interval [begin,end) */ + int klee_range(int begin, int end, const char *name); + + /* Return a symbolic integer */ + int klee_int(const char *name); + + void *klee_malloc_n(unsigned nelems, unsigned size, unsigned alignment); + + /* terminate the state without generating a test file */ + __attribute__((noreturn)) + void klee_silent_exit(int status); + + __attribute__((noreturn)) + void klee_abort(void); + + /** Report an error and terminate the state. */ + __attribute__((noreturn)) + void klee_report_error(const char *file, + int line, + const char *message, + const char *suffix); + + /* called by checking code to get size of memory. */ + unsigned klee_get_obj_size(void *ptr); + + /* print the tree associated w/ a given expression. */ + void klee_print_expr(const char *msg, ...); + + /* NB: this *does not* fork n times and return [0,n) in children. + * It makes n be symbolic and returns: caller must compare N times. + */ + unsigned klee_choose(unsigned n); + + /* special klee assert macro. this assert should be used when path consistency + * across platforms is desired (e.g., in tests). + * NB: __assert_fail is a klee "special" function + */ +# define klee_assert(expr) \ + ((expr) \ + ? (void) (0) \ + : __assert_fail (#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__)) \ + + /* Return true if the given value is symbolic (represented by an + * expression) in the current state. This is primarily for debugging + * and writing tests but can also be used to enable prints in replay + * mode. + */ + unsigned klee_is_symbolic(unsigned n); + + + /* The following intrinsics are primarily intended for internal use + and may have peculiar semantics. */ + + void klee_assume(unsigned condition); + void klee_warning(const char *message); + void klee_warning_once(const char *message); + void klee_prefer_cex(void *object, unsigned condition); + void klee_mark_global(void *object); + + /* Return a possible constant value for the input expression. This + allows programs to forcibly concretize values on their own. */ + unsigned klee_get_value(unsigned expr); + + /* Ensure that memory in the range [address, address+size) is + accessible to the program. If some byte in the range is not + accessible an error will be generated and the state + terminated. + + The current implementation requires both address and size to be + constants and that the range lie within a single object. */ + void klee_check_memory_access(const void *address, unsigned size); + + /* Enable/disable forking. */ + void klee_set_forking(unsigned enable); + + /* klee_alias_function("foo", "bar") will replace, at runtime (on + the current path and all paths spawned on the current path), all + calls to foo() by calls to bar(). foo() and bar() have to exist + and have identical types. Use klee_alias_function("foo", "foo") + to undo. Be aware that some special functions, such as exit(), + may not always work. */ + void klee_alias_function(const char* fn_name, const char* new_fn_name); + +#ifdef __cplusplus +} +#endif + +#endif /* __KLEE_H__ */ diff --git a/include/klee/util/Assignment.h b/include/klee/util/Assignment.h new file mode 100644 index 00000000..19f89a03 --- /dev/null +++ b/include/klee/util/Assignment.h @@ -0,0 +1,100 @@ +//===-- Assignment.h --------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_UTIL_ASSIGNMENT_H +#define KLEE_UTIL_ASSIGNMENT_H + +#include <map> + +#include "klee/util/ExprEvaluator.h" + +// FIXME: Rename? + +namespace klee { + class Array; + + class Assignment { + public: + typedef std::map<const Array*, std::vector<unsigned char> > bindings_ty; + + bool allowFreeValues; + bindings_ty bindings; + + public: + Assignment(bool _allowFreeValues=false) + : allowFreeValues(_allowFreeValues) {} + Assignment(std::vector<const Array*> &objects, + std::vector< std::vector<unsigned char> > &values, + bool _allowFreeValues=false) + : allowFreeValues(_allowFreeValues){ + std::vector< std::vector<unsigned char> >::iterator valIt = + values.begin(); + for (std::vector<const Array*>::iterator it = objects.begin(), + ie = objects.end(); it != ie; ++it) { + const Array *os = *it; + std::vector<unsigned char> &arr = *valIt; + bindings.insert(std::make_pair(os, arr)); + ++valIt; + } + } + + ref<Expr> evaluate(const Array *mo, unsigned index) const; + ref<Expr> evaluate(ref<Expr> e); + + template<typename InputIterator> + bool satisfies(InputIterator begin, InputIterator end); + }; + + class AssignmentEvaluator : public ExprEvaluator { + const Assignment &a; + + protected: + ref<Expr> getInitialValue(const Array &mo, unsigned index) { + return a.evaluate(&mo, index); + } + + public: + AssignmentEvaluator(const Assignment &_a) : a(_a) {} + }; + + /***/ + + inline ref<Expr> Assignment::evaluate(const Array *array, + unsigned index) const { + bindings_ty::const_iterator it = bindings.find(array); + if (it!=bindings.end() && index<it->second.size()) { + return ref<Expr>(it->second[index], Expr::Int8); + } else { + if (allowFreeValues) { + return ReadExpr::create(UpdateList(array, true, 0), + ref<Expr>(index, Expr::Int32)); + } else { + return ref<Expr>(0, Expr::Int8); + } + } + } + + inline ref<Expr> Assignment::evaluate(ref<Expr> e) { + AssignmentEvaluator v(*this); + return v.visit(e); + } + + template<typename InputIterator> + inline bool Assignment::satisfies(InputIterator begin, InputIterator end) { + AssignmentEvaluator v(*this); + for (; begin!=end; ++begin) { + ref<Expr> res = v.visit(*begin); + if (!res.isConstant() || !res.getConstantValue()) + return false; + } + return true; + } +} + +#endif diff --git a/include/klee/util/BitArray.h b/include/klee/util/BitArray.h new file mode 100644 index 00000000..6f887600 --- /dev/null +++ b/include/klee/util/BitArray.h @@ -0,0 +1,42 @@ +//===-- BitArray.h ----------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_UTIL_BITARRAY_H +#define KLEE_UTIL_BITARRAY_H + +namespace klee { + + // XXX would be nice not to have + // two allocations here for allocated + // BitArrays +class BitArray { +private: + uint32_t *bits; + +protected: + static uint32_t length(unsigned size) { return (size+31)/32; } + +public: + BitArray(unsigned size, bool value = false) : bits(new uint32_t[length(size)]) { + memset(bits, value?0xFF:0, sizeof(*bits)*length(size)); + } + BitArray(const BitArray &b, unsigned size) : bits(new uint32_t[length(size)]) { + memcpy(bits, b.bits, sizeof(*bits)*length(size)); + } + ~BitArray() { delete[] bits; } + + bool get(unsigned idx) { return (bool) ((bits[idx/32]>>(idx&0x1F))&1); } + void set(unsigned idx) { bits[idx/32] |= 1<<(idx&0x1F); } + void unset(unsigned idx) { bits[idx/32] &= ~(1<<(idx&0x1F)); } + void set(unsigned idx, bool value) { if (value) set(idx); else unset(idx); } +}; + +} // End klee namespace + +#endif diff --git a/include/klee/util/Bits.h b/include/klee/util/Bits.h new file mode 100644 index 00000000..ffbda09e --- /dev/null +++ b/include/klee/util/Bits.h @@ -0,0 +1,102 @@ +//===-- Bits.h --------------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_UTIL_BITS_H +#define KLEE_UTIL_BITS_H + +#include "llvm/Support/DataTypes.h" + +namespace klee { + namespace bits32 { + // @pre(0 <= N <= 32) + // @post(retval = max([truncateToNBits(i,N) for i in naturals()])) + inline unsigned maxValueOfNBits(unsigned N) { + if (N==0) + return 0; + return ((unsigned) -1) >> (32 - N); + } + + // @pre(0 < N <= 32) + inline unsigned truncateToNBits(unsigned x, unsigned N) { + return x&(((unsigned) -1) >> (32 - N)); + } + + inline unsigned withoutRightmostBit(unsigned x) { + return x&(x-1); + } + + inline unsigned isolateRightmostBit(unsigned x) { + return x&-x; + } + + inline unsigned isPowerOfTwo(unsigned x) { + if (x==0) return 0; + return !(x&(x-1)); + } + + // @pre(withoutRightmostBit(x) == 0) + // @post((1 << retval) == x) + inline unsigned indexOfSingleBit(unsigned x) { + unsigned res = 0; + if (x&0xFFFF0000) res += 16; + if (x&0xFF00FF00) res += 8; + if (x&0xF0F0F0F0) res += 4; + if (x&0xCCCCCCCC) res += 2; + if (x&0xAAAAAAAA) res += 1; + return res; + } + + inline unsigned indexOfRightmostBit(unsigned x) { + return indexOfSingleBit(isolateRightmostBit(x)); + } + } + + namespace bits64 { + // @pre(0 <= N <= 32) + // @post(retval = max([truncateToNBits(i,N) for i in naturals()])) + inline uint64_t maxValueOfNBits(unsigned N) { + if (N==0) + return 0; + return ((uint64_t) (int64_t) -1) >> (64 - N); + } + + // @pre(0 < N <= 64) + inline uint64_t truncateToNBits(uint64_t x, unsigned N) { + return x&(((uint64_t) (int64_t) -1) >> (64 - N)); + } + + inline uint64_t withoutRightmostBit(uint64_t x) { + return x&(x-1); + } + + inline uint64_t isolateRightmostBit(uint64_t x) { + return x&-x; + } + + inline uint64_t isPowerOfTwo(uint64_t x) { + if (x==0) return 0; + return !(x&(x-1)); + } + + // @pre((x&(x-1)) == 0) + // @post((1 << retval) == x) + inline unsigned indexOfSingleBit(uint64_t x) { + unsigned res = bits32::indexOfSingleBit((unsigned) (x | (x>>32))); + if (x&((uint64_t) 0xFFFFFFFF << 32)) + res += 32; + return res; + } + + inline uint64_t indexOfRightmostBit(uint64_t x) { + return indexOfSingleBit(isolateRightmostBit(x)); + } + } +} // End klee namespace + +#endif diff --git a/include/klee/util/ExprEvaluator.h b/include/klee/util/ExprEvaluator.h new file mode 100644 index 00000000..be98942d --- /dev/null +++ b/include/klee/util/ExprEvaluator.h @@ -0,0 +1,39 @@ +//===-- ExprEvaluator.h -----------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_EXPREVALUATOR_H +#define KLEE_EXPREVALUATOR_H + +#include "klee/Expr.h" +#include "klee/util/ExprVisitor.h" + +namespace klee { + class ExprEvaluator : public ExprVisitor { + protected: + Action evalRead(const UpdateList &ul, unsigned index); + Action visitRead(const ReadExpr &re); + + Action protectedDivOperation(const BinaryExpr &e); + Action visitUDiv(const UDivExpr &e); + Action visitSDiv(const SDivExpr &e); + Action visitURem(const URemExpr &e); + Action visitSRem(const SRemExpr &e); + + public: + ExprEvaluator() {} + + // override to implement evaluation, this function is called to + // get the initial value for a symbolic byte. if the value is + // unknown then the user can simply return a ReadExpr at version 0 + // of this MemoryObject. + virtual ref<Expr> getInitialValue(const Array& os, unsigned index) = 0; + }; +} + +#endif diff --git a/include/klee/util/ExprHashMap.h b/include/klee/util/ExprHashMap.h new file mode 100644 index 00000000..d9f95bff --- /dev/null +++ b/include/klee/util/ExprHashMap.h @@ -0,0 +1,48 @@ +//===-- ExprHashMap.h -------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_EXPRHASHMAP_H +#define KLEE_EXPRHASHMAP_H + +#include "klee/Expr.h" +#include <tr1/unordered_map> +#include <tr1/unordered_set> + +namespace klee { + + namespace util { + struct ExprHash { + unsigned operator()(const ref<Expr> e) const { + return e.hash(); + } + }; + + struct ExprCmp { + bool operator()(const ref<Expr> &a, const ref<Expr> &b) const { + return a==b; + } + }; + } + + template<class T> + class ExprHashMap : + + public std::tr1::unordered_map<ref<Expr>, + T, + klee::util::ExprHash, + klee::util::ExprCmp> { + }; + + typedef std::tr1::unordered_set<ref<Expr>, + klee::util::ExprHash, + klee::util::ExprCmp> ExprHashSet; + +} + +#endif diff --git a/include/klee/util/ExprPPrinter.h b/include/klee/util/ExprPPrinter.h new file mode 100644 index 00000000..a1961e2b --- /dev/null +++ b/include/klee/util/ExprPPrinter.h @@ -0,0 +1,58 @@ +//===-- ExprPPrinter.h ------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_EXPRPPRINTER_H +#define KLEE_EXPRPPRINTER_H + +#include "klee/Expr.h" + +namespace klee { + class ConstraintManager; + + class ExprPPrinter { + protected: + ExprPPrinter() {} + + public: + static ExprPPrinter *create(std::ostream &os); + + virtual ~ExprPPrinter() {} + + virtual void setNewline(const std::string &newline) = 0; + virtual void reset() = 0; + virtual void scan(const ref<Expr> &e) = 0; + virtual void print(const ref<Expr> &e, unsigned indent=0) = 0; + + // utility methods + + template<class Container> + void scan(Container c) { + scan(c.begin(), c.end()); + } + + template<class InputIterator> + void scan(InputIterator it, InputIterator end) { + for (; it!=end; ++it) + scan(*it); + } + + static void printOne(std::ostream &os, const char *message, + const ref<Expr> &e); + + static void printConstraints(std::ostream &os, + const ConstraintManager &constraints); + + static void printQuery(std::ostream &os, + const ConstraintManager &constraints, + const ref<Expr> &q); + }; + +} + +#endif diff --git a/include/klee/util/ExprRangeEvaluator.h b/include/klee/util/ExprRangeEvaluator.h new file mode 100644 index 00000000..3729b5c2 --- /dev/null +++ b/include/klee/util/ExprRangeEvaluator.h @@ -0,0 +1,283 @@ +//===-- ExprRangeEvaluator.h ------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_EXPRRANGEEVALUATOR_H +#define KLEE_EXPRRANGEEVALUATOR_H + +#include "klee/Expr.h" +#include "klee/util/Bits.h" + +namespace klee { + +/* +class ValueType { +public: + ValueType(); // empty range + ValueType(uint64_t value); + ValueType(uint64_t min, uint64_t max); + + bool mustEqual(const uint64_t b); + bool mustEqual(const ValueType &b); + bool mayEqual(const uint64_t b); + bool mayEqual(const ValueType &b); + + bool isFullRange(unsigned width); + + ValueType set_union(ValueType &); + ValueType set_intersection(ValueType &); + ValueType set_difference(ValueType &); + + ValueType binaryAnd(ValueType &); + ValueType binaryOr(ValueType &); + ValueType binaryXor(ValueType &); + ValueType concat(ValueType &, unsigned width); + ValueType add(ValueType &, unsigned width); + ValueType sub(ValueType &, unsigned width); + ValueType mul(ValueType &, unsigned width); + ValueType udiv(ValueType &, unsigned width); + ValueType sdiv(ValueType &, unsigned width); + ValueType urem(ValueType &, unsigned width); + ValueType srem(ValueType &, unsigned width); + + uint64_t min(); + uint64_t max(); + int64_t minSigned(unsigned width); + int64_t maxSigned(unsigned width); +} +*/ + +template<class T> +class ExprRangeEvaluator { +protected: + virtual T getInitialReadRange(const Array &os, T index) = 0; + + T evalRead(const UpdateList &ul, T index); + +public: + ExprRangeEvaluator() {} + virtual ~ExprRangeEvaluator() {} + + T evaluate(const ref<Expr> &e); +}; + +template<class T> +T ExprRangeEvaluator<T>::evalRead(const UpdateList &ul, + T index) { + T res; + + for (const UpdateNode *un=ul.head; un; un=un->next) { + T ui = evaluate(un->index); + + if (ui.mustEqual(index)) { + return res.set_union(evaluate(un->value)); + } else if (ui.mayEqual(index)) { + res = res.set_union(evaluate(un->value)); + if (res.isFullRange(8)) { + return res; + } + } + } + + return res.set_union(getInitialReadRange(*ul.root, index)); +} + +template<class T> +T ExprRangeEvaluator<T>::evaluate(const ref<Expr> &e) { + switch (e.getKind()) { + case Expr::Constant: + return T(e.getConstantValue()); + + case Expr::NotOptimized: + break; + + case Expr::Read: { + const ReadExpr *re = static_ref_cast<const ReadExpr>(e); + T index = evaluate(re->index); + + assert(re->getWidth()==Expr::Int8 && "unexpected multibyte read"); + + return evalRead(re->updates, index); + } + + case Expr::Select: { + const SelectExpr *se = static_ref_cast<const SelectExpr>(e); + T cond = evaluate(se->cond); + + if (cond.mustEqual(1)) { + return evaluate(se->trueExpr); + } else if (cond.mustEqual(0)) { + return evaluate(se->falseExpr); + } else { + return evaluate(se->trueExpr).set_union(evaluate(se->falseExpr)); + } + } + + // XXX these should be unrolled to ensure nice inline + case Expr::Concat: { + const Expr *ep = e.get(); + T res(0); + for (unsigned i=0; i<ep->getNumKids(); i++) + res = res.concat(evaluate(ep->getKid(i)),8); + return res; + } + + // Arithmetic + + case Expr::Add: { + const BinaryExpr *be = static_ref_cast<const BinaryExpr>(e); + unsigned width = be->left.getWidth(); + return evaluate(be->left).add(evaluate(be->right), width); + } + case Expr::Sub: { + const BinaryExpr *be = static_ref_cast<const BinaryExpr>(e); + unsigned width = be->left.getWidth(); + return evaluate(be->left).sub(evaluate(be->right), width); + } + case Expr::Mul: { + const BinaryExpr *be = static_ref_cast<const BinaryExpr>(e); + unsigned width = be->left.getWidth(); + return evaluate(be->left).mul(evaluate(be->right), width); + } + case Expr::UDiv: { + const BinaryExpr *be = static_ref_cast<const BinaryExpr>(e); + unsigned width = be->left.getWidth(); + return evaluate(be->left).udiv(evaluate(be->right), width); + } + case Expr::SDiv: { + const BinaryExpr *be = static_ref_cast<const BinaryExpr>(e); + unsigned width = be->left.getWidth(); + return evaluate(be->left).sdiv(evaluate(be->right), width); + } + case Expr::URem: { + const BinaryExpr *be = static_ref_cast<const BinaryExpr>(e); + unsigned width = be->left.getWidth(); + return evaluate(be->left).urem(evaluate(be->right), width); + } + case Expr::SRem: { + const BinaryExpr *be = static_ref_cast<const BinaryExpr>(e); + unsigned width = be->left.getWidth(); + return evaluate(be->left).srem(evaluate(be->right), width); + } + + // Binary + + case Expr::And: { + const BinaryExpr *be = static_ref_cast<const BinaryExpr>(e); + return evaluate(be->left).binaryAnd(evaluate(be->right)); + } + case Expr::Or: { + const BinaryExpr *be = static_ref_cast<const BinaryExpr>(e); + return evaluate(be->left).binaryOr(evaluate(be->right)); + } + case Expr::Xor: { + const BinaryExpr *be = static_ref_cast<const BinaryExpr>(e); + return evaluate(be->left).binaryXor(evaluate(be->right)); + } + case Expr::Shl: { + // BinaryExpr *be = static_ref_cast<BinaryExpr>(e); + // unsigned width = be->left.getWidth(); + // return evaluate(be->left).shl(evaluate(be->right), width); + break; + } + case Expr::LShr: { + // BinaryExpr *be = static_ref_cast<BinaryExpr>(e); + // unsigned width = be->left.getWidth(); + // return evaluate(be->left).lshr(evaluate(be->right), width); + break; + } + case Expr::AShr: { + // BinaryExpr *be = static_ref_cast<BinaryExpr>(e); + // unsigned width = be->left.getWidth(); + // return evaluate(be->left).ashr(evaluate(be->right), width); + break; + } + + // Comparison + + case Expr::Eq: { + const BinaryExpr *be = static_ref_cast<const BinaryExpr>(e); + T left = evaluate(be->left); + T right = evaluate(be->right); + + if (left.mustEqual(right)) { + return T(1); + } else if (!left.mayEqual(right)) { + return T(0); + } + break; + } + + case Expr::Ult: { + const BinaryExpr *be = static_ref_cast<const BinaryExpr>(e); + T left = evaluate(be->left); + T right = evaluate(be->right); + + if (left.max() < right.min()) { + return T(1); + } else if (left.min() >= right.max()) { + return T(0); + } + break; + } + case Expr::Ule: { + const BinaryExpr *be = static_ref_cast<const BinaryExpr>(e); + T left = evaluate(be->left); + T right = evaluate(be->right); + + if (left.max() <= right.min()) { + return T(1); + } else if (left.min() > right.max()) { + return T(0); + } + break; + } + case Expr::Slt: { + const BinaryExpr *be = static_ref_cast<const BinaryExpr>(e); + T left = evaluate(be->left); + T right = evaluate(be->right); + unsigned bits = be->left.getWidth(); + + if (left.maxSigned(bits) < right.minSigned(bits)) { + return T(1); + } else if (left.minSigned(bits) >= right.maxSigned(bits)) { + return T(0); + } + break; + } + case Expr::Sle: { + const BinaryExpr *be = static_ref_cast<const BinaryExpr>(e); + T left = evaluate(be->left); + T right = evaluate(be->right); + unsigned bits = be->left.getWidth(); + + if (left.maxSigned(bits) <= right.minSigned(bits)) { + return T(1); + } else if (left.minSigned(bits) > right.maxSigned(bits)) { + return T(0); + } + break; + } + + case Expr::Ne: + case Expr::Ugt: + case Expr::Uge: + case Expr::Sgt: + case Expr::Sge: + assert(0 && "invalid expressions (uncanonicalized)"); + + default: + break; + } + + return T(0, bits64::maxValueOfNBits(e.getWidth())); +} + +} + +#endif diff --git a/include/klee/util/ExprUtil.h b/include/klee/util/ExprUtil.h new file mode 100644 index 00000000..a81c299f --- /dev/null +++ b/include/klee/util/ExprUtil.h @@ -0,0 +1,43 @@ +//===-- ExprUtil.h ----------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_EXPRUTIL_H +#define KLEE_EXPRUTIL_H + +#include <vector> + +namespace klee { + class Array; + class Expr; + class ReadExpr; + template<typename T> class ref; + + /// Find all ReadExprs used in the expression DAG. If visitUpdates + /// is true then this will including those reachable by traversing + /// update lists. Note that this may be slow and return a large + /// number of results. + void findReads(ref<Expr> e, + bool visitUpdates, + std::vector< ref<ReadExpr> > &result); + + /// Return a list of all unique symbolic objects referenced by the given + /// expression. + void findSymbolicObjects(ref<Expr> e, + std::vector<const Array*> &results); + + /// Return a list of all unique symbolic objects referenced by the + /// given expression range. + template<typename InputIterator> + void findSymbolicObjects(InputIterator begin, + InputIterator end, + std::vector<const Array*> &results); + +} + +#endif diff --git a/include/klee/util/ExprVisitor.h b/include/klee/util/ExprVisitor.h new file mode 100644 index 00000000..8f8617e3 --- /dev/null +++ b/include/klee/util/ExprVisitor.h @@ -0,0 +1,95 @@ +//===-- ExprVisitor.h -------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_EXPRVISITOR_H +#define KLEE_EXPRVISITOR_H + +#include "ExprHashMap.h" + +namespace klee { + class ExprVisitor { + protected: + // typed variant, but non-virtual for efficiency + class Action { + public: + enum Kind { SkipChildren, DoChildren, ChangeTo }; + + private: + // Action() {} + Action(Kind _kind) + : kind(_kind), argument(0,Expr::Bool) {} + Action(Kind _kind, const ref<Expr> &_argument) + : kind(_kind), argument(_argument) {} + + friend class ExprVisitor; + + public: + Kind kind; + ref<Expr> argument; + + static Action changeTo(const ref<Expr> &expr) { return Action(ChangeTo,expr); } + static Action doChildren() { return Action(DoChildren); } + static Action skipChildren() { return Action(SkipChildren); } + }; + + protected: + explicit + ExprVisitor(bool _recursive=false) : recursive(_recursive) {} + virtual ~ExprVisitor() {} + + virtual Action visitExpr(const Expr&); + virtual Action visitExprPost(const Expr&); + + virtual Action visitNotOptimized(const NotOptimizedExpr&); + virtual Action visitRead(const ReadExpr&); + virtual Action visitSelect(const SelectExpr&); + virtual Action visitConcat(const ConcatExpr&); + virtual Action visitExtract(const ExtractExpr&); + virtual Action visitZExt(const ZExtExpr&); + virtual Action visitSExt(const SExtExpr&); + virtual Action visitAdd(const AddExpr&); + virtual Action visitSub(const SubExpr&); + virtual Action visitMul(const MulExpr&); + virtual Action visitUDiv(const UDivExpr&); + virtual Action visitSDiv(const SDivExpr&); + virtual Action visitURem(const URemExpr&); + virtual Action visitSRem(const SRemExpr&); + virtual Action visitAnd(const AndExpr&); + virtual Action visitOr(const OrExpr&); + virtual Action visitXor(const XorExpr&); + virtual Action visitShl(const ShlExpr&); + virtual Action visitLShr(const LShrExpr&); + virtual Action visitAShr(const AShrExpr&); + virtual Action visitEq(const EqExpr&); + virtual Action visitNe(const NeExpr&); + virtual Action visitUlt(const UltExpr&); + virtual Action visitUle(const UleExpr&); + virtual Action visitUgt(const UgtExpr&); + virtual Action visitUge(const UgeExpr&); + virtual Action visitSlt(const SltExpr&); + virtual Action visitSle(const SleExpr&); + virtual Action visitSgt(const SgtExpr&); + virtual Action visitSge(const SgeExpr&); + + private: + typedef ExprHashMap< ref<Expr> > visited_ty; + visited_ty visited; + bool recursive; + + ref<Expr> visitActual(const ref<Expr> &e); + + public: + // apply the visitor to the expression and return a possibly + // modified new expression. + ref<Expr> visit(const ref<Expr> &e); + }; + +} + +#endif diff --git a/include/klee/util/Ref.h b/include/klee/util/Ref.h new file mode 100644 index 00000000..a70b09cf --- /dev/null +++ b/include/klee/util/Ref.h @@ -0,0 +1,303 @@ +//===-- Ref.h ---------------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_REF_H +#define KLEE_REF_H + +#include <assert.h> + +class Expr; +class BinaryExpr; +class CastExpr; +class CmpExpr; + +class ConstantExpr; +class ReadExpr; +class UpdateNode; +class NotOptimizedExpr; +class ReadExpr; +class SelectExpr; +class ConcatExpr; +class ExtractExpr; +class ZExtExpr; +class SExtExpr; +class AddExpr; +class SubExpr; +class MulExpr; +class UDivExpr; +class SDivExpr; +class URemExpr; +class SRemExpr; +class AndExpr; +class OrExpr; +class XorExpr; +class ShlExpr; +class LShrExpr; +class AShrExpr; +class EqExpr; +class NeExpr; +class UltExpr; +class UleExpr; +class UgtExpr; +class UgeExpr; +class SltExpr; +class SleExpr; +class SgtExpr; +class SgeExpr; +class KModule; + + class ExprVisitor; + class StackFrame; + class ObjectState; + +template<class T> +class ref { +public: + // default constructor: create a NULL reference + ref() : constantWidth(Expr::InvalidWidth) { + contents.ptr = 0; + } + +private: + // if NotConstant, not this ref is not a constant. + // otherwise, it's the width of the constant. + Expr::Width constantWidth; + static const Expr::Width NotConstant = (Expr::Width) 0; + + union { + T *ptr; + uint64_t val; + } contents; + + void inc() { + if (constantWidth == NotConstant && + contents.ptr) { + contents.ptr->refCount++; + } + } + + void dec() { + if (constantWidth == NotConstant && + contents.ptr && + --contents.ptr->refCount == 0) { + delete contents.ptr; + } + } + + friend class ExprVisitor; + friend class Cell; + friend class ObjectState; + friend class KModule; + +public: + template<class U> friend class ref; + template<class U> friend U* dyn_ref_cast(ref &src); + template<class U> friend const U* dyn_ref_cast(const ref &src); + template<class U> friend U* static_ref_cast(ref &src); + template<class U> friend const U* static_ref_cast(const ref &src); + + // constructor from pointer + ref(T *p) : constantWidth(NotConstant) { + contents.ptr = p; + inc(); + } + + // construct from constant + ref(uint64_t val, Expr::Width w) : constantWidth(w) { + contents.val = val; + } + + // normal copy constructor + ref (const ref<T> &r) + : constantWidth(r.constantWidth), contents(r.contents) { + inc(); + } + + // conversion constructor + template<class U> + ref (const ref<U> &r) { + constantWidth = r.constantWidth; + contents.val = r.contents.val; + inc(); + } + + // pointer operations + T *get () { + // demand(constantWidth == NotConstant, "deref of constant"); + + // allocate + if (constantWidth != NotConstant) { + contents.ptr = dynamic_cast<T*>(Expr::createConstant(contents.val, constantWidth)); + assert(contents.ptr && "error with lazy constant initialization"); + constantWidth = NotConstant; + + inc(); + } + return contents.ptr; + } + + T *get () const { + assert(constantWidth == NotConstant && "deref of constant"); + return contents.ptr; + } + + // method calls for the constant optimization + bool isConstant() const { + if (constantWidth != NotConstant) { + return true; + } else if (contents.ptr) { + return contents.ptr->getKind() == Expr::Constant; + } else { + return false; // should never happen, but nice check + } + } + + uint64_t getConstantValue() const { + if (constantWidth) { + return contents.val; + } else { + return contents.ptr->getConstantValue(); + } + } + + unsigned hash() const { + if (constantWidth) { + return Expr::hashConstant(contents.val, constantWidth); + } else { + return contents.ptr->hash(); + } + } + + unsigned computeHash() const { + if (isConstant()) { + return Expr::hashConstant(contents.val, constantWidth); + } else { + return contents.ptr->computeHash(); + } + } + + void rehash() const { + if (!isConstant()) + contents.ptr->computeHash(); + } + + Expr::Width getWidth() const { + if (constantWidth != NotConstant) + return constantWidth; + return contents.ptr->getWidth(); + } + + Expr::Kind getKind() const { + if (constantWidth != NotConstant) + return Expr::Constant; + return contents.ptr->getKind(); + } + + unsigned getNumKids() const { + if (constantWidth != NotConstant) + return 0; + return contents.ptr->getNumKids(); + } + + ref<Expr> getKid(unsigned k) { + if (constantWidth != NotConstant) + return 0; + return contents.ptr->getKid(k); + } + + ~ref () { dec (); } + + /* The copy assignment operator must also explicitly be defined, + * despite a redundant template. */ + ref<T> &operator= (const ref<T> &r) { + dec(); + constantWidth = r.constantWidth; + contents.val = r.contents.val; + inc(); + + return *this; + } + + template<class U> ref<T> &operator= (const ref<U> &r) { + dec(); + constantWidth = r.constantWidth; + contents.val = r.contents.val; + inc(); + + return *this; + } + + bool isNull() const { return !constantWidth && !contents.ptr; } + + // assumes non-null arguments + int compare(const ref &rhs) const { + Expr::Kind ak = getKind(), bk = rhs.getKind(); + if (ak!=bk) + return (ak < bk) ? -1 : 1; + if (ak==Expr::Constant) { + Expr::Width at = getWidth(), bt = rhs.getWidth(); + if (at!=bt) + return (at < bt) ? -1 : 1; + uint64_t av = getConstantValue(), bv = rhs.getConstantValue(); + if (av<bv) { + return -1; + } else if (av>bv) { + return 1; + } else { + return 0; + } + } else { + return get()->compare(*rhs.get()); + } + } + + // assumes non-null arguments + bool operator<(const ref &rhs) const { return compare(rhs)<0; } + bool operator==(const ref &rhs) const { return compare(rhs)==0; } + bool operator!=(const ref &rhs) const { return compare(rhs)!=0; } +}; + + +template<class T> +inline std::ostream &operator<<(std::ostream &os, const ref<T> &e) { + if (e.isConstant()) { + os << e.getConstantValue(); + } else { + os << *e.get(); + } + return os; +} + +template<class U> +U* dyn_ref_cast(ref<Expr> &src) { + if (src.constantWidth != ref<Expr>::NotConstant) + return 0; + + return dynamic_cast<U*>(src.contents.ptr); +} + +template<class U> +const U* dyn_ref_cast(const ref<Expr> &src) { + if (src.constantWidth != ref<Expr>::NotConstant) + return 0; + + return dynamic_cast<const U*>(src.contents.ptr); +} + +template<class U> +U* static_ref_cast(ref<Expr> &src) { + return static_cast<U*>(src.contents.ptr); +} + +template<class U> +const U* static_ref_cast(const ref<Expr> &src) { + return static_cast<const U*>(src.contents.ptr); +} + +#endif /* KLEE_REF_H */ diff --git a/lib/Basic/BOut.cpp b/lib/Basic/BOut.cpp new file mode 100644 index 00000000..42d17e27 --- /dev/null +++ b/lib/Basic/BOut.cpp @@ -0,0 +1,236 @@ +//===-- BOut.c ------------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Internal/ADT/BOut.h" + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#define BOUT_MAGIC "BOUT\n" +#define BOUT_MAGIC_SIZE 5 +#define BOUT_VERSION 2 + +/***/ + +static int read_uint32(FILE *f, unsigned *value_out) { + unsigned char data[4]; + if (fread(data, 4, 1, f)!=1) + return 0; + *value_out = (((((data[0]<<8) + data[1])<<8) + data[2])<<8) + data[3]; + return 1; +} + +static int write_uint32(FILE *f, unsigned value) { + unsigned char data[4]; + data[0] = value>>24; + data[1] = value>>16; + data[2] = value>> 8; + data[3] = value>> 0; + return fwrite(data, 1, 4, f)==4; +} + +static int read_string(FILE *f, char **value_out) { + unsigned len; + if (!read_uint32(f, &len)) + return 0; + *value_out = (char*) malloc(len+1); + if (!*value_out) + return 0; + if (fread(*value_out, len, 1, f)!=1) + return 0; + (*value_out)[len] = 0; + return 1; +} + +static int write_string(FILE *f, const char *value) { + unsigned len = strlen(value); + if (!write_uint32(f, len)) + return 0; + if (fwrite(value, len, 1, f)!=1) + return 0; + return 1; +} + +/***/ + + +unsigned bOut_getCurrentVersion() { + return BOUT_VERSION; +} + + +static int bOut_checkHeader(FILE *f) { + char header[BOUT_MAGIC_SIZE]; + if (fread(header, BOUT_MAGIC_SIZE, 1, f)!=1) + return 0; + if (memcmp(header, BOUT_MAGIC, BOUT_MAGIC_SIZE)) + return 0; + return 1; +} + +int bOut_isBOutFile(const char *path) { + FILE *f = fopen(path, "rb"); + int res; + + if (!f) + return 0; + res = bOut_checkHeader(f); + fclose(f); + + return res; +} + +BOut *bOut_fromFile(const char *path) { + FILE *f = fopen(path, "rb"); + BOut *res = 0; + unsigned i, version; + + if (!f) + goto error; + if (!bOut_checkHeader(f)) + goto error; + + res = (BOut*) calloc(1, sizeof(*res)); + if (!res) + goto error; + + if (!read_uint32(f, &version)) + goto error; + + if (version > bOut_getCurrentVersion()) + goto error; + + res->version = version; + + if (!read_uint32(f, &res->numArgs)) + goto error; + res->args = (char**) calloc(res->numArgs, sizeof(*res->args)); + if (!res->args) + goto error; + + for (i=0; i<res->numArgs; i++) + if (!read_string(f, &res->args[i])) + goto error; + + if (version >= 2) { + if (!read_uint32(f, &res->symArgvs)) + goto error; + if (!read_uint32(f, &res->symArgvLen)) + goto error; + } + + if (!read_uint32(f, &res->numObjects)) + goto error; + res->objects = (BOutObject*) calloc(res->numObjects, sizeof(*res->objects)); + if (!res->objects) + goto error; + for (i=0; i<res->numObjects; i++) { + BOutObject *o = &res->objects[i]; + if (!read_string(f, &o->name)) + goto error; + if (!read_uint32(f, &o->numBytes)) + goto error; + o->bytes = (unsigned char*) malloc(o->numBytes); + if (fread(o->bytes, o->numBytes, 1, f)!=1) + goto error; + } + + fclose(f); + + return res; + error: + if (res) { + if (res->args) { + for (i=0; i<res->numArgs; i++) + if (res->args[i]) + free(res->args[i]); + free(res->args); + } + if (res->objects) { + for (i=0; i<res->numObjects; i++) { + BOutObject *bo = &res->objects[i]; + if (bo->name) + free(bo->name); + if (bo->bytes) + free(bo->bytes); + } + free(res->objects); + } + free(res); + } + + if (f) fclose(f); + + return 0; +} + +int bOut_toFile(BOut *bo, const char *path) { + FILE *f = fopen(path, "wb"); + unsigned i; + + if (!f) + goto error; + if (fwrite(BOUT_MAGIC, strlen(BOUT_MAGIC), 1, f)!=1) + goto error; + if (!write_uint32(f, BOUT_VERSION)) + goto error; + + if (!write_uint32(f, bo->numArgs)) + goto error; + for (i=0; i<bo->numArgs; i++) { + if (!write_string(f, bo->args[i])) + goto error; + } + + if (!write_uint32(f, bo->symArgvs)) + goto error; + if (!write_uint32(f, bo->symArgvLen)) + goto error; + + if (!write_uint32(f, bo->numObjects)) + goto error; + for (i=0; i<bo->numObjects; i++) { + BOutObject *o = &bo->objects[i]; + if (!write_string(f, o->name)) + goto error; + if (!write_uint32(f, o->numBytes)) + goto error; + if (fwrite(o->bytes, o->numBytes, 1, f)!=1) + goto error; + } + + fclose(f); + + return 1; + error: + if (f) fclose(f); + + return 0; +} + +unsigned bOut_numBytes(BOut *bo) { + unsigned i, res = 0; + for (i=0; i<bo->numObjects; i++) + res += bo->objects[i].numBytes; + return res; +} + +void bOut_free(BOut *bo) { + unsigned i; + for (i=0; i<bo->numArgs; i++) + free(bo->args[i]); + free(bo->args); + for (i=0; i<bo->numObjects; i++) { + free(bo->objects[i].name); + free(bo->objects[i].bytes); + } + free(bo->objects); + free(bo); +} diff --git a/lib/Basic/Makefile b/lib/Basic/Makefile new file mode 100644 index 00000000..d4481e7f --- /dev/null +++ b/lib/Basic/Makefile @@ -0,0 +1,16 @@ +#===-- lib/Basic/Makefile ----------------------------------*- Makefile -*--===# +# +# The KLEE Symbolic Virtual Machine +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +LEVEL=../.. + +LIBRARYNAME=kleeBasic +DONT_BUILD_RELINKED=1 +BUILD_ARCHIVE=1 + +include $(LEVEL)/Makefile.common diff --git a/lib/Basic/README.txt b/lib/Basic/README.txt new file mode 100644 index 00000000..b13df6bd --- /dev/null +++ b/lib/Basic/README.txt @@ -0,0 +1,3 @@ +This directory holds the most basic support facilities provided for +both the klee and kleaver libraries. The code in this directory should +have no dependencies on LLVM or any other klee libraries. diff --git a/lib/Basic/Statistics.cpp b/lib/Basic/Statistics.cpp new file mode 100644 index 00000000..9c95a891 --- /dev/null +++ b/lib/Basic/Statistics.cpp @@ -0,0 +1,84 @@ +//===-- Statistics.cpp ----------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Statistics.h" + +#include <vector> + +using namespace klee; + +StatisticManager::StatisticManager() + : enabled(true), + globalStats(0), + indexedStats(0), + contextStats(0), + index(0) { +} + +StatisticManager::~StatisticManager() { + if (globalStats) delete[] globalStats; + if (indexedStats) delete[] indexedStats; +} + +void StatisticManager::useIndexedStats(unsigned totalIndices) { + if (indexedStats) delete[] indexedStats; + indexedStats = new uint64_t[totalIndices * stats.size()]; + memset(indexedStats, 0, sizeof(*indexedStats) * totalIndices * stats.size()); +} + +void StatisticManager::registerStatistic(Statistic &s) { + if (globalStats) delete[] globalStats; + s.id = stats.size(); + stats.push_back(&s); + globalStats = new uint64_t[stats.size()]; + memset(globalStats, 0, sizeof(*globalStats)*stats.size()); +} + +int StatisticManager::getStatisticID(const std::string &name) const { + for (unsigned i=0; i<stats.size(); i++) + if (stats[i]->getName() == name) + return i; + return -1; +} + +Statistic *StatisticManager::getStatisticByName(const std::string &name) const { + for (unsigned i=0; i<stats.size(); i++) + if (stats[i]->getName() == name) + return stats[i]; + return 0; +} + +StatisticManager *klee::theStatisticManager = 0; + +static StatisticManager &getStatisticManager() { + static StatisticManager sm; + theStatisticManager = &sm; + return sm; +} + +/* *** */ + +Statistic::Statistic(const std::string &_name, + const std::string &_shortName) + : name(_name), + shortName(_shortName) { + getStatisticManager().registerStatistic(*this); +} + +Statistic::~Statistic() { +} + +Statistic &Statistic::operator +=(const uint64_t addend) { + theStatisticManager->incrementStatistic(*this, addend); + return *this; +} + +uint64_t Statistic::getValue() const { + return theStatisticManager->getValue(*this); +} diff --git a/lib/Core/AddressSpace.cpp b/lib/Core/AddressSpace.cpp new file mode 100644 index 00000000..fb032fd5 --- /dev/null +++ b/lib/Core/AddressSpace.cpp @@ -0,0 +1,334 @@ +//===-- AddressSpace.cpp --------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AddressSpace.h" +#include "CoreStats.h" +#include "Memory.h" +#include "TimingSolver.h" + +#include "klee/Expr.h" +#include "klee/TimerStatIncrementer.h" + +using namespace klee; + +/// + +void AddressSpace::bindObject(const MemoryObject *mo, ObjectState *os) { + assert(os->copyOnWriteOwner==0 && "object already has owner"); + os->copyOnWriteOwner = cowKey; + objects = objects.replace(std::make_pair(mo, os)); +} + +void AddressSpace::unbindObject(const MemoryObject *mo) { + objects = objects.remove(mo); +} + +const ObjectState *AddressSpace::findObject(const MemoryObject *mo) const { + const MemoryMap::value_type *res = objects.lookup(mo); + + return res ? res->second : 0; +} + +ObjectState *AddressSpace::getWriteable(const MemoryObject *mo, + const ObjectState *os) { + assert(!os->readOnly); + + if (cowKey==os->copyOnWriteOwner) { + return const_cast<ObjectState*>(os); + } else { + ObjectState *n = new ObjectState(*os); + n->copyOnWriteOwner = cowKey; + objects = objects.replace(std::make_pair(mo, n)); + return n; + } +} + +/// + +bool AddressSpace::resolveOne(uint64_t addr64, ObjectPair &result) { + unsigned address = (unsigned) addr64; + MemoryObject hack(address); + + if (const MemoryMap::value_type *res = objects.lookup_previous(&hack)) { + const MemoryObject *mo = res->first; + if ((mo->size==0 && address==mo->address) || + (address - mo->address < mo->size)) { + result = *res; + return true; + } + } + + return false; +} + +bool AddressSpace::resolveOne(ExecutionState &state, + TimingSolver *solver, + ref<Expr> address, + ObjectPair &result, + bool &success) { + if (address.isConstant()) { + success = resolveOne(address.getConstantValue(), result); + return true; + } else { + TimerStatIncrementer timer(stats::resolveTime); + + // try cheap search, will succeed for any inbounds pointer + + ref<Expr> cex(0); + if (!solver->getValue(state, address, cex)) + return false; + unsigned example = (unsigned) cex.getConstantValue(); + MemoryObject hack(example); + const MemoryMap::value_type *res = objects.lookup_previous(&hack); + + if (res) { + const MemoryObject *mo = res->first; + if (example - mo->address < mo->size) { + result = *res; + success = true; + return true; + } + } + + // didn't work, now we have to search + + MemoryMap::iterator oi = objects.upper_bound(&hack); + MemoryMap::iterator begin = objects.begin(); + MemoryMap::iterator end = objects.end(); + + MemoryMap::iterator start = oi; + while (oi!=begin) { + --oi; + const MemoryObject *mo = oi->first; + + bool mayBeTrue; + if (!solver->mayBeTrue(state, + mo->getBoundsCheckPointer(address), mayBeTrue)) + return false; + if (mayBeTrue) { + result = *oi; + success = true; + return true; + } else { + bool mustBeTrue; + if (!solver->mustBeTrue(state, + UgeExpr::create(address, mo->getBaseExpr()), + mustBeTrue)) + return false; + if (mustBeTrue) + break; + } + } + + // search forwards + for (oi=start; oi!=end; ++oi) { + const MemoryObject *mo = oi->first; + + bool mustBeTrue; + if (!solver->mustBeTrue(state, + UltExpr::create(address, mo->getBaseExpr()), + mustBeTrue)) + return false; + if (mustBeTrue) { + break; + } else { + bool mayBeTrue; + + if (!solver->mayBeTrue(state, + mo->getBoundsCheckPointer(address), + mayBeTrue)) + return false; + if (mayBeTrue) { + result = *oi; + success = true; + return true; + } + } + } + + success = false; + return true; + } +} + +bool AddressSpace::resolve(ExecutionState &state, + TimingSolver *solver, + ref<Expr> p, + ResolutionList &rl, + unsigned maxResolutions, + double timeout) { + if (p.isConstant()) { + ObjectPair res; + if (resolveOne(p.getConstantValue(), res)) + rl.push_back(res); + return false; + } else { + TimerStatIncrementer timer(stats::resolveTime); + uint64_t timeout_us = (uint64_t) (timeout*1000000.); + + // XXX in general this isn't exactly what we want... for + // a multiple resolution case (or for example, a \in {b,c,0}) + // we want to find the first object, find a cex assuming + // not the first, find a cex assuming not the second... + // etc. + + // XXX how do we smartly amortize the cost of checking to + // see if we need to keep searching up/down, in bad cases? + // maybe we don't care? + + // XXX we really just need a smart place to start (although + // if its a known solution then the code below is guaranteed + // to hit the fast path with exactly 2 queries). we could also + // just get this by inspection of the expr. + + ref<Expr> cex(0); + if (!solver->getValue(state, p, cex)) + return true; + unsigned example = (unsigned) cex.getConstantValue(); + MemoryObject hack(example); + + MemoryMap::iterator oi = objects.upper_bound(&hack); + MemoryMap::iterator begin = objects.begin(); + MemoryMap::iterator end = objects.end(); + + MemoryMap::iterator start = oi; + + // XXX in the common case we can save one query if we ask + // mustBeTrue before mayBeTrue for the first result. easy + // to add I just want to have a nice symbolic test case first. + + // search backwards, start with one minus because this + // is the object that p *should* be within, which means we + // get write off the end with 4 queries (XXX can be better, + // no?) + while (oi!=begin) { + --oi; + const MemoryObject *mo = oi->first; + if (timeout_us && timeout_us < timer.check()) + return true; + + // XXX I think there is some query wasteage here? + ref<Expr> inBounds = mo->getBoundsCheckPointer(p); + bool mayBeTrue; + if (!solver->mayBeTrue(state, inBounds, mayBeTrue)) + return true; + if (mayBeTrue) { + rl.push_back(*oi); + + // fast path check + unsigned size = rl.size(); + if (size==1) { + bool mustBeTrue; + if (!solver->mustBeTrue(state, inBounds, mustBeTrue)) + return true; + if (mustBeTrue) + return false; + } else if (size==maxResolutions) { + return true; + } + } + + bool mustBeTrue; + if (!solver->mustBeTrue(state, + UgeExpr::create(p, mo->getBaseExpr()), + mustBeTrue)) + return true; + if (mustBeTrue) + break; + } + // search forwards + for (oi=start; oi!=end; ++oi) { + const MemoryObject *mo = oi->first; + if (timeout_us && timeout_us < timer.check()) + return true; + + bool mustBeTrue; + if (!solver->mustBeTrue(state, + UltExpr::create(p, mo->getBaseExpr()), + mustBeTrue)) + return true; + if (mustBeTrue) + break; + + // XXX I think there is some query wasteage here? + ref<Expr> inBounds = mo->getBoundsCheckPointer(p); + bool mayBeTrue; + if (!solver->mayBeTrue(state, inBounds, mayBeTrue)) + return true; + if (mayBeTrue) { + rl.push_back(*oi); + + // fast path check + unsigned size = rl.size(); + if (size==1) { + bool mustBeTrue; + if (!solver->mustBeTrue(state, inBounds, mustBeTrue)) + return true; + if (mustBeTrue) + return false; + } else if (size==maxResolutions) { + return true; + } + } + } + } + + return false; +} + +// These two are pretty big hack so we can sort of pass memory back +// and forth to externals. They work by abusing the concrete cache +// store inside of the object states, which allows them to +// transparently avoid screwing up symbolics (if the byte is symbolic +// then its concrete cache byte isn't being used) but is just a hack. + +void AddressSpace::copyOutConcretes() { + for (MemoryMap::iterator it = objects.begin(), ie = objects.end(); + it != ie; ++it) { + const MemoryObject *mo = it->first; + + if (!mo->isUserSpecified) { + ObjectState *os = it->second; + uint8_t *address = (uint8_t*) (unsigned long) mo->address; + + if (!os->readOnly) + memcpy(address, os->concreteStore, mo->size); + } + } +} + +bool AddressSpace::copyInConcretes() { + for (MemoryMap::iterator it = objects.begin(), ie = objects.end(); + it != ie; ++it) { + const MemoryObject *mo = it->first; + + if (!mo->isUserSpecified) { + const ObjectState *os = it->second; + uint8_t *address = (uint8_t*) (unsigned long) mo->address; + + if (memcmp(address, os->concreteStore, mo->size)!=0) { + if (os->readOnly) { + return false; + } else { + ObjectState *wos = getWriteable(mo, os); + memcpy(wos->concreteStore, address, mo->size); + } + } + } + } + + return true; +} + +/***/ + +bool MemoryObjectLT::operator()(const MemoryObject *a, const MemoryObject *b) const { + return a->address < b->address; +} + diff --git a/lib/Core/AddressSpace.h b/lib/Core/AddressSpace.h new file mode 100644 index 00000000..a281714c --- /dev/null +++ b/lib/Core/AddressSpace.h @@ -0,0 +1,131 @@ +//===-- AddressSpace.h ------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_ADDRESSSPACE_H +#define KLEE_ADDRESSSPACE_H + +#include "ObjectHolder.h" + +#include "klee/Expr.h" +#include "klee/Internal/ADT/ImmutableMap.h" + +namespace klee { + class ExecutionState; + class MemoryObject; + class ObjectState; + class TimingSolver; + + template<class T> class ref; + + typedef std::pair<const MemoryObject*, const ObjectState*> ObjectPair; + typedef std::vector<ObjectPair> ResolutionList; + + /// Function object ordering MemoryObject's by address. + struct MemoryObjectLT { + bool operator()(const MemoryObject *a, const MemoryObject *b) const; + }; + + typedef ImmutableMap<const MemoryObject*, ObjectHolder, MemoryObjectLT> MemoryMap; + + class AddressSpace { + private: + /// Epoch counter used to control ownership of objects. + mutable unsigned cowKey; + + /// Unsupported, use copy constructor + AddressSpace &operator=(const AddressSpace&); + + public: + /// The MemoryObject -> ObjectState map that constitutes the + /// address space. + /// + /// The set of objects where o->copyOnWriteOwner == cowKey are the + /// objects that we own. + /// + /// \invariant forall o in objects, o->copyOnWriteOwner <= cowKey + MemoryMap objects; + + public: + AddressSpace() : cowKey(1) {} + AddressSpace(const AddressSpace &b) : cowKey(++b.cowKey), objects(b.objects) { } + ~AddressSpace() {} + + /// Resolve address to an ObjectPair in result. + /// \return true iff an object was found. + bool resolveOne(uint64_t address, + ObjectPair &result); + + /// Resolve address to an ObjectPair in result. + /// + /// \param state The state this address space is part of. + /// \param solver A solver used to determine possible + /// locations of the \a address. + /// \param address The address to search for. + /// \param[out] result An ObjectPair this address can resolve to + /// (when returning true). + /// \return true iff an object was found at \a address. + bool resolveOne(ExecutionState &state, + TimingSolver *solver, + ref<Expr> address, + ObjectPair &result, + bool &success); + + /// Resolve address to a list of ObjectPairs it can point to. If + /// maxResolutions is non-zero then no more than that many pairs + /// will be returned. + /// + /// \return true iff the resolution is incomplete (maxResolutions + /// is non-zero and the search terminated early, or a query timed out). + bool resolve(ExecutionState &state, + TimingSolver *solver, + ref<Expr> address, + ResolutionList &rl, + unsigned maxResolutions=0, + double timeout=0.); + + /***/ + + /// Add a binding to the address space. + void bindObject(const MemoryObject *mo, ObjectState *os); + + /// Remove a binding from the address space. + void unbindObject(const MemoryObject *mo); + + /// Lookup a binding from a MemoryObject. + const ObjectState *findObject(const MemoryObject *mo) const; + + /// \brief Obtain an ObjectState suitable for writing. + /// + /// This returns a writeable object state, creating a new copy of + /// the given ObjectState if necessary. If the address space owns + /// the ObjectState then this routine effectively just strips the + /// const qualifier it. + /// + /// \param mo The MemoryObject to get a writeable ObjectState for. + /// \param os The current binding of the MemoryObject. + /// \return A writeable ObjectState (\a os or a copy). + ObjectState *getWriteable(const MemoryObject *mo, const ObjectState *os); + + /// Copy the concrete values of all managed ObjectStates into the + /// actual system memory location they were allocated at. + void copyOutConcretes(); + + /// Copy the concrete values of all managed ObjectStates back from + /// the actual system memory location they were allocated + /// at. ObjectStates will only be written to (and thus, + /// potentially copied) if the memory values are different from + /// the current concrete values. + /// + /// \retval true The copy succeeded. + /// \retval false The copy failed because a read-only object was modified. + bool copyInConcretes(); + }; +} // End klee namespace + +#endif diff --git a/lib/Core/CallPathManager.cpp b/lib/Core/CallPathManager.cpp new file mode 100644 index 00000000..d0a61b31 --- /dev/null +++ b/lib/Core/CallPathManager.cpp @@ -0,0 +1,103 @@ +//===-- CallPathManager.cpp -----------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CallPathManager.h" + +#include "klee/Statistics.h" + +#include <map> +#include <vector> +#include "llvm/Function.h" +#include "llvm/Support/Streams.h" + +using namespace llvm; +using namespace klee; + +/// + +CallPathNode::CallPathNode(CallPathNode *_parent, + Instruction *_callSite, + Function *_function) + : parent(_parent), + callSite(_callSite), + function(_function), + count(0) { +} + +void CallPathNode::print() { + llvm::cerr << " (Function: " << this->function->getName() << ", " + << "Callsite: " << callSite << ", " + << "Count: " << this->count << ")"; + if (parent && parent->callSite) { + llvm::cerr << ";\n"; + parent->print(); + } + else llvm::cerr << "\n"; +} + +/// + +CallPathManager::CallPathManager() : root(0, 0, 0) { +} + +CallPathManager::~CallPathManager() { + for (std::vector<CallPathNode*>::iterator it = paths.begin(), + ie = paths.end(); it != ie; ++it) + delete *it; +} + +void CallPathManager::getSummaryStatistics(CallSiteSummaryTable &results) { + results.clear(); + + for (std::vector<CallPathNode*>::iterator it = paths.begin(), + ie = paths.end(); it != ie; ++it) + (*it)->summaryStatistics = (*it)->statistics; + + // compute summary bottom up, while building result table + for (std::vector<CallPathNode*>::reverse_iterator it = paths.rbegin(), + ie = paths.rend(); it != ie; ++it) { + CallPathNode *cp = *it; + cp->parent->summaryStatistics += cp->summaryStatistics; + + CallSiteInfo &csi = results[cp->callSite][cp->function]; + csi.count += cp->count; + csi.statistics += cp->summaryStatistics; + } +} + + +CallPathNode *CallPathManager::computeCallPath(CallPathNode *parent, + Instruction *cs, + Function *f) { + for (CallPathNode *p=parent; p; p=p->parent) + if (cs==p->callSite && f==p->function) + return p; + + CallPathNode *cp = new CallPathNode(parent, cs, f); + paths.push_back(cp); + return cp; +} + +CallPathNode *CallPathManager::getCallPath(CallPathNode *parent, + Instruction *cs, + Function *f) { + std::pair<Instruction*,Function*> key(cs, f); + if (!parent) + parent = &root; + + CallPathNode::children_ty::iterator it = parent->children.find(key); + if (it==parent->children.end()) { + CallPathNode *cp = computeCallPath(parent, cs, f); + parent->children.insert(std::make_pair(key, cp)); + return cp; + } else { + return it->second; + } +} + diff --git a/lib/Core/CallPathManager.h b/lib/Core/CallPathManager.h new file mode 100644 index 00000000..2e16d72b --- /dev/null +++ b/lib/Core/CallPathManager.h @@ -0,0 +1,83 @@ +//===-- CallPathManager.h ---------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __UTIL_CALLPATHMANAGER_H__ +#define __UTIL_CALLPATHMANAGER_H__ + +#include "klee/Statistics.h" + +#include <map> +#include <vector> + +namespace llvm { + class Instruction; + class Function; +} + +namespace klee { + class StatisticRecord; + + struct CallSiteInfo { + unsigned count; + StatisticRecord statistics; + + public: + CallSiteInfo() : count(0) {} + }; + + typedef std::map<llvm::Instruction*, + std::map<llvm::Function*, CallSiteInfo> > CallSiteSummaryTable; + + class CallPathNode { + friend class CallPathManager; + + public: + typedef std::map<std::pair<llvm::Instruction*, + llvm::Function*>, CallPathNode*> children_ty; + + // form list of (callSite,function) path + CallPathNode *parent; + llvm::Instruction *callSite; + llvm::Function *function; + children_ty children; + + StatisticRecord statistics; + StatisticRecord summaryStatistics; + unsigned count; + + public: + CallPathNode(CallPathNode *parent, + llvm::Instruction *callSite, + llvm::Function *function); + + void print(); + }; + + class CallPathManager { + CallPathNode root; + std::vector<CallPathNode*> paths; + + private: + CallPathNode *computeCallPath(CallPathNode *parent, + llvm::Instruction *callSite, + llvm::Function *f); + + public: + CallPathManager(); + ~CallPathManager(); + + void getSummaryStatistics(CallSiteSummaryTable &result); + + CallPathNode *getCallPath(CallPathNode *parent, + llvm::Instruction *callSite, + llvm::Function *f); + }; +} + +#endif diff --git a/lib/Core/Common.cpp b/lib/Core/Common.cpp new file mode 100644 index 00000000..479c4465 --- /dev/null +++ b/lib/Core/Common.cpp @@ -0,0 +1,110 @@ +//===-- Common.cpp --------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Common.h" + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <assert.h> +#include <string.h> + +#include <set> + +using namespace klee; + +FILE* klee::klee_warning_file = NULL; +FILE* klee::klee_message_file = NULL; + + +/* Prints a message/warning. + + If pfx is NULL, this is a regular message, and it's sent to + klee_message_file (messages.txt). Otherwise, it is sent to + klee_warning_file (warnings.txt). + + Iff onlyToFile is false, the message is also printed on stderr. +*/ +static void klee_vmessage(const char *pfx, bool onlyToFile, const char *msg, va_list ap) { + FILE *f = stderr; + if (!onlyToFile) { + fprintf(f, "KLEE: "); + if (pfx) fprintf(f, "%s: ", pfx); + vfprintf(f, msg, ap); + fprintf(f, "\n"); + fflush(f); + } + + if (pfx == NULL) + f = klee_message_file; + else f = klee_warning_file; + + if (f) { + fprintf(f, "KLEE: "); + if (pfx) fprintf(f, "%s: ", pfx); + vfprintf(f, msg, ap); + fprintf(f, "\n"); + fflush(f); + } +} + + +void klee::klee_message(const char *msg, ...) { + va_list ap; + va_start(ap, msg); + klee_vmessage(NULL, false, msg, ap); + va_end(ap); +} + +/* Message to be written only to file */ +void klee::klee_message_to_file(const char *msg, ...) { + va_list ap; + va_start(ap, msg); + klee_vmessage(NULL, true, msg, ap); + va_end(ap); +} + +void klee::klee_error(const char *msg, ...) { + va_list ap; + va_start(ap, msg); + klee_vmessage("ERROR", false, msg, ap); + va_end(ap); + exit(1); +} + +void klee::klee_warning(const char *msg, ...) { + va_list ap; + va_start(ap, msg); + klee_vmessage("WARNING", false, msg, ap); + va_end(ap); +} + + +/* Prints a warning once per message. */ +void klee::klee_warning_once(const void *id, const char *msg, ...) { + static std::set< std::pair<const void*, const char*> > keys; + std::pair<const void*, const char*> key; + + + /* "calling external" messages contain the actual arguments with + which we called the external function, so we need to ignore them + when computing the key. */ + if (strncmp(msg, "calling external", strlen("calling external")) != 0) + key = std::make_pair(id, msg); + else key = std::make_pair(id, "calling external"); + + if (!keys.count(key)) { + keys.insert(key); + + va_list ap; + va_start(ap, msg); + klee_vmessage("WARNING", false, msg, ap); + va_end(ap); + } +} diff --git a/lib/Core/Common.h b/lib/Core/Common.h new file mode 100644 index 00000000..ce05b536 --- /dev/null +++ b/lib/Core/Common.h @@ -0,0 +1,56 @@ +//===-- Common.h ------------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __KLEE_COMMON_H__ +#define __KLEE_COMMON_H__ + +#ifdef __CYGWIN__ +#ifndef WINDOWS +#define WINDOWS +#endif +#endif + +#include <stdio.h> + +// XXX ugh +namespace klee { + class Solver; + + extern FILE* klee_warning_file; + extern FILE* klee_message_file; + + /// Print "KLEE: ERROR" followed by the msg in printf format and a + /// newline on stderr and to warnings.txt, then exit with an error. + void klee_error(const char *msg, ...) + __attribute__ ((format (printf, 1, 2), noreturn)); + + /// Print "KLEE: " followed by the msg in printf format and a + /// newline on stderr and to messages.txt. + void klee_message(const char *msg, ...) + __attribute__ ((format (printf, 1, 2))); + + /// Print "KLEE: " followed by the msg in printf format and a + /// newline to messages.txt. + void klee_message_to_file(const char *msg, ...) + __attribute__ ((format (printf, 1, 2))); + + /// Print "KLEE: WARNING" followed by the msg in printf format and a + /// newline on stderr and to warnings.txt. + void klee_warning(const char *msg, ...) + __attribute__ ((format (printf, 1, 2))); + + /// Print "KLEE: WARNING" followed by the msg in printf format and a + /// newline on stderr and to warnings.txt. However, the warning is only + /// printed once for each unique (id, msg) pair (as pointers). + void klee_warning_once(const void *id, + const char *msg, ...) + __attribute__ ((format (printf, 2, 3))); +} + +#endif /* __KLEE_COMMON_H__ */ diff --git a/lib/Core/CoreStats.cpp b/lib/Core/CoreStats.cpp new file mode 100644 index 00000000..ca2ef1c9 --- /dev/null +++ b/lib/Core/CoreStats.cpp @@ -0,0 +1,29 @@ +//===-- CoreStats.cpp -----------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CoreStats.h" + +using namespace klee; + +Statistic stats::allocations("Allocations", "Alloc"); +Statistic stats::coveredInstructions("CoveredInstructions", "Icov"); +Statistic stats::falseBranches("FalseBranches", "Bf"); +Statistic stats::forkTime("ForkTime", "Ftime"); +Statistic stats::forks("Forks", "Forks"); +Statistic stats::instructionRealTime("InstructionRealTimes", "Ireal"); +Statistic stats::instructionTime("InstructionTimes", "Itime"); +Statistic stats::instructions("Instructions", "I"); +Statistic stats::minDistToReturn("MinDistToReturn", "Rdist"); +Statistic stats::minDistToUncovered("MinDistToUncovered", "UCdist"); +Statistic stats::reachableUncovered("ReachableUncovered", "IuncovReach"); +Statistic stats::resolveTime("ResolveTime", "Rtime"); +Statistic stats::solverTime("SolverTime", "Stime"); +Statistic stats::states("States", "States"); +Statistic stats::trueBranches("TrueBranches", "Bt"); +Statistic stats::uncoveredInstructions("UncoveredInstructions", "Iuncov"); diff --git a/lib/Core/CoreStats.h b/lib/Core/CoreStats.h new file mode 100644 index 00000000..09845a89 --- /dev/null +++ b/lib/Core/CoreStats.h @@ -0,0 +1,53 @@ +//===-- CoreStats.h ---------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_CORESTATS_H +#define KLEE_CORESTATS_H + +#include "klee/Statistic.h" + +namespace klee { +namespace stats { + + extern Statistic allocations; + extern Statistic resolveTime; + extern Statistic instructions; + extern Statistic instructionTime; + extern Statistic instructionRealTime; + extern Statistic coveredInstructions; + extern Statistic uncoveredInstructions; + extern Statistic trueBranches; + extern Statistic falseBranches; + extern Statistic forkTime; + extern Statistic solverTime; + + /// The number of process forks. + extern Statistic forks; + + /// Number of states, this is a "fake" statistic used by istats, it + /// isn't normally up-to-date. + extern Statistic states; + + /// Instruction level statistic for tracking number of reachable + /// uncovered instructions. + extern Statistic reachableUncovered; + + /// Instruction level statistic tracking the minimum intraprocedural + /// distance to an uncovered instruction; this is only periodically + /// updated. + extern Statistic minDistToUncovered; + + /// Instruction level statistic tracking the minimum intraprocedural + /// distance to a function return. + extern Statistic minDistToReturn; + +} +} + +#endif diff --git a/lib/Core/ExecutionState.cpp b/lib/Core/ExecutionState.cpp new file mode 100644 index 00000000..dd6d4647 --- /dev/null +++ b/lib/Core/ExecutionState.cpp @@ -0,0 +1,417 @@ +//===-- ExecutionState.cpp ------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/ExecutionState.h" + +#include "klee/Internal/Module/Cell.h" +#include "klee/Internal/Module/InstructionInfoTable.h" +#include "klee/Internal/Module/KInstruction.h" +#include "klee/Internal/Module/KModule.h" + +#include "klee/Expr.h" + +#include "Memory.h" + +#include "llvm/Function.h" +#include "llvm/Support/CommandLine.h" + +#include <iostream> +#include <cassert> +#include <map> +#include <set> +#include <stdarg.h> + +using namespace llvm; +using namespace klee; + +namespace { + cl::opt<bool> + DebugLogStateMerge("debug-log-state-merge"); +} + +/***/ + +StackFrame::StackFrame(KInstIterator _caller, KFunction *_kf) + : caller(_caller), kf(_kf), callPathNode(0), + minDistToUncoveredOnReturn(0), varargs(0) { + locals = new Cell[kf->numRegisters]; +} + +StackFrame::StackFrame(const StackFrame &s) + : caller(s.caller), + kf(s.kf), + callPathNode(s.callPathNode), + allocas(s.allocas), + minDistToUncoveredOnReturn(s.minDistToUncoveredOnReturn), + varargs(s.varargs) { + locals = new Cell[s.kf->numRegisters]; + for (unsigned i=0; i<s.kf->numRegisters; i++) + locals[i] = s.locals[i]; +} + +StackFrame::~StackFrame() { + delete[] locals; +} + +/***/ + +ExecutionState::ExecutionState(KFunction *kf) + : fakeState(false), + underConstrained(false), + depth(0), + pc(kf->instructions), + prevPC(pc), + queryCost(0.), + weight(1), + instsSinceCovNew(0), + coveredNew(false), + forkDisabled(false), + ptreeNode(0) { + pushFrame(0, kf); +} + +ExecutionState::ExecutionState(const std::vector<ref<Expr> > &assumptions) + : fakeState(true), + underConstrained(false), + constraints(assumptions), + queryCost(0.), + ptreeNode(0) { +} + +ExecutionState::~ExecutionState() { + while (!stack.empty()) popFrame(); +} + +ExecutionState *ExecutionState::branch() { + depth++; + + ExecutionState *falseState = new ExecutionState(*this); + falseState->coveredNew = false; + falseState->coveredLines.clear(); + + weight *= .5; + falseState->weight -= weight; + + return falseState; +} + +void ExecutionState::pushFrame(KInstIterator caller, KFunction *kf) { + stack.push_back(StackFrame(caller,kf)); +} + +void ExecutionState::popFrame() { + StackFrame &sf = stack.back(); + for (std::vector<const MemoryObject*>::iterator it = sf.allocas.begin(), + ie = sf.allocas.end(); it != ie; ++it) + addressSpace.unbindObject(*it); + stack.pop_back(); +} + +/// + +std::string ExecutionState::getFnAlias(std::string fn) { + std::map < std::string, std::string >::iterator it = fnAliases.find(fn); + if (it != fnAliases.end()) + return it->second; + else return ""; +} + +void ExecutionState::addFnAlias(std::string old_fn, std::string new_fn) { + fnAliases[old_fn] = new_fn; +} + +void ExecutionState::removeFnAlias(std::string fn) { + fnAliases.erase(fn); +} + +/**/ + +std::ostream &klee::operator<<(std::ostream &os, const MemoryMap &mm) { + os << "{"; + MemoryMap::iterator it = mm.begin(); + MemoryMap::iterator ie = mm.end(); + if (it!=ie) { + os << "MO" << it->first->id << ":" << it->second; + for (++it; it!=ie; ++it) + os << ", MO" << it->first->id << ":" << it->second; + } + os << "}"; + return os; +} + +bool ExecutionState::merge(const ExecutionState &b) { + if (DebugLogStateMerge) + llvm::cerr << "-- attempting merge of A:" + << this << " with B:" << &b << "--\n"; + if (pc != b.pc) + return false; + + // XXX is it even possible for these to differ? does it matter? probably + // implies difference in object states? + if (symbolics!=b.symbolics) + return false; + + { + std::vector<StackFrame>::const_iterator itA = stack.begin(); + std::vector<StackFrame>::const_iterator itB = b.stack.begin(); + while (itA!=stack.end() && itB!=b.stack.end()) { + // XXX vaargs? + if (itA->caller!=itB->caller || itA->kf!=itB->kf) + return false; + ++itA; + ++itB; + } + if (itA!=stack.end() || itB!=b.stack.end()) + return false; + } + + std::set< ref<Expr> > aConstraints(constraints.begin(), constraints.end()); + std::set< ref<Expr> > bConstraints(b.constraints.begin(), + b.constraints.end()); + std::set< ref<Expr> > commonConstraints, aSuffix, bSuffix; + std::set_intersection(aConstraints.begin(), aConstraints.end(), + bConstraints.begin(), bConstraints.end(), + std::inserter(commonConstraints, commonConstraints.begin())); + std::set_difference(aConstraints.begin(), aConstraints.end(), + commonConstraints.begin(), commonConstraints.end(), + std::inserter(aSuffix, aSuffix.end())); + std::set_difference(bConstraints.begin(), bConstraints.end(), + commonConstraints.begin(), commonConstraints.end(), + std::inserter(bSuffix, bSuffix.end())); + if (DebugLogStateMerge) { + llvm::cerr << "\tconstraint prefix: ["; + for (std::set< ref<Expr> >::iterator it = commonConstraints.begin(), + ie = commonConstraints.end(); it != ie; ++it) + llvm::cerr << *it << ", "; + llvm::cerr << "]\n"; + llvm::cerr << "\tA suffix: ["; + for (std::set< ref<Expr> >::iterator it = aSuffix.begin(), + ie = aSuffix.end(); it != ie; ++it) + llvm::cerr << *it << ", "; + llvm::cerr << "]\n"; + llvm::cerr << "\tB suffix: ["; + for (std::set< ref<Expr> >::iterator it = bSuffix.begin(), + ie = bSuffix.end(); it != ie; ++it) + llvm::cerr << *it << ", "; + llvm::cerr << "]\n"; + } + + // We cannot merge if addresses would resolve differently in the + // states. This means: + // + // 1. Any objects created since the branch in either object must + // have been free'd. + // + // 2. We cannot have free'd any pre-existing object in one state + // and not the other + + if (DebugLogStateMerge) { + llvm::cerr << "\tchecking object states\n"; + llvm::cerr << "A: " << addressSpace.objects << "\n"; + llvm::cerr << "B: " << b.addressSpace.objects << "\n"; + } + + std::set<const MemoryObject*> mutated; + MemoryMap::iterator ai = addressSpace.objects.begin(); + MemoryMap::iterator bi = b.addressSpace.objects.begin(); + MemoryMap::iterator ae = addressSpace.objects.end(); + MemoryMap::iterator be = b.addressSpace.objects.end(); + for (; ai!=ae && bi!=be; ++ai, ++bi) { + if (ai->first != bi->first) { + if (DebugLogStateMerge) { + if (ai->first < bi->first) { + llvm::cerr << "\t\tB misses binding for: " << ai->first->id << "\n"; + } else { + llvm::cerr << "\t\tA misses binding for: " << bi->first->id << "\n"; + } + } + return false; + } + if (ai->second != bi->second) { + if (DebugLogStateMerge) + llvm::cerr << "\t\tmutated: " << ai->first->id << "\n"; + mutated.insert(ai->first); + } + } + if (ai!=ae || bi!=be) { + if (DebugLogStateMerge) + llvm::cerr << "\t\tmappings differ\n"; + return false; + } + + // merge stack + + ref<Expr> inA(1, Expr::Bool), inB(1, Expr::Bool); + for (std::set< ref<Expr> >::iterator it = aSuffix.begin(), + ie = aSuffix.end(); it != ie; ++it) + inA = AndExpr::create(inA, *it); + for (std::set< ref<Expr> >::iterator it = bSuffix.begin(), + ie = bSuffix.end(); it != ie; ++it) + inB = AndExpr::create(inB, *it); + + // XXX should we have a preference as to which predicate to use? + // it seems like it can make a difference, even though logically + // they must contradict each other and so inA => !inB + + std::vector<StackFrame>::iterator itA = stack.begin(); + std::vector<StackFrame>::const_iterator itB = b.stack.begin(); + for (; itA!=stack.end(); ++itA, ++itB) { + StackFrame &af = *itA; + const StackFrame &bf = *itB; + for (unsigned i=0; i<af.kf->numRegisters; i++) { + ref<Expr> &av = af.locals[i].value; + const ref<Expr> &bv = bf.locals[i].value; + if (av.isNull() || bv.isNull()) { + // if one is null then by implication (we are at same pc) + // we cannot reuse this local, so just ignore + } else { + av = SelectExpr::create(inA, av, bv); + } + } + } + + for (std::set<const MemoryObject*>::iterator it = mutated.begin(), + ie = mutated.end(); it != ie; ++it) { + const MemoryObject *mo = *it; + const ObjectState *os = addressSpace.findObject(mo); + const ObjectState *otherOS = b.addressSpace.findObject(mo); + assert(os && !os->readOnly && + "objects mutated but not writable in merging state"); + assert(otherOS); + + ObjectState *wos = addressSpace.getWriteable(mo, os); + for (unsigned i=0; i<mo->size; i++) { + ref<Expr> av = wos->read8(i); + ref<Expr> bv = otherOS->read8(i); + wos->write(i, SelectExpr::create(inA, av, bv)); + } + } + + constraints = ConstraintManager(); + for (std::set< ref<Expr> >::iterator it = commonConstraints.begin(), + ie = commonConstraints.end(); it != ie; ++it) + constraints.addConstraint(*it); + constraints.addConstraint(OrExpr::create(inA, inB)); + + return true; +} + +/**/ + +/* + Used for tainting: create a clone of os that we can revirt to with + the behavior that all constraints are preserved, but writes are + discarded. When we revirt it will be at the same address. + */ +ObjectState *ExecutionState::cloneObject(ObjectState *os, + MemoryObject *mo) { + MemoryMap::iterator it = shadowObjects.find(mo); + if (it != shadowObjects.end()) + assert(0 && "Cannot exist already!"); + + llvm::cerr << "DRE: Inserting a cloned object: " << mo << "\n"; + shadowObjects = shadowObjects.replace(std::make_pair(mo, os)); + os = new ObjectState(*os); + addressSpace.bindObject(mo, os); + return os; +} + +/***/ + + +ExecutionTraceEvent::ExecutionTraceEvent(ExecutionState& state, + KInstruction* ki) + : consecutiveCount(1) +{ + file = ki->info->file; + line = ki->info->line; + funcName = state.stack.back().kf->function->getName(); + stackDepth = state.stack.size(); +} + +bool ExecutionTraceEvent::ignoreMe() const { + // ignore all events occurring in certain pesky uclibc files: + if (file.find("libc/stdio/") != std::string::npos) { + return true; + } + + return false; +} + +void ExecutionTraceEvent::print(std::ostream &os) const { + os.width(stackDepth); + os << ' '; + printDetails(os); + os << ' ' << file << ':' << line << ':' << funcName; + if (consecutiveCount > 1) + os << " (" << consecutiveCount << "x)\n"; + else + os << '\n'; +} + + +bool ExecutionTraceEventEquals(ExecutionTraceEvent* e1, ExecutionTraceEvent* e2) { + // first see if their base class members are identical: + if (!((e1->file == e2->file) && + (e1->line == e2->line) && + (e1->funcName == e2->funcName))) + return false; + + // fairly ugly, but i'm no OOP master, so this is the way i'm + // doing it for now ... lemme know if there's a cleaner way: + BranchTraceEvent* be1 = dynamic_cast<BranchTraceEvent*>(e1); + BranchTraceEvent* be2 = dynamic_cast<BranchTraceEvent*>(e2); + if (be1 && be2) { + return ((be1->trueTaken == be2->trueTaken) && + (be1->canForkGoBothWays == be2->canForkGoBothWays)); + } + + // don't tolerate duplicates in anything else: + return false; +} + + +void BranchTraceEvent::printDetails(std::ostream &os) const { + os << "BRANCH " << (trueTaken ? "T" : "F") << ' ' << + (canForkGoBothWays ? "2-way" : "1-way"); +} + +void ExecutionTraceManager::addEvent(ExecutionTraceEvent* evt) { + // don't trace anything before __user_main, except for global events + if (!hasSeenUserMain) { + if (evt->funcName == "__user_main") { + hasSeenUserMain = true; + } + else if (evt->funcName != "global_def") { + return; + } + } + + // custom ignore events: + if (evt->ignoreMe()) + return; + + if (events.size() > 0) { + // compress consecutive duplicates: + ExecutionTraceEvent* last = events.back(); + if (ExecutionTraceEventEquals(last, evt)) { + last->consecutiveCount++; + return; + } + } + + events.push_back(evt); +} + +void ExecutionTraceManager::printAllEvents(std::ostream &os) const { + for (unsigned i = 0; i != events.size(); ++i) + events[i]->print(os); +} + +/***/ diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp new file mode 100644 index 00000000..d3409908 --- /dev/null +++ b/lib/Core/Executor.cpp @@ -0,0 +1,3260 @@ +//===-- Executor.cpp ------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Common.h" + +#include "Executor.h" + +#include "CoreStats.h" +#include "ExternalDispatcher.h" +#include "ImpliedValue.h" +#include "Memory.h" +#include "MemoryManager.h" +#include "PTree.h" +#include "Searcher.h" +#include "SeedInfo.h" +#include "SpecialFunctionHandler.h" +#include "StatsTracker.h" +#include "TimingSolver.h" +#include "UserSearcher.h" +#include "../Solver/SolverStats.h" + +#include "klee/ExecutionState.h" +#include "klee/Expr.h" +#include "klee/Interpreter.h" +#include "klee/Machine.h" +#include "klee/TimerStatIncrementer.h" +#include "klee/util/Assignment.h" +#include "klee/util/ExprPPrinter.h" +#include "klee/util/ExprUtil.h" +#include "klee/Config/config.h" +#include "klee/Internal/ADT/BOut.h" +#include "klee/Internal/ADT/RNG.h" +#include "klee/Internal/Module/Cell.h" +#include "klee/Internal/Module/InstructionInfoTable.h" +#include "klee/Internal/Module/KInstruction.h" +#include "klee/Internal/Module/KModule.h" +#include "klee/Internal/Support/FloatEvaluation.h" +#include "klee/Internal/System/Time.h" + +#include "llvm/Attributes.h" +#include "llvm/BasicBlock.h" +#include "llvm/Constants.h" +#include "llvm/Function.h" +#include "llvm/Instructions.h" +#include "llvm/IntrinsicInst.h" +#include "llvm/Module.h" +#include "llvm/Support/CallSite.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/GetElementPtrTypeIterator.h" +#include "llvm/System/Process.h" +#include "llvm/Target/TargetData.h" + +#include <cassert> +#include <algorithm> +#include <iostream> +#include <iomanip> +#include <fstream> +#include <sstream> +#include <vector> +#include <string> + +#include <sys/mman.h> + +#include <errno.h> +#include <cxxabi.h> + +using namespace llvm; +using namespace klee; + +// omg really hard to share cl opts across files ... +bool WriteTraces = false; + +namespace { + cl::opt<bool> + DumpStatesOnHalt("dump-states-on-halt", + cl::init(true)); + + cl::opt<bool> + NoPreferCex("no-prefer-cex", + cl::init(false)); + + cl::opt<bool> + UseAsmAddresses("use-asm-addresses", + cl::init(false)); + + cl::opt<bool> + RandomizeFork("randomize-fork", + cl::init(false)); + + cl::opt<bool> + AllowExternalSymCalls("allow-external-sym-calls", + cl::init(false)); + + cl::opt<bool> + DebugPrintInstructions("debug-print-instructions", + cl::desc("Print instructions during execution.")); + + cl::opt<bool> + DebugCheckForImpliedValues("debug-check-for-implied-values"); + + + cl::opt<bool> + SimplifySymIndices("simplify-sym-indices", + cl::init(false)); + + cl::opt<unsigned> + MaxSymArraySize("max-sym-array-size", + cl::init(0)); + + cl::opt<bool> + DebugValidateSolver("debug-validate-solver", + cl::init(false)); + + cl::opt<bool> + SuppressExternalWarnings("suppress-external-warnings"); + + cl::opt<bool> + AllExternalWarnings("all-external-warnings"); + + cl::opt<bool> + OnlyOutputStatesCoveringNew("only-output-states-covering-new", + cl::init(false)); + + cl::opt<bool> + AlwaysOutputSeeds("always-output-seeds", + cl::init(true)); + + cl::opt<bool> + UseFastCexSolver("use-fast-cex-solver", + cl::init(false)); + + cl::opt<bool> + UseIndependentSolver("use-independent-solver", + cl::init(true), + cl::desc("Use constraint independence")); + + cl::opt<bool> + EmitAllErrors("emit-all-errors", + cl::init(false), + cl::desc("Generate tests cases for all errors " + "(default=one per (error,instruction) pair)")); + + cl::opt<bool> + UseCexCache("use-cex-cache", + cl::init(true), + cl::desc("Use counterexample caching")); + + cl::opt<bool> + UseQueryLog("use-query-log", + cl::init(false)); + + cl::opt<bool> + UseQueryPCLog("use-query-pc-log", + cl::init(false)); + + cl::opt<bool> + UseSTPQueryPCLog("use-stp-query-pc-log", + cl::init(false)); + + cl::opt<bool> + NoExternals("no-externals", + cl::desc("Do not allow external functin calls")); + + cl::opt<bool> + UseCache("use-cache", + cl::init(true), + cl::desc("Use validity caching")); + + cl::opt<bool> + OnlyReplaySeeds("only-replay-seeds", + cl::desc("Discard states that do not have a seed.")); + + cl::opt<bool> + OnlySeed("only-seed", + cl::desc("Stop execution after seeding is done without doing regular search.")); + + cl::opt<bool> + AllowSeedExtension("allow-seed-extension", + cl::desc("Allow extra (unbound) values to become symbolic during seeding.")); + + cl::opt<bool> + ZeroSeedExtension("zero-seed-extension"); + + cl::opt<bool> + AllowSeedTruncation("allow-seed-truncation", + cl::desc("Allow smaller buffers than in seeds.")); + + cl::opt<bool> + NamedSeedMatching("named-seed-matching", + cl::desc("Use names to match symbolic objects to inputs.")); + + cl::opt<double> + MaxStaticForkPct("max-static-fork-pct", cl::init(1.)); + cl::opt<double> + MaxStaticSolvePct("max-static-solve-pct", cl::init(1.)); + cl::opt<double> + MaxStaticCPForkPct("max-static-cpfork-pct", cl::init(1.)); + cl::opt<double> + MaxStaticCPSolvePct("max-static-cpsolve-pct", cl::init(1.)); + + cl::opt<double> + MaxInstructionTime("max-instruction-time", + cl::desc("Only allow a single instruction to take this much time (default=0 (off))"), + cl::init(0)); + + cl::opt<double> + SeedTime("seed-time", + cl::desc("Amount of time to dedicate to seeds, before normal search (default=0 (off))"), + cl::init(0)); + + cl::opt<double> + MaxSTPTime("max-stp-time", + cl::desc("Maximum amount of time for a single query (default=120s)"), + cl::init(120.0)); + + cl::opt<unsigned int> + StopAfterNInstructions("stop-after-n-instructions", + cl::desc("Stop execution after specified number of instructions (0=off)"), + cl::init(0)); + + cl::opt<unsigned> + MaxForks("max-forks", + cl::desc("Only fork this many times (-1=off)"), + cl::init(~0u)); + + cl::opt<unsigned> + MaxDepth("max-depth", + cl::desc("Only allow this many symbolic branches (0=off)"), + cl::init(0)); + + cl::opt<unsigned> + MaxMemory("max-memory", + cl::desc("Refuse to fork when more above this about of memory (in MB, 0=off)"), + cl::init(0)); + + cl::opt<bool> + MaxMemoryInhibit("max-memory-inhibit", + cl::desc("Inhibit forking at memory cap (vs. random terminat)"), + cl::init(true)); + + // use 'external storage' because also needed by tools/klee/main.cpp + cl::opt<bool, true> + WriteTracesProxy("write-traces", + cl::desc("Write .trace file for each terminated state"), + cl::location(WriteTraces), + cl::init(false)); + + cl::opt<bool> + UseForkedSTP("use-forked-stp", + cl::desc("Run STP in forked process")); +} + + +static void *theMMap = 0; +static unsigned theMMapSize = 0; + +namespace klee { + RNG theRNG; +} + +Solver *constructSolverChain(STPSolver *stpSolver, + std::string queryLogPath, + std::string stpQueryLogPath, + std::string queryPCLogPath, + std::string stpQueryPCLogPath) { + Solver *solver = stpSolver; + + if (UseSTPQueryPCLog) + solver = createPCLoggingSolver(solver, + stpQueryLogPath); + + if (UseFastCexSolver) + solver = createFastCexSolver(solver); + + if (UseCexCache) + solver = createCexCachingSolver(solver); + + if (UseCache) + solver = createCachingSolver(solver); + + if (UseIndependentSolver) + solver = createIndependentSolver(solver); + + if (DebugValidateSolver) + solver = createValidatingSolver(solver, stpSolver); + + if (UseQueryPCLog) + solver = createPCLoggingSolver(solver, + queryPCLogPath); + + return solver; +} + +Executor::Executor(const InterpreterOptions &opts, + InterpreterHandler *ih) + : Interpreter(opts), + kmodule(0), + interpreterHandler(ih), + searcher(0), + externalDispatcher(new ExternalDispatcher()), + statsTracker(0), + pathWriter(0), + symPathWriter(0), + specialFunctionHandler(0), + processTree(0), + replayOut(0), + replayPath(0), + usingSeeds(0), + atMemoryLimit(false), + inhibitForking(false), + haltExecution(false), + ivcEnabled(false), + stpTimeout(std::min(MaxSTPTime,MaxInstructionTime)) { + STPSolver *stpSolver = new STPSolver(UseForkedSTP); + Solver *solver = + constructSolverChain(stpSolver, + interpreterHandler->getOutputFilename("queries.qlog"), + interpreterHandler->getOutputFilename("stp-queries.qlog"), + interpreterHandler->getOutputFilename("queries.pc"), + interpreterHandler->getOutputFilename("stp-queries.pc")); + + this->solver = new TimingSolver(solver, stpSolver); + + memory = new MemoryManager(); +} + + +const Module *Executor::setModule(llvm::Module *module, + const ModuleOptions &opts) { + assert(!kmodule && module && "can only register one module"); // XXX gross + + kmodule = new KModule(module); + + specialFunctionHandler = new SpecialFunctionHandler(*this); + + specialFunctionHandler->prepare(); + kmodule->prepare(opts, interpreterHandler); + specialFunctionHandler->bind(); + + if (StatsTracker::useStatistics()) { + statsTracker = + new StatsTracker(*this, + interpreterHandler->getOutputFilename("assembly.ll"), + userSearcherRequiresMD2U()); + } + + return module; +} + +Executor::~Executor() { + delete memory; + delete externalDispatcher; + if (processTree) + delete processTree; + if (specialFunctionHandler) + delete specialFunctionHandler; + if (statsTracker) + delete statsTracker; + delete solver; + delete kmodule; +} + +/***/ + +void Executor::initializeGlobalObject(ExecutionState &state, ObjectState *os, + Constant *c, + unsigned offset) { + TargetData *targetData = kmodule->targetData; + if (ConstantVector *cp = dyn_cast<ConstantVector>(c)) { + unsigned elementSize = + targetData->getTypeStoreSize(cp->getType()->getElementType()); + for (unsigned i=0, e=cp->getNumOperands(); i != e; ++i) + initializeGlobalObject(state, os, cp->getOperand(i), + offset + i*elementSize); + } else if (isa<ConstantAggregateZero>(c)) { + unsigned i, size = targetData->getTypeStoreSize(c->getType()); + for (i=0; i<size; i++) + os->write8(offset+i, (uint8_t) 0); + } else if (ConstantArray *ca = dyn_cast<ConstantArray>(c)) { + unsigned elementSize = + targetData->getTypeStoreSize(ca->getType()->getElementType()); + for (unsigned i=0, e=ca->getNumOperands(); i != e; ++i) + initializeGlobalObject(state, os, ca->getOperand(i), + offset + i*elementSize); + } else if (ConstantStruct *cs = dyn_cast<ConstantStruct>(c)) { + const StructLayout *sl = + targetData->getStructLayout(cast<StructType>(cs->getType())); + for (unsigned i=0, e=cs->getNumOperands(); i != e; ++i) + initializeGlobalObject(state, os, cs->getOperand(i), + offset + sl->getElementOffset(i)); + } else { + os->write(offset, evalConstant(c)); + } +} + +MemoryObject * Executor::addExternalObject(ExecutionState &state, + void *addr, unsigned size, + bool isReadOnly) { + MemoryObject *mo = memory->allocateFixed((uint64_t) (unsigned long) addr, + size, 0); + ObjectState *os = bindObjectInState(state, mo, false); + for(unsigned i = 0; i < size; i++) + os->write8(i, ((uint8_t*)addr)[i]); + if(isReadOnly) + os->setReadOnly(true); + return mo; +} + +void Executor::initializeGlobals(ExecutionState &state) { + Module *m = kmodule->module; + + if (m->getModuleInlineAsm() != "") + klee_warning("executable has module level assembly (ignoring)"); + + assert(m->lib_begin() == m->lib_end() && + "XXX do not support dependent libraries"); + + // represent function globals using the address of the actual llvm function + // object. given that we use malloc to allocate memory in states this also + // ensures that we won't conflict. we don't need to allocate a memory object + // since reading/writing via a function pointer is unsupported anyway. + for (Module::iterator i = m->begin(), ie = m->end(); i != ie; ++i) { + Function *f = i; + ref<Expr> addr(0); + + // If the symbol has external weak linkage then it is implicitly + // not defined in this module; if it isn't resolvable then it + // should be null. + if (f->hasExternalWeakLinkage() && + !externalDispatcher->resolveSymbol(f->getName())) { + addr = Expr::createPointer(0); + } else { + addr = Expr::createPointer((unsigned long) (void*) f); + legalFunctions.insert(f); + } + + globalAddresses.insert(std::make_pair(f, addr)); + } + + // Disabled, we don't want to promote use of live externals. +#ifdef HAVE_CTYPE_EXTERNALS +#ifndef WINDOWS +#ifndef DARWIN + /* From /usr/include/errno.h: it [errno] is a per-thread variable. */ + int *errno_addr = __errno_location(); + addExternalObject(state, (void *)errno_addr, sizeof *errno_addr, false); + + /* from /usr/include/ctype.h: + These point into arrays of 384, so they can be indexed by any `unsigned + char' value [0,255]; by EOF (-1); or by any `signed char' value + [-128,-1). ISO C requires that the ctype functions work for `unsigned */ + const uint16_t **addr = __ctype_b_loc(); + addExternalObject(state, (void *)(*addr-128), + 384 * sizeof **addr, true); + addExternalObject(state, addr, 4, true); + + const int32_t **lower_addr = __ctype_tolower_loc(); + addExternalObject(state, (void *)(*lower_addr-128), + 384 * sizeof **lower_addr, true); + addExternalObject(state, lower_addr, 4, true); + + const int32_t **upper_addr = __ctype_toupper_loc(); + addExternalObject(state, (void *)(*upper_addr-128), + 384 * sizeof **upper_addr, true); + addExternalObject(state, upper_addr, 4, true); +#endif +#endif +#endif + + // allocate and initialize globals, done in two passes since we may + // need address of a global in order to initialize some other one. + + // allocate memory objects for all globals + for (Module::const_global_iterator i = m->global_begin(), + e = m->global_end(); + i != e; ++i) { + if (i->isDeclaration()) { + // FIXME: We have no general way of handling unknown external + // symbols. If we really cared about making external stuff work + // better we could support user definition, or use the EXE style + // hack where we check the object file information. + + const Type *ty = i->getType()->getElementType(); + const std::string &name = i->getName(); + uint64_t size = kmodule->targetData->getTypeStoreSize(ty); + + // XXX - DWD - hardcode some things until we decide how to fix. +#ifndef WINDOWS + if (name == "_ZTVN10__cxxabiv117__class_type_infoE") { + size = 0x2C; + } else if (name == "_ZTVN10__cxxabiv120__si_class_type_infoE") { + size = 0x2C; + } else if (name == "_ZTVN10__cxxabiv121__vmi_class_type_infoE") { + size = 0x2C; + } +#endif + + if (size == 0) { + llvm::cerr << "Unable to find size for global variable: " << i->getName() + << " (use will result in out of bounds access)\n"; + } + + MemoryObject *mo = memory->allocate(size, false, true, i); + ObjectState *os = bindObjectInState(state, mo, false); + globalObjects.insert(std::make_pair(i, mo)); + globalAddresses.insert(std::make_pair(i, mo->getBaseExpr())); + + // Program already running = object already initialized. Read + // concrete value and write it to our copy. + if (size) { + void *addr; + if (name=="__dso_handle") { + extern void *__dso_handle __attribute__ ((__weak__)); + addr = &__dso_handle; // wtf ? + } else { + addr = externalDispatcher->resolveSymbol(name); + } + if (!addr) + klee_error("unable to load symbol(%s) while initializing globals.", + name.c_str()); + + for (unsigned offset=0; offset<mo->size; offset++) + os->write8(offset, ((unsigned char*)addr)[offset]); + } + } else { + const std::string &name = i->getName(); + const Type *ty = i->getType()->getElementType(); + uint64_t size = kmodule->targetData->getTypeStoreSize(ty); + MemoryObject *mo = 0; + + if (UseAsmAddresses && name[0]=='\01') { + char *end; + uint64_t address = ::strtoll(name.c_str()+1, &end, 0); + + if (end && *end == '\0') { + klee_message("NOTE: allocated global at asm specified address: %#08llx" + " (%llu bytes)", + address, size); + mo = memory->allocateFixed(address, size, &*i); + mo->isUserSpecified = true; // XXX hack; + } + } + + if (!mo) + mo = memory->allocate(size, false, true, &*i); + assert(mo && "out of memory"); + ObjectState *os = bindObjectInState(state, mo, false); + globalObjects.insert(std::make_pair(i, mo)); + globalAddresses.insert(std::make_pair(i, mo->getBaseExpr())); + + if (!i->hasInitializer()) + os->initializeToRandom(); + } + } + + // link aliases to their definitions (if bound) + for (Module::alias_iterator i = m->alias_begin(), ie = m->alias_end(); + i != ie; ++i) { + // Map the alias to its aliasee's address. This works because we have + // addresses for everything, even undefined functions. + globalAddresses.insert(std::make_pair(i, evalConstant(i->getAliasee()))); + } + + // once all objects are allocated, do the actual initialization + for (Module::const_global_iterator i = m->global_begin(), + e = m->global_end(); + i != e; ++i) { + if (i->hasInitializer()) { + MemoryObject *mo = globalObjects.find(i)->second; + const ObjectState *os = state.addressSpace.findObject(mo); + assert(os); + ObjectState *wos = state.addressSpace.getWriteable(mo, os); + + initializeGlobalObject(state, wos, i->getInitializer(), 0); + // if(i->isConstant()) os->setReadOnly(true); + } + } +} + +void Executor::branch(ExecutionState &state, + const std::vector< ref<Expr> > &conditions, + std::vector<ExecutionState*> &result) { + TimerStatIncrementer timer(stats::forkTime); + unsigned N = conditions.size(); + assert(N); + + stats::forks += N-1; + + // XXX do proper balance or keep random? + result.push_back(&state); + for (unsigned i=1; i<N; ++i) { + ExecutionState *es = result[theRNG.getInt32() % i]; + ExecutionState *ns = es->branch(); + addedStates.insert(ns); + result.push_back(ns); + es->ptreeNode->data = 0; + std::pair<PTree::Node*,PTree::Node*> res = + processTree->split(es->ptreeNode, ns, es); + ns->ptreeNode = res.first; + es->ptreeNode = res.second; + } + + // If necessary redistribute seeds to match conditions, killing + // states if necessary due to OnlyReplaySeeds (inefficient but + // simple). + + std::map< ExecutionState*, std::vector<SeedInfo> >::iterator it = + seedMap.find(&state); + if (it != seedMap.end()) { + std::vector<SeedInfo> seeds = it->second; + seedMap.erase(it); + + // Assume each seed only satisfies one condition (necessarily true + // when conditions are mutually exclusive and their conjunction is + // a tautology). + for (std::vector<SeedInfo>::iterator siit = seeds.begin(), + siie = seeds.end(); siit != siie; ++siit) { + unsigned i; + for (i=0; i<N; ++i) { + ref<Expr> res; + bool success = + solver->getValue(state, siit->assignment.evaluate(conditions[i]), + res); + assert(success && "FIXME: Unhandled solver failure"); + if (res.getConstantValue()) + break; + } + + // If we didn't find a satisfying condition randomly pick one + // (the seed will be patched). + if (i==N) + i = theRNG.getInt32() % N; + + seedMap[result[i]].push_back(*siit); + } + + if (OnlyReplaySeeds) { + for (unsigned i=0; i<N; ++i) { + if (!seedMap.count(result[i])) { + terminateState(*result[i]); + result[i] = NULL; + } + } + } + } + + for (unsigned i=0; i<N; ++i) + if (result[i]) + addConstraint(*result[i], conditions[i]); +} + +Executor::StatePair +Executor::fork(ExecutionState ¤t, ref<Expr> condition, bool isInternal) { + Solver::Validity res; + std::map< ExecutionState*, std::vector<SeedInfo> >::iterator it = + seedMap.find(¤t); + bool isSeeding = it != seedMap.end(); + + if (!isSeeding && + !condition.isConstant() && + (MaxStaticForkPct!=1. || MaxStaticSolvePct != 1. || + MaxStaticCPForkPct!=1. || MaxStaticCPSolvePct != 1.) && + statsTracker->elapsed() > 60.) { + StatisticManager &sm = *theStatisticManager; + CallPathNode *cpn = current.stack.back().callPathNode; + if ((MaxStaticForkPct<1. && + sm.getIndexedValue(stats::forks, sm.getIndex()) > + stats::forks*MaxStaticForkPct) || + (MaxStaticCPForkPct<1. && + cpn && (cpn->statistics.getValue(stats::forks) > + stats::forks*MaxStaticCPForkPct)) || + (MaxStaticSolvePct<1 && + sm.getIndexedValue(stats::solverTime, sm.getIndex()) > + stats::solverTime*MaxStaticSolvePct) || + (MaxStaticCPForkPct<1. && + cpn && (cpn->statistics.getValue(stats::solverTime) > + stats::solverTime*MaxStaticCPSolvePct))) { + ref<Expr> value; + bool success = solver->getValue(current, condition, value); + assert(success && "FIXME: Unhandled solver failure"); + addConstraint(current, EqExpr::create(value, condition)); + condition = value; + } + } + + double timeout = stpTimeout; + if (isSeeding) + timeout *= it->second.size(); + solver->setTimeout(timeout); + bool success = solver->evaluate(current, condition, res); + solver->setTimeout(0); + if (!success) { + current.pc = current.prevPC; + terminateStateEarly(current, "query timed out"); + return StatePair(0, 0); + } + + if (!isSeeding) { + if (replayPath && !isInternal) { + assert(replayPosition<replayPath->size() && + "ran out of branches in replay path mode"); + bool branch = (*replayPath)[replayPosition++]; + + if (res==Solver::True) { + assert(branch && "hit invalid branch in replay path mode"); + } else if (res==Solver::False) { + assert(!branch && "hit invalid branch in replay path mode"); + } else { + // add constraints + if(branch) { + res = Solver::True; + addConstraint(current, condition); + } else { + res = Solver::False; + addConstraint(current, Expr::createNot(condition)); + } + } + } else if (res==Solver::Unknown) { + assert(!replayOut && "in replay mode, only one branch can be true."); + + if ((MaxMemoryInhibit && atMemoryLimit) || + current.forkDisabled || + inhibitForking || + (MaxForks!=~0u && stats::forks >= MaxForks)) { + TimerStatIncrementer timer(stats::forkTime); + if (theRNG.getBool()) { + addConstraint(current, condition); + res = Solver::True; + } else { + addConstraint(current, Expr::createNot(condition)); + res = Solver::False; + } + } + } + } + + // Fix branch in only-replay-seed mode, if we don't have both true + // and false seeds. + if (isSeeding && + (current.forkDisabled || OnlyReplaySeeds) && + res == Solver::Unknown) { + bool trueSeed=false, falseSeed=false; + // Is seed extension still ok here? + for (std::vector<SeedInfo>::iterator siit = it->second.begin(), + siie = it->second.end(); siit != siie; ++siit) { + ref<Expr> res; + bool success = + solver->getValue(current, siit->assignment.evaluate(condition), res); + assert(success && "FIXME: Unhandled solver failure"); + if (res.isConstant()) { + if (res.getConstantValue()) { + trueSeed = true; + } else { + falseSeed = true; + } + if (trueSeed && falseSeed) + break; + } + } + if (!(trueSeed && falseSeed)) { + assert(trueSeed || falseSeed); + + res = trueSeed ? Solver::True : Solver::False; + addConstraint(current, trueSeed ? condition : Expr::createNot(condition)); + } + } + + + // XXX - even if the constraint is provable one way or the other we + // can probably benefit by adding this constraint and allowing it to + // reduce the other constraints. For example, if we do a binary + // search on a particular value, and then see a comparison against + // the value it has been fixed at, we should take this as a nice + // hint to just use the single constraint instead of all the binary + // search ones. If that makes sense. + if (res==Solver::True) { + if (!isInternal) { + if (pathWriter) { + current.pathOS << "1"; + } + } + + return StatePair(¤t, 0); + } else if (res==Solver::False) { + if (!isInternal) { + if (pathWriter) { + current.pathOS << "0"; + } + } + + return StatePair(0, ¤t); + } else { + TimerStatIncrementer timer(stats::forkTime); + ExecutionState *falseState, *trueState = ¤t; + + ++stats::forks; + + falseState = trueState->branch(); + addedStates.insert(falseState); + + if (RandomizeFork && theRNG.getBool()) + std::swap(trueState, falseState); + + if (it != seedMap.end()) { + std::vector<SeedInfo> seeds = it->second; + it->second.clear(); + std::vector<SeedInfo> &trueSeeds = seedMap[trueState]; + std::vector<SeedInfo> &falseSeeds = seedMap[falseState]; + for (std::vector<SeedInfo>::iterator siit = seeds.begin(), + siie = seeds.end(); siit != siie; ++siit) { + ref<Expr> res; + bool success = + solver->getValue(current, siit->assignment.evaluate(condition), res); + assert(success && "FIXME: Unhandled solver failure"); + if (res.getConstantValue()) { + trueSeeds.push_back(*siit); + } else { + falseSeeds.push_back(*siit); + } + } + + bool swapInfo = false; + if (trueSeeds.empty()) { + if (¤t == trueState) swapInfo = true; + seedMap.erase(trueState); + } + if (falseSeeds.empty()) { + if (¤t == falseState) swapInfo = true; + seedMap.erase(falseState); + } + if (swapInfo) { + std::swap(trueState->coveredNew, falseState->coveredNew); + std::swap(trueState->coveredLines, falseState->coveredLines); + } + } + + current.ptreeNode->data = 0; + std::pair<PTree::Node*, PTree::Node*> res = + processTree->split(current.ptreeNode, falseState, trueState); + falseState->ptreeNode = res.first; + trueState->ptreeNode = res.second; + + if (!isInternal) { + if (pathWriter) { + falseState->pathOS = pathWriter->open(current.pathOS); + trueState->pathOS << "1"; + falseState->pathOS << "0"; + } + if (symPathWriter) { + falseState->symPathOS = symPathWriter->open(current.symPathOS); + trueState->symPathOS << "1"; + falseState->symPathOS << "0"; + } + } + + addConstraint(*trueState, condition); + addConstraint(*falseState, Expr::createNot(condition)); + + // Kinda gross, do we even really still want this option? + if (MaxDepth && MaxDepth<=trueState->depth) { + terminateStateEarly(*trueState, "max-depth exceeded"); + terminateStateEarly(*falseState, "max-depth exceeded"); + return StatePair(0, 0); + } + + return StatePair(trueState, falseState); + } +} + +void Executor::addConstraint(ExecutionState &state, ref<Expr> condition) { + if (condition.isConstant()) { + assert(condition.getConstantValue() && + "attempt to add invalid constraint"); + return; + } + + // Check to see if this constraint violates seeds. + std::map< ExecutionState*, std::vector<SeedInfo> >::iterator it = + seedMap.find(&state); + if (it != seedMap.end()) { + bool warn = false; + for (std::vector<SeedInfo>::iterator siit = it->second.begin(), + siie = it->second.end(); siit != siie; ++siit) { + bool res; + bool success = + solver->mustBeFalse(state, siit->assignment.evaluate(condition), res); + assert(success && "FIXME: Unhandled solver failure"); + if (res) { + siit->patchSeed(state, condition, solver); + warn = true; + } + } + if (warn) + klee_warning("seeds patched for violating constraint"); + } + + state.addConstraint(condition); + if (ivcEnabled) + doImpliedValueConcretization(state, condition, ref<Expr>(1, Expr::Bool)); +} + +ref<Expr> Executor::evalConstant(Constant *c) { + if (llvm::ConstantExpr *ce = dyn_cast<llvm::ConstantExpr>(c)) { + return evalConstantExpr(ce); + } else { + if (const ConstantInt *ci = dyn_cast<ConstantInt>(c)) { + switch(ci->getBitWidth()) { + case 1: return ConstantExpr::create(ci->getZExtValue(), Expr::Bool); + case 8: return ConstantExpr::create(ci->getZExtValue(), Expr::Int8); + case 16: return ConstantExpr::create(ci->getZExtValue(), Expr::Int16); + case 32: return ConstantExpr::create(ci->getZExtValue(), Expr::Int32); + case 64: return ConstantExpr::create(ci->getZExtValue(), Expr::Int64); + default: + assert(0 && "XXX arbitrary bit width constants unhandled"); + } + } else if (const ConstantFP *cf = dyn_cast<ConstantFP>(c)) { + switch(cf->getType()->getTypeID()) { + case Type::FloatTyID: { + float f = cf->getValueAPF().convertToFloat(); + return ConstantExpr::create(floats::FloatAsUInt64(f), Expr::Int32); + } + case Type::DoubleTyID: { + double d = cf->getValueAPF().convertToDouble(); + return ConstantExpr::create(floats::DoubleAsUInt64(d), Expr::Int64); + } + case Type::X86_FP80TyID: { + // FIXME: This is really broken, but for now we just convert + // to a double. This isn't going to work at all in general, + // but we need support for wide constants. + APFloat apf = cf->getValueAPF(); + bool ignored; + APFloat::opStatus r = apf.convert(APFloat::IEEEdouble, + APFloat::rmNearestTiesToAway, + &ignored); + (void) r; + //assert(!(r & APFloat::opOverflow) && !(r & APFloat::opUnderflow) && + // "Overflow/underflow while converting from FP80 (x87) to 64-bit double"); + double d = apf.convertToDouble(); + return ConstantExpr::create(floats::DoubleAsUInt64(d), Expr::Int64); + } + default: + llvm::cerr << "Constant of type " << cf->getType()->getDescription() + << " not supported\n"; + llvm::cerr << "Constant used at "; + KConstant *kc = kmodule->getKConstant((Constant*) cf); + if (kc && kc->ki && kc->ki->info) + llvm::cerr << kc->ki->info->file << ":" << kc->ki->info->line << "\n"; + else llvm::cerr << "<unknown>\n"; + + assert(0 && "Arbitrary bit width floating point constants unsupported"); + } + } else if (const GlobalValue *gv = dyn_cast<GlobalValue>(c)) { + return globalAddresses.find(gv)->second; + } else if (isa<ConstantPointerNull>(c)) { + return Expr::createPointer(0); + } else if (isa<UndefValue>(c)) { + return ConstantExpr::create(0, Expr::getWidthForLLVMType(c->getType())); + } else { + // Constant{AggregateZero,Array,Struct,Vector} + assert(0 && "invalid argument to evalConstant()"); + } + } +} + +ref<Expr> Executor::eval(KInstruction *ki, + unsigned index, + ExecutionState &state) { + assert(index < ki->inst->getNumOperands()); + int vnumber = ki->operands[index]; + + // Determine if this is a constant or not. + if (vnumber < 0) { + unsigned index = -vnumber - 2; + Cell &c = kmodule->constantTable[index]; + return c.value; + } else { + unsigned index = vnumber; + StackFrame &sf = state.stack.back(); + Cell &c = sf.locals[index]; + return c.value; + } +} + +void Executor::bindLocal(KInstruction *target, ExecutionState &state, + ref<Expr> value) { + StackFrame &sf = state.stack.back(); + unsigned reg = target->dest; + Cell &c = sf.locals[reg]; + c.value = value; +} + +void Executor::bindArgument(KFunction *kf, unsigned index, + ExecutionState &state, ref<Expr> value) { + StackFrame &sf = state.stack.back(); + unsigned reg = kf->getArgRegister(index); + Cell &c = sf.locals[reg]; + c.value = value; +} + +ref<Expr> Executor::toUnique(const ExecutionState &state, + ref<Expr> &e) { + ref<Expr> result = e; + + if (!e.isConstant()) { + ref<Expr> value(0); + bool isTrue = false; + + solver->setTimeout(stpTimeout); + if (solver->getValue(state, e, value) && + solver->mustBeTrue(state, EqExpr::create(e, value), isTrue) && + isTrue) + result = value; + solver->setTimeout(0); + } + + return result; +} + + +/* Concretize the given expression, and return a possible constant value. + 'reason' is just a documentation string stating the reason for concretization. */ +ref<Expr> Executor::toConstant(ExecutionState &state, + ref<Expr> e, + const char *reason) { + e = state.constraints.simplifyExpr(e); + if (!e.isConstant()) { + ref<Expr> value; + bool success = solver->getValue(state, e, value); + assert(success && "FIXME: Unhandled solver failure"); + + std::ostringstream os; + os << "silently concretizing (reason: " << reason << ") expression " << e + << " to value " << value + << " (" << (*(state.pc)).info->file << ":" << (*(state.pc)).info->line << ")"; + + if (AllExternalWarnings) + klee_warning(reason, os.str().c_str()); + else + klee_warning_once(reason, "%s", os.str().c_str()); + + addConstraint(state, EqExpr::create(e, value)); + + return value; + } else { + return e; + } +} + +void Executor::executeGetValue(ExecutionState &state, + ref<Expr> e, + KInstruction *target) { + e = state.constraints.simplifyExpr(e); + std::map< ExecutionState*, std::vector<SeedInfo> >::iterator it = + seedMap.find(&state); + if (it==seedMap.end() || e.isConstant()) { + ref<Expr> value; + bool success = solver->getValue(state, e, value); + assert(success && "FIXME: Unhandled solver failure"); + bindLocal(target, state, value); + } else { + std::set< ref<Expr> > values; + for (std::vector<SeedInfo>::iterator siit = it->second.begin(), + siie = it->second.end(); siit != siie; ++siit) { + ref<Expr> value; + bool success = + solver->getValue(state, siit->assignment.evaluate(e), value); + assert(success && "FIXME: Unhandled solver failure"); + values.insert(value); + } + + std::vector< ref<Expr> > conditions; + for (std::set< ref<Expr> >::iterator vit = values.begin(), + vie = values.end(); vit != vie; ++vit) + conditions.push_back(EqExpr::create(e, *vit)); + + std::vector<ExecutionState*> branches; + branch(state, conditions, branches); + + std::vector<ExecutionState*>::iterator bit = branches.begin(); + for (std::set< ref<Expr> >::iterator vit = values.begin(), + vie = values.end(); vit != vie; ++vit) { + ExecutionState *es = *bit; + if (es) + bindLocal(target, *es, *vit); + ++bit; + } + } +} + +void Executor::stepInstruction(ExecutionState &state) { + if (DebugPrintInstructions) { + printFileLine(state, state.pc); + llvm::cerr << std::setw(10) << stats::instructions << " " << *state.pc->inst; + } + + if (statsTracker) + statsTracker->stepInstruction(state); + + ++stats::instructions; + state.prevPC = state.pc; + ++state.pc; + + if (stats::instructions==StopAfterNInstructions) + haltExecution = true; +} + +void Executor::executeCall(ExecutionState &state, + KInstruction *ki, + Function *f, + std::vector< ref<Expr> > &arguments) { + if (WriteTraces) { + // don't print out special debug stop point 'function' calls + if (f->getIntrinsicID() != Intrinsic::dbg_stoppoint) { + const std::string& calleeFuncName = f->getName(); + state.exeTraceMgr.addEvent(new FunctionCallTraceEvent(state, ki, calleeFuncName)); + } + } + + Instruction *i = ki->inst; + if (f && f->isDeclaration()) { + if (f!=kmodule->dbgStopPointFn) { // special case speed hack + switch(f->getIntrinsicID()) { + case Intrinsic::dbg_stoppoint: + case Intrinsic::dbg_region_start: + case Intrinsic::dbg_region_end: + case Intrinsic::dbg_func_start: + case Intrinsic::dbg_declare: + case Intrinsic::not_intrinsic: + // state may be destroyed by this call, cannot touch + callExternalFunction(state, ki, f, arguments); + break; + + // vararg is handled by caller and intrinsic lowering, + // see comment for ExecutionState::varargs + case Intrinsic::vastart: { + StackFrame &sf = state.stack.back(); + assert(sf.varargs && + "vastart called in function with no vararg object"); + executeMemoryOperation(state, true, arguments[0], + sf.varargs->getBaseExpr(), 0); + break; + } + case Intrinsic::vaend: // va_end is a noop for the interpreter + break; + + case Intrinsic::vacopy: // should be lowered + default: + klee_error("unknown intrinsic: %s", f->getName().c_str()); + } + } + + if (InvokeInst *ii = dyn_cast<InvokeInst>(i)) { + transferToBasicBlock(ii->getNormalDest(), i->getParent(), state); + } + } else { + // XXX not really happy about this reliance on prevPC but is ok I + // guess. This just done to avoid having to pass KInstIterator + // everywhere instead of the actual instruction, since we can't + // make a KInstIterator from just an instruction (unlike LLVM). + KFunction *kf = kmodule->functionMap[f]; + state.pushFrame(state.prevPC, kf); + state.pc = kf->instructions; + + if (statsTracker) + statsTracker->framePushed(state, &state.stack[state.stack.size()-2]); + + unsigned callingArgs = arguments.size(); + unsigned funcArgs = f->arg_size(); + if (!f->isVarArg()) { + if (callingArgs > funcArgs) { + klee_warning_once(f, "calling %s with extra arguments.", + f->getName().c_str()); + } else if (callingArgs < funcArgs) { + terminateStateOnError(state, "calling function with too few arguments", + "user.err"); + return; + } + } else { + if (callingArgs < funcArgs) { + terminateStateOnError(state, "calling function with too few arguments", + "user.err"); + return; + } + + StackFrame &sf = state.stack.back(); + unsigned size = 0; + for (unsigned i = funcArgs; i < callingArgs; i++) + size += Expr::getMinBytesForWidth(arguments[i].getWidth()); + + MemoryObject *mo = sf.varargs = memory->allocate(size, true, false, + state.prevPC->inst); + if (!mo) { + terminateStateOnExecError(state, "out of memory (varargs)"); + return; + } + ObjectState *os = bindObjectInState(state, mo, true); + unsigned offset = 0; + for (unsigned i = funcArgs; i < callingArgs; i++) { + // XXX: DRE: i think we bind memory objects here? + os->write(offset, arguments[i]); + offset += Expr::getMinBytesForWidth(arguments[i].getWidth()); + } + } + + unsigned numFormals = f->arg_size(); + for (unsigned i=0; i<numFormals; ++i) + bindArgument(kf, i, state, arguments[i]); + } +} + +void Executor::transferToBasicBlock(BasicBlock *dst, BasicBlock *src, + ExecutionState &state) { + // Note that in general phi nodes can reuse phi values from the same + // block but the incoming value is the eval() result *before* the + // execution of any phi nodes. this is pathological and doesn't + // really seem to occur, but just in case we run the PhiCleanerPass + // which makes sure this cannot happen and so it is safe to just + // eval things in order. The PhiCleanerPass also makes sure that all + // incoming blocks have the same order for each PHINode so we only + // have to compute the index once. + // + // With that done we simply set an index in the state so that PHI + // instructions know which argument to eval, set the pc, and continue. + + // XXX this lookup has to go ? + KFunction *kf = state.stack.back().kf; + unsigned entry = kf->basicBlockEntry[dst]; + state.pc = &kf->instructions[entry]; + if (state.pc->inst->getOpcode() == Instruction::PHI) { + PHINode *first = static_cast<PHINode*>(state.pc->inst); + state.incomingBBIndex = first->getBasicBlockIndex(src); + } +} + +void Executor::printFileLine(ExecutionState &state, KInstruction *ki) { + const InstructionInfo &ii = *ki->info; + if (ii.file != "") + llvm::cerr << " " << ii.file << ":" << ii.line << ":"; + else + llvm::cerr << " [no debug info]:"; +} + + +Function* Executor::getCalledFunction(CallSite &cs, ExecutionState &state) { + Function *f = cs.getCalledFunction(); + + if (f) { + std::string alias = state.getFnAlias(f->getName()); + if (alias != "") { + //llvm::cerr << f->getName() << "() is aliased with " << alias << "()\n"; + llvm::Module* currModule = kmodule->module; + Function* old_f = f; + f = currModule->getFunction(alias); + if (!f) { + llvm::cerr << "Function " << alias << "(), alias for " << old_f->getName() << " not found!\n"; + assert(f && "function alias not found"); + } + } + } + + return f; +} + + +void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { + Instruction *i = ki->inst; + switch (i->getOpcode()) { + // Control flow + case Instruction::Ret: { + ReturnInst *ri = cast<ReturnInst>(i); + KInstIterator kcaller = state.stack.back().caller; + Instruction *caller = kcaller ? kcaller->inst : 0; + bool isVoidReturn = (ri->getNumOperands() == 0); + ref<Expr> result(0,Expr::Bool); + + if (WriteTraces) { + state.exeTraceMgr.addEvent(new FunctionReturnTraceEvent(state, ki)); + } + + if (!isVoidReturn) { + result = eval(ki, 0, state); + } + + if (state.stack.size() <= 1) { + assert(!caller && "caller set on initial stack frame"); + terminateStateOnExit(state); + } else { + state.popFrame(); + + if (statsTracker) + statsTracker->framePopped(state); + + if (InvokeInst *ii = dyn_cast<InvokeInst>(caller)) { + transferToBasicBlock(ii->getNormalDest(), caller->getParent(), state); + } else { + state.pc = kcaller; + ++state.pc; + } + + if (!isVoidReturn) { + const Type *t = caller->getType(); + if (t != Type::VoidTy) { + // may need to do coercion due to bitcasts + Expr::Width from = result.getWidth(); + Expr::Width to = Expr::getWidthForLLVMType(t); + + if (from != to) { + CallSite cs = (isa<InvokeInst>(caller) ? CallSite(cast<InvokeInst>(caller)) : + CallSite(cast<CallInst>(caller))); + + // XXX need to check other param attrs ? + if (cs.paramHasAttr(0, llvm::Attribute::SExt)) { + result = SExtExpr::create(result, to); + } else { + result = ZExtExpr::create(result, to); + } + } + + bindLocal(kcaller, state, result); + } + } else { + // We check that the return value has no users instead of + // checking the type, since C defaults to returning int for + // undeclared functions. + if (!caller->use_empty()) { + terminateStateOnExecError(state, "return void when caller expected a result"); + } + } + } + break; + } + case Instruction::Unwind: { + for (;;) { + KInstruction *kcaller = state.stack.back().caller; + state.popFrame(); + + if (statsTracker) + statsTracker->framePopped(state); + + if (state.stack.empty()) { + terminateStateOnExecError(state, "unwind from initial stack frame"); + break; + } else { + Instruction *caller = kcaller->inst; + if (InvokeInst *ii = dyn_cast<InvokeInst>(caller)) { + transferToBasicBlock(ii->getUnwindDest(), caller->getParent(), state); + break; + } + } + } + break; + } + case Instruction::Br: { + BranchInst *bi = cast<BranchInst>(i); + if (bi->isUnconditional()) { + transferToBasicBlock(bi->getSuccessor(0), bi->getParent(), state); + } else { + // FIXME: Find a way that we don't have this hidden dependency. + assert(bi->getCondition() == bi->getOperand(0) && + "Wrong operand index!"); + ref<Expr> cond = eval(ki, 0, state); + Executor::StatePair branches = fork(state, cond, false); + + if (WriteTraces) { + bool isTwoWay = (branches.first && branches.second); + + if (branches.first) { + branches.first->exeTraceMgr.addEvent( + new BranchTraceEvent(state, ki, true, isTwoWay)); + } + + if (branches.second) { + branches.second->exeTraceMgr.addEvent( + new BranchTraceEvent(state, ki, false, isTwoWay)); + } + } + + // NOTE: There is a hidden dependency here, markBranchVisited + // requires that we still be in the context of the branch + // instruction (it reuses its statistic id). Should be cleaned + // up with convenient instruction specific data. + if (statsTracker && state.stack.back().kf->trackCoverage) + statsTracker->markBranchVisited(branches.first, branches.second); + + if (branches.first) + transferToBasicBlock(bi->getSuccessor(0), bi->getParent(), *branches.first); + if (branches.second) + transferToBasicBlock(bi->getSuccessor(1), bi->getParent(), *branches.second); + } + break; + } + case Instruction::Switch: { + SwitchInst *si = cast<SwitchInst>(i); + ref<Expr> cond = eval(ki, 0, state); + unsigned cases = si->getNumCases(); + BasicBlock *bb = si->getParent(); + + cond = toUnique(state, cond); + if (cond.isConstant()) { + // Somewhat gross to create these all the time, but fine till we + // switch to an internal rep. + ConstantInt *ci = ConstantInt::get(si->getCondition()->getType(), + cond.getConstantValue()); + unsigned index = si->findCaseValue(ci); + transferToBasicBlock(si->getSuccessor(index), si->getParent(), state); + } else { + std::map<BasicBlock*, ref<Expr> > targets; + ref<Expr> isDefault(1,Expr::Bool); + for (unsigned i=1; i<cases; ++i) { + ref<Expr> value = evalConstant(si->getCaseValue(i)); + ref<Expr> match = EqExpr::create(cond, value); + isDefault = AndExpr::create(isDefault, Expr::createNot(match)); + bool result; + bool success = solver->mayBeTrue(state, match, result); + assert(success && "FIXME: Unhandled solver failure"); + if (result) { + std::map<BasicBlock*, ref<Expr> >::iterator it = + targets.insert(std::make_pair(si->getSuccessor(i), + ref<Expr>(0,Expr::Bool))).first; + it->second = OrExpr::create(match, it->second); + } + } + bool res; + bool success = solver->mayBeTrue(state, isDefault, res); + assert(success && "FIXME: Unhandled solver failure"); + if (res) + targets.insert(std::make_pair(si->getSuccessor(0), isDefault)); + + std::vector< ref<Expr> > conditions; + for (std::map<BasicBlock*, ref<Expr> >::iterator it = + targets.begin(), ie = targets.end(); + it != ie; ++it) + conditions.push_back(it->second); + + std::vector<ExecutionState*> branches; + branch(state, conditions, branches); + + std::vector<ExecutionState*>::iterator bit = branches.begin(); + for (std::map<BasicBlock*, ref<Expr> >::iterator it = + targets.begin(), ie = targets.end(); + it != ie; ++it) { + ExecutionState *es = *bit; + if (es) + transferToBasicBlock(it->first, bb, *es); + ++bit; + } + } + break; + } + case Instruction::Unreachable: + // Note that this is not necessarily an internal bug, llvm will + // generate unreachable instructions in cases where it knows the + // program will crash. So it is effectively a SEGV or internal + // error. + terminateStateOnExecError(state, "reached \"unreachable\" instruction"); + break; + + case Instruction::Invoke: + case Instruction::Call: { + CallSite cs; + unsigned argStart; + if (i->getOpcode()==Instruction::Call) { + cs = CallSite(cast<CallInst>(i)); + argStart = 1; + } else { + cs = CallSite(cast<InvokeInst>(i)); + argStart = 3; + } + + unsigned numArgs = cs.arg_size(); + Function *f = getCalledFunction(cs, state); + + // evaluate arguments + std::vector< ref<Expr> > arguments; + arguments.reserve(numArgs); + + for (unsigned j=0; j<numArgs; ++j) + arguments.push_back(eval(ki, argStart+j, state)); + + if (!f) { + // special case the call with a bitcast case + Value *fp = cs.getCalledValue(); + llvm::ConstantExpr *ce = dyn_cast<llvm::ConstantExpr>(fp); + + if (ce && ce->getOpcode()==Instruction::BitCast) { + f = dyn_cast<Function>(ce->getOperand(0)); + assert(f && "XXX unrecognized constant expression in call"); + const FunctionType *fType = + dyn_cast<FunctionType>(cast<PointerType>(f->getType())->getElementType()); + const FunctionType *ceType = + dyn_cast<FunctionType>(cast<PointerType>(ce->getType())->getElementType()); + assert(fType && ceType && "unable to get function type"); + + // XXX check result coercion + + // XXX this really needs thought and validation + unsigned i=0; + for (std::vector< ref<Expr> >::iterator + ai = arguments.begin(), ie = arguments.end(); + ai != ie; ++ai) { + Expr::Width to, from = (*ai).getWidth(); + + if (i<fType->getNumParams()) { + to = Expr::getWidthForLLVMType(fType->getParamType(i)); + + if (from != to) { + // XXX need to check other param attrs ? + if (cs.paramHasAttr(i+1, llvm::Attribute::SExt)) { + arguments[i] = SExtExpr::create(arguments[i], to); + } else { + arguments[i] = ZExtExpr::create(arguments[i], to); + } + } + } + + i++; + } + } else if (isa<InlineAsm>(fp)) { + terminateStateOnExecError(state, "inline assembly is unsupported"); + break; + } + } + + if (f) { + executeCall(state, ki, f, arguments); + } else { + ref<Expr> v = eval(ki, 0, state); + + ExecutionState *free = &state; + bool hasInvalid = false, first = true; + + /* XXX This is wasteful, no need to do a full evaluate since we + have already got a value. But in the end the caches should + handle it for us, albeit with some overhead. */ + do { + ref<Expr> value; + bool success = solver->getValue(*free, v, value); + assert(success && "FIXME: Unhandled solver failure"); + StatePair res = fork(*free, EqExpr::create(v, value), true); + if (res.first) { + void *addr = (void*) (unsigned long) value.getConstantValue(); + std::set<void*>::iterator it = legalFunctions.find(addr); + if (it != legalFunctions.end()) { + f = (Function*) addr; + + // Don't give warning on unique resolution + if (res.second || !first) + klee_warning_once(addr, + "resolved symbolic function pointer to: %s", + f->getName().c_str()); + + executeCall(*res.first, ki, f, arguments); + } else { + if (!hasInvalid) { + terminateStateOnExecError(state, "invalid function pointer"); + hasInvalid = true; + } + } + } + + first = false; + free = res.second; + } while (free); + } + break; + } + case Instruction::PHI: { + ref<Expr> result = eval(ki, state.incomingBBIndex * 2, state); + bindLocal(ki, state, result); + break; + } + + // Special instructions + case Instruction::Select: { + SelectInst *SI = cast<SelectInst>(ki->inst); + assert(SI->getCondition() == SI->getOperand(0) && + "Wrong operand index!"); + ref<Expr> cond = eval(ki, 0, state); + ref<Expr> tExpr = eval(ki, 1, state); + ref<Expr> fExpr = eval(ki, 2, state); + ref<Expr> result = SelectExpr::create(cond, tExpr, fExpr); + bindLocal(ki, state, result); + break; + } + + case Instruction::VAArg: + terminateStateOnExecError(state, "unexpected VAArg instruction"); + break; + + // Arithmetic / logical +#define FP_CONSTANT_BINOP(op, type, l, r, target, state) \ + bindLocal(target, state, \ + ref<Expr>(op(toConstant(state, l, "floating point").getConstantValue(), \ + toConstant(state, r, "floating point").getConstantValue(), \ + type), type)) + case Instruction::Add: { + BinaryOperator *bi = cast<BinaryOperator>(i); + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + + if( bi->getType()->getTypeID() == llvm::Type::IntegerTyID ) { + bindLocal(ki, state, AddExpr::create(left, right)); + } else { + Expr::Width type = Expr::getWidthForLLVMType(bi->getType()); + FP_CONSTANT_BINOP(floats::add, type, left, right, ki, state); + } + + break; + } + + case Instruction::Sub: { + BinaryOperator *bi = cast<BinaryOperator>(i); + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + + if( bi->getType()->getTypeID() == llvm::Type::IntegerTyID ) { + bindLocal(ki, state, SubExpr::create(left, right)); + } else { + Expr::Width type = Expr::getWidthForLLVMType(bi->getType()); + FP_CONSTANT_BINOP(floats::sub, type, left, right, ki, state); + } + + break; + } + + case Instruction::Mul: { + BinaryOperator *bi = cast<BinaryOperator>(i); + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + + if( bi->getType()->getTypeID() == llvm::Type::IntegerTyID ) { + bindLocal(ki, state, MulExpr::create(left, right)); + } else { + Expr::Width type = Expr::getWidthForLLVMType(bi->getType()); + FP_CONSTANT_BINOP(floats::mul, type, left, right, ki, state); + } + + break; + } + + case Instruction::UDiv: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = UDivExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case Instruction::SDiv: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = SDivExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case Instruction::URem: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = URemExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case Instruction::SRem: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = SRemExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case Instruction::And: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = AndExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case Instruction::Or: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = OrExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case Instruction::Xor: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = XorExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case Instruction::Shl: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = ShlExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case Instruction::LShr: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = LShrExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case Instruction::AShr: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = AShrExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + // Compare + + case Instruction::ICmp: { + CmpInst *ci = cast<CmpInst>(i); + ICmpInst *ii = cast<ICmpInst>(ci); + + switch(ii->getPredicate()) { + case ICmpInst::ICMP_EQ: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = EqExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case ICmpInst::ICMP_NE: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = NeExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case ICmpInst::ICMP_UGT: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = UgtExpr::create(left, right); + bindLocal(ki, state,result); + break; + } + + case ICmpInst::ICMP_UGE: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = UgeExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case ICmpInst::ICMP_ULT: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = UltExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case ICmpInst::ICMP_ULE: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = UleExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case ICmpInst::ICMP_SGT: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = SgtExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case ICmpInst::ICMP_SGE: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = SgeExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case ICmpInst::ICMP_SLT: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = SltExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case ICmpInst::ICMP_SLE: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = SleExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + default: + terminateStateOnExecError(state, "invalid ICmp predicate"); + } + break; + } + + // Memory instructions... + case Instruction::Alloca: + case Instruction::Malloc: { + AllocationInst *ai = cast<AllocationInst>(i); + unsigned elementSize = + kmodule->targetData->getTypeStoreSize(ai->getAllocatedType()); + ref<Expr> size = Expr::createPointer(elementSize); + if (ai->isArrayAllocation()) { + // XXX coerce? + ref<Expr> count = eval(ki, 0, state); + size = MulExpr::create(count, size); + } + bool isLocal = i->getOpcode()==Instruction::Alloca; + executeAlloc(state, size, isLocal, ki); + break; + } + case Instruction::Free: { + executeFree(state, eval(ki, 0, state)); + break; + } + + case Instruction::Load: { + ref<Expr> base = eval(ki, 0, state); + executeMemoryOperation(state, false, base, 0, ki); + break; + } + case Instruction::Store: { + ref<Expr> base = eval(ki, 1, state); + ref<Expr> value = eval(ki, 0, state); + executeMemoryOperation(state, true, base, value, 0); + break; + } + + case Instruction::GetElementPtr: { + KGEPInstruction *kgepi = static_cast<KGEPInstruction*>(ki); + ref<Expr> base = eval(ki, 0, state); + + for (std::vector< std::pair<unsigned, unsigned> >::iterator + it = kgepi->indices.begin(), ie = kgepi->indices.end(); + it != ie; ++it) { + unsigned elementSize = it->second; + ref<Expr> index = eval(ki, it->first, state); + base = AddExpr::create(base, + MulExpr::create(Expr::createCoerceToPointerType(index), + Expr::createPointer(elementSize))); + } + if (kgepi->offset) + base = AddExpr::create(base, + Expr::createPointer(kgepi->offset)); + bindLocal(ki, state, base); + break; + } + + // Conversion + case Instruction::Trunc: { + CastInst *ci = cast<CastInst>(i); + ref<Expr> result = ExtractExpr::createByteOff(eval(ki, 0, state), + 0, + Expr::getWidthForLLVMType(ci->getType())); + bindLocal(ki, state, result); + break; + } + case Instruction::ZExt: { + CastInst *ci = cast<CastInst>(i); + ref<Expr> result = ZExtExpr::create(eval(ki, 0, state), + Expr::getWidthForLLVMType(ci->getType())); + bindLocal(ki, state, result); + break; + } + case Instruction::SExt: { + CastInst *ci = cast<CastInst>(i); + ref<Expr> result = SExtExpr::create(eval(ki, 0, state), + Expr::getWidthForLLVMType(ci->getType())); + bindLocal(ki, state, result); + break; + } + + case Instruction::IntToPtr: { + CastInst *ci = cast<CastInst>(i); + Expr::Width pType = Expr::getWidthForLLVMType(ci->getType()); + ref<Expr> arg = eval(ki, 0, state); + bindLocal(ki, state, ZExtExpr::create(arg, pType)); + break; + } + case Instruction::PtrToInt: { + CastInst *ci = cast<CastInst>(i); + Expr::Width iType = Expr::getWidthForLLVMType(ci->getType()); + ref<Expr> arg = eval(ki, 0, state); + bindLocal(ki, state, ZExtExpr::create(arg, iType)); + break; + } + + case Instruction::BitCast: { + ref<Expr> result = eval(ki, 0, state); + bindLocal(ki, state, result); + break; + } + + // Floating Point specific instructions + case Instruction::FPTrunc: { + FPTruncInst *fi = cast<FPTruncInst>(i); + Expr::Width resultType = Expr::getWidthForLLVMType(fi->getType()); + ref<Expr> arg = toConstant(state, eval(ki, 0, state), + "floating point"); + uint64_t value = floats::trunc(arg.getConstantValue(), + resultType, + arg.getWidth()); + ref<Expr> result(value, resultType); + bindLocal(ki, state, result); + break; + } + + case Instruction::FPExt: { + FPExtInst *fi = cast<FPExtInst>(i); + Expr::Width resultType = Expr::getWidthForLLVMType(fi->getType()); + ref<Expr> arg = toConstant(state, eval(ki, 0, state), + "floating point"); + uint64_t value = floats::ext(arg.getConstantValue(), + resultType, + arg.getWidth()); + ref<Expr> result(value, resultType); + bindLocal(ki, state, result); + break; + } + + case Instruction::FPToUI: { + FPToUIInst *fi = cast<FPToUIInst>(i); + Expr::Width resultType = Expr::getWidthForLLVMType(fi->getType()); + ref<Expr> arg = toConstant(state, eval(ki, 0, state), + "floating point"); + uint64_t value = floats::toUnsignedInt(arg.getConstantValue(), + resultType, + arg.getWidth()); + ref<Expr> result(value, resultType); + bindLocal(ki, state, result); + break; + } + + case Instruction::FPToSI: { + FPToSIInst *fi = cast<FPToSIInst>(i); + Expr::Width resultType = Expr::getWidthForLLVMType(fi->getType()); + ref<Expr> arg = toConstant(state, eval(ki, 0, state), + "floating point"); + uint64_t value = floats::toSignedInt(arg.getConstantValue(), + resultType, + arg.getWidth()); + ref<Expr> result(value, resultType); + bindLocal(ki, state, result); + break; + } + + case Instruction::UIToFP: { + UIToFPInst *fi = cast<UIToFPInst>(i); + Expr::Width resultType = Expr::getWidthForLLVMType(fi->getType()); + ref<Expr> arg = toConstant(state, eval(ki, 0, state), + "floating point"); + uint64_t value = floats::UnsignedIntToFP(arg.getConstantValue(), + resultType); + ref<Expr> result(value, resultType); + bindLocal(ki, state, result); + break; + } + + case Instruction::SIToFP: { + SIToFPInst *fi = cast<SIToFPInst>(i); + Expr::Width resultType = Expr::getWidthForLLVMType(fi->getType()); + ref<Expr> arg = toConstant(state, eval(ki, 0, state), + "floating point"); + uint64_t value = floats::SignedIntToFP(arg.getConstantValue(), + resultType, + arg.getWidth()); + ref<Expr> result(value, resultType); + bindLocal(ki, state, result); + break; + } + + case Instruction::FCmp: { + FCmpInst *fi = cast<FCmpInst>(i); + Expr::Width resultType = Expr::getWidthForLLVMType(fi->getType()); + ref<Expr> left = toConstant(state, eval(ki, 0, state), + "floating point"); + ref<Expr> right = toConstant(state, eval(ki, 1, state), + "floating point"); + uint64_t leftVal = left.getConstantValue(); + uint64_t rightVal = right.getConstantValue(); + + //determine whether the operands are NANs + unsigned inWidth = left.getWidth(); + bool leftIsNaN = floats::isNaN( leftVal, inWidth ); + bool rightIsNaN = floats::isNaN( rightVal, inWidth ); + + //handle NAN based on whether the predicate is "ordered" or "unordered" + uint64_t ret = (uint64_t)-1; + bool done = false; + switch( fi->getPredicate() ) { + //predicates which only care about whether or not the operands are NaNs + case FCmpInst::FCMP_ORD: + done = true; + ret = !leftIsNaN && !rightIsNaN; + break; + + case FCmpInst::FCMP_UNO: + done = true; + ret = leftIsNaN || rightIsNaN; + break; + + //ordered comparisons return false if either operand is NaN + case FCmpInst::FCMP_OEQ: + case FCmpInst::FCMP_OGT: + case FCmpInst::FCMP_OGE: + case FCmpInst::FCMP_OLT: + case FCmpInst::FCMP_OLE: + case FCmpInst::FCMP_ONE: + if( !leftIsNaN && !rightIsNaN) //only fall through and return false if there are NaN(s) + break; + + case FCmpInst::FCMP_FALSE: { //always return false for this predicate + done = true; + ret = false; + break; + } + + //unordered comparisons return true if either operand is NaN + case FCmpInst::FCMP_UEQ: + case FCmpInst::FCMP_UGT: + case FCmpInst::FCMP_UGE: + case FCmpInst::FCMP_ULT: + case FCmpInst::FCMP_ULE: + case FCmpInst::FCMP_UNE: + if( !leftIsNaN && !rightIsNaN) //only fall through and return true if there are NaN(s) + break; + + case FCmpInst::FCMP_TRUE: //always return true for this predicate + done = true; + ret = true; + + default: + case FCmpInst::BAD_FCMP_PREDICATE: /* will fall through and trigger fatal in the next switch */ + break; + } + + //if not done, then we need to actually do a comparison to get the result + if( !done ) { + switch( fi->getPredicate() ) { + //ordered comparisons return false if either operand is NaN + case FCmpInst::FCMP_OEQ: + case FCmpInst::FCMP_UEQ: + ret = floats::eq( leftVal, rightVal, inWidth ); + break; + + case FCmpInst::FCMP_OGT: + case FCmpInst::FCMP_UGT: + ret = floats::gt( leftVal, rightVal, inWidth ); + break; + + case FCmpInst::FCMP_OGE: + case FCmpInst::FCMP_UGE: + ret = floats::ge( leftVal, rightVal, inWidth ); + break; + + case FCmpInst::FCMP_OLT: + case FCmpInst::FCMP_ULT: + ret = floats::lt( leftVal, rightVal, inWidth ); + break; + + case FCmpInst::FCMP_OLE: + case FCmpInst::FCMP_ULE: + ret = floats::le( leftVal, rightVal, inWidth ); + break; + + case FCmpInst::FCMP_ONE: + case FCmpInst::FCMP_UNE: + ret = floats::ne( leftVal, rightVal, inWidth ); + break; + + default: + terminateStateOnExecError(state, "invalid FCmp predicate"); + } + } + + ref<Expr> result(ret, resultType); + bindLocal(ki, state, result); + break; + } + + case Instruction::FDiv: { + BinaryOperator *bi = cast<BinaryOperator>(i); + + ref<Expr> dividend = eval(ki, 0, state); + ref<Expr> divisor = eval(ki, 1, state); + Expr::Width type = Expr::getWidthForLLVMType(bi->getType()); + FP_CONSTANT_BINOP(floats::div, type, dividend, divisor, ki, state); + break; + } + + case Instruction::FRem: { + BinaryOperator *bi = cast<BinaryOperator>(i); + + ref<Expr> dividend = eval(ki, 0, state); + ref<Expr> divisor = eval(ki, 1, state); + Expr::Width type = Expr::getWidthForLLVMType(bi->getType()); + FP_CONSTANT_BINOP(floats::mod, type, dividend, divisor, ki, state); + break; + } + + + // Other instructions... + // Unhandled + case Instruction::ExtractElement: + case Instruction::InsertElement: + case Instruction::ShuffleVector: + terminateStateOnError(state, "XXX vector instructions unhandled", + "xxx.err"); + break; + + default: + terminateStateOnExecError(state, "invalid instruction"); + break; + } +} + +void Executor::updateStates(ExecutionState *current) { + if (searcher) { + searcher->update(current, addedStates, removedStates); + } + + states.insert(addedStates.begin(), addedStates.end()); + addedStates.clear(); + + for (std::set<ExecutionState*>::iterator + it = removedStates.begin(), ie = removedStates.end(); + it != ie; ++it) { + ExecutionState *es = *it; + std::set<ExecutionState*>::iterator it2 = states.find(es); + assert(it2!=states.end()); + states.erase(it2); + std::map<ExecutionState*, std::vector<SeedInfo> >::iterator it3 = + seedMap.find(es); + if (it3 != seedMap.end()) + seedMap.erase(it3); + processTree->remove(es->ptreeNode); + delete es; + } + removedStates.clear(); +} + +void Executor::bindInstructionConstants(KInstruction *KI) { + GetElementPtrInst *gepi = dyn_cast<GetElementPtrInst>(KI->inst); + if (!gepi) + return; + + KGEPInstruction *kgepi = static_cast<KGEPInstruction*>(KI); + ref<Expr> constantOffset = Expr::createPointer(0); + unsigned index = 1; + for (gep_type_iterator ii = gep_type_begin(gepi), ie = gep_type_end(gepi); + ii != ie; ++ii) { + if (const StructType *st = dyn_cast<StructType>(*ii)) { + const StructLayout *sl = + kmodule->targetData->getStructLayout(st); + const ConstantInt *ci = cast<ConstantInt>(ii.getOperand()); + ref<Expr> addend = Expr::createPointer(sl->getElementOffset((unsigned) + ci->getZExtValue())); + constantOffset = AddExpr::create(constantOffset, addend); + } else { + const SequentialType *st = cast<SequentialType>(*ii); + unsigned elementSize = + kmodule->targetData->getTypeStoreSize(st->getElementType()); + Value *operand = ii.getOperand(); + if (Constant *c = dyn_cast<Constant>(operand)) { + ref<Expr> index = evalConstant(c); + ref<Expr> addend = MulExpr::create(Expr::createCoerceToPointerType(index), + Expr::createPointer(elementSize)); + constantOffset = AddExpr::create(constantOffset, addend); + } else { + kgepi->indices.push_back(std::make_pair(index, elementSize)); + } + } + index++; + } + assert(constantOffset.isConstant()); + kgepi->offset = constantOffset.getConstantValue(); +} + +void Executor::bindModuleConstants() { + for (std::vector<KFunction*>::iterator it = kmodule->functions.begin(), + ie = kmodule->functions.end(); it != ie; ++it) { + KFunction *kf = *it; + for (unsigned i=0; i<kf->numInstructions; ++i) + bindInstructionConstants(kf->instructions[i]); + } + + kmodule->constantTable = new Cell[kmodule->constants.size()]; + for (unsigned i=0; i<kmodule->constants.size(); ++i) { + Cell &c = kmodule->constantTable[i]; + c.value = evalConstant(kmodule->constants[i]); + } +} + +void Executor::run(ExecutionState &initialState) { + bindModuleConstants(); + + // Delay init till now so that ticks don't accrue during + // optimization and such. + initTimers(); + + states.insert(&initialState); + + if (usingSeeds) { + std::vector<SeedInfo> &v = seedMap[&initialState]; + + for (std::vector<BOut*>::const_iterator it = usingSeeds->begin(), + ie = usingSeeds->end(); it != ie; ++it) + v.push_back(SeedInfo(*it)); + + int lastNumSeeds = usingSeeds->size()+10; + double lastTime, startTime = lastTime = util::getWallTime(); + ExecutionState *lastState = 0; + while (!seedMap.empty()) { + if (haltExecution) goto dump; + + std::map<ExecutionState*, std::vector<SeedInfo> >::iterator it = + seedMap.upper_bound(lastState); + if (it == seedMap.end()) + it = seedMap.begin(); + lastState = it->first; + unsigned numSeeds = it->second.size(); + ExecutionState &state = *lastState; + KInstruction *ki = state.pc; + stepInstruction(state); + + executeInstruction(state, ki); + processTimers(&state, MaxInstructionTime * numSeeds); + updateStates(&state); + + if ((stats::instructions % 1000) == 0) { + int numSeeds = 0, numStates = 0; + for (std::map<ExecutionState*, std::vector<SeedInfo> >::iterator + it = seedMap.begin(), ie = seedMap.end(); + it != ie; ++it) { + numSeeds += it->second.size(); + numStates++; + } + double time = util::getWallTime(); + if (SeedTime>0. && time > startTime + SeedTime) { + klee_warning("seed time expired, %d seeds remain over %d states", + numSeeds, numStates); + break; + } else if (numSeeds<=lastNumSeeds-10 || + time >= lastTime+10) { + lastTime = time; + lastNumSeeds = numSeeds; + klee_message("%d seeds remaining over: %d states", + numSeeds, numStates); + } + } + } + + klee_message("seeding done (%d states remain)", (int) states.size()); + + // XXX total hack, just because I like non uniform better but want + // seed results to be equally weighted. + for (std::set<ExecutionState*>::iterator + it = states.begin(), ie = states.end(); + it != ie; ++it) { + (*it)->weight = 1.; + } + + if (OnlySeed) + goto dump; + } + + searcher = constructUserSearcher(*this); + + searcher->update(0, states, std::set<ExecutionState*>()); + + while (!states.empty() && !haltExecution) { + ExecutionState &state = searcher->selectState(); + KInstruction *ki = state.pc; + stepInstruction(state); + + executeInstruction(state, ki); + processTimers(&state, MaxInstructionTime); + + if (MaxMemory) { + if ((stats::instructions & 0xFFFF) == 0) { + // We need to avoid calling GetMallocUsage() often because it + // is O(elts on freelist). This is really bad since we start + // to pummel the freelist once we hit the memory cap. + unsigned mbs = sys::Process::GetTotalMemoryUsage() >> 20; + + if (mbs > MaxMemory) { + if (mbs > MaxMemory + 100) { + // just guess at how many to kill + unsigned numStates = states.size(); + unsigned toKill = std::max(1U, numStates - numStates*MaxMemory/mbs); + + if (MaxMemoryInhibit) + klee_warning("killing %d states (over memory cap)", + toKill); + + std::vector<ExecutionState*> arr(states.begin(), states.end()); + for (unsigned i=0,N=arr.size(); N && i<toKill; ++i,--N) { + unsigned idx = rand() % N; + + // Make two pulls to try and not hit a state that + // covered new code. + if (arr[idx]->coveredNew) + idx = rand() % N; + + std::swap(arr[idx], arr[N-1]); + terminateStateEarly(*arr[N-1], "memory limit"); + } + } + atMemoryLimit = true; + } else { + atMemoryLimit = false; + } + } + } + + updateStates(&state); + } + + delete searcher; + searcher = 0; + + dump: + if (DumpStatesOnHalt && !states.empty()) { + llvm::cerr << "KLEE: halting execution, dumping remaining states\n"; + for (std::set<ExecutionState*>::iterator + it = states.begin(), ie = states.end(); + it != ie; ++it) { + ExecutionState &state = **it; + stepInstruction(state); // keep stats rolling + terminateStateEarly(state, "execution halting"); + } + updateStates(0); + } +} + +std::string Executor::getAddressInfo(ExecutionState &state, + ref<Expr> address) const{ + std::ostringstream info; + info << "\taddress: " << address << "\n"; + uint64_t example; + if (address.isConstant()) { + example = address.getConstantValue(); + } else { + ref<Expr> value; + bool success = solver->getValue(state, address, value); + assert(success && "FIXME: Unhandled solver failure"); + example = value.getConstantValue(); + info << "\texample: " << example << "\n"; + std::pair< ref<Expr>, ref<Expr> > res = solver->getRange(state, address); + info << "\trange: [" << res.first << ", " << res.second <<"]\n"; + } + + MemoryObject hack((unsigned) example); + MemoryMap::iterator lower = state.addressSpace.objects.upper_bound(&hack); + info << "\tnext: "; + if (lower==state.addressSpace.objects.end()) { + info << "none\n"; + } else { + const MemoryObject *mo = lower->first; + info << "object at " << mo->address + << " of size " << mo->size << "\n"; + } + if (lower!=state.addressSpace.objects.begin()) { + --lower; + info << "\tprev: "; + if (lower==state.addressSpace.objects.end()) { + info << "none\n"; + } else { + const MemoryObject *mo = lower->first; + info << "object at " << mo->address + << " of size " << mo->size << "\n"; + } + } + + return info.str(); +} + +void Executor::terminateState(ExecutionState &state) { + if (replayOut && replayPosition!=replayOut->numObjects) { + klee_warning_once(replayOut, "replay did not consume all objects in .bout input."); + } + + interpreterHandler->incPathsExplored(); + + std::set<ExecutionState*>::iterator it = addedStates.find(&state); + if (it==addedStates.end()) { + state.pc = state.prevPC; + + removedStates.insert(&state); + } else { + // never reached searcher, just delete immediately + std::map< ExecutionState*, std::vector<SeedInfo> >::iterator it3 = + seedMap.find(&state); + if (it3 != seedMap.end()) + seedMap.erase(it3); + addedStates.erase(it); + processTree->remove(state.ptreeNode); + delete &state; + } +} + +void Executor::terminateStateEarly(ExecutionState &state, std::string message) { + if (!OnlyOutputStatesCoveringNew || state.coveredNew || + (AlwaysOutputSeeds && seedMap.count(&state))) + interpreterHandler->processTestCase(state, (message + "\n").c_str(), "early"); + terminateState(state); +} + +void Executor::terminateStateOnExit(ExecutionState &state) { + if (!OnlyOutputStatesCoveringNew || state.coveredNew || + (AlwaysOutputSeeds && seedMap.count(&state))) + interpreterHandler->processTestCase(state, 0, 0); + terminateState(state); +} + +void Executor::terminateStateOnError(ExecutionState &state, + const std::string &message, + const std::string &suffix, + const std::string &info) { + static std::set< std::pair<Instruction*, std::string> > emittedErrors; + const InstructionInfo &ii = *state.prevPC->info; + + if (EmitAllErrors || + emittedErrors.insert(std::make_pair(state.prevPC->inst,message)).second) { + if (ii.file != "") { + klee_message("ERROR: %s:%d: %s", ii.file.c_str(), ii.line, message.c_str()); + } else { + klee_message("ERROR: %s", message.c_str()); + } + if (!EmitAllErrors) + klee_message("NOTE: now ignoring this error at this location"); + + std::ostringstream msg; + msg << "Error: " << message << "\n"; + if (ii.file != "") { + msg << "File: " << ii.file << "\n"; + msg << "Line: " << ii.line << "\n"; + } + msg << "Stack: \n"; + unsigned idx = 0; + const KInstruction *target = state.prevPC; + for (ExecutionState::stack_ty::reverse_iterator + it = state.stack.rbegin(), ie = state.stack.rend(); + it != ie; ++it) { + StackFrame &sf = *it; + Function *f = sf.kf->function; + const InstructionInfo &ii = *target->info; + msg << "\t#" << idx++ + << " " << std::setw(8) << std::setfill('0') << ii.assemblyLine + << " in " << f->getName() << " ("; + // Yawn, we could go up and print varargs if we wanted to. + unsigned index = 0; + for (Function::arg_iterator ai = f->arg_begin(), ae = f->arg_end(); + ai != ae; ++ai) { + if (ai!=f->arg_begin()) msg << ", "; + + msg << ai->getName(); + // XXX should go through function + ref<Expr> value = sf.locals[sf.kf->getArgRegister(index++)].value; + if (value.isConstant()) + msg << "=" << value; + } + msg << ")"; + if (ii.file != "") + msg << " at " << ii.file << ":" << ii.line; + msg << "\n"; + target = sf.caller; + } + + if (info != "") + msg << "Info: \n" << info; + interpreterHandler->processTestCase(state, msg.str().c_str(), suffix.c_str()); + } + + terminateState(state); +} + +// XXX shoot me +static const char *okExternalsList[] = { "printf", + "fprintf", + "puts", + "getpid" }; +static std::set<std::string> okExternals(okExternalsList, + okExternalsList + + (sizeof(okExternalsList)/sizeof(okExternalsList[0]))); + +void Executor::callExternalFunction(ExecutionState &state, + KInstruction *target, + Function *function, + std::vector< ref<Expr> > &arguments) { + // check if specialFunctionHandler wants it + if (specialFunctionHandler->handle(state, function, target, arguments)) + return; + + if (NoExternals && !okExternals.count(function->getName())) { + llvm::cerr << "KLEE:ERROR: Calling not-OK external function : " << function->getName() << "\n"; + terminateStateOnError(state, "externals disallowed", "user.err"); + return; + } + + // normal external function handling path + uint64_t *args = (uint64_t*) alloca(sizeof(*args) * (arguments.size() + 1)); + memset(args, 0, sizeof(*args) * (arguments.size() + 1)); + + unsigned i = 1; + for (std::vector<ref<Expr> >::iterator ai = arguments.begin(), ae = arguments.end(); + ai!=ae; ++ai, ++i) { + if (AllowExternalSymCalls) { // don't bother checking uniqueness + ref<Expr> ce; + bool success = solver->getValue(state, *ai, ce); + assert(success && "FIXME: Unhandled solver failure"); + static_cast<ConstantExpr*>(ce.get())->toMemory((void*) &args[i]); + } else { + ref<Expr> arg = toUnique(state, *ai); + if (arg.isConstant()) { + // XXX kick toMemory functions from here + static_cast<ConstantExpr*>(arg.get())->toMemory((void*) &args[i]); + } else { + std::string msg = "external call with symbolic argument: " + function->getName(); + terminateStateOnExecError(state, msg); + return; + } + } + } + + state.addressSpace.copyOutConcretes(); + + if (!SuppressExternalWarnings) { + std::ostringstream os; + os << "calling external: " << function->getName().c_str() << "("; + for (unsigned i=0; i<arguments.size(); i++) { + os << arguments[i]; + if (i != arguments.size()-1) + os << ", "; + } + os << ")"; + + if (AllExternalWarnings) + klee_warning("%s", os.str().c_str()); + else + klee_warning_once(function, "%s", os.str().c_str()); + } + + bool success = externalDispatcher->executeCall(function, target->inst, args); + if (!success) { + terminateStateOnError(state, "failed external call: " + function->getName(), "external.err"); + return; + } + + if (!state.addressSpace.copyInConcretes()) { + terminateStateOnError(state, "external modified read-only object", "external.err"); + return; + } + + const Type *resultType = target->inst->getType(); + if (resultType != Type::VoidTy) { + ref<Expr> e = ConstantExpr::fromMemory((void*) args, + Expr::getWidthForLLVMType(resultType)); + bindLocal(target, state, e); + } +} + +/***/ + +ref<Expr> Executor::replaceReadWithSymbolic(ExecutionState &state, + ref<Expr> e) { + unsigned n = interpreterOpts.MakeConcreteSymbolic; + if (!n || replayOut || replayPath) + return e; + + // right now, we don't replace symbolics (is there any reason too?) + if (!e.isConstant()) + return e; + + if (n != 1 && random() % n) + return e; + + // create a new fresh location, assert it is equal to concrete value in e + // and return it. + + const MemoryObject *mo = memory->allocate(Expr::getMinBytesForWidth(e.getWidth()), + false, false, + state.prevPC->inst); + assert(mo && "out of memory"); + ref<Expr> res = Expr::createTempRead(mo->array, e.getWidth()); + ref<Expr> eq = NotOptimizedExpr::create(EqExpr::create(e, res)); + llvm::cerr << "Making symbolic: " << eq << "\n"; + state.addConstraint(eq); + return res; +} + +ObjectState *Executor::bindObjectInState(ExecutionState &state, const MemoryObject *mo, + bool isLocal) { + ObjectState *os = new ObjectState(mo, mo->size); + state.addressSpace.bindObject(mo, os); + + // Its possible that multiple bindings of the same mo in the state + // will put multiple copies on this list, but it doesn't really + // matter because all we use this list for is to unbind the object + // on function return. + if (isLocal) + state.stack.back().allocas.push_back(mo); + + return os; +} + +void Executor::executeAllocN(ExecutionState &state, + uint64_t nelems, + uint64_t size, + uint64_t alignment, + bool isLocal, + KInstruction *target) { +#if 0 + // over-allocate so that we can properly align the whole buffer + uint64_t address = (uint64_t) (unsigned) malloc(nelems * size + alignment - 1); + address += (alignment - address % alignment); +#else + theMMap = + mmap((void*) 0x90000000, + nelems*size, PROT_READ|PROT_WRITE, + MAP_PRIVATE +#ifdef MAP_ANONYMOUS + |MAP_ANONYMOUS +#endif + , 0, 0); + uint64_t address = (uintptr_t) theMMap; + theMMapSize = nelems*size; +#endif + + for (unsigned i = 0; i < nelems; i++) { + MemoryObject *mo = memory->allocateFixed(address + i*size, size, state.prevPC->inst); + ObjectState *os = bindObjectInState(state, mo, isLocal); + os->initializeToRandom(); + + // bind the local to the first memory object in the whole array + if (i == 0) + bindLocal(target, state, mo->getBaseExpr()); + } + + llvm::cerr << "KLEE: allocN at: " << address << "\n"; +} + +void Executor::executeAlloc(ExecutionState &state, + ref<Expr> size, + bool isLocal, + KInstruction *target, + bool zeroMemory, + const ObjectState *reallocFrom) { + size = toUnique(state, size); + if (size.isConstant()) { + MemoryObject *mo = memory->allocate(size.getConstantValue(), isLocal, false, + state.prevPC->inst); + if (!mo) { + bindLocal(target, state, ref<Expr>(0, kMachinePointerType)); + } else { + ObjectState *os = bindObjectInState(state, mo, isLocal); + if (zeroMemory) { + os->initializeToZero(); + } else { + os->initializeToRandom(); + } + bindLocal(target, state, mo->getBaseExpr()); + + if (reallocFrom) { + unsigned count = std::min(reallocFrom->size, os->size); + for (unsigned i=0; i<count; i++) + os->write(i, reallocFrom->read8(i)); + state.addressSpace.unbindObject(reallocFrom->getObject()); + } + } + } else { + // XXX For now we just pick a size. Ideally we would support + // symbolic sizes fully but even if we don't it would be better to + // "smartly" pick a value, for example we could fork and pick the + // min and max values and perhaps some intermediate (reasonable + // value). + // + // It would also be nice to recognize the case when size has + // exactly two values and just fork (but we need to get rid of + // return argument first). This shows up in pcre when llvm + // collapses the size expression with a select. + + ref<Expr> example; + bool success = solver->getValue(state, size, example); + assert(success && "FIXME: Unhandled solver failure"); + + // Try and start with a small example + while (example.getConstantValue()>128) { + ref<Expr> tmp = ref<Expr>(example.getConstantValue() >> 1, + example.getWidth()); + bool res; + bool success = solver->mayBeTrue(state, EqExpr::create(tmp, size), res); + assert(success && "FIXME: Unhandled solver failure"); + if (!res) + break; + example = tmp; + } + + StatePair fixedSize = fork(state, EqExpr::create(example, size), true); + + if (fixedSize.second) { + // Check for exactly two values + ref<Expr> tmp; + bool success = solver->getValue(*fixedSize.second, size, tmp); + assert(success && "FIXME: Unhandled solver failure"); + bool res; + success = solver->mustBeTrue(*fixedSize.second, + EqExpr::create(tmp, size), + res); + assert(success && "FIXME: Unhandled solver failure"); + if (res) { + executeAlloc(*fixedSize.second, tmp, isLocal, + target, zeroMemory, reallocFrom); + } else { + // See if a *really* big value is possible. If so assume + // malloc will fail for it, so lets fork and return 0. + StatePair hugeSize = fork(*fixedSize.second, + UltExpr::create(ref<Expr>(1<<31, Expr::Int32), size), + true); + if (hugeSize.first) { + klee_message("NOTE: found huge malloc, returing 0"); + bindLocal(target, *hugeSize.first, ref<Expr>(0,kMachinePointerType)); + } + + if (hugeSize.second) { + std::ostringstream info; + ExprPPrinter::printOne(info, " size expr", size); + info << " concretization : " << example << "\n"; + info << " unbound example: " << tmp << "\n"; + terminateStateOnError(*hugeSize.second, + "concretized symbolic size", + "model.err", + info.str()); + } + } + } + + if (fixedSize.first) // can be zero when fork fails + executeAlloc(*fixedSize.first, example, isLocal, + target, zeroMemory, reallocFrom); + } +} + +void Executor::executeFree(ExecutionState &state, + ref<Expr> address, + KInstruction *target) { + StatePair zeroPointer = fork(state, Expr::createIsZero(address), true); + if (zeroPointer.first) { + if (target) + bindLocal(target, *zeroPointer.first, Expr::createPointer(0)); + } + if (zeroPointer.second) { // address != 0 + ExactResolutionList rl; + resolveExact(*zeroPointer.second, address, rl, "free"); + + for (Executor::ExactResolutionList::iterator it = rl.begin(), + ie = rl.end(); it != ie; ++it) { + const MemoryObject *mo = it->first.first; + if (mo->isLocal) { + terminateStateOnError(*it->second, + "free of alloca", + "free.err", + getAddressInfo(*it->second, address)); + } else if (mo->isGlobal) { + terminateStateOnError(*it->second, + "free of global", + "free.err", + getAddressInfo(*it->second, address)); + } else { + it->second->addressSpace.unbindObject(mo); + if (target) + bindLocal(target, *it->second, Expr::createPointer(0)); + } + } + } +} + +void Executor::resolveExact(ExecutionState &state, + ref<Expr> p, + ExactResolutionList &results, + const std::string &name) { + // XXX we may want to be capping this? + ResolutionList rl; + state.addressSpace.resolve(state, solver, p, rl); + + ExecutionState *unbound = &state; + for (ResolutionList::iterator it = rl.begin(), ie = rl.end(); + it != ie; ++it) { + ref<Expr> inBounds = EqExpr::create(p, it->first->getBaseExpr()); + + StatePair branches = fork(*unbound, inBounds, true); + + if (branches.first) + results.push_back(std::make_pair(*it, branches.first)); + + unbound = branches.second; + if (!unbound) // Fork failure + break; + } + + if (unbound) { + terminateStateOnError(*unbound, + "memory error: invalid pointer: " + name, + "ptr.err", + getAddressInfo(*unbound, p)); + } +} + +void Executor::executeMemoryOperation(ExecutionState &state, + bool isWrite, + ref<Expr> address, + ref<Expr> value /* undef if read */, + KInstruction *target /* undef if write */) { + Expr::Width type = (isWrite ? value.getWidth() : + Expr::getWidthForLLVMType(target->inst->getType())); + unsigned bytes = Expr::getMinBytesForWidth(type); + + if (SimplifySymIndices) { + if (!address.isConstant()) + address = state.constraints.simplifyExpr(address); + if (isWrite && !value.isConstant()) + value = state.constraints.simplifyExpr(value); + } + + // fast path: single in-bounds resolution + ObjectPair op; + bool success; + solver->setTimeout(stpTimeout); + if (!state.addressSpace.resolveOne(state, solver, address, op, success)) { + address = toConstant(state, address, "resolveOne failure"); + success = state.addressSpace.resolveOne(address.getConstantValue(), op); + } + solver->setTimeout(0); + + if (success) { + const MemoryObject *mo = op.first; + + if (MaxSymArraySize && mo->size>=MaxSymArraySize) { + address = toConstant(state, address, "max-sym-array-size"); + } + + ref<Expr> offset = mo->getOffsetExpr(address); + + bool inBounds; + solver->setTimeout(stpTimeout); + bool success = solver->mustBeTrue(state, + mo->getBoundsCheckOffset(offset, bytes), + inBounds); + solver->setTimeout(0); + if (!success) { + state.pc = state.prevPC; + terminateStateEarly(state, "query timed out"); + return; + } + + if (inBounds) { + const ObjectState *os = op.second; + if (isWrite) { + if (os->readOnly) { + terminateStateOnError(state, + "memory error: object read only", + "readonly.err"); + } else { + ObjectState *wos = state.addressSpace.getWriteable(mo, os); + wos->write(offset, value); + } + } else { + ref<Expr> result = os->read(offset, type); + + if (interpreterOpts.MakeConcreteSymbolic) + result = replaceReadWithSymbolic(state, result); + + bindLocal(target, state, result); + } + + return; + } + } + + // we are on an error path (no resolution, multiple resolution, one + // resolution with out of bounds) + + ResolutionList rl; + solver->setTimeout(stpTimeout); + bool incomplete = state.addressSpace.resolve(state, solver, address, rl, + 0, stpTimeout); + solver->setTimeout(0); + + // XXX there is some query wasteage here. who cares? + ExecutionState *unbound = &state; + + for (ResolutionList::iterator i = rl.begin(), ie = rl.end(); i != ie; ++i) { + const MemoryObject *mo = i->first; + const ObjectState *os = i->second; + ref<Expr> inBounds = mo->getBoundsCheckPointer(address, bytes); + + StatePair branches = fork(*unbound, inBounds, true); + ExecutionState *bound = branches.first; + + // bound can be 0 on failure or overlapped + if (bound) { + if (isWrite) { + if (os->readOnly) { + terminateStateOnError(*bound, + "memory error: object read only", + "readonly.err"); + } else { + ObjectState *wos = bound->addressSpace.getWriteable(mo, os); + wos->write(mo->getOffsetExpr(address), value); + } + } else { + ref<Expr> result = os->read(mo->getOffsetExpr(address), type); + bindLocal(target, *bound, result); + } + } + + unbound = branches.second; + if (!unbound) + break; + } + + // XXX should we distinguish out of bounds and overlapped cases? + if (unbound) { + if (incomplete) { + terminateStateEarly(*unbound, "query timed out (resolve)"); + } else { + terminateStateOnError(*unbound, + "memory error: out of bound pointer", + "ptr.err", + getAddressInfo(*unbound, address)); + } + } +} + +void Executor::executeMakeSymbolic(ExecutionState &state, + const MemoryObject *mo) { + // make a new one and rebind, we use bind here because we want to + // create a flat out new state, not a copy. although I'm not really + // sure it matters. + ObjectState *os = bindObjectInState(state, mo, false); + if (!replayOut) { + os->makeSymbolic(); + state.addSymbolic(mo); + + std::map< ExecutionState*, std::vector<SeedInfo> >::iterator it = + seedMap.find(&state); + if (it!=seedMap.end()) { // In seed mode we need to add this as a + // binding. + for (std::vector<SeedInfo>::iterator siit = it->second.begin(), + siie = it->second.end(); siit != siie; ++siit) { + SeedInfo &si = *siit; + BOutObject *obj = si.getNextInput(mo, + NamedSeedMatching); + + if (!obj) { + if (ZeroSeedExtension) { + std::vector<unsigned char> &values = + si.assignment.bindings[mo->array]; + values = std::vector<unsigned char>(mo->size, '\0'); + } else if (!AllowSeedExtension) { + terminateStateOnError(state, + "ran out of inputs during seeding", + "user.err"); + break; + } + } else { + if (obj->numBytes != mo->size && + ((!(AllowSeedExtension || ZeroSeedExtension) + && obj->numBytes < mo->size) || + (!AllowSeedTruncation && obj->numBytes > mo->size))) { + std::stringstream msg; + msg << "replace size mismatch: " + << mo->name << "[" << mo->size << "]" + << " vs " << obj->name << "[" << obj->numBytes << "]" + << " in bout\n"; + + terminateStateOnError(state, + msg.str(), + "user.err"); + break; + } else { + std::vector<unsigned char> &values = + si.assignment.bindings[mo->array]; + values.insert(values.begin(), obj->bytes, + obj->bytes + std::min(obj->numBytes, mo->size)); + if (ZeroSeedExtension) { + for (unsigned i=obj->numBytes; i<mo->size; ++i) + values.push_back('\0'); + } + } + } + } + } + } else { + if (replayPosition >= replayOut->numObjects) { + terminateStateOnError(state, "replay count mismatch", "user.err"); + } else { + BOutObject *obj = &replayOut->objects[replayPosition++]; + if (obj->numBytes != mo->size) { + terminateStateOnError(state, "replay size mismatch", "user.err"); + } else { + for (unsigned i=0; i<mo->size; i++) + os->write8(i, obj->bytes[i]); + } + } + } +} + +/***/ + +void Executor::runFunctionAsMain(Function *f, + int argc, + char **argv, + char **envp) { + std::vector<ref<Expr> > arguments; + + // force deterministic initialization of memory objects + srand(1); + srandom(1); + + MemoryObject *argvMO = 0; + + // In order to make uclibc happy and be closer to what the system is + // doing we lay out the environments at the end of the argv array + // (both are terminated by a null). There is also a final terminating + // null that uclibc seems to expect, possibly the ELF header? + + int envc; + for (envc=0; envp[envc]; ++envc) ; + + KFunction *kf = kmodule->functionMap[f]; + assert(kf); + Function::arg_iterator ai = f->arg_begin(), ae = f->arg_end(); + if (ai!=ae) { + arguments.push_back(ref<Expr>(argc, Expr::Int32)); + + if (++ai!=ae) { + argvMO = memory->allocate((argc+1+envc+1+1) * kMachinePointerSize, false, true, + f->begin()->begin()); + + arguments.push_back(argvMO->getBaseExpr()); + + if (++ai!=ae) { + uint64_t envp_start = argvMO->address + (argc+1)*kMachinePointerSize; + arguments.push_back(Expr::createPointer(envp_start)); + + if (++ai!=ae) + klee_error("invalid main function (expect 0-3 arguments)"); + } + } + } + + ExecutionState *state = new ExecutionState(kmodule->functionMap[f]); + + if (pathWriter) + state->pathOS = pathWriter->open(); + if (symPathWriter) + state->symPathOS = symPathWriter->open(); + + + if (statsTracker) + statsTracker->framePushed(*state, 0); + + assert(arguments.size() == f->arg_size() && "wrong number of arguments"); + for (unsigned i = 0, e = f->arg_size(); i != e; ++i) + bindArgument(kf, i, *state, arguments[i]); + + if (argvMO) { + ObjectState *argvOS = bindObjectInState(*state, argvMO, false); + + for (int i=0; i<argc+1+envc+1+1; i++) { + MemoryObject *arg; + + if (i==argc || i>=argc+1+envc) { + arg = 0; + } else { + char *s = i<argc ? argv[i] : envp[i-(argc+1)]; + int j, len = strlen(s); + + arg = memory->allocate(len+1, false, true, state->pc->inst); + ObjectState *os = bindObjectInState(*state, arg, false); + for (j=0; j<len+1; j++) + os->write8(j, s[j]); + } + + if (arg) { + argvOS->write(i * kMachinePointerSize, arg->getBaseExpr()); + } else { + argvOS->write(i * kMachinePointerSize, Expr::createPointer(0)); + } + } + } + + initializeGlobals(*state); + + processTree = new PTree(state); + state->ptreeNode = processTree->root; + run(*state); + delete processTree; + processTree = 0; + + // hack to clear memory objects + delete memory; + memory = new MemoryManager(); + + globalObjects.clear(); + globalAddresses.clear(); + + if (statsTracker) + statsTracker->done(); + + if (theMMap) { + munmap(theMMap, theMMapSize); + theMMap = 0; + } +} + +unsigned Executor::getPathStreamID(const ExecutionState &state) { + assert(pathWriter); + return state.pathOS.getID(); +} + +unsigned Executor::getSymbolicPathStreamID(const ExecutionState &state) { + assert(symPathWriter); + return state.symPathOS.getID(); +} + +void Executor::getConstraintLog(const ExecutionState &state, + std::string &res, + bool asCVC) { + if (asCVC) { + Query query(state.constraints, ref<Expr>(0, Expr::Bool)); + char *log = solver->stpSolver->getConstraintLog(query); + res = std::string(log); + free(log); + } else { + std::ostringstream info; + ExprPPrinter::printConstraints(info, state.constraints); + res = info.str(); + } +} + +bool Executor::getSymbolicSolution(const ExecutionState &state, + std::vector< + std::pair<std::string, + std::vector<unsigned char> > > + &res) { + solver->setTimeout(stpTimeout); + + ExecutionState tmp(state); + if (!NoPreferCex) { + for (std::vector<const MemoryObject*>::const_iterator + it = state.symbolics.begin(), ie = state.symbolics.end(); + it != ie; ++it) { + const MemoryObject *mo = *it; + std::vector< ref<Expr> >::const_iterator pi = + mo->cexPreferences.begin(), pie = mo->cexPreferences.end(); + for (; pi != pie; ++pi) { + bool mustBeTrue; + bool success = solver->mustBeTrue(tmp, Expr::createNot(*pi), + mustBeTrue); + if (!success) break; + if (!mustBeTrue) tmp.addConstraint(*pi); + } + if (pi!=pie) break; + } + } + + std::vector< std::vector<unsigned char> > values; + std::vector<const Array*> objects; + for (unsigned i = 0; i != state.symbolics.size(); ++i) + objects.push_back(state.symbolics[i]->array); + bool success = solver->getInitialValues(tmp, objects, values); + solver->setTimeout(0); + if (!success) { + klee_warning("unable to compute initial values (invalid constraints?)!"); + ExprPPrinter::printQuery(std::cerr, + state.constraints, + ref<Expr>(0,Expr::Bool)); + return false; + } + + unsigned i = 0; + for (std::vector<const MemoryObject*>::const_iterator + it = state.symbolics.begin(), ie = state.symbolics.end(); + it != ie; ++it) { + res.push_back(std::make_pair((*it)->name, values[i])); + ++i; + } + return true; +} + +void Executor::getCoveredLines(const ExecutionState &state, + std::map<const std::string*, std::set<unsigned> > &res) { + res = state.coveredLines; +} + +void Executor::doImpliedValueConcretization(ExecutionState &state, + ref<Expr> e, + ref<Expr> value) { + assert(value.isConstant() && "non-constant passed in place of constant"); + + if (DebugCheckForImpliedValues) + ImpliedValue::checkForImpliedValues(solver->solver, e, value); + + ImpliedValueList results; + ImpliedValue::getImpliedValues(e, value, results); + for (ImpliedValueList::iterator it = results.begin(), ie = results.end(); + it != ie; ++it) { + ReadExpr *re = it->first.get(); + + if (re->index.isConstant()) { + // FIXME: This is the sole remaining usage of the Array object + // variable. Kill me. + const MemoryObject *mo = re->updates.root->object; + const ObjectState *os = state.addressSpace.findObject(mo); + + if (!os) { + // object has been free'd, no need to concretize (although as + // in other cases we would like to concretize the outstanding + // reads, but we have no facility for that yet) + } else { + assert(!os->readOnly && "not possible? read only object with static read?"); + ObjectState *wos = state.addressSpace.getWriteable(mo, os); + wos->write(re->index.getConstantValue(), it->second); + } + } + } +} + +/// + +Interpreter *Interpreter::create(const InterpreterOptions &opts, + InterpreterHandler *ih) { + return new Executor(opts, ih); +} diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h new file mode 100644 index 00000000..76868291 --- /dev/null +++ b/lib/Core/Executor.h @@ -0,0 +1,445 @@ +//===-- Executor.h ----------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Class to perform actual execution, hides implementation details from external +// interpreter. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_EXECUTOR_H +#define KLEE_EXECUTOR_H + +#include "klee/Interpreter.h" +#include "llvm/Support/CallSite.h" +#include <vector> +#include <string> +#include <map> +#include <set> + +struct BOut; + +namespace llvm { + class BasicBlock; + class BranchInst; + class CallInst; + class Constant; + class ConstantExpr; + class Function; + class GlobalValue; + class Instruction; + class TargetData; + class Value; +} + +namespace klee { + class ExecutionState; + class ExternalDispatcher; + class Expr; + class InstructionInfoTable; + class KFunction; + class KInstruction; + class KInstIterator; + class KModule; + class MemoryManager; + class MemoryObject; + class ObjectState; + class PTree; + class Searcher; + class SeedInfo; + class SpecialFunctionHandler; + class StackFrame; + class StatsTracker; + class TimingSolver; + class TreeStreamWriter; + template<class T> class ref; + + /// \todo Add a context object to keep track of data only live + /// during an instruction step. Should contain addedStates, + /// removedStates, and haltExecution, among others. + +class Executor : public Interpreter { + friend class BumpMergingSearcher; + friend class MergingSearcher; + friend class RandomPathSearcher; + friend class OwningSearcher; + friend class WeightedRandomSearcher; + friend class SpecialFunctionHandler; + friend class StatsTracker; + +public: + class Timer { + public: + Timer(); + virtual ~Timer(); + + /// The event callback. + virtual void run() = 0; + }; + + typedef std::pair<ExecutionState*,ExecutionState*> StatePair; + +private: + class TimerInfo; + + KModule *kmodule; + InterpreterHandler *interpreterHandler; + Searcher *searcher; + + ExternalDispatcher *externalDispatcher; + TimingSolver *solver; + MemoryManager *memory; + std::set<ExecutionState*> states; + StatsTracker *statsTracker; + TreeStreamWriter *pathWriter, *symPathWriter; + SpecialFunctionHandler *specialFunctionHandler; + std::vector<TimerInfo*> timers; + PTree *processTree; + + /// Used to track states that have been added during the current + /// instructions step. + /// \invariant \ref addedStates is a subset of \ref states. + /// \invariant \ref addedStates and \ref removedStates are disjoint. + std::set<ExecutionState*> addedStates; + /// Used to track states that have been removed during the current + /// instructions step. + /// \invariant \ref removedStates is a subset of \ref states. + /// \invariant \ref addedStates and \ref removedStates are disjoint. + std::set<ExecutionState*> removedStates; + + /// When non-empty the Executor is running in "seed" mode. The + /// states in this map will be executed in an arbitrary order + /// (outside the normal search interface) until they terminate. When + /// the states reach a symbolic branch then either direction that + /// satisfies one or more seeds will be added to this map. What + /// happens with other states (that don't satisfy the seeds) depends + /// on as-yet-to-be-determined flags. + std::map<ExecutionState*, std::vector<SeedInfo> > seedMap; + + /// Map of globals to their representative memory object. + std::map<const llvm::GlobalValue*, MemoryObject*> globalObjects; + + /// Map of globals to their bound address. This also includes + /// globals that have no representative object (i.e. functions). + std::map<const llvm::GlobalValue*, ref<Expr> > globalAddresses; + + /// The set of legal function addresses, used to validate function + /// pointers. + std::set<void*> legalFunctions; + + /// When non-null the bindings that will be used for calls to + /// klee_make_symbolic in order replay. + const struct BOut *replayOut; + /// When non-null a list of branch decisions to be used for replay. + const std::vector<bool> *replayPath; + /// The index into the current \ref replayOut or \ref replayPath + /// object. + unsigned replayPosition; + + /// When non-null a list of "seed" inputs which will be used to + /// drive execution. + const std::vector<struct BOut *> *usingSeeds; + + /// Disables forking, instead a random path is chosen. Enabled as + /// needed to control memory usage. \see fork() + bool atMemoryLimit; + + /// Disables forking, set by client. \see setInhibitForking() + bool inhibitForking; + + /// Signals the executor to halt execution at the next instruction + /// step. + bool haltExecution; + + /// Whether implied-value concretization is enabled. Currently + /// false, it is buggy (it needs to validate its writes). + bool ivcEnabled; + + /// The maximum time to allow for a single stp query. + double stpTimeout; + + llvm::Function* getCalledFunction(llvm::CallSite &cs, ExecutionState &state); + + void executeInstruction(ExecutionState &state, KInstruction *ki); + + void printFileLine(ExecutionState &state, KInstruction *ki); + + void run(ExecutionState &initialState); + + // Given a concrete object in our [klee's] address space, add it to + // objects checked code can reference. + MemoryObject *addExternalObject(ExecutionState &state, void *addr, + unsigned size, bool isReadOnly); + + void initializeGlobalObject(ExecutionState &state, ObjectState *os, + llvm::Constant *c, + unsigned offset); + void initializeGlobals(ExecutionState &state); + + void stepInstruction(ExecutionState &state); + void updateStates(ExecutionState *current); + void transferToBasicBlock(llvm::BasicBlock *dst, + llvm::BasicBlock *src, + ExecutionState &state); + + void callExternalFunction(ExecutionState &state, + KInstruction *target, + llvm::Function *function, + std::vector< ref<Expr> > &arguments); + + ObjectState *bindObjectInState(ExecutionState &state, const MemoryObject *mo, + bool isLocal); + + /// Resolve a pointer to the memory objects it could point to the + /// start of, forking execution when necessary and generating errors + /// for pointers to invalid locations (either out of bounds or + /// address inside the middle of objects). + /// + /// \param results[out] A list of ((MemoryObject,ObjectState), + /// state) pairs for each object the given address can point to the + /// beginning of. + typedef std::vector< std::pair<std::pair<const MemoryObject*, const ObjectState*>, + ExecutionState*> > ExactResolutionList; + void resolveExact(ExecutionState &state, + ref<Expr> p, + ExactResolutionList &results, + const std::string &name); + + /// Allocate and bind a new object in a particular state. NOTE: This + /// function may fork. + /// + /// \param isLocal Flag to indicate if the object should be + /// automatically deallocated on function return (this also makes it + /// illegal to free directly). + /// + /// \param target Value at which to bind the base address of the new + /// object. + /// + /// \param reallocFrom If non-zero and the allocation succeeds, + /// initialize the new object from the given one and unbind it when + /// done (realloc semantics). The initialized bytes will be the + /// minimum of the size of the old and new objects, with remaining + /// bytes initialized as specified by zeroMemory. + void executeAlloc(ExecutionState &state, + ref<Expr> size, + bool isLocal, + KInstruction *target, + bool zeroMemory=false, + const ObjectState *reallocFrom=0); + + /// XXX not for public use (this is for histar, it allocations a + /// contiguous set of objects, while guaranteeing page alignment) + void executeAllocN(ExecutionState &state, + uint64_t nelems, + uint64_t size, + uint64_t alignment, + bool isLocal, + KInstruction *target); + + /// Free the given address with checking for errors. If target is + /// given it will be bound to 0 in the resulting states (this is a + /// convenience for realloc). Note that this function can cause the + /// state to fork and that \ref state cannot be safely accessed + /// afterwards. + void executeFree(ExecutionState &state, + ref<Expr> address, + KInstruction *target = 0); + + void executeCall(ExecutionState &state, + KInstruction *ki, + llvm::Function *f, + std::vector< ref<Expr> > &arguments); + + // do address resolution / object binding / out of bounds checking + // and perform the operation + void executeMemoryOperation(ExecutionState &state, + bool isWrite, + ref<Expr> address, + ref<Expr> value /* undef if read */, + KInstruction *target /* undef if write */); + + void executeMakeSymbolic(ExecutionState &state, const MemoryObject *mo); + + /// Create a new state where each input condition has been added as + /// a constraint and return the results. The input state is included + /// as one of the results. Note that the output vector may included + /// NULL pointers for states which were unable to be created. + void branch(ExecutionState &state, + const std::vector< ref<Expr> > &conditions, + std::vector<ExecutionState*> &result); + + // Fork current and return states in which condition holds / does + // not hold, respectively. One of the states is necessarily the + // current state, and one of the states may be null. + StatePair fork(ExecutionState ¤t, ref<Expr> condition, bool isInternal); + + /// Add the given (boolean) condition as a constraint on state. This + /// function is a wrapper around the state's addConstraint function + /// which also manages manages propogation of implied values, + /// validity checks, and seed patching. + void addConstraint(ExecutionState &state, ref<Expr> condition); + + // Called on [for now] concrete reads, replaces constant with a symbolic + // Used for testing. + ref<Expr> replaceReadWithSymbolic(ExecutionState &state, ref<Expr> e); + + ref<Expr> eval(KInstruction *ki, + unsigned index, + ExecutionState &state); + + void bindLocal(KInstruction *target, + ExecutionState &state, + ref<Expr> value); + void bindArgument(KFunction *kf, + unsigned index, + ExecutionState &state, + ref<Expr> value); + + ref<Expr> evalConstantExpr(llvm::ConstantExpr *ce); + + /// Return a unique constant value for the given expression in the + /// given state, if it has one (i.e. it provably only has a single + /// value). Otherwise return the original expression. + ref<Expr> toUnique(const ExecutionState &state, ref<Expr> &e); + + /// Return a constant value for the given expression, forcing it to + /// be constant in the given state by adding a constraint if + /// necessary. Note that this function breaks completeness and + /// should generally be avoided. + /// + /// \param purpose An identify string to printed in case of concretization. + ref<Expr> toConstant(ExecutionState &state, ref<Expr> e, const char *purpose); + + /// Bind a constant value for e to the given target. NOTE: This + /// function may fork state if the state has multiple seeds. + void executeGetValue(ExecutionState &state, ref<Expr> e, KInstruction *target); + + /// Get textual information regarding a memory address. + std::string getAddressInfo(ExecutionState &state, ref<Expr> address) const; + + // remove state from queue and delete + void terminateState(ExecutionState &state); + // call exit handler and terminate state + void terminateStateEarly(ExecutionState &state, std::string message); + // call exit handler and terminate state + void terminateStateOnExit(ExecutionState &state); + // call error handler and terminate state + void terminateStateOnError(ExecutionState &state, + const std::string &message, + const std::string &suffix, + const std::string &longMessage=""); + + // call error handler and terminate state, for execution errors + // (things that should not be possible, like illegal instruction or + // unlowered instrinsic, or are unsupported, like inline assembly) + void terminateStateOnExecError(ExecutionState &state, + const std::string &message, + const std::string &info="") { + terminateStateOnError(state, message, "exec.err", info); + } + + /// bindModuleConstants - Initialize the module constant table. + void bindModuleConstants(); + + /// bindInstructionConstants - Initialize any necessary per instruction + /// constant values. + void bindInstructionConstants(KInstruction *KI); + + void handlePointsToObj(ExecutionState &state, + KInstruction *target, + const std::vector<ref<Expr> > &arguments); + + void doImpliedValueConcretization(ExecutionState &state, + ref<Expr> e, + ref<Expr> value); + + /// Add a timer to be executed periodically. + /// + /// \param timer The timer object to run on firings. + /// \param rate The approximate delay (in seconds) between firings. + void addTimer(Timer *timer, double rate); + + void initTimers(); + void processTimers(ExecutionState *current, + double maxInstTime); + +public: + Executor(const InterpreterOptions &opts, InterpreterHandler *ie); + virtual ~Executor(); + + const InterpreterHandler& getHandler() { + return *interpreterHandler; + } + + // XXX should just be moved out to utility module + ref<Expr> evalConstant(llvm::Constant *c); + + virtual void setPathWriter(TreeStreamWriter *tsw) { + pathWriter = tsw; + } + virtual void setSymbolicPathWriter(TreeStreamWriter *tsw) { + symPathWriter = tsw; + } + + virtual void setReplayOut(const struct BOut *out) { + assert(!replayPath && "cannot replay both buffer and path"); + replayOut = out; + replayPosition = 0; + } + + virtual void setReplayPath(const std::vector<bool> *path) { + assert(!replayOut && "cannot replay both buffer and path"); + replayPath = path; + replayPosition = 0; + } + + virtual const llvm::Module * + setModule(llvm::Module *module, const ModuleOptions &opts); + + virtual void useSeeds(const std::vector<struct BOut *> *seeds) { + usingSeeds = seeds; + } + + virtual void runFunctionAsMain(llvm::Function *f, + int argc, + char **argv, + char **envp); + + /*** Runtime options ***/ + + virtual void setHaltExecution(bool value) { + haltExecution = value; + } + + virtual void setInhibitForking(bool value) { + inhibitForking = value; + } + + /*** State accessor methods ***/ + + virtual unsigned getPathStreamID(const ExecutionState &state); + + virtual unsigned getSymbolicPathStreamID(const ExecutionState &state); + + virtual void getConstraintLog(const ExecutionState &state, + std::string &res, + bool asCVC = false); + + virtual bool getSymbolicSolution(const ExecutionState &state, + std::vector< + std::pair<std::string, + std::vector<unsigned char> > > + &res); + + virtual void getCoveredLines(const ExecutionState &state, + std::map<const std::string*, std::set<unsigned> > &res); +}; + +} // End klee namespace + +#endif diff --git a/lib/Core/ExecutorTimers.cpp b/lib/Core/ExecutorTimers.cpp new file mode 100644 index 00000000..51792e0d --- /dev/null +++ b/lib/Core/ExecutorTimers.cpp @@ -0,0 +1,220 @@ +//===-- ExecutorTimers.cpp ------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Common.h" + +#include "CoreStats.h" +#include "Executor.h" +#include "PTree.h" +#include "StatsTracker.h" + +#include "klee/ExecutionState.h" +#include "klee/Internal/Module/InstructionInfoTable.h" +#include "klee/Internal/Module/KInstruction.h" +#include "klee/Internal/Module/KModule.h" +#include "klee/Internal/System/Time.h" + +#include "llvm/Function.h" +#include "llvm/Support/CommandLine.h" + +#include <unistd.h> +#include <signal.h> +#include <sys/time.h> +#include <math.h> + + +using namespace llvm; +using namespace klee; + +cl::opt<double> +MaxTime("max-time", + cl::desc("Halt execution after the specified number of seconds (0=off)"), + cl::init(0)); + +/// + +class HaltTimer : public Executor::Timer { + Executor *executor; + +public: + HaltTimer(Executor *_executor) : executor(_executor) {} + ~HaltTimer() {} + + void run() { + llvm::cerr << "KLEE: HaltTimer invoked\n"; + executor->setHaltExecution(true); + } +}; + +/// + +static const double kSecondsPerTick = .1; +static volatile unsigned timerTicks = 0; + +// XXX hack +extern "C" unsigned dumpStates, dumpPTree; +unsigned dumpStates = 0, dumpPTree = 0; + +static void onAlarm(int) { + ++timerTicks; +} + +// oooogalay +static void setupHandler() { + struct itimerval t; + struct timeval tv; + + tv.tv_sec = (long) kSecondsPerTick; + tv.tv_usec = (long) (fmod(kSecondsPerTick, 1.)*1000000); + + t.it_interval = t.it_value = tv; + + ::setitimer(ITIMER_REAL, &t, 0); + ::signal(SIGALRM, onAlarm); +} + +void Executor::initTimers() { + static bool first = true; + + if (first) { + first = false; + setupHandler(); + } + + if (MaxTime) { + addTimer(new HaltTimer(this), MaxTime); + } +} + +/// + +Executor::Timer::Timer() {} + +Executor::Timer::~Timer() {} + +class Executor::TimerInfo { +public: + Timer *timer; + + /// Approximate delay per timer firing. + double rate; + /// Wall time for next firing. + double nextFireTime; + +public: + TimerInfo(Timer *_timer, double _rate) + : timer(_timer), + rate(_rate), + nextFireTime(util::getWallTime() + rate) {} + ~TimerInfo() { delete timer; } +}; + +void Executor::addTimer(Timer *timer, double rate) { + timers.push_back(new TimerInfo(timer, rate)); +} + +void Executor::processTimers(ExecutionState *current, + double maxInstTime) { + static unsigned callsWithoutCheck = 0; + unsigned ticks = timerTicks; + + if (!ticks && ++callsWithoutCheck > 1000) { + setupHandler(); + ticks = 1; + } + + if (ticks || dumpPTree || dumpStates) { + if (dumpPTree) { + char name[32]; + sprintf(name, "ptree%08d.dot", (int) stats::instructions); + std::ostream *os = interpreterHandler->openOutputFile(name); + if (os) { + processTree->dump(*os); + delete os; + } + + dumpPTree = 0; + } + + if (dumpStates) { + std::ostream *os = interpreterHandler->openOutputFile("states.txt"); + + if (os) { + for (std::set<ExecutionState*>::const_iterator it = states.begin(), + ie = states.end(); it != ie; ++it) { + ExecutionState *es = *it; + *os << "(" << es << ","; + *os << "["; + ExecutionState::stack_ty::iterator next = es->stack.begin(); + ++next; + for (ExecutionState::stack_ty::iterator sfIt = es->stack.begin(), + sf_ie = es->stack.end(); sfIt != sf_ie; ++sfIt) { + *os << "('" << sfIt->kf->function->getName() << "',"; + if (next == es->stack.end()) { + *os << es->prevPC->info->line << "), "; + } else { + *os << next->caller->info->line << "), "; + ++next; + } + } + *os << "], "; + + StackFrame &sf = es->stack.back(); + uint64_t md2u = computeMinDistToUncovered(es->pc, + sf.minDistToUncoveredOnReturn); + uint64_t icnt = theStatisticManager->getIndexedValue(stats::instructions, + es->pc->info->id); + uint64_t cpicnt = sf.callPathNode->statistics.getValue(stats::instructions); + + *os << "{"; + *os << "'depth' : " << es->depth << ", "; + *os << "'weight' : " << es->weight << ", "; + *os << "'queryCost' : " << es->queryCost << ", "; + *os << "'coveredNew' : " << es->coveredNew << ", "; + *os << "'instsSinceCovNew' : " << es->instsSinceCovNew << ", "; + *os << "'md2u' : " << md2u << ", "; + *os << "'icnt' : " << icnt << ", "; + *os << "'CPicnt' : " << cpicnt << ", "; + *os << "}"; + *os << ")\n"; + } + + delete os; + } + + dumpStates = 0; + } + + if (maxInstTime>0 && current && !removedStates.count(current)) { + if (timerTicks*kSecondsPerTick > maxInstTime) { + klee_warning("max-instruction-time exceeded: %.2fs", + timerTicks*kSecondsPerTick); + terminateStateEarly(*current, "max-instruction-time exceeded"); + } + } + + if (!timers.empty()) { + double time = util::getWallTime(); + + for (std::vector<TimerInfo*>::iterator it = timers.begin(), + ie = timers.end(); it != ie; ++it) { + TimerInfo *ti = *it; + + if (time >= ti->nextFireTime) { + ti->timer->run(); + ti->nextFireTime = time + ti->rate; + } + } + } + + timerTicks = 0; + callsWithoutCheck = 0; + } +} + diff --git a/lib/Core/ExecutorUtil.cpp b/lib/Core/ExecutorUtil.cpp new file mode 100644 index 00000000..3b11dd42 --- /dev/null +++ b/lib/Core/ExecutorUtil.cpp @@ -0,0 +1,144 @@ +//===-- ExecutorUtil.cpp --------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Executor.h" + +#include "klee/Expr.h" +#include "klee/Interpreter.h" +#include "klee/Machine.h" +#include "klee/Solver.h" + +#include "klee/Internal/Module/KModule.h" + +#include "llvm/Constants.h" +#include "llvm/Function.h" +#include "llvm/Instructions.h" +#include "llvm/Module.h" +#include "llvm/ModuleProvider.h" +#include "llvm/Support/CallSite.h" +#include "llvm/Support/GetElementPtrTypeIterator.h" +#include "llvm/Support/Streams.h" +#include "llvm/Target/TargetData.h" +#include <iostream> +#include <cassert> + +using namespace klee; +using namespace llvm; + +namespace klee { + +ref<Expr> +Executor::evalConstantExpr(llvm::ConstantExpr *ce) { + const llvm::Type *type = ce->getType(); + + ref<Expr> op1(0,Expr::Bool), op2(0,Expr::Bool), op3(0,Expr::Bool); + int numOperands = ce->getNumOperands(); + + if (numOperands > 0) op1 = evalConstant(ce->getOperand(0)); + if (numOperands > 1) op2 = evalConstant(ce->getOperand(1)); + if (numOperands > 2) op3 = evalConstant(ce->getOperand(2)); + + switch (ce->getOpcode()) { + case Instruction::Trunc: return ExtractExpr::createByteOff(op1, + 0, + Expr::getWidthForLLVMType(type)); + case Instruction::ZExt: return ZExtExpr::create(op1, + Expr::getWidthForLLVMType(type)); + case Instruction::SExt: return SExtExpr::create(op1, + Expr::getWidthForLLVMType(type)); + case Instruction::Add: return AddExpr::create(op1, op2); + case Instruction::Sub: return SubExpr::create(op1, op2); + case Instruction::Mul: return MulExpr::create(op1, op2); + case Instruction::SDiv: return SDivExpr::create(op1, op2); + case Instruction::UDiv: return UDivExpr::create(op1, op2); + case Instruction::SRem: return SRemExpr::create(op1, op2); + case Instruction::URem: return URemExpr::create(op1, op2); + case Instruction::And: return AndExpr::create(op1, op2); + case Instruction::Or: return OrExpr::create(op1, op2); + case Instruction::Xor: return XorExpr::create(op1, op2); + case Instruction::Shl: return ShlExpr::create(op1, op2); + case Instruction::LShr: return LShrExpr::create(op1, op2); + case Instruction::AShr: return AShrExpr::create(op1, op2); + case Instruction::BitCast: return op1; + + case Instruction::IntToPtr: { + return ZExtExpr::create(op1, Expr::getWidthForLLVMType(type)); + } + + case Instruction::PtrToInt: { + return ZExtExpr::create(op1, Expr::getWidthForLLVMType(type)); + } + + case Instruction::GetElementPtr: { + ref<Expr> base = op1; + + for (gep_type_iterator ii = gep_type_begin(ce), ie = gep_type_end(ce); + ii != ie; ++ii) { + ref<Expr> addend(0, kMachinePointerType); + + if (const StructType *st = dyn_cast<StructType>(*ii)) { + const StructLayout *sl = kmodule->targetData->getStructLayout(st); + const ConstantInt *ci = cast<ConstantInt>(ii.getOperand()); + + addend = Expr::createPointer(sl->getElementOffset((unsigned) + ci->getZExtValue())); + } else { + const SequentialType *st = cast<SequentialType>(*ii); + ref<Expr> index = evalConstant(cast<Constant>(ii.getOperand())); + unsigned elementSize = kmodule->targetData->getTypeStoreSize(st->getElementType()); + + index = Expr::createCoerceToPointerType(index); + addend = MulExpr::create(index, + Expr::createPointer(elementSize)); + } + + base = AddExpr::create(base, addend); + } + + return base; + } + + case Instruction::ICmp: { + switch(ce->getPredicate()) { + case ICmpInst::ICMP_EQ: return EqExpr::create(op1, op2); + case ICmpInst::ICMP_NE: return NeExpr::create(op1, op2); + case ICmpInst::ICMP_UGT: return UgtExpr::create(op1, op2); + case ICmpInst::ICMP_UGE: return UgeExpr::create(op1, op2); + case ICmpInst::ICMP_ULT: return UltExpr::create(op1, op2); + case ICmpInst::ICMP_ULE: return UleExpr::create(op1, op2); + case ICmpInst::ICMP_SGT: return SgtExpr::create(op1, op2); + case ICmpInst::ICMP_SGE: return SgeExpr::create(op1, op2); + case ICmpInst::ICMP_SLT: return SltExpr::create(op1, op2); + case ICmpInst::ICMP_SLE: return SleExpr::create(op1, op2); + default: + assert(0 && "unhandled ICmp predicate"); + } + } + + case Instruction::Select: { + return SelectExpr::create(op1, op2, op3); + } + + case Instruction::FDiv: + case Instruction::FRem: + case Instruction::FPTrunc: + case Instruction::FPExt: + case Instruction::UIToFP: + case Instruction::SIToFP: + case Instruction::FPToUI: + case Instruction::FPToSI: + case Instruction::FCmp: + assert(0 && "floating point ConstantExprs unsupported"); + + default : + assert(0 && "unknown ConstantExpr type"); + } +} + +} diff --git a/lib/Core/ExternalDispatcher.cpp b/lib/Core/ExternalDispatcher.cpp new file mode 100644 index 00000000..9e3b0a49 --- /dev/null +++ b/lib/Core/ExternalDispatcher.cpp @@ -0,0 +1,230 @@ +//===-- ExternalDispatcher.cpp --------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ExternalDispatcher.h" + +#include "llvm/Module.h" +#include "llvm/Constants.h" +#include "llvm/DerivedTypes.h" +#include "llvm/Instructions.h" +#include "llvm/ModuleProvider.h" +#include "llvm/ExecutionEngine/JIT.h" +#include "llvm/ExecutionEngine/GenericValue.h" +#include "llvm/Support/CallSite.h" +#include "llvm/System/DynamicLibrary.h" +#include "llvm/Support/Streams.h" +#include "llvm/Support/raw_ostream.h" +#include <setjmp.h> +#include <signal.h> + +using namespace llvm; +using namespace klee; + +/***/ + +static jmp_buf escapeCallJmpBuf; + +extern "C" { + +static void sigsegv_handler(int signal, siginfo_t *info, void *context) { + longjmp(escapeCallJmpBuf, 1); +} + +} + +void *ExternalDispatcher::resolveSymbol(const std::string &name) { + assert(executionEngine); + + const char *str = name.c_str(); + + // We use this to validate that function names can be resolved so we + // need to match how the JIT does it. Unfortunately we can't + // directly access the JIT resolution function + // JIT::getPointerToNamedFunction so we emulate the important points. + + if (str[0] == 1) // asm specifier, skipped + ++str; + + void *addr = dl_symbols.SearchForAddressOfSymbol(str); + if (addr) + return addr; + + // If it has an asm specifier and starts with an underscore we retry + // without the underscore. I (DWD) don't know why. + if (name[0] == 1 && str[0]=='_') { + ++str; + addr = dl_symbols.SearchForAddressOfSymbol(str); + } + + return addr; +} + +ExternalDispatcher::ExternalDispatcher() { + dispatchModule = new Module("ExternalDispatcher"); + ExistingModuleProvider* MP = new ExistingModuleProvider(dispatchModule); + + std::string error; + executionEngine = ExecutionEngine::createJIT(MP, &error); + if (!executionEngine) { + llvm::cerr << "unable to make jit: " << error << "\n"; + abort(); + } + + // from ExecutionEngine::create + if (executionEngine) { + // Make sure we can resolve symbols in the program as well. The zero arg + // to the function tells DynamicLibrary to load the program, not a library. + try { + dl_symbols.LoadLibraryPermanently(0); + } catch (...) { + assert(0 && "Exception in LoadLibraryPermantently.\n"); + } + } + +#ifdef WINDOWS + preboundFunctions["getpid"] = (void*) (long) getpid; + preboundFunctions["putchar"] = (void*) (long) putchar; + preboundFunctions["printf"] = (void*) (long) printf; + preboundFunctions["fprintf"] = (void*) (long) fprintf; + preboundFunctions["sprintf"] = (void*) (long) sprintf; +#endif +} + +ExternalDispatcher::~ExternalDispatcher() { + delete executionEngine; +} + +bool ExternalDispatcher::executeCall(Function *f, Instruction *i, uint64_t *args) { + dispatchers_ty::iterator it = dispatchers.find(i); + Function *dispatcher; + + if (it == dispatchers.end()) { +#ifdef WINDOWS + std::map<std::string, void*>::iterator it2 = + preboundFunctions.find(f->getName())); + + if (it2 != preboundFunctions.end()) { + // only bind once + if (it2->second) { + executionEngine->addGlobalMapping(f, it2->second); + it2->second = 0; + } + } +#endif + + dispatcher = createDispatcher(f,i); + + dispatchers.insert(std::make_pair(i, dispatcher)); + + if (dispatcher) { + // force the JIT execution engine to go ahead and build the + // function. this ensures that any errors or assertions in the + // compilation process will trigger crashes instead of being + // caught as aborts in the external function. + executionEngine->recompileAndRelinkFunction(dispatcher); + } + } else { + dispatcher = it->second; + } + + return runProtectedCall(dispatcher, args); +} + +// XXX not reentrant +static uint64_t *gTheArgsP; + +bool ExternalDispatcher::runProtectedCall(Function *f, uint64_t *args) { + struct sigaction segvAction, segvActionOld; + bool res; + + if (!f) + return false; + + std::vector<GenericValue> gvArgs; + gTheArgsP = args; + + segvAction.sa_handler = 0; + memset(&segvAction.sa_mask, 0, sizeof(segvAction.sa_mask)); + segvAction.sa_flags = SA_SIGINFO; + segvAction.sa_sigaction = ::sigsegv_handler; + sigaction(SIGSEGV, &segvAction, &segvActionOld); + + if (setjmp(escapeCallJmpBuf)) { + res = false; + } else { + executionEngine->runFunction(f, gvArgs); + res = true; + } + + sigaction(SIGSEGV, &segvActionOld, 0); + return res; +} + +// for performance purposes we construct the stub in such a way that +// the arguments pointer is passed through the static global variable +// gTheArgsP in this file. This is done so that the stub function +// prototype trivially matches the special cases that the JIT knows +// how to directly call. If this is not done, then the jit will end up +// generating a nullary stub just to call our stub, for every single +// function call. +Function *ExternalDispatcher::createDispatcher(Function *target, Instruction *inst) { + if (!resolveSymbol(target->getName())) + return 0; + + CallSite cs; + if (inst->getOpcode()==Instruction::Call) { + cs = CallSite(cast<CallInst>(inst)); + } else { + cs = CallSite(cast<InvokeInst>(inst)); + } + + Value **args = new Value*[cs.arg_size()]; + + std::vector<const Type*> nullary; + + Function *dispatcher = Function::Create(FunctionType::get(Type::VoidTy, + nullary, false), + GlobalVariable::ExternalLinkage, + "", + dispatchModule); + + + BasicBlock *dBB = BasicBlock::Create("entry", dispatcher); + + Instruction *argI64sp = new IntToPtrInst(ConstantInt::get(Type::Int64Ty, (long) (void*) &gTheArgsP), + PointerType::getUnqual(PointerType::getUnqual(Type::Int64Ty)), + "argsp", + dBB); + Instruction *argI64s = new LoadInst(argI64sp, "args", dBB); + + unsigned i = 0; + for (CallSite::arg_iterator ai = cs.arg_begin(), ae = cs.arg_end(); + ai!=ae; ++ai, ++i) { + Value *index = ConstantInt::get(Type::Int32Ty, i+1); + + Instruction *argI64p = GetElementPtrInst::Create(argI64s, index, "", dBB); + Instruction *argp = new BitCastInst(argI64p, + PointerType::getUnqual((*ai)->getType()), "", dBB); + args[i] = new LoadInst(argp, "", dBB); + } + + Instruction *result = CallInst::Create(target, args, args+i, "", dBB); + + if (result->getType() != Type::VoidTy) { + Instruction *resp = new BitCastInst(argI64s, + PointerType::getUnqual(result->getType()), "", dBB); + new StoreInst(result, resp, dBB); + } + + ReturnInst::Create(dBB); + + delete[] args; + + return dispatcher; +} diff --git a/lib/Core/ExternalDispatcher.h b/lib/Core/ExternalDispatcher.h new file mode 100644 index 00000000..fc8f80f4 --- /dev/null +++ b/lib/Core/ExternalDispatcher.h @@ -0,0 +1,50 @@ +//===-- ExternalDispatcher.h ------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_EXTERNALDISPATCHER_H +#define KLEE_EXTERNALDISPATCHER_H + +#include <map> +#include "llvm/System/DynamicLibrary.h" + +namespace llvm { + class ExecutionEngine; + class Instruction; + class Function; + class FunctionType; + class Module; +} + +namespace klee { + class ExternalDispatcher { + private: + typedef std::map<const llvm::Instruction*,llvm::Function*> dispatchers_ty; + dispatchers_ty dispatchers; + llvm::Module *dispatchModule; + llvm::ExecutionEngine *executionEngine; + llvm::sys::DynamicLibrary dl_symbols; + std::map<std::string, void*> preboundFunctions; + + llvm::Function *createDispatcher(llvm::Function *f, llvm::Instruction *i); + bool runProtectedCall(llvm::Function *f, uint64_t *args); + + public: + ExternalDispatcher(); + ~ExternalDispatcher(); + + /* Call the given function using the parameter passing convention of + * ci with arguments in args[1], args[2], ... and writing the result + * into args[0]. + */ + bool executeCall(llvm::Function *function, llvm::Instruction *i, uint64_t *args); + void *resolveSymbol(const std::string &name); + }; +} + +#endif diff --git a/lib/Core/ImpliedValue.cpp b/lib/Core/ImpliedValue.cpp new file mode 100644 index 00000000..386c8d80 --- /dev/null +++ b/lib/Core/ImpliedValue.cpp @@ -0,0 +1,274 @@ +//===-- ImpliedValue.cpp --------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ImpliedValue.h" + +#include "klee/Constraints.h" +#include "klee/Expr.h" +#include "klee/Solver.h" +// FIXME: Use APInt. +#include "klee/Internal/Support/IntEvaluation.h" + +#include "klee/util/ExprUtil.h" + +#include <iostream> +#include <map> +#include <set> + +using namespace klee; + +// XXX we really want to do some sort of canonicalization of exprs +// globally so that cases below become simpler +static void _getImpliedValue(ref<Expr> e, + uint64_t value, + ImpliedValueList &results) { + switch (e.getKind()) { + + case Expr::Constant: { + assert(value == e.getConstantValue() && "error in implied value calculation"); + break; + } + + // Special + + case Expr::NotOptimized: break; + + case Expr::Read: { + // XXX in theory it is possible to descend into a symbolic index + // under certain circumstances (all values known, known value + // unique, or range known, max / min hit). Seems unlikely this + // would work often enough to be worth the effort. + ReadExpr *re = static_ref_cast<ReadExpr>(e); + results.push_back(std::make_pair(re, + ConstantExpr::create(value, e.getWidth()))); + break; + } + + case Expr::Select: { + // not much to do, could improve with range analysis + SelectExpr *se = static_ref_cast<SelectExpr>(e); + + if (se->trueExpr.isConstant()) { + if (se->falseExpr.isConstant()) { + if (se->trueExpr.getConstantValue() != se->falseExpr.getConstantValue()) { + if (value == se->trueExpr.getConstantValue()) { + _getImpliedValue(se->cond, 1, results); + } else { + assert(value == se->falseExpr.getConstantValue() && + "err in implied value calculation"); + _getImpliedValue(se->cond, 0, results); + } + } + } + } + break; + } + + case Expr::Concat: { + ConcatExpr *ce = static_ref_cast<ConcatExpr>(e); + _getImpliedValue(ce->getKid(0), (value >> ce->getKid(1).getWidth()) & ((1 << ce->getKid(0).getWidth()) - 1), results); + _getImpliedValue(ce->getKid(1), value & ((1 << ce->getKid(1).getWidth()) - 1), results); + break; + } + + case Expr::Extract: { + // XXX, could do more here with "some bits" mask + break; + } + + // Casting + + case Expr::ZExt: + case Expr::SExt: { + CastExpr *ce = static_ref_cast<CastExpr>(e); + _getImpliedValue(ce->src, + bits64::truncateToNBits(value, + ce->src.getWidth()), + results); + break; + } + + // Arithmetic + + case Expr::Add: { // constants on left + BinaryExpr *be = static_ref_cast<BinaryExpr>(e); + if (be->left.isConstant()) { + uint64_t nvalue = ints::sub(value, + be->left.getConstantValue(), + be->left.getWidth()); + _getImpliedValue(be->right, nvalue, results); + } + break; + } + case Expr::Sub: { // constants on left + BinaryExpr *be = static_ref_cast<BinaryExpr>(e); + if (be->left.isConstant()) { + uint64_t nvalue = ints::sub(be->left.getConstantValue(), + value, + be->left.getWidth()); + _getImpliedValue(be->right, nvalue, results); + } + break; + } + case Expr::Mul: { + // XXX can do stuff here, but need valid mask and other things + // because of bits that might be lost + break; + } + + case Expr::UDiv: + case Expr::SDiv: + case Expr::URem: + case Expr::SRem: + // no, no, no + break; + + // Binary + + case Expr::And: { + BinaryExpr *be = static_ref_cast<BinaryExpr>(e); + if (be->getWidth() == Expr::Bool) { + if (value) { + _getImpliedValue(be->left, value, results); + _getImpliedValue(be->right, value, results); + } + } else { + // XXX, we can basically propogate a mask here + // where we know "some bits". may or may not be + // useful. + } + break; + } + case Expr::Or: { + BinaryExpr *be = static_ref_cast<BinaryExpr>(e); + if (!value) { + _getImpliedValue(be->left, 0, results); + _getImpliedValue(be->right, 0, results); + } else { + // XXX, can do more? + } + break; + } + case Expr::Xor: { // constants on left + BinaryExpr *be = static_ref_cast<BinaryExpr>(e); + if (be->left.isConstant()) { + _getImpliedValue(be->right, value ^ be->left.getConstantValue(), results); + } + break; + } + + // Comparison + case Expr::Ne: + value = !value; + /* fallthru */ + case Expr::Eq: { + EqExpr *ee = static_ref_cast<EqExpr>(e); + if (value) { + if (ee->left.isConstant()) + _getImpliedValue(ee->right, ee->left.getConstantValue(), results); + } else { + // look for limited value range, woohoo + // + // in general anytime one side was restricted to two values we + // can apply this trick. the only obvious case where this + // occurs, aside from booleans, is as the result of a select + // expression where the true and false branches are single + // valued and distinct. + + if (ee->left.isConstant()) { + if (ee->left.getWidth() == Expr::Bool) { + _getImpliedValue(ee->right, !ee->left.getConstantValue(), results); + } + } + } + break; + } + + default: + break; + } +} + +void ImpliedValue::getImpliedValues(ref<Expr> e, + ref<Expr> value, + ImpliedValueList &results) { + assert(value.isConstant() && "non-constant in place of constant"); + _getImpliedValue(e, value.getConstantValue(), results); +} + +void ImpliedValue::checkForImpliedValues(Solver *S, ref<Expr> e, + ref<Expr> value) { + assert(value.isConstant() && "non-constant in place of constant"); + + std::vector<ref<ReadExpr> > reads; + std::map<ref<ReadExpr>, ref<Expr> > found; + ImpliedValueList results; + + getImpliedValues(e, value, results); + + for (ImpliedValueList::iterator i = results.begin(), ie = results.end(); + i != ie; ++i) { + std::map<ref<ReadExpr>, ref<Expr> >::iterator it = found.find(i->first); + if (it != found.end()) { + assert(it->second.getConstantValue() == i->second.getConstantValue() && + "I don't think so Scott"); + } else { + found.insert(std::make_pair(i->first, i->second)); + } + } + + findReads(e, false, reads); + std::set< ref<ReadExpr> > readsSet(reads.begin(), reads.end()); + reads = std::vector< ref<ReadExpr> >(readsSet.begin(), readsSet.end()); + + std::vector<ref<Expr> > assumption; + assumption.push_back(EqExpr::create(e, value)); + + // obscure... we need to make sure that all the read indices are + // bounds checked. if we don't do this we can end up constructing + // invalid counterexamples because STP will happily make out of + // bounds indices which will not get picked up. this is of utmost + // importance if we are being backed by the CexCachingSolver. + + for (std::vector< ref<ReadExpr> >::iterator i = reads.begin(), + ie = reads.end(); i != ie; ++i) { + ReadExpr *re = i->get(); + ref<Expr> size = ref<Expr>(re->updates.root->size, kMachinePointerType); + assumption.push_back(UltExpr::create(re->index, size)); + } + + ConstraintManager assume(assumption); + for (std::vector< ref<ReadExpr> >::iterator i = reads.begin(), + ie = reads.end(); i != ie; ++i) { + ref<ReadExpr> var = *i; + ref<Expr> possible; + bool success = S->getValue(Query(assume, var), possible); + assert(success && "FIXME: Unhandled solver failure"); + std::map<ref<ReadExpr>, ref<Expr> >::iterator it = found.find(var); + bool res; + success = S->mustBeTrue(Query(assume, EqExpr::create(var, possible)), res); + assert(success && "FIXME: Unhandled solver failure"); + if (res) { + if (it != found.end()) { + assert(possible.getConstantValue() == it->second.getConstantValue()); + found.erase(it); + } + } else { + if (it!=found.end()) { + ref<Expr> binding = it->second; + llvm::cerr << "checkForImpliedValues: " << e << " = " << value << "\n" + << "\t\t implies " << var << " == " << binding + << " (error)\n"; + assert(0); + } + } + } + + assert(found.empty()); +} diff --git a/lib/Core/ImpliedValue.h b/lib/Core/ImpliedValue.h new file mode 100644 index 00000000..51ec6e9b --- /dev/null +++ b/lib/Core/ImpliedValue.h @@ -0,0 +1,38 @@ +//===-- ImpliedValue.h ------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_IMPLIEDVALUE_H +#define KLEE_IMPLIEDVALUE_H + +#include "klee/Expr.h" + +#include <vector> + +// The idea of implied values is that often we know the result of some +// expression e is a concrete value C. In many cases this directly +// implies that some variable x embedded in e is also a concrete value +// (derived from C). This module is used for finding such variables +// and their computed values. + +namespace klee { + class ConstantExpr; + class Expr; + class ReadExpr; + class Solver; + + typedef std::vector< std::pair<ref<ReadExpr>, ref<Expr> > > ImpliedValueList; + + namespace ImpliedValue { + void getImpliedValues(ref<Expr> e, ref<Expr> cvalue, ImpliedValueList &result); + void checkForImpliedValues(Solver *S, ref<Expr> e, ref<Expr> cvalue); + } + +} + +#endif diff --git a/lib/Core/Makefile b/lib/Core/Makefile new file mode 100755 index 00000000..4da3c7ea --- /dev/null +++ b/lib/Core/Makefile @@ -0,0 +1,16 @@ +#===-- lib/Core/Makefile -----------------------------------*- Makefile -*--===# +# +# The KLEE Symbolic Virtual Machine +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +LEVEL=../.. + +LIBRARYNAME=kleeCore +DONT_BUILD_RELINKED=1 +BUILD_ARCHIVE=1 + +include $(LEVEL)/Makefile.common diff --git a/lib/Core/Memory.cpp b/lib/Core/Memory.cpp new file mode 100644 index 00000000..cd563551 --- /dev/null +++ b/lib/Core/Memory.cpp @@ -0,0 +1,812 @@ +//===-- Memory.cpp --------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Common.h" + +#include "Memory.h" + +#include "klee/Expr.h" +#include "klee/Machine.h" +#include "klee/Solver.h" +#include "klee/util/BitArray.h" + +#include "ObjectHolder.h" + +#include <llvm/Function.h> +#include <llvm/Instruction.h> +#include <llvm/Value.h> + +#include <iostream> +#include <cassert> +#include <sstream> + +using namespace llvm; +using namespace klee; + +/***/ + +ObjectHolder::ObjectHolder(const ObjectHolder &b) : os(b.os) { + if (os) ++os->refCount; +} + +ObjectHolder::ObjectHolder(ObjectState *_os) : os(_os) { + if (os) ++os->refCount; +} + +ObjectHolder::~ObjectHolder() { + if (os && --os->refCount==0) delete os; +} + +ObjectHolder &ObjectHolder::operator=(const ObjectHolder &b) { + if (b.os) ++b.os->refCount; + if (os && --os->refCount==0) delete os; + os = b.os; + return *this; +} + +/***/ + +int MemoryObject::counter = 0; + +extern "C" void vc_DeleteExpr(void*); + +MemoryObject::~MemoryObject() { + // FIXME: This shouldn't be necessary. Array's should be ref-counted + // just like everything else, and the interaction with the STP array + // should hide at least inside the Expr/Solver layers. + if (array) { + if (array->stpInitialArray) { + ::vc_DeleteExpr(array->stpInitialArray); + array->stpInitialArray = 0; + } + delete array; + } +} + +void MemoryObject::getAllocInfo(std::string &result) const { + std::ostringstream info; + + info << "MO" << id << "[" << size << "]"; + + if (allocSite) { + info << " allocated at "; + if (const Instruction *i = dyn_cast<Instruction>(allocSite)) { + info << i->getParent()->getParent()->getName() << "():"; + info << *i; + } else if (const GlobalValue *gv = dyn_cast<GlobalValue>(allocSite)) { + info << "global:" << gv->getName(); + } else { + info << "value:" << *allocSite; + } + } else { + info << " (no allocation info)"; + } + + result = info.str(); +} + +/***/ + +ObjectState::ObjectState(const MemoryObject *mo, unsigned _size) + : copyOnWriteOwner(0), + refCount(0), + object(mo), + concreteStore(new uint8_t[_size]), + concreteMask(0), + flushMask(0), + knownSymbolics(0), + size(_size), + updates(mo->array, false, 0), + readOnly(false) { +} + +ObjectState::ObjectState(const ObjectState &os) + : copyOnWriteOwner(0), + refCount(0), + object(os.object), + concreteStore(new uint8_t[os.size]), + concreteMask(os.concreteMask ? new BitArray(*os.concreteMask, os.size) : 0), + flushMask(os.flushMask ? new BitArray(*os.flushMask, os.size) : 0), + knownSymbolics(0), + size(os.size), + updates(os.updates), + readOnly(false) { + assert(!os.readOnly && "no need to copy read only object?"); + + if (os.knownSymbolics) { + knownSymbolics = new ref<Expr>[size]; + for (unsigned i=0; i<size; i++) + knownSymbolics[i] = os.knownSymbolics[i]; + } + + memcpy(concreteStore, os.concreteStore, size*sizeof(*concreteStore)); +} + +ObjectState::~ObjectState() { + if (concreteMask) delete concreteMask; + if (flushMask) delete flushMask; + if (knownSymbolics) delete[] knownSymbolics; + delete[] concreteStore; +} + +/***/ + +void ObjectState::makeConcrete() { + if (concreteMask) delete concreteMask; + if (flushMask) delete flushMask; + if (knownSymbolics) delete[] knownSymbolics; + concreteMask = 0; + flushMask = 0; + knownSymbolics = 0; +} + +void ObjectState::makeSymbolic() { + assert(!updates.head && + "XXX makeSymbolic of objects with symbolic values is unsupported"); + updates.isRooted = true; + + // XXX simplify this, can just delete various arrays I guess + for (unsigned i=0; i<size; i++) { + markByteSymbolic(i); + setKnownSymbolic(i, 0); + markByteFlushed(i); + } +} + +void ObjectState::initializeToZero() { + makeConcrete(); + memset(concreteStore, 0, size); +} + +void ObjectState::initializeToRandom() { + makeConcrete(); + for (unsigned i=0; i<size; i++) { + // randomly selected by 256 sided die + concreteStore[i] = 0xAB; + } +} + +/* +Cache Invariants +-- +isByteKnownSymbolic(i) => !isByteConcrete(i) +isByteConcrete(i) => !isByteKnownSymbolic(i) +!isByteFlushed(i) => (isByteConcrete(i) || isByteKnownSymbolic(i)) + */ + +void ObjectState::fastRangeCheckOffset(ref<Expr> offset, + unsigned *base_r, + unsigned *size_r) const { + *base_r = 0; + *size_r = size; +} + +void ObjectState::flushRangeForRead(unsigned rangeBase, + unsigned rangeSize) const { + if (!flushMask) flushMask = new BitArray(size, true); + + for (unsigned offset=rangeBase; offset<rangeBase+rangeSize; offset++) { + if (!isByteFlushed(offset)) { + if (isByteConcrete(offset)) { + updates.extend(ConstantExpr::create(offset, kMachinePointerType), + ConstantExpr::create(concreteStore[offset], Expr::Int8)); + } else { + assert(isByteKnownSymbolic(offset) && "invalid bit set in flushMask"); + updates.extend(ConstantExpr::create(offset, kMachinePointerType), + knownSymbolics[offset]); + } + + flushMask->unset(offset); + } + } +} + +void ObjectState::flushRangeForWrite(unsigned rangeBase, + unsigned rangeSize) { + if (!flushMask) flushMask = new BitArray(size, true); + + for (unsigned offset=rangeBase; offset<rangeBase+rangeSize; offset++) { + if (!isByteFlushed(offset)) { + if (isByteConcrete(offset)) { + updates.extend(ConstantExpr::create(offset, kMachinePointerType), + ConstantExpr::create(concreteStore[offset], Expr::Int8)); + markByteSymbolic(offset); + } else { + assert(isByteKnownSymbolic(offset) && "invalid bit set in flushMask"); + updates.extend(ConstantExpr::create(offset, kMachinePointerType), + knownSymbolics[offset]); + setKnownSymbolic(offset, 0); + } + + flushMask->unset(offset); + } else { + // flushed bytes that are written over still need + // to be marked out + if (isByteConcrete(offset)) { + markByteSymbolic(offset); + } else if (isByteKnownSymbolic(offset)) { + setKnownSymbolic(offset, 0); + } + } + } +} + +bool ObjectState::isByteConcrete(unsigned offset) const { + return !concreteMask || concreteMask->get(offset); +} + +bool ObjectState::isByteFlushed(unsigned offset) const { + return flushMask && !flushMask->get(offset); +} + +bool ObjectState::isByteKnownSymbolic(unsigned offset) const { + return knownSymbolics && knownSymbolics[offset].get(); +} + +void ObjectState::markByteConcrete(unsigned offset) { + if (concreteMask) + concreteMask->set(offset); +} + +void ObjectState::markByteSymbolic(unsigned offset) { + if (!concreteMask) + concreteMask = new BitArray(size, true); + concreteMask->unset(offset); +} + +void ObjectState::markByteUnflushed(unsigned offset) { + if (flushMask) + flushMask->set(offset); +} + +void ObjectState::markByteFlushed(unsigned offset) { + if (!flushMask) { + flushMask = new BitArray(size, false); + } else { + flushMask->unset(offset); + } +} + +void ObjectState::setKnownSymbolic(unsigned offset, + Expr *value /* can be null */) { + if (knownSymbolics) { + knownSymbolics[offset] = value; + } else { + if (value) { + knownSymbolics = new ref<Expr>[size]; + knownSymbolics[offset] = value; + } + } +} + +/***/ + +ref<Expr> ObjectState::read8(unsigned offset) const { + if (isByteConcrete(offset)) { + return ConstantExpr::create(concreteStore[offset], Expr::Int8); + } else if (isByteKnownSymbolic(offset)) { + return knownSymbolics[offset]; + } else { + assert(isByteFlushed(offset) && "unflushed byte without cache value"); + + return ReadExpr::create(updates, + ConstantExpr::create(offset, kMachinePointerType)); + } +} + +ref<Expr> ObjectState::read8(ref<Expr> offset) const { + assert(!offset.isConstant() && "constant offset passed to symbolic read8"); + unsigned base, size; + fastRangeCheckOffset(offset, &base, &size); + flushRangeForRead(base, size); + + if (size>4096) { + std::string allocInfo; + object->getAllocInfo(allocInfo); + klee_warning_once(0, "flushing %d bytes on read, may be slow and/or crash: %s", + size, + allocInfo.c_str()); + } + + return ReadExpr::create(updates, offset); +} + +void ObjectState::write8(unsigned offset, uint8_t value) { + //assert(read_only == false && "writing to read-only object!"); + concreteStore[offset] = value; + setKnownSymbolic(offset, 0); + + markByteConcrete(offset); + markByteUnflushed(offset); +} + +void ObjectState::write8(unsigned offset, ref<Expr> value) { + // can happen when ExtractExpr special cases + if (value.isConstant()) { + write8(offset, (uint8_t) value.getConstantValue()); + } else { + setKnownSymbolic(offset, value.get()); + + markByteSymbolic(offset); + markByteUnflushed(offset); + } +} + +void ObjectState::write8(ref<Expr> offset, ref<Expr> value) { + assert(!offset.isConstant() && "constant offset passed to symbolic write8"); + unsigned base, size; + fastRangeCheckOffset(offset, &base, &size); + flushRangeForWrite(base, size); + + if (size>4096) { + std::string allocInfo; + object->getAllocInfo(allocInfo); + klee_warning_once(0, "flushing %d bytes on read, may be slow and/or crash: %s", + size, + allocInfo.c_str()); + } + + updates.extend(offset, value); +} + +/***/ + +ref<Expr> ObjectState::read(ref<Expr> offset, Expr::Width width) const { + if (offset.isConstant()) { + return read((unsigned) offset.getConstantValue(), width); + } else { + switch (width) { + case Expr::Bool: return read1(offset); + case Expr::Int8: return read8(offset); + case Expr::Int16: return read16(offset); + case Expr::Int32: return read32(offset); + case Expr::Int64: return read64(offset); + default: assert(0 && "invalid type"); + } + } +} + +ref<Expr> ObjectState::read(unsigned offset, Expr::Width width) const { + switch (width) { + case Expr::Bool: return read1(offset); + case Expr::Int8: return read8(offset); + case Expr::Int16: return read16(offset); + case Expr::Int32: return read32(offset); + case Expr::Int64: return read64(offset); + default: assert(0 && "invalid type"); + } +} + +ref<Expr> ObjectState::read1(unsigned offset) const { + return ExtractExpr::createByteOff(read8(offset), 0, Expr::Bool); +} + +ref<Expr> ObjectState::read1(ref<Expr> offset) const { + return ExtractExpr::createByteOff(read8(offset), 0, Expr::Bool); +} + +ref<Expr> ObjectState::read16(unsigned offset) const { + if (kMachineByteOrder == machine::MSB) { + return ConcatExpr::create(read8(offset+0), + read8(offset+1)); + } else { + return ConcatExpr::create(read8(offset+1), + read8(offset+0)); + } +} + +ref<Expr> ObjectState::read16(ref<Expr> offset) const { + if (kMachineByteOrder == machine::MSB) { + return ConcatExpr::create + (read8(AddExpr::create(offset, + ConstantExpr::create(0, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(1, + kMachinePointerType)))); + } else { + return ConcatExpr::create + (read8(AddExpr::create(offset, + ConstantExpr::create(1, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(0, + kMachinePointerType)))); + } +} + +ref<Expr> ObjectState::read32(unsigned offset) const { + if (kMachineByteOrder == machine::MSB) { + return ConcatExpr::create4(read8(offset+0), + read8(offset+1), + read8(offset+2), + read8(offset+3)); + } else { + return ConcatExpr::create4(read8(offset+3), + read8(offset+2), + read8(offset+1), + read8(offset+0)); + } +} + +ref<Expr> ObjectState::read32(ref<Expr> offset) const { + if (kMachineByteOrder == machine::MSB) { + return ConcatExpr::create4 + (read8(AddExpr::create(offset, + ConstantExpr::create(0, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(1, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(2, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(3, + kMachinePointerType)))); + } else { + return ConcatExpr::create4 + (read8(AddExpr::create(offset, + ConstantExpr::create(3, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(2, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(1, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(0, + kMachinePointerType)))); + } +} + +ref<Expr> ObjectState::read64(unsigned offset) const { + if (kMachineByteOrder == machine::MSB) { + return ConcatExpr::create8(read8(offset+0), + read8(offset+1), + read8(offset+2), + read8(offset+3), + read8(offset+4), + read8(offset+5), + read8(offset+6), + read8(offset+7)); + } else { + return ConcatExpr::create8(read8(offset+7), + read8(offset+6), + read8(offset+5), + read8(offset+4), + read8(offset+3), + read8(offset+2), + read8(offset+1), + read8(offset+0)); + } +} + +ref<Expr> ObjectState::read64(ref<Expr> offset) const { + if (kMachineByteOrder == machine::MSB) { + return ConcatExpr::create8 + (read8(AddExpr::create(offset, + ConstantExpr::create(0, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(1, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(2, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(3, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(4, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(5, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(6, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(7, + kMachinePointerType)))); + } else { + return ConcatExpr::create8 + (read8(AddExpr::create(offset, + ConstantExpr::create(7, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(6, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(5, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(4, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(3, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(2, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(1, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(0, + kMachinePointerType)))); + } +} + +void ObjectState::write(ref<Expr> offset, ref<Expr> value) { + Expr::Width w = value.getWidth(); + if (offset.isConstant()) { + write(offset.getConstantValue(), value); + } else { + switch(w) { + case Expr::Bool: write1(offset, value); break; + case Expr::Int8: write8(offset, value); break; + case Expr::Int16: write16(offset, value); break; + case Expr::Int32: write32(offset, value); break; + case Expr::Int64: write64(offset, value); break; + default: assert(0 && "invalid number of bytes in write"); + } + } +} + +void ObjectState::write(unsigned offset, ref<Expr> value) { + Expr::Width w = value.getWidth(); + if (value.isConstant()) { + uint64_t val = value.getConstantValue(); + switch(w) { + case Expr::Bool: + case Expr::Int8: write8(offset, val); break; + case Expr::Int16: write16(offset, val); break; + case Expr::Int32: write32(offset, val); break; + case Expr::Int64: write64(offset, val); break; + default: assert(0 && "invalid number of bytes in write"); + } + } else { + switch(w) { + case Expr::Bool: write1(offset, value); break; + case Expr::Int8: write8(offset, value); break; + case Expr::Int16: write16(offset, value); break; + case Expr::Int32: write32(offset, value); break; + case Expr::Int64: write64(offset, value); break; + default: assert(0 && "invalid number of bytes in write"); + } + } +} + +void ObjectState::write1(unsigned offset, ref<Expr> value) { + write8(offset, ZExtExpr::create(value, Expr::Int8)); +} + +void ObjectState::write1(ref<Expr> offset, ref<Expr> value) { + write8(offset, ZExtExpr::create(value, Expr::Int8)); +} + +void ObjectState::write16(unsigned offset, uint16_t value) { + if (kMachineByteOrder == machine::MSB) { + write8(offset+0, (uint8_t) (value >> 8)); + write8(offset+1, (uint8_t) (value >> 0)); + } else { + write8(offset+1, (uint8_t) (value >> 8)); + write8(offset+0, (uint8_t) (value >> 0)); + } +} + +void ObjectState::write16(unsigned offset, ref<Expr> value) { + if (kMachineByteOrder == machine::MSB) { + write8(offset+0, ExtractExpr::createByteOff(value, 1)); + write8(offset+1, ExtractExpr::createByteOff(value, 0)); + } else { + write8(offset+1, ExtractExpr::createByteOff(value, 1)); + write8(offset+0, ExtractExpr::createByteOff(value, 0)); + } +} + + +void ObjectState::write16(ref<Expr> offset, ref<Expr> value) { + if (kMachineByteOrder == machine::MSB) { + write8(AddExpr::create(offset, + ConstantExpr::create(0, kMachinePointerType)), + ExtractExpr::createByteOff(value,1)); + write8(AddExpr::create(offset, + ConstantExpr::create(0, kMachinePointerType)), + ExtractExpr::createByteOff(value,0)); + } else { + write8(AddExpr::create(offset, + ConstantExpr::create(1, kMachinePointerType)), + ExtractExpr::createByteOff(value,1)); + write8(AddExpr::create(offset, + ConstantExpr::create(0, kMachinePointerType)), + ExtractExpr::createByteOff(value,0)); + } +} + +void ObjectState::write32(unsigned offset, uint32_t value) { + if (kMachineByteOrder == machine::MSB) { + write8(offset+0, (uint8_t) (value >> 24)); + write8(offset+1, (uint8_t) (value >> 16)); + write8(offset+2, (uint8_t) (value >> 8)); + write8(offset+3, (uint8_t) (value >> 0)); + } else { + write8(offset+3, (uint8_t) (value >> 24)); + write8(offset+2, (uint8_t) (value >> 16)); + write8(offset+1, (uint8_t) (value >> 8)); + write8(offset+0, (uint8_t) (value >> 0)); + } +} + +void ObjectState::write32(unsigned offset, ref<Expr> value) { + if (kMachineByteOrder == machine::MSB) { + write8(offset+0, ExtractExpr::createByteOff(value, 3)); + write8(offset+1, ExtractExpr::createByteOff(value, 2)); + write8(offset+2, ExtractExpr::createByteOff(value, 1)); + write8(offset+3, ExtractExpr::createByteOff(value, 0)); + } else { + write8(offset+3, ExtractExpr::createByteOff(value, 3)); + write8(offset+2, ExtractExpr::createByteOff(value, 2)); + write8(offset+1, ExtractExpr::createByteOff(value, 1)); + write8(offset+0, ExtractExpr::createByteOff(value, 0)); + } +} + +void ObjectState::write32(ref<Expr> offset, ref<Expr> value) { + if (kMachineByteOrder == machine::MSB) { + write8(AddExpr::create(offset, + ConstantExpr::create(0, kMachinePointerType)), + ExtractExpr::createByteOff(value,3)); + write8(AddExpr::create(offset, + ConstantExpr::create(1, kMachinePointerType)), + ExtractExpr::createByteOff(value,2)); + write8(AddExpr::create(offset, + ConstantExpr::create(2, kMachinePointerType)), + ExtractExpr::createByteOff(value,1)); + write8(AddExpr::create(offset, + ConstantExpr::create(3, kMachinePointerType)), + ExtractExpr::createByteOff(value,0)); + } else { + write8(AddExpr::create(offset, + ConstantExpr::create(3, kMachinePointerType)), + ExtractExpr::createByteOff(value,3)); + write8(AddExpr::create(offset, + ConstantExpr::create(2, kMachinePointerType)), + ExtractExpr::createByteOff(value,2)); + write8(AddExpr::create(offset, + ConstantExpr::create(1, kMachinePointerType)), + ExtractExpr::createByteOff(value,1)); + write8(AddExpr::create(offset, + ConstantExpr::create(0, kMachinePointerType)), + ExtractExpr::createByteOff(value,0)); + } +} + +void ObjectState::write64(unsigned offset, uint64_t value) { + if (kMachineByteOrder == machine::MSB) { + write8(offset+0, (uint8_t) (value >> 56)); + write8(offset+1, (uint8_t) (value >> 48)); + write8(offset+2, (uint8_t) (value >> 40)); + write8(offset+3, (uint8_t) (value >> 32)); + write8(offset+4, (uint8_t) (value >> 24)); + write8(offset+5, (uint8_t) (value >> 16)); + write8(offset+6, (uint8_t) (value >> 8)); + write8(offset+7, (uint8_t) (value >> 0)); + } else { + write8(offset+7, (uint8_t) (value >> 56)); + write8(offset+6, (uint8_t) (value >> 48)); + write8(offset+5, (uint8_t) (value >> 40)); + write8(offset+4, (uint8_t) (value >> 32)); + write8(offset+3, (uint8_t) (value >> 24)); + write8(offset+2, (uint8_t) (value >> 16)); + write8(offset+1, (uint8_t) (value >> 8)); + write8(offset+0, (uint8_t) (value >> 0)); + } +} + +void ObjectState::write64(unsigned offset, ref<Expr> value) { + if (kMachineByteOrder == machine::MSB) { + write8(offset+0, ExtractExpr::createByteOff(value, 7)); + write8(offset+1, ExtractExpr::createByteOff(value, 6)); + write8(offset+2, ExtractExpr::createByteOff(value, 5)); + write8(offset+3, ExtractExpr::createByteOff(value, 4)); + write8(offset+4, ExtractExpr::createByteOff(value, 3)); + write8(offset+5, ExtractExpr::createByteOff(value, 2)); + write8(offset+6, ExtractExpr::createByteOff(value, 1)); + write8(offset+7, ExtractExpr::createByteOff(value, 0)); + } else { + write8(offset+7, ExtractExpr::createByteOff(value, 7)); + write8(offset+6, ExtractExpr::createByteOff(value, 6)); + write8(offset+5, ExtractExpr::createByteOff(value, 5)); + write8(offset+4, ExtractExpr::createByteOff(value, 4)); + write8(offset+3, ExtractExpr::createByteOff(value, 3)); + write8(offset+2, ExtractExpr::createByteOff(value, 2)); + write8(offset+1, ExtractExpr::createByteOff(value, 1)); + write8(offset+0, ExtractExpr::createByteOff(value, 0)); + } +} + +void ObjectState::write64(ref<Expr> offset, ref<Expr> value) { + if (kMachineByteOrder == machine::MSB) { + write8(AddExpr::create(offset, + ConstantExpr::create(0, kMachinePointerType)), + ExtractExpr::createByteOff(value,7)); + write8(AddExpr::create(offset, + ConstantExpr::create(1, kMachinePointerType)), + ExtractExpr::createByteOff(value,6)); + write8(AddExpr::create(offset, + ConstantExpr::create(2, kMachinePointerType)), + ExtractExpr::createByteOff(value,5)); + write8(AddExpr::create(offset, + ConstantExpr::create(3, kMachinePointerType)), + ExtractExpr::createByteOff(value,4)); + write8(AddExpr::create(offset, + ConstantExpr::create(4, kMachinePointerType)), + ExtractExpr::createByteOff(value,3)); + write8(AddExpr::create(offset, + ConstantExpr::create(5, kMachinePointerType)), + ExtractExpr::createByteOff(value,2)); + write8(AddExpr::create(offset, + ConstantExpr::create(6, kMachinePointerType)), + ExtractExpr::createByteOff(value,1)); + write8(AddExpr::create(offset, + ConstantExpr::create(7, kMachinePointerType)), + ExtractExpr::createByteOff(value,0)); + } else { + write8(AddExpr::create(offset, + ConstantExpr::create(7, kMachinePointerType)), + ExtractExpr::createByteOff(value,7)); + write8(AddExpr::create(offset, + ConstantExpr::create(6, kMachinePointerType)), + ExtractExpr::createByteOff(value,6)); + write8(AddExpr::create(offset, + ConstantExpr::create(5, kMachinePointerType)), + ExtractExpr::createByteOff(value,5)); + write8(AddExpr::create(offset, + ConstantExpr::create(4, kMachinePointerType)), + ExtractExpr::createByteOff(value,4)); + write8(AddExpr::create(offset, + ConstantExpr::create(3, kMachinePointerType)), + ExtractExpr::createByteOff(value,3)); + write8(AddExpr::create(offset, + ConstantExpr::create(2, kMachinePointerType)), + ExtractExpr::createByteOff(value,2)); + write8(AddExpr::create(offset, + ConstantExpr::create(1, kMachinePointerType)), + ExtractExpr::createByteOff(value,1)); + write8(AddExpr::create(offset, + ConstantExpr::create(0, kMachinePointerType)), + ExtractExpr::createByteOff(value,0)); + } +} + +void ObjectState::print() { + llvm::cerr << "-- ObjectState --\n"; + llvm::cerr << "\tMemoryObject ID: " << object->id << "\n"; + llvm::cerr << "\tRoot Object: " << updates.root << "\n"; + llvm::cerr << "\tIs Rooted? " << updates.isRooted << "\n"; + llvm::cerr << "\tSize: " << size << "\n"; + + llvm::cerr << "\tBytes:\n"; + for (unsigned i=0; i<size; i++) { + llvm::cerr << "\t\t["<<i<<"]" + << " concrete? " << isByteConcrete(i) + << " known-sym? " << isByteKnownSymbolic(i) + << " flushed? " << isByteFlushed(i) << " = "; + ref<Expr> e = read8(i); + llvm::cerr << e << "\n"; + } + + llvm::cerr << "\tUpdates:\n"; + for (const UpdateNode *un=updates.head; un; un=un->next) { + llvm::cerr << "\t\t[" << un->index << "] = " << un->value << "\n"; + } +} diff --git a/lib/Core/Memory.h b/lib/Core/Memory.h new file mode 100644 index 00000000..0f09b162 --- /dev/null +++ b/lib/Core/Memory.h @@ -0,0 +1,239 @@ +//===-- Memory.h ------------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_MEMORY_H +#define KLEE_MEMORY_H + +#include "klee/Expr.h" + +#include <vector> +#include <string> + +namespace llvm { + class Value; +} + +namespace klee { + +class BitArray; +class MemoryManager; +class Solver; + +class MemoryObject { + friend class STPBuilder; + +private: + static int counter; + +public: + unsigned id; + uint64_t address; + Array *array; + + /// size in bytes + unsigned size; + std::string name; + + bool isLocal; + bool isGlobal; + bool isFixed; + + /// true if created by us. + bool fake_object; + bool isUserSpecified; + + /// "Location" for which this memory object was allocated. This + /// should be either the allocating instruction or the global object + /// it was allocated for (or whatever else makes sense). + const llvm::Value *allocSite; + + /// A list of boolean expressions the user has requested be true of + /// a counterexample. Mutable since we play a little fast and loose + /// with allowing it to be added to during execution (although + /// should sensibly be only at creation time). + mutable std::vector< ref<Expr> > cexPreferences; + + // DO NOT IMPLEMENT + MemoryObject(const MemoryObject &b); + MemoryObject &operator=(const MemoryObject &b); + +public: + // XXX this is just a temp hack, should be removed + explicit + MemoryObject(uint64_t _address) + : id(counter++), + address(_address), + array(new Array(this, 0, id)), + size(0), + isFixed(true), + allocSite(0) { + } + + MemoryObject(uint64_t _address, unsigned _size, + bool _isLocal, bool _isGlobal, bool _isFixed, + const llvm::Value *_allocSite) + : id(counter++), + address(_address), + array(new Array(this, id, _size)), + size(_size), + name("unnamed"), + isLocal(_isLocal), + isGlobal(_isGlobal), + isFixed(_isFixed), + fake_object(false), + isUserSpecified(false), + allocSite(_allocSite) { + } + + ~MemoryObject(); + + /// Get an identifying string for this allocation. + void getAllocInfo(std::string &result) const; + + void setName(std::string name) { + this->name = name; + } + + ref<Expr> getBaseExpr() const { + return ConstantExpr::create(address, kMachinePointerType); + } + ref<Expr> getSizeExpr() const { + return ConstantExpr::create(size, kMachinePointerType); + } + ref<Expr> getOffsetExpr(ref<Expr> pointer) const { + return SubExpr::create(pointer, getBaseExpr()); + } + ref<Expr> getBoundsCheckPointer(ref<Expr> pointer) const { + return getBoundsCheckOffset(getOffsetExpr(pointer)); + } + ref<Expr> getBoundsCheckPointer(ref<Expr> pointer, unsigned bytes) const { + return getBoundsCheckOffset(getOffsetExpr(pointer), bytes); + } + + ref<Expr> getBoundsCheckOffset(ref<Expr> offset) const { + if (size==0) { + return EqExpr::create(offset, ref<Expr>(0, kMachinePointerType)); + } else { + return UltExpr::create(offset, getSizeExpr()); + } + } + ref<Expr> getBoundsCheckOffset(ref<Expr> offset, unsigned bytes) const { + if (bytes<=size) { + return UltExpr::create(offset, + ref<Expr>(size - bytes + 1, kMachinePointerType)); + } else { + return ref<Expr>(0, Expr::Bool); + } + } +}; + +class ObjectState { +private: + friend class AddressSpace; + unsigned copyOnWriteOwner; // exclusively for AddressSpace + + friend class ObjectHolder; + unsigned refCount; + + const MemoryObject *object; + + uint8_t *concreteStore; + // XXX cleanup name of flushMask (its backwards or something) + BitArray *concreteMask; + + // mutable because may need flushed during read of const + mutable BitArray *flushMask; + + ref<Expr> *knownSymbolics; + +public: + unsigned size; + + // mutable because we may need flush during read of const + mutable UpdateList updates; + + bool readOnly; + +public: + // initial contents are undefined but concrete, it is the creators + // responsibility to initialize the object contents appropriate + ObjectState(const MemoryObject *mo, unsigned size); + ObjectState(const ObjectState &os); + ~ObjectState(); + + const MemoryObject *getObject() const { return object; } + + void setReadOnly(bool ro) { readOnly = ro; } + + // make all bytes are concrete with undefined values + void makeConcrete(); + + void makeSymbolic(); + + // make contents all concrete and zero + void initializeToZero(); + // make contents all concrete and random + void initializeToRandom(); + + ref<Expr> read(ref<Expr> offset, Expr::Width width) const; + ref<Expr> read(unsigned offset, Expr::Width width) const; + ref<Expr> read1(unsigned offset) const; + ref<Expr> read8(unsigned offset) const; + ref<Expr> read16(unsigned offset) const; + ref<Expr> read32(unsigned offset) const; + ref<Expr> read64(unsigned offset) const; + + // return bytes written. + void write(unsigned offset, ref<Expr> value); + void write(ref<Expr> offset, ref<Expr> value); + + void write8(unsigned offset, uint8_t value); + void write16(unsigned offset, uint16_t value); + void write32(unsigned offset, uint32_t value); + void write64(unsigned offset, uint64_t value); + +private: + ref<Expr> read1(ref<Expr> offset) const; + ref<Expr> read8(ref<Expr> offset) const; + ref<Expr> read16(ref<Expr> offset) const; + ref<Expr> read32(ref<Expr> offset) const; + ref<Expr> read64(ref<Expr> offset) const; + + void write1(unsigned offset, ref<Expr> value); + void write1(ref<Expr> offset, ref<Expr> value); + void write8(unsigned offset, ref<Expr> value); + void write8(ref<Expr> offset, ref<Expr> value); + void write16(unsigned offset, ref<Expr> value); + void write16(ref<Expr> offset, ref<Expr> value); + void write32(unsigned offset, ref<Expr> value); + void write32(ref<Expr> offset, ref<Expr> value); + void write64(unsigned offset, ref<Expr> value); + void write64(ref<Expr> offset, ref<Expr> value); + + + void fastRangeCheckOffset(ref<Expr> offset, unsigned *base_r, unsigned *size_r) const; + void flushRangeForRead(unsigned rangeBase, unsigned rangeSize) const; + void flushRangeForWrite(unsigned rangeBase, unsigned rangeSize); + + bool isByteConcrete(unsigned offset) const; + bool isByteFlushed(unsigned offset) const; + bool isByteKnownSymbolic(unsigned offset) const; + + void markByteConcrete(unsigned offset); + void markByteSymbolic(unsigned offset); + void markByteFlushed(unsigned offset); + void markByteUnflushed(unsigned offset); + void setKnownSymbolic(unsigned offset, Expr *value); + + void print(); +}; + +} // End klee namespace + +#endif diff --git a/lib/Core/MemoryManager.cpp b/lib/Core/MemoryManager.cpp new file mode 100644 index 00000000..cec7b7d1 --- /dev/null +++ b/lib/Core/MemoryManager.cpp @@ -0,0 +1,69 @@ +//===-- MemoryManager.cpp -------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Common.h" + +#include "CoreStats.h" +#include "Memory.h" +#include "MemoryManager.h" + +#include "klee/ExecutionState.h" +#include "klee/Expr.h" +#include "klee/Solver.h" + +#include "llvm/Support/CommandLine.h" + +using namespace klee; + +/***/ + +MemoryManager::~MemoryManager() { + while (!objects.empty()) { + MemoryObject *mo = objects.back(); + objects.pop_back(); + delete mo; + } +} + +MemoryObject *MemoryManager::allocate(uint64_t size, bool isLocal, bool isGlobal, + const llvm::Value *allocSite) { + if (size>10*1024*1024) { + klee_warning_once(0, "failing large alloc: %u bytes", (unsigned) size); + return 0; + } + uint64_t address = (uint64_t) (unsigned long) malloc((unsigned) size); + if (!address) + return 0; + + ++stats::allocations; + MemoryObject *res = new MemoryObject(address, size, isLocal, isGlobal, false, + allocSite); + objects.push_back(res); + return res; +} + +MemoryObject *MemoryManager::allocateFixed(uint64_t address, uint64_t size, + const llvm::Value *allocSite) { + for (objects_ty::iterator it = objects.begin(), ie = objects.end(); + it != ie; ++it) { + MemoryObject *mo = *it; + assert(!(address+size > mo->address && address < mo->address+mo->size) && + "allocated an overlapping object"); + } + + ++stats::allocations; + MemoryObject *res = new MemoryObject(address, size, false, true, true, + allocSite); + objects.push_back(res); + return res; +} + +void MemoryManager::deallocate(const MemoryObject *mo) { + assert(0); +} diff --git a/lib/Core/MemoryManager.h b/lib/Core/MemoryManager.h new file mode 100644 index 00000000..adb2ba22 --- /dev/null +++ b/lib/Core/MemoryManager.h @@ -0,0 +1,41 @@ +//===-- MemoryManager.h -----------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_MEMORYMANAGER_H +#define KLEE_MEMORYMANAGER_H + +#include <vector> +#include <stdint.h> + +namespace llvm { + class Value; +} + +namespace klee { + class MemoryObject; + + class MemoryManager { + private: + typedef std::vector<MemoryObject*> objects_ty; + objects_ty objects; + + public: + MemoryManager() {} + ~MemoryManager(); + + MemoryObject *allocate(uint64_t size, bool isLocal, bool isGlobal, + const llvm::Value *allocSite); + MemoryObject *allocateFixed(uint64_t address, uint64_t size, + const llvm::Value *allocSite); + void deallocate(const MemoryObject *mo); + }; + +} // End klee namespace + +#endif diff --git a/lib/Core/ObjectHolder.h b/lib/Core/ObjectHolder.h new file mode 100644 index 00000000..abf2c6f0 --- /dev/null +++ b/lib/Core/ObjectHolder.h @@ -0,0 +1,33 @@ +//===-- ObjectHolder.h ------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_OBJECTHOLDER_H +#define KLEE_OBJECTHOLDER_H + +namespace klee { + class ObjectState; + + class ObjectHolder { + ObjectState *os; + + public: + ObjectHolder() : os(0) {} + ObjectHolder(ObjectState *_os); + ObjectHolder(const ObjectHolder &b); + ~ObjectHolder(); + + ObjectHolder &operator=(const ObjectHolder &b); + + operator class ObjectState *() { return os; } + operator class ObjectState *() const { return (ObjectState*) os; } + }; +} + +#endif + diff --git a/lib/Core/PTree.cpp b/lib/Core/PTree.cpp new file mode 100644 index 00000000..349761cd --- /dev/null +++ b/lib/Core/PTree.cpp @@ -0,0 +1,103 @@ +//===-- PTree.cpp ---------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PTree.h" + +#include <klee/Expr.h> +#include <klee/util/ExprPPrinter.h> + +#include <vector> +#include <iostream> + +using namespace klee; + + /* *** */ + +PTree::PTree(const data_type &_root) : root(new Node(0,_root)) { +} + +PTree::~PTree() {} + +std::pair<PTreeNode*, PTreeNode*> +PTree::split(Node *n, + const data_type &leftData, + const data_type &rightData) { + assert(n && !n->left && !n->right); + n->left = new Node(n, leftData); + n->right = new Node(n, rightData); + return std::make_pair(n->left, n->right); +} + +void PTree::remove(Node *n) { + assert(!n->left && !n->right); + do { + Node *p = n->parent; + delete n; + if (p) { + if (n == p->left) { + p->left = 0; + } else { + assert(n == p->right); + p->right = 0; + } + } + n = p; + } while (n && !n->left && !n->right); +} + +void PTree::dump(std::ostream &os) { + ExprPPrinter *pp = ExprPPrinter::create(os); + pp->setNewline("\\l"); + os << "digraph G {\n"; + os << "\tsize=\"10,7.5\";\n"; + os << "\tratio=fill;\n"; + os << "\trotate=90;\n"; + os << "\tcenter = \"true\";\n"; + os << "\tnode [style=\"filled\",width=.1,height=.1,fontname=\"Terminus\"]\n"; + os << "\tedge [arrowsize=.3]\n"; + std::vector<PTree::Node*> stack; + stack.push_back(root); + while (!stack.empty()) { + PTree::Node *n = stack.back(); + stack.pop_back(); + if (n->condition.isNull()) { + os << "\tn" << n << " [label=\"\""; + } else { + os << "\tn" << n << " [label=\""; + pp->print(n->condition); + os << "\",shape=diamond"; + } + if (n->data) + os << ",fillcolor=green"; + os << "];\n"; + if (n->left) { + os << "\tn" << n << " -> n" << n->left << ";\n"; + stack.push_back(n->left); + } + if (n->right) { + os << "\tn" << n << " -> n" << n->right << ";\n"; + stack.push_back(n->right); + } + } + os << "}\n"; + delete pp; +} + +PTreeNode::PTreeNode(PTreeNode *_parent, + ExecutionState *_data) + : parent(_parent), + left(0), + right(0), + data(_data), + condition(0) { +} + +PTreeNode::~PTreeNode() { +} + diff --git a/lib/Core/PTree.h b/lib/Core/PTree.h new file mode 100644 index 00000000..6accc8e2 --- /dev/null +++ b/lib/Core/PTree.h @@ -0,0 +1,53 @@ +//===-- PTree.h -------------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __UTIL_PTREE_H__ +#define __UTIL_PTREE_H__ + +#include <klee/Expr.h> + +#include <utility> +#include <cassert> +#include <iostream> + +namespace klee { + class ExecutionState; + + class PTree { + typedef ExecutionState* data_type; + + public: + typedef class PTreeNode Node; + Node *root; + + PTree(const data_type &_root); + ~PTree(); + + std::pair<Node*,Node*> split(Node *n, + const data_type &leftData, + const data_type &rightData); + void remove(Node *n); + + void dump(std::ostream &os); + }; + + class PTreeNode { + friend class PTree; + public: + PTreeNode *parent, *left, *right; + ExecutionState *data; + ref<Expr> condition; + + private: + PTreeNode(PTreeNode *_parent, ExecutionState *_data); + ~PTreeNode(); + }; +} + +#endif diff --git a/lib/Core/Searcher.cpp b/lib/Core/Searcher.cpp new file mode 100644 index 00000000..4c94c59b --- /dev/null +++ b/lib/Core/Searcher.cpp @@ -0,0 +1,575 @@ +//===-- Searcher.cpp ------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Common.h" + +#include "Searcher.h" + +#include "CoreStats.h" +#include "Executor.h" +#include "PTree.h" +#include "StatsTracker.h" + +#include "klee/ExecutionState.h" +#include "klee/Statistics.h" +#include "klee/Internal/Module/InstructionInfoTable.h" +#include "klee/Internal/Module/KInstruction.h" +#include "klee/Internal/Module/KModule.h" +#include "klee/Internal/ADT/DiscretePDF.h" +#include "klee/Internal/ADT/RNG.h" +#include "klee/Internal/Support/ModuleUtil.h" +#include "klee/Internal/System/Time.h" + +#include "llvm/Constants.h" +#include "llvm/Instructions.h" +#include "llvm/Module.h" +#include "llvm/Support/CallSite.h" +#include "llvm/Support/CFG.h" +#include "llvm/Support/CommandLine.h" + +#include <cassert> +#include <fstream> +#include <climits> + +using namespace klee; +using namespace llvm; + +namespace { + cl::opt<bool> + DebugLogMerge("debug-log-merge"); +} + +namespace klee { + extern RNG theRNG; +} + +Searcher::~Searcher() { +} + +/// + +ExecutionState &DFSSearcher::selectState() { + return *states.back(); +} + +void DFSSearcher::update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates) { + states.insert(states.end(), + addedStates.begin(), + addedStates.end()); + for (std::set<ExecutionState*>::const_iterator it = removedStates.begin(), + ie = removedStates.end(); it != ie; ++it) { + ExecutionState *es = *it; + if (es == states.back()) { + states.pop_back(); + } else { + bool ok = false; + + for (std::vector<ExecutionState*>::iterator it = states.begin(), + ie = states.end(); it != ie; ++it) { + if (es==*it) { + states.erase(it); + ok = true; + break; + } + } + + assert(ok && "invalid state removed"); + } + } +} + +/// + +ExecutionState &RandomSearcher::selectState() { + return *states[theRNG.getInt32()%states.size()]; +} + +void RandomSearcher::update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates) { + states.insert(states.end(), + addedStates.begin(), + addedStates.end()); + for (std::set<ExecutionState*>::const_iterator it = removedStates.begin(), + ie = removedStates.end(); it != ie; ++it) { + ExecutionState *es = *it; + bool ok = false; + + for (std::vector<ExecutionState*>::iterator it = states.begin(), + ie = states.end(); it != ie; ++it) { + if (es==*it) { + states.erase(it); + ok = true; + break; + } + } + + assert(ok && "invalid state removed"); + } +} + +/// + +WeightedRandomSearcher::WeightedRandomSearcher(Executor &_executor, + WeightType _type) + : executor(_executor), + states(new DiscretePDF<ExecutionState*>()), + type(_type) { + switch(type) { + case Depth: + updateWeights = false; + break; + case InstCount: + case CPInstCount: + case QueryCost: + case MinDistToUncovered: + case CoveringNew: + updateWeights = true; + break; + default: + assert(0 && "invalid weight type"); + } +} + +WeightedRandomSearcher::~WeightedRandomSearcher() { + delete states; +} + +ExecutionState &WeightedRandomSearcher::selectState() { + return *states->choose(theRNG.getDoubleL()); +} + +double WeightedRandomSearcher::getWeight(ExecutionState *es) { + switch(type) { + default: + case Depth: + return es->weight; + case InstCount: { + uint64_t count = theStatisticManager->getIndexedValue(stats::instructions, + es->pc->info->id); + double inv = 1. / std::max((uint64_t) 1, count); + return inv * inv; + } + case CPInstCount: { + StackFrame &sf = es->stack.back(); + uint64_t count = sf.callPathNode->statistics.getValue(stats::instructions); + double inv = 1. / std::max((uint64_t) 1, count); + return inv; + } + case QueryCost: + return (es->queryCost < .1) ? 1. : 1./es->queryCost; + case CoveringNew: + case MinDistToUncovered: { + uint64_t md2u = computeMinDistToUncovered(es->pc, + es->stack.back().minDistToUncoveredOnReturn); + + double invMD2U = 1. / (md2u ? md2u : 10000); + if (type==CoveringNew) { + double invCovNew = 0.; + if (es->instsSinceCovNew) + invCovNew = 1. / std::max(1, (int) es->instsSinceCovNew - 1000); + return (invCovNew * invCovNew + invMD2U * invMD2U); + } else { + return invMD2U * invMD2U; + } + } + } +} + +void WeightedRandomSearcher::update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates) { + if (current && updateWeights && !removedStates.count(current)) + states->update(current, getWeight(current)); + + for (std::set<ExecutionState*>::const_iterator it = addedStates.begin(), + ie = addedStates.end(); it != ie; ++it) { + ExecutionState *es = *it; + states->insert(es, getWeight(es)); + } + + for (std::set<ExecutionState*>::const_iterator it = removedStates.begin(), + ie = removedStates.end(); it != ie; ++it) { + states->remove(*it); + } +} + +bool WeightedRandomSearcher::empty() { + return states->empty(); +} + +/// + +RandomPathSearcher::RandomPathSearcher(Executor &_executor) + : executor(_executor) { +} + +RandomPathSearcher::~RandomPathSearcher() { +} + +ExecutionState &RandomPathSearcher::selectState() { + unsigned flips=0, bits=0; + PTree::Node *n = executor.processTree->root; + + while (!n->data) { + if (!n->left) { + n = n->right; + } else if (!n->right) { + n = n->left; + } else { + if (bits==0) { + flips = theRNG.getInt32(); + bits = 32; + } + --bits; + n = (flips&(1<<bits)) ? n->left : n->right; + } + } + + return *n->data; +} + +void RandomPathSearcher::update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates) { +} + +bool RandomPathSearcher::empty() { + return executor.states.empty(); +} + +/// + +BumpMergingSearcher::BumpMergingSearcher(Executor &_executor, Searcher *_baseSearcher) + : executor(_executor), + baseSearcher(_baseSearcher), + mergeFunction(executor.kmodule->kleeMergeFn) { +} + +BumpMergingSearcher::~BumpMergingSearcher() { + delete baseSearcher; +} + +/// + +Instruction *BumpMergingSearcher::getMergePoint(ExecutionState &es) { + if (mergeFunction) { + Instruction *i = es.pc->inst; + + if (i->getOpcode()==Instruction::Call) { + CallSite cs(cast<CallInst>(i)); + if (mergeFunction==cs.getCalledFunction()) + return i; + } + } + + return 0; +} + +ExecutionState &BumpMergingSearcher::selectState() { +entry: + // out of base states, pick one to pop + if (baseSearcher->empty()) { + std::map<llvm::Instruction*, ExecutionState*>::iterator it = + statesAtMerge.begin(); + ExecutionState *es = it->second; + statesAtMerge.erase(it); + ++es->pc; + + baseSearcher->addState(es); + } + + ExecutionState &es = baseSearcher->selectState(); + + if (Instruction *mp = getMergePoint(es)) { + std::map<llvm::Instruction*, ExecutionState*>::iterator it = + statesAtMerge.find(mp); + + baseSearcher->removeState(&es); + + if (it==statesAtMerge.end()) { + statesAtMerge.insert(std::make_pair(mp, &es)); + } else { + ExecutionState *mergeWith = it->second; + if (mergeWith->merge(es)) { + // hack, because we are terminating the state we need to let + // the baseSearcher know about it again + baseSearcher->addState(&es); + executor.terminateState(es); + } else { + it->second = &es; // the bump + ++mergeWith->pc; + + baseSearcher->addState(mergeWith); + } + } + + goto entry; + } else { + return es; + } +} + +void BumpMergingSearcher::update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates) { + baseSearcher->update(current, addedStates, removedStates); +} + +/// + +MergingSearcher::MergingSearcher(Executor &_executor, Searcher *_baseSearcher) + : executor(_executor), + baseSearcher(_baseSearcher), + mergeFunction(executor.kmodule->kleeMergeFn) { +} + +MergingSearcher::~MergingSearcher() { + delete baseSearcher; +} + +/// + +Instruction *MergingSearcher::getMergePoint(ExecutionState &es) { + if (mergeFunction) { + Instruction *i = es.pc->inst; + + if (i->getOpcode()==Instruction::Call) { + CallSite cs(cast<CallInst>(i)); + if (mergeFunction==cs.getCalledFunction()) + return i; + } + } + + return 0; +} + +ExecutionState &MergingSearcher::selectState() { + while (!baseSearcher->empty()) { + ExecutionState &es = baseSearcher->selectState(); + if (getMergePoint(es)) { + baseSearcher->removeState(&es, &es); + statesAtMerge.insert(&es); + } else { + return es; + } + } + + // build map of merge point -> state list + std::map<Instruction*, std::vector<ExecutionState*> > merges; + for (std::set<ExecutionState*>::const_iterator it = statesAtMerge.begin(), + ie = statesAtMerge.end(); it != ie; ++it) { + ExecutionState &state = **it; + Instruction *mp = getMergePoint(state); + + merges[mp].push_back(&state); + } + + if (DebugLogMerge) + llvm::cerr << "-- all at merge --\n"; + for (std::map<Instruction*, std::vector<ExecutionState*> >::iterator + it = merges.begin(), ie = merges.end(); it != ie; ++it) { + if (DebugLogMerge) { + llvm::cerr << "\tmerge: " << it->first << " ["; + for (std::vector<ExecutionState*>::iterator it2 = it->second.begin(), + ie2 = it->second.end(); it2 != ie2; ++it2) { + ExecutionState *state = *it2; + llvm::cerr << state << ", "; + } + llvm::cerr << "]\n"; + } + + // merge states + std::set<ExecutionState*> toMerge(it->second.begin(), it->second.end()); + while (!toMerge.empty()) { + ExecutionState *base = *toMerge.begin(); + toMerge.erase(toMerge.begin()); + + std::set<ExecutionState*> toErase; + for (std::set<ExecutionState*>::iterator it = toMerge.begin(), + ie = toMerge.end(); it != ie; ++it) { + ExecutionState *mergeWith = *it; + + if (base->merge(*mergeWith)) { + toErase.insert(mergeWith); + } + } + if (DebugLogMerge && !toErase.empty()) { + llvm::cerr << "\t\tmerged: " << base << " with ["; + for (std::set<ExecutionState*>::iterator it = toErase.begin(), + ie = toErase.end(); it != ie; ++it) { + if (it!=toErase.begin()) llvm::cerr << ", "; + llvm::cerr << *it; + } + llvm::cerr << "]\n"; + } + for (std::set<ExecutionState*>::iterator it = toErase.begin(), + ie = toErase.end(); it != ie; ++it) { + std::set<ExecutionState*>::iterator it2 = toMerge.find(*it); + assert(it2!=toMerge.end()); + executor.terminateState(**it); + toMerge.erase(it2); + } + + // step past merge and toss base back in pool + statesAtMerge.erase(statesAtMerge.find(base)); + ++base->pc; + baseSearcher->addState(base); + } + } + + if (DebugLogMerge) + llvm::cerr << "-- merge complete, continuing --\n"; + + return selectState(); +} + +void MergingSearcher::update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates) { + if (!removedStates.empty()) { + std::set<ExecutionState *> alt = removedStates; + for (std::set<ExecutionState*>::const_iterator it = removedStates.begin(), + ie = removedStates.end(); it != ie; ++it) { + ExecutionState *es = *it; + std::set<ExecutionState*>::const_iterator it = statesAtMerge.find(es); + if (it!=statesAtMerge.end()) { + statesAtMerge.erase(it); + alt.erase(alt.find(es)); + } + } + baseSearcher->update(current, addedStates, alt); + } else { + baseSearcher->update(current, addedStates, removedStates); + } +} + +/// + +BatchingSearcher::BatchingSearcher(Searcher *_baseSearcher, + double _timeBudget, + unsigned _instructionBudget) + : baseSearcher(_baseSearcher), + timeBudget(_timeBudget), + instructionBudget(_instructionBudget), + lastState(0) { + +} + +BatchingSearcher::~BatchingSearcher() { + delete baseSearcher; +} + +ExecutionState &BatchingSearcher::selectState() { + if (!lastState || + (util::getWallTime()-lastStartTime)>timeBudget || + (stats::instructions-lastStartInstructions)>instructionBudget) { + if (lastState) { + double delta = util::getWallTime()-lastStartTime; + if (delta>timeBudget*1.1) { + llvm::cerr << "KLEE: increased time budget from " << timeBudget << " to " << delta << "\n"; + timeBudget = delta; + } + } + lastState = &baseSearcher->selectState(); + lastStartTime = util::getWallTime(); + lastStartInstructions = stats::instructions; + return *lastState; + } else { + return *lastState; + } +} + +void BatchingSearcher::update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates) { + if (removedStates.count(lastState)) + lastState = 0; + baseSearcher->update(current, addedStates, removedStates); +} + +/***/ + +IterativeDeepeningTimeSearcher::IterativeDeepeningTimeSearcher(Searcher *_baseSearcher) + : baseSearcher(_baseSearcher), + time(1.) { +} + +IterativeDeepeningTimeSearcher::~IterativeDeepeningTimeSearcher() { + delete baseSearcher; +} + +ExecutionState &IterativeDeepeningTimeSearcher::selectState() { + ExecutionState &res = baseSearcher->selectState(); + startTime = util::getWallTime(); + return res; +} + +void IterativeDeepeningTimeSearcher::update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates) { + double elapsed = util::getWallTime() - startTime; + + if (!removedStates.empty()) { + std::set<ExecutionState *> alt = removedStates; + for (std::set<ExecutionState*>::const_iterator it = removedStates.begin(), + ie = removedStates.end(); it != ie; ++it) { + ExecutionState *es = *it; + std::set<ExecutionState*>::const_iterator it = pausedStates.find(es); + if (it!=pausedStates.end()) { + pausedStates.erase(it); + alt.erase(alt.find(es)); + } + } + baseSearcher->update(current, addedStates, alt); + } else { + baseSearcher->update(current, addedStates, removedStates); + } + + if (current && !removedStates.count(current) && elapsed>time) { + pausedStates.insert(current); + baseSearcher->removeState(current); + } + + if (baseSearcher->empty()) { + time *= 2; + llvm::cerr << "KLEE: increasing time budget to: " << time << "\n"; + baseSearcher->update(0, pausedStates, std::set<ExecutionState*>()); + pausedStates.clear(); + } +} + +/***/ + +InterleavedSearcher::InterleavedSearcher(const std::vector<Searcher*> &_searchers) + : searchers(_searchers), + index(1) { +} + +InterleavedSearcher::~InterleavedSearcher() { + for (std::vector<Searcher*>::const_iterator it = searchers.begin(), + ie = searchers.end(); it != ie; ++it) + delete *it; +} + +ExecutionState &InterleavedSearcher::selectState() { + Searcher *s = searchers[--index]; + if (index==0) index = searchers.size(); + return s->selectState(); +} + +void InterleavedSearcher::update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates) { + for (std::vector<Searcher*>::const_iterator it = searchers.begin(), + ie = searchers.end(); it != ie; ++it) + (*it)->update(current, addedStates, removedStates); +} diff --git a/lib/Core/Searcher.h b/lib/Core/Searcher.h new file mode 100644 index 00000000..455a7679 --- /dev/null +++ b/lib/Core/Searcher.h @@ -0,0 +1,279 @@ +//===-- Searcher.h ----------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_SEARCHER_H +#define KLEE_SEARCHER_H + +#include <vector> +#include <set> +#include <map> +#include <queue> + +// FIXME: Move out of header, use llvm streams. +#include <ostream> + +namespace llvm { + class BasicBlock; + class Function; + class Instruction; +} + +namespace klee { + template<class T> class DiscretePDF; + class ExecutionState; + class Executor; + + class Searcher { + public: + virtual ~Searcher(); + + virtual ExecutionState &selectState() = 0; + + virtual void update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates) = 0; + + virtual bool empty() = 0; + + // prints name of searcher as a klee_message() + // TODO: could probably make prettier or more flexible + virtual void printName(std::ostream &os) { + os << "<unnamed searcher>\n"; + } + + // pgbovine - to be called when a searcher gets activated and + // deactivated, say, by a higher-level searcher; most searchers + // don't need this functionality, so don't have to override. + virtual void activate() {}; + virtual void deactivate() {}; + + // utility functions + + void addState(ExecutionState *es, ExecutionState *current = 0) { + std::set<ExecutionState*> tmp; + tmp.insert(es); + update(current, tmp, std::set<ExecutionState*>()); + } + + void removeState(ExecutionState *es, ExecutionState *current = 0) { + std::set<ExecutionState*> tmp; + tmp.insert(es); + update(current, std::set<ExecutionState*>(), tmp); + } + }; + + class DFSSearcher : public Searcher { + std::vector<ExecutionState*> states; + + public: + ExecutionState &selectState(); + void update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates); + bool empty() { return states.empty(); } + void printName(std::ostream &os) { + os << "DFSSearcher\n"; + } + }; + + class RandomSearcher : public Searcher { + std::vector<ExecutionState*> states; + + public: + ExecutionState &selectState(); + void update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates); + bool empty() { return states.empty(); } + void printName(std::ostream &os) { + os << "RandomSearcher\n"; + } + }; + + class WeightedRandomSearcher : public Searcher { + public: + enum WeightType { + Depth, + QueryCost, + InstCount, + CPInstCount, + MinDistToUncovered, + CoveringNew + }; + + private: + Executor &executor; + DiscretePDF<ExecutionState*> *states; + WeightType type; + bool updateWeights; + + double getWeight(ExecutionState*); + + public: + WeightedRandomSearcher(Executor &executor, WeightType type); + ~WeightedRandomSearcher(); + + ExecutionState &selectState(); + void update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates); + bool empty(); + void printName(std::ostream &os) { + os << "WeightedRandomSearcher::"; + switch(type) { + case Depth : os << "Depth\n"; return; + case QueryCost : os << "QueryCost\n"; return; + case InstCount : os << "InstCount\n"; return; + case CPInstCount : os << "CPInstCount\n"; return; + case MinDistToUncovered : os << "MinDistToUncovered\n"; return; + case CoveringNew : os << "CoveringNew\n"; return; + default : os << "<unknown type>\n"; return; + } + } + }; + + class RandomPathSearcher : public Searcher { + Executor &executor; + + public: + RandomPathSearcher(Executor &_executor); + ~RandomPathSearcher(); + + ExecutionState &selectState(); + void update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates); + bool empty(); + void printName(std::ostream &os) { + os << "RandomPathSearcher\n"; + } + }; + + class MergingSearcher : public Searcher { + Executor &executor; + std::set<ExecutionState*> statesAtMerge; + Searcher *baseSearcher; + llvm::Function *mergeFunction; + + private: + llvm::Instruction *getMergePoint(ExecutionState &es); + + public: + MergingSearcher(Executor &executor, Searcher *baseSearcher); + ~MergingSearcher(); + + ExecutionState &selectState(); + void update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates); + bool empty() { return baseSearcher->empty() && statesAtMerge.empty(); } + void printName(std::ostream &os) { + os << "MergingSearcher\n"; + } + }; + + class BumpMergingSearcher : public Searcher { + Executor &executor; + std::map<llvm::Instruction*, ExecutionState*> statesAtMerge; + Searcher *baseSearcher; + llvm::Function *mergeFunction; + + private: + llvm::Instruction *getMergePoint(ExecutionState &es); + + public: + BumpMergingSearcher(Executor &executor, Searcher *baseSearcher); + ~BumpMergingSearcher(); + + ExecutionState &selectState(); + void update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates); + bool empty() { return baseSearcher->empty() && statesAtMerge.empty(); } + void printName(std::ostream &os) { + os << "BumpMergingSearcher\n"; + } + }; + + class BatchingSearcher : public Searcher { + Searcher *baseSearcher; + double timeBudget; + unsigned instructionBudget; + + ExecutionState *lastState; + double lastStartTime; + unsigned lastStartInstructions; + + public: + BatchingSearcher(Searcher *baseSearcher, + double _timeBudget, + unsigned _instructionBudget); + ~BatchingSearcher(); + + ExecutionState &selectState(); + void update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates); + bool empty() { return baseSearcher->empty(); } + void printName(std::ostream &os) { + os << "<BatchingSearcher> timeBudget: " << timeBudget + << ", instructionBudget: " << instructionBudget + << ", baseSearcher:\n"; + baseSearcher->printName(os); + os << "</BatchingSearcher>\n"; + } + }; + + class IterativeDeepeningTimeSearcher : public Searcher { + Searcher *baseSearcher; + double time, startTime; + std::set<ExecutionState*> pausedStates; + + public: + IterativeDeepeningTimeSearcher(Searcher *baseSearcher); + ~IterativeDeepeningTimeSearcher(); + + ExecutionState &selectState(); + void update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates); + bool empty() { return baseSearcher->empty() && pausedStates.empty(); } + void printName(std::ostream &os) { + os << "IterativeDeepeningTimeSearcher\n"; + } + }; + + class InterleavedSearcher : public Searcher { + typedef std::vector<Searcher*> searchers_ty; + + searchers_ty searchers; + unsigned index; + + public: + explicit InterleavedSearcher(const searchers_ty &_searchers); + ~InterleavedSearcher(); + + ExecutionState &selectState(); + void update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates); + bool empty() { return searchers[0]->empty(); } + void printName(std::ostream &os) { + os << "<InterleavedSearcher> containing " + << searchers.size() << " searchers:\n"; + for (searchers_ty::iterator it = searchers.begin(), ie = searchers.end(); + it != ie; ++it) + (*it)->printName(os); + os << "</InterleavedSearcher>\n"; + } + }; + +} + +#endif diff --git a/lib/Core/SeedInfo.cpp b/lib/Core/SeedInfo.cpp new file mode 100644 index 00000000..d76d75dc --- /dev/null +++ b/lib/Core/SeedInfo.cpp @@ -0,0 +1,151 @@ +//===-- SeedInfo.cpp ------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Common.h" + +#include "Memory.h" +#include "SeedInfo.h" +#include "TimingSolver.h" + +#include "klee/ExecutionState.h" +#include "klee/Expr.h" +#include "klee/util/ExprUtil.h" +#include "klee/Internal/ADT/BOut.h" + +using namespace klee; + +BOutObject *SeedInfo::getNextInput(const MemoryObject *mo, + bool byName) { + if (byName) { + unsigned i; + + for (i=0; i<input->numObjects; ++i) { + BOutObject *obj = &input->objects[i]; + if (std::string(obj->name) == mo->name) + if (used.insert(obj).second) + return obj; + } + + // If first unused input matches in size then accept that as + // well. + for (i=0; i<input->numObjects; ++i) + if (!used.count(&input->objects[i])) + break; + if (i<input->numObjects) { + BOutObject *obj = &input->objects[i]; + if (obj->numBytes == mo->size) { + used.insert(obj); + klee_warning_once(mo, "using seed input %s[%d] for: %s (no name match)", + obj->name, obj->numBytes, mo->name.c_str()); + return obj; + } + } + + klee_warning_once(mo, "no seed input for: %s", mo->name.c_str()); + return 0; + } else { + if (inputPosition >= input->numObjects) { + return 0; + } else { + return &input->objects[inputPosition++]; + } + } +} + +void SeedInfo::patchSeed(const ExecutionState &state, + ref<Expr> condition, + TimingSolver *solver) { + std::vector< ref<Expr> > required(state.constraints.begin(), + state.constraints.end()); + ExecutionState tmp(required); + tmp.addConstraint(condition); + + // Try and patch direct reads first, this is likely to resolve the + // problem quickly and avoids long traversal of all seed + // values. There are other smart ways to do this, the nicest is if + // we got a minimal counterexample from STP, in which case we would + // just inject those values back into the seed. + std::set< std::pair<const Array*, unsigned> > directReads; + std::vector< ref<ReadExpr> > reads; + findReads(condition, false, reads); + for (std::vector< ref<ReadExpr> >::iterator it = reads.begin(), + ie = reads.end(); it != ie; ++it) { + ReadExpr *re = it->get(); + if (re->index.isConstant()) { + unsigned index = (unsigned) re->index.getConstantValue(); + directReads.insert(std::make_pair(re->updates.root, index)); + } + } + + for (std::set< std::pair<const Array*, unsigned> >::iterator + it = directReads.begin(), ie = directReads.end(); it != ie; ++it) { + const Array *array = it->first; + unsigned i = it->second; + ref<Expr> read = ReadExpr::create(UpdateList(array, true, 0), + ref<Expr>(i, Expr::Int32)); + + // If not in bindings then this can't be a violation? + Assignment::bindings_ty::iterator it2 = assignment.bindings.find(array); + if (it2 != assignment.bindings.end()) { + ref<Expr> isSeed = EqExpr::create(read, ref<Expr>(it2->second[i], Expr::Int8)); + bool res; + bool success = solver->mustBeFalse(tmp, isSeed, res); + assert(success && "FIXME: Unhandled solver failure"); + if (res) { + ref<Expr> value; + bool success = solver->getValue(tmp, read, value); + assert(success && "FIXME: Unhandled solver failure"); + it2->second[i] = value.getConstantValue(); + tmp.addConstraint(EqExpr::create(read, ref<Expr>(it2->second[i], Expr::Int8))); + } else { + tmp.addConstraint(isSeed); + } + } + } + + bool res; + bool success = solver->mayBeTrue(state, assignment.evaluate(condition), res); + assert(success && "FIXME: Unhandled solver failure"); + if (res) + return; + + // We could still do a lot better than this, for example by looking at + // independence. But really, this shouldn't be happening often. + for (Assignment::bindings_ty::iterator it = assignment.bindings.begin(), + ie = assignment.bindings.end(); it != ie; ++it) { + const Array *array = it->first; + for (unsigned i=0; i<array->size; ++i) { + ref<Expr> read = ReadExpr::create(UpdateList(array, true, 0), + ref<Expr>(i, Expr::Int32)); + ref<Expr> isSeed = EqExpr::create(read, ref<Expr>(it->second[i], Expr::Int8)); + bool res; + bool success = solver->mustBeFalse(tmp, isSeed, res); + assert(success && "FIXME: Unhandled solver failure"); + if (res) { + ref<Expr> value; + bool success = solver->getValue(tmp, read, value); + assert(success && "FIXME: Unhandled solver failure"); + it->second[i] = value.getConstantValue(); + tmp.addConstraint(EqExpr::create(read, ref<Expr>(it->second[i], Expr::Int8))); + } else { + tmp.addConstraint(isSeed); + } + } + } + +#ifndef NDEBUG + { + bool res; + bool success = + solver->mayBeTrue(state, assignment.evaluate(condition), res); + assert(success && "FIXME: Unhandled solver failure"); + assert(res && "seed patching failed"); + } +#endif +} diff --git a/lib/Core/SeedInfo.h b/lib/Core/SeedInfo.h new file mode 100644 index 00000000..dd151ed0 --- /dev/null +++ b/lib/Core/SeedInfo.h @@ -0,0 +1,48 @@ +//===-- SeedInfo.h ----------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_SEEDINFO_H +#define KLEE_SEEDINFO_H + +#include "klee/util/Assignment.h" + +extern "C" { + struct BOut; + struct BOutObject; +} + +namespace klee { + class ExecutionState; + class TimingSolver; + + class SeedInfo { + public: + Assignment assignment; + BOut *input; + unsigned inputPosition; + std::set<struct BOutObject*> used; + + public: + explicit + SeedInfo(BOut *_input) : assignment(true), + input(_input), + inputPosition(0) {} + + BOutObject *getNextInput(const MemoryObject *mo, + bool byName); + + /// Patch the seed so that condition is satisfied while retaining as + /// many of the seed values as possible. + void patchSeed(const ExecutionState &state, + ref<Expr> condition, + TimingSolver *solver); + }; +} + +#endif diff --git a/lib/Core/SpecialFunctionHandler.cpp b/lib/Core/SpecialFunctionHandler.cpp new file mode 100644 index 00000000..da2a4a49 --- /dev/null +++ b/lib/Core/SpecialFunctionHandler.cpp @@ -0,0 +1,727 @@ +//===-- SpecialFunctionHandler.cpp ----------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Common.h" + +#include "Memory.h" +#include "SpecialFunctionHandler.h" +#include "TimingSolver.h" + +#include "klee/ExecutionState.h" + +#include "klee/Internal/Module/KInstruction.h" +#include "klee/Internal/Module/KModule.h" + +#include "Executor.h" +#include "MemoryManager.h" + +#include "llvm/Module.h" + +#include <errno.h> + +using namespace llvm; +using namespace klee; + +/// \todo Almost all of the demands in this file should be replaced +/// with terminateState calls. + +/// + +struct HandlerInfo { + const char *name; + SpecialFunctionHandler::Handler handler; + bool doesNotReturn; /// Intrinsic terminates the process + bool hasReturnValue; /// Intrinsic has a return value + bool doNotOverride; /// Intrinsic should not be used if already defined +}; + +// FIXME: We are more or less committed to requiring an intrinsic +// library these days. We can move some of this stuff there, +// especially things like realloc which have complicated semantics +// w.r.t. forking. Among other things this makes delayed query +// dispatch easier to implement. +HandlerInfo handlerInfo[] = { +#define add(name, handler, ret) { name, \ + &SpecialFunctionHandler::handler, \ + false, ret, false } +#define addDNR(name, handler) { name, \ + &SpecialFunctionHandler::handler, \ + true, false, false } + addDNR("__assert_rtn", handleAssertFail), + addDNR("__assert_fail", handleAssertFail), + addDNR("_assert", handleAssert), + addDNR("abort", handleAbort), + addDNR("_exit", handleExit), + { "exit", &SpecialFunctionHandler::handleExit, true, false, true }, + addDNR("klee_abort", handleAbort), + addDNR("klee_silent_exit", handleSilentExit), + addDNR("klee_report_error", handleReportError), + + add("calloc", handleCalloc, true), + add("free", handleFree, false), + add("klee_assume", handleAssume, false), + add("klee_check_memory_access", handleCheckMemoryAccess, false), + add("klee_get_value", handleGetValue, true), + add("klee_define_fixed_object", handleDefineFixedObject, false), + add("klee_get_obj_size", handleGetObjSize, true), + add("klee_get_errno", handleGetErrno, true), + add("klee_is_symbolic", handleIsSymbolic, true), + add("klee_make_symbolic_name", handleMakeSymbolic, false), + add("klee_mark_global", handleMarkGlobal, false), + add("klee_malloc_n", handleMallocN, true), + add("klee_merge", handleMerge, false), + add("klee_prefer_cex", handlePreferCex, false), + add("klee_print_expr", handlePrintExpr, false), + add("klee_print_range", handlePrintRange, false), + add("klee_set_forking", handleSetForking, false), + add("klee_warning", handleWarning, false), + add("klee_warning_once", handleWarningOnce, false), + add("klee_under_constrained", handleUnderConstrained, false), + add("klee_alias_function", handleAliasFunction, false), + add("malloc", handleMalloc, true), + add("realloc", handleRealloc, true), + + // operator delete[](void*) + add("_ZdaPv", handleDeleteArray, false), + // operator delete(void*) + add("_ZdlPv", handleDelete, false), + + // operator new[](unsigned int) + add("_Znaj", handleNewArray, true), + // operator new(unsigned int) + add("_Znwj", handleNew, true), + + // FIXME-64: This is wrong for 64-bit long... + + // operator new[](unsigned long) + add("_Znam", handleNewArray, true), + // operator new(unsigned long) + add("_Znwm", handleNew, true), + +#undef addDNR +#undef add +}; + +SpecialFunctionHandler::SpecialFunctionHandler(Executor &_executor) + : executor(_executor) {} + + +void SpecialFunctionHandler::prepare() { + unsigned N = sizeof(handlerInfo)/sizeof(handlerInfo[0]); + + for (unsigned i=0; i<N; ++i) { + HandlerInfo &hi = handlerInfo[i]; + Function *f = executor.kmodule->module->getFunction(hi.name); + + // No need to create if the function doesn't exist, since it cannot + // be called in that case. + + if (f && (!hi.doNotOverride || f->isDeclaration())) { + // Make sure NoReturn attribute is set, for optimization and + // coverage counting. + if (hi.doesNotReturn) + f->addFnAttr(Attribute::NoReturn); + + // Change to a declaration since we handle internally (simplifies + // module and allows deleting dead code). + if (!f->isDeclaration()) + f->deleteBody(); + } + } +} + +void SpecialFunctionHandler::bind() { + unsigned N = sizeof(handlerInfo)/sizeof(handlerInfo[0]); + + for (unsigned i=0; i<N; ++i) { + HandlerInfo &hi = handlerInfo[i]; + Function *f = executor.kmodule->module->getFunction(hi.name); + + if (f && (!hi.doNotOverride || f->isDeclaration())) + handlers[f] = std::make_pair(hi.handler, hi.hasReturnValue); + } +} + + +bool SpecialFunctionHandler::handle(ExecutionState &state, + Function *f, + KInstruction *target, + std::vector< ref<Expr> > &arguments) { + handlers_ty::iterator it = handlers.find(f); + if (it != handlers.end()) { + Handler h = it->second.first; + bool hasReturnValue = it->second.second; + // FIXME: Check this... add test? + if (!hasReturnValue && !target->inst->use_empty()) { + executor.terminateStateOnExecError(state, + "expected return value from void special function"); + } else { + (this->*h)(state, target, arguments); + } + return true; + } else { + return false; + } +} + +/****/ + +// reads a concrete string from memory +std::string SpecialFunctionHandler::readStringAtAddress(ExecutionState &state, + ref<Expr> address) { + ObjectPair op; + address = executor.toUnique(state, address); + assert(address.isConstant() && "symbolic string arg to intrinsic"); + if (!state.addressSpace.resolveOne(address.getConstantValue(), op)) + assert(0 && "XXX out of bounds / multiple resolution unhandled"); + bool res; + assert(executor.solver->mustBeTrue(state, + EqExpr::create(address, + op.first->getBaseExpr()), + res) && + res && + "XXX interior pointer unhandled"); + const MemoryObject *mo = op.first; + const ObjectState *os = op.second; + + char *buf = new char[mo->size]; + + unsigned i; + for (i = 0; i < mo->size - 1; i++) { + ref<Expr> cur = os->read8(i); + cur = executor.toUnique(state, cur); + assert(cur.isConstant() && + "hit symbolic char while reading concrete string"); + buf[i] = cur.getConstantValue(); + } + buf[i] = 0; + + std::string result(buf); + delete[] buf; + return result; +} + +/****/ + +void SpecialFunctionHandler::handleAbort(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==0 && "invalid number of arguments to abort"); + + //XXX:DRE:TAINT + if(state.underConstrained) { + llvm::cerr << "TAINT: skipping abort fail\n"; + executor.terminateState(state); + } else { + executor.terminateStateOnError(state, "abort failure", "abort.err"); + } +} + +void SpecialFunctionHandler::handleExit(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==1 && "invalid number of arguments to exit"); + executor.terminateStateOnExit(state); +} + +void SpecialFunctionHandler::handleSilentExit(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==1 && "invalid number of arguments to exit"); + executor.terminateState(state); +} + +void SpecialFunctionHandler::handleAliasFunction(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==2 && + "invalid number of arguments to klee_alias_function"); + std::string old_fn = readStringAtAddress(state, arguments[0]); + std::string new_fn = readStringAtAddress(state, arguments[1]); + //llvm::cerr << "Replacing " << old_fn << "() with " << new_fn << "()\n"; + if (old_fn == new_fn) + state.removeFnAlias(old_fn); + else state.addFnAlias(old_fn, new_fn); +} + +void SpecialFunctionHandler::handleAssert(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==3 && "invalid number of arguments to _assert"); + + //XXX:DRE:TAINT + if(state.underConstrained) { + llvm::cerr << "TAINT: skipping assertion:" + << readStringAtAddress(state, arguments[0]) << "\n"; + executor.terminateState(state); + } else + executor.terminateStateOnError(state, + "ASSERTION FAIL: " + readStringAtAddress(state, arguments[0]), + "assert.err"); +} + +void SpecialFunctionHandler::handleAssertFail(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==4 && "invalid number of arguments to __assert_fail"); + + //XXX:DRE:TAINT + if(state.underConstrained) { + llvm::cerr << "TAINT: skipping assertion:" + << readStringAtAddress(state, arguments[0]) << "\n"; + executor.terminateState(state); + } else + executor.terminateStateOnError(state, + "ASSERTION FAIL: " + readStringAtAddress(state, arguments[0]), + "assert.err"); +} + +void SpecialFunctionHandler::handleReportError(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==4 && "invalid number of arguments to klee_report_error"); + + // arguments[0], arguments[1] are file, line + + //XXX:DRE:TAINT + if(state.underConstrained) { + llvm::cerr << "TAINT: skipping klee_report_error:" + << readStringAtAddress(state, arguments[2]) << ":" + << readStringAtAddress(state, arguments[3]) << "\n"; + executor.terminateState(state); + } else + executor.terminateStateOnError(state, + readStringAtAddress(state, arguments[2]), + readStringAtAddress(state, arguments[3])); +} + +void SpecialFunctionHandler::handleMerge(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // nop +} + +void SpecialFunctionHandler::handleNew(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // XXX should type check args + assert(arguments.size()==1 && "invalid number of arguments to new"); + + executor.executeAlloc(state, arguments[0], false, target); +} + +void SpecialFunctionHandler::handleDelete(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // XXX should type check args + assert(arguments.size()==1 && "invalid number of arguments to delete"); + executor.executeFree(state, arguments[0]); +} + +void SpecialFunctionHandler::handleNewArray(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // XXX should type check args + assert(arguments.size()==1 && "invalid number of arguments to new[]"); + executor.executeAlloc(state, arguments[0], false, target); +} + +void SpecialFunctionHandler::handleDeleteArray(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // XXX should type check args + assert(arguments.size()==1 && "invalid number of arguments to delete[]"); + executor.executeFree(state, arguments[0]); +} + +void SpecialFunctionHandler::handleMalloc(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // XXX should type check args + assert(arguments.size()==1 && "invalid number of arguments to malloc"); + executor.executeAlloc(state, arguments[0], false, target); +} + +void SpecialFunctionHandler::handleMallocN(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + + // XXX should type check args + assert(arguments.size() == 3 && "invalid number of arguments to malloc"); + + // mallocn(number, size, alignment) + ref<Expr> numElems = executor.toUnique(state, arguments[0]); + ref<Expr> elemSize = executor.toUnique(state, arguments[1]); + ref<Expr> elemAlignment = executor.toUnique(state, arguments[2]); + + assert(numElems.isConstant() && + elemSize.isConstant() && + elemAlignment.isConstant() && + "symbolic arguments passed to klee_mallocn"); + + executor.executeAllocN(state, + numElems.getConstantValue(), + elemSize.getConstantValue(), + elemAlignment.getConstantValue(), + false, + target); +} + +void SpecialFunctionHandler::handleAssume(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==1 && "invalid number of arguments to klee_assume"); + + ref<Expr> e = arguments[0]; + + if(e.getWidth() != Expr::Bool) + e = NeExpr::create(e, ConstantExpr::create(0, e.getWidth())); + + bool res; + bool success = executor.solver->mustBeFalse(state, e, res); + assert(success && "FIXME: Unhandled solver failure"); + if (res) { + executor.terminateStateOnError(state, + "invalid klee_assume call (provably false)", + "user.err"); + } else { + executor.addConstraint(state, e); + } +} + +void SpecialFunctionHandler::handleIsSymbolic(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==1 && "invalid number of arguments to klee_is_symbolic"); + + executor.bindLocal(target, state, + ConstantExpr::create(!arguments[0].isConstant(), Expr::Int32)); +} + +void SpecialFunctionHandler::handlePreferCex(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==2 && + "invalid number of arguments to klee_prefex_cex"); + + ref<Expr> cond = arguments[1]; + if (cond.getWidth() != Expr::Bool) + cond = NeExpr::create(cond, ref<Expr>(0, cond.getWidth())); + + Executor::ExactResolutionList rl; + executor.resolveExact(state, arguments[0], rl, "prefex_cex"); + + assert(rl.size() == 1 && + "prefer_cex target must resolve to precisely one object"); + + rl[0].first.first->cexPreferences.push_back(cond); +} + +void SpecialFunctionHandler::handlePrintExpr(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==2 && + "invalid number of arguments to klee_print_expr"); + + std::string msg_str = readStringAtAddress(state, arguments[0]); + llvm::cerr << msg_str << ":" << arguments[1] << "\n"; +} + + +void SpecialFunctionHandler::handleUnderConstrained(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // XXX should type check args + assert(arguments.size()==1 && + "invalid number of arguments to klee_under_constrained()."); + assert(arguments[0].isConstant() && + "symbolic argument given to klee_under_constrained!"); + + unsigned v = arguments[0].getConstantValue(); + llvm::cerr << "argument = " << v << " under=" << state.underConstrained << "\n"; + if(v) { + assert(state.underConstrained == false && + "Bogus call to klee_under_constrained()."); + state.underConstrained = v; + llvm::cerr << "turning on under!\n"; + } else { + assert(state.underConstrained != 0 && "Bogus call to klee_taint_end()"); + state.underConstrained = 0; + llvm::cerr << "turning off under!\n"; + } +} + +void SpecialFunctionHandler::handleSetForking(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==1 && + "invalid number of arguments to klee_set_forking"); + ref<Expr> value = executor.toUnique(state, arguments[0]); + + if (!value.isConstant()) { + executor.terminateStateOnError(state, + "klee_set_forking requires a constant arg", + "user.err"); + } else { + state.forkDisabled = !value.getConstantValue(); + } +} + +void SpecialFunctionHandler::handleWarning(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==1 && "invalid number of arguments to klee_warning"); + + std::string msg_str = readStringAtAddress(state, arguments[0]); + klee_warning("%s: %s", state.stack.back().kf->function->getName().c_str(), + msg_str.c_str()); +} + +void SpecialFunctionHandler::handleWarningOnce(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==1 && + "invalid number of arguments to klee_warning_once"); + + std::string msg_str = readStringAtAddress(state, arguments[0]); + klee_warning_once(0, "%s: %s", state.stack.back().kf->function->getName().c_str(), + msg_str.c_str()); +} + +void SpecialFunctionHandler::handlePrintRange(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==2 && + "invalid number of arguments to klee_print_range"); + + std::string msg_str = readStringAtAddress(state, arguments[0]); + llvm::cerr << msg_str << ":" << arguments[1]; + if (!arguments[1].isConstant()) { + // FIXME: Pull into a unique value method? + ref<Expr> value; + bool success = executor.solver->getValue(state, arguments[1], value); + assert(success && "FIXME: Unhandled solver failure"); + bool res; + success = executor.solver->mustBeTrue(state, + EqExpr::create(arguments[1], value), + res); + assert(success && "FIXME: Unhandled solver failure"); + if (res) { + llvm::cerr << " == " << value; + } else { + llvm::cerr << " ~= " << value; + std::pair< ref<Expr>, ref<Expr> > res = + executor.solver->getRange(state, arguments[1]); + llvm::cerr << " (in [" << res.first << ", " << res.second <<"])"; + } + } + llvm::cerr << "\n"; +} + +void SpecialFunctionHandler::handleGetObjSize(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // XXX should type check args + assert(arguments.size()==1 && + "invalid number of arguments to klee_get_obj_size"); + Executor::ExactResolutionList rl; + executor.resolveExact(state, arguments[0], rl, "klee_get_obj_size"); + for (Executor::ExactResolutionList::iterator it = rl.begin(), + ie = rl.end(); it != ie; ++it) { + executor.bindLocal(target, *it->second, + ConstantExpr::create(it->first.first->size, Expr::Int32)); + } +} + +void SpecialFunctionHandler::handleGetErrno(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // XXX should type check args + assert(arguments.size()==0 && + "invalid number of arguments to klee_get_obj_size"); + executor.bindLocal(target, state, + ConstantExpr::create(errno, Expr::Int32)); +} + +void SpecialFunctionHandler::handleCalloc(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // XXX should type check args + assert(arguments.size()==2 && + "invalid number of arguments to calloc"); + + ref<Expr> size = MulExpr::create(arguments[0], + arguments[1]); + executor.executeAlloc(state, size, false, target, true); +} + +void SpecialFunctionHandler::handleRealloc(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // XXX should type check args + assert(arguments.size()==2 && + "invalid number of arguments to realloc"); + ref<Expr> address = arguments[0]; + ref<Expr> size = arguments[1]; + + Executor::StatePair zeroSize = executor.fork(state, + Expr::createIsZero(size), + true); + + if (zeroSize.first) { // size == 0 + executor.executeFree(*zeroSize.first, address, target); + } + if (zeroSize.second) { // size != 0 + Executor::StatePair zeroPointer = executor.fork(*zeroSize.second, + Expr::createIsZero(address), + true); + + if (zeroPointer.first) { // address == 0 + executor.executeAlloc(*zeroPointer.first, size, false, target); + } + if (zeroPointer.second) { // address != 0 + Executor::ExactResolutionList rl; + executor.resolveExact(*zeroPointer.second, address, rl, "realloc"); + + for (Executor::ExactResolutionList::iterator it = rl.begin(), + ie = rl.end(); it != ie; ++it) { + executor.executeAlloc(*it->second, size, false, target, false, + it->first.second); + } + } + } +} + +void SpecialFunctionHandler::handleFree(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // XXX should type check args + assert(arguments.size()==1 && + "invalid number of arguments to free"); + executor.executeFree(state, arguments[0]); +} + +void SpecialFunctionHandler::handleCheckMemoryAccess(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==2 && + "invalid number of arguments to klee_check_memory_access"); + + ref<Expr> address = executor.toUnique(state, arguments[0]); + ref<Expr> size = executor.toUnique(state, arguments[1]); + if (!address.isConstant() || !size.isConstant()) { + executor.terminateStateOnError(state, + "check_memory_access requires constant args", + "user.err"); + } else { + ObjectPair op; + + if (!state.addressSpace.resolveOne(address.getConstantValue(), op)) { + executor.terminateStateOnError(state, + "check_memory_access: memory error", + "ptr.err", + executor.getAddressInfo(state, address)); + } else { + ref<Expr> chk = op.first->getBoundsCheckPointer(address, + size.getConstantValue()); + assert(chk.isConstant()); + if (!chk.getConstantValue()) { + executor.terminateStateOnError(state, + "check_memory_access: memory error", + "ptr.err", + executor.getAddressInfo(state, address)); + } + } + } +} + +void SpecialFunctionHandler::handleGetValue(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==1 && + "invalid number of arguments to klee_get_value"); + + executor.executeGetValue(state, arguments[0], target); +} + +void SpecialFunctionHandler::handleDefineFixedObject(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==2 && + "invalid number of arguments to klee_define_fixed_object"); + assert(arguments[0].isConstant() && + "expect constant address argument to klee_define_fixed_object"); + assert(arguments[1].isConstant() && + "expect constant size argument to klee_define_fixed_object"); + + uint64_t address = arguments[0].getConstantValue(); + uint64_t size = arguments[1].getConstantValue(); + MemoryObject *mo = executor.memory->allocateFixed(address, size, state.prevPC->inst); + executor.bindObjectInState(state, mo, false); + mo->isUserSpecified = true; // XXX hack; +} + +void SpecialFunctionHandler::handleMakeSymbolic(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==3 && + "invalid number of arguments to klee_make_symbolic[_name]"); + + Executor::ExactResolutionList rl; + executor.resolveExact(state, arguments[0], rl, "make_symbolic"); + + for (Executor::ExactResolutionList::iterator it = rl.begin(), + ie = rl.end(); it != ie; ++it) { + MemoryObject *mo = (MemoryObject*) it->first.first; + std::string name = readStringAtAddress(state, arguments[2]); + mo->setName(name); + + const ObjectState *old = it->first.second; + ExecutionState *s = it->second; + + if (old->readOnly) { + executor.terminateStateOnError(*s, + "cannot make readonly object symbolic", + "user.err"); + return; + } + + bool res; + bool success = + executor.solver->mustBeTrue(*s, EqExpr::create(arguments[1], + mo->getSizeExpr()), + res); + assert(success && "FIXME: Unhandled solver failure"); + + if (res) { + executor.executeMakeSymbolic(*s, mo); + } else { + executor.terminateStateOnError(*s, + "wrong size given to klee_make_symbolic[_name]", + "user.err"); + } + } +} + +void SpecialFunctionHandler::handleMarkGlobal(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==1 && + "invalid number of arguments to klee_mark_global"); + + Executor::ExactResolutionList rl; + executor.resolveExact(state, arguments[0], rl, "mark_global"); + + for (Executor::ExactResolutionList::iterator it = rl.begin(), + ie = rl.end(); it != ie; ++it) { + MemoryObject *mo = (MemoryObject*) it->first.first; + assert(!mo->isLocal); + mo->isGlobal = true; + } +} diff --git a/lib/Core/SpecialFunctionHandler.h b/lib/Core/SpecialFunctionHandler.h new file mode 100644 index 00000000..d5d1af93 --- /dev/null +++ b/lib/Core/SpecialFunctionHandler.h @@ -0,0 +1,106 @@ +//===-- SpecialFunctionHandler.h --------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_SPECIALFUNCTIONHANDLER_H +#define KLEE_SPECIALFUNCTIONHANDLER_H + +#include <map> +#include <vector> +#include <string> + +namespace llvm { + class Function; +} + +namespace klee { + class Executor; + class Expr; + class ExecutionState; + class KInstruction; + template<typename T> class ref; + + class SpecialFunctionHandler { + public: + typedef void (SpecialFunctionHandler::*Handler)(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > + &arguments); + typedef std::map<const llvm::Function*, + std::pair<Handler,bool> > handlers_ty; + + handlers_ty handlers; + class Executor &executor; + + public: + SpecialFunctionHandler(Executor &_executor); + + /// Perform any modifications on the LLVM module before it is + /// prepared for execution. At the moment this involves deleting + /// unused function bodies and marking intrinsics with appropriate + /// flags for use in optimizations. + void prepare(); + + /// Initialize the internal handler map after the module has been + /// prepared for execution. + void bind(); + + bool handle(ExecutionState &state, + llvm::Function *f, + KInstruction *target, + std::vector< ref<Expr> > &arguments); + + /* Convenience routines */ + + std::string readStringAtAddress(ExecutionState &state, ref<Expr> address); + + /* Handlers */ + +#define HANDLER(name) void name(ExecutionState &state, \ + KInstruction *target, \ + std::vector< ref<Expr> > &arguments) + HANDLER(handleAbort); + HANDLER(handleAssert); + HANDLER(handleAssertFail); + HANDLER(handleAssume); + HANDLER(handleCalloc); + HANDLER(handleCheckMemoryAccess); + HANDLER(handleDefineFixedObject); + HANDLER(handleDelete); + HANDLER(handleDeleteArray); + HANDLER(handleExit); + HANDLER(handleAliasFunction); + HANDLER(handleFree); + HANDLER(handleGetErrno); + HANDLER(handleGetObjSize); + HANDLER(handleGetValue); + HANDLER(handleIsSymbolic); + HANDLER(handleMakeSymbolic); + HANDLER(handleMalloc); + HANDLER(handleMallocN); + HANDLER(handleMarkGlobal); + HANDLER(handleMerge); + HANDLER(handleNew); + HANDLER(handleNewArray); + HANDLER(handlePreferCex); + HANDLER(handlePrintExpr); + HANDLER(handlePrintRange); + HANDLER(handleRange); + HANDLER(handleRealloc); + HANDLER(handleReportError); + HANDLER(handleRevirtObjects); + HANDLER(handleSetForking); + HANDLER(handleSilentExit); + HANDLER(handleUnderConstrained); + HANDLER(handleWarning); + HANDLER(handleWarningOnce); +#undef HANDLER + }; +} // End klee namespace + +#endif diff --git a/lib/Core/StatsTracker.cpp b/lib/Core/StatsTracker.cpp new file mode 100644 index 00000000..35c073a3 --- /dev/null +++ b/lib/Core/StatsTracker.cpp @@ -0,0 +1,814 @@ +//===-- StatsTracker.cpp --------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Common.h" + +#include "StatsTracker.h" + +#include "klee/ExecutionState.h" +#include "klee/Statistics.h" +#include "klee/Internal/Module/InstructionInfoTable.h" +#include "klee/Internal/Module/KModule.h" +#include "klee/Internal/Module/KInstruction.h" +#include "klee/Internal/Support/ModuleUtil.h" +#include "klee/Internal/System/Time.h" + +#include "CallPathManager.h" +#include "CoreStats.h" +#include "Executor.h" +#include "MemoryManager.h" +#include "UserSearcher.h" +#include "../Solver/SolverStats.h" + +#include "llvm/BasicBlock.h" +#include "llvm/Function.h" +#include "llvm/Instructions.h" +#include "llvm/IntrinsicInst.h" +#include "llvm/InlineAsm.h" +#include "llvm/Module.h" +#include "llvm/Type.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/CFG.h" +#include "llvm/System/Process.h" +#include "llvm/System/Path.h" + +#include <iostream> +#include <fstream> + +using namespace klee; +using namespace llvm; + +/// + +namespace { + cl::opt<bool> + TrackInstructionTime("track-instruction-time", + cl::desc("Enable tracking of time for individual instructions"), + cl::init(false)); + + cl::opt<bool> + OutputStats("output-stats", + cl::desc("Write running stats trace file"), + cl::init(true)); + + cl::opt<bool> + OutputIStats("output-istats", + cl::desc("Write instruction level statistics (in callgrind format)"), + cl::init(true)); + + cl::opt<double> + StatsWriteInterval("stats-write-interval", + cl::desc("Approximate number of seconds between stats writes (default: 1.0)"), + cl::init(1.)); + + cl::opt<double> + IStatsWriteInterval("istats-write-interval", + cl::desc("Approximate number of seconds between istats writes (default: 10.0)"), + cl::init(10.)); + + /* + cl::opt<double> + BranchCovCountsWriteInterval("branch-cov-counts-write-interval", + cl::desc("Approximate number of seconds between run.branches writes (default: 5.0)"), + cl::init(5.)); + */ + + // XXX I really would like to have dynamic rate control for something like this. + cl::opt<double> + UncoveredUpdateInterval("uncovered-update-interval", + cl::init(30.)); + + cl::opt<bool> + UseCallPaths("use-call-paths", + cl::desc("Enable calltree tracking for instruction level statistics"), + cl::init(true)); + +} + +/// + +bool StatsTracker::useStatistics() { + return OutputStats || OutputIStats; +} + +namespace klee { + class WriteIStatsTimer : public Executor::Timer { + StatsTracker *statsTracker; + + public: + WriteIStatsTimer(StatsTracker *_statsTracker) : statsTracker(_statsTracker) {} + ~WriteIStatsTimer() {} + + void run() { statsTracker->writeIStats(); } + }; + + class WriteStatsTimer : public Executor::Timer { + StatsTracker *statsTracker; + + public: + WriteStatsTimer(StatsTracker *_statsTracker) : statsTracker(_statsTracker) {} + ~WriteStatsTimer() {} + + void run() { statsTracker->writeStatsLine(); } + }; + + class UpdateReachableTimer : public Executor::Timer { + StatsTracker *statsTracker; + + public: + UpdateReachableTimer(StatsTracker *_statsTracker) : statsTracker(_statsTracker) {} + + void run() { statsTracker->computeReachableUncovered(); } + }; + +} + +// + +/// Check for special cases where we statically know an instruction is +/// uncoverable. Currently the case is an unreachable instruction +/// following a noreturn call; the instruction is really only there to +/// satisfy LLVM's termination requirement. +static bool instructionIsCoverable(Instruction *i) { + if (i->getOpcode() == Instruction::Unreachable) { + BasicBlock *bb = i->getParent(); + BasicBlock::iterator it(i); + if (it==bb->begin()) { + return true; + } else { + Instruction *prev = --it; + if (isa<CallInst>(prev) || isa<InvokeInst>(prev)) { + Function *target = getDirectCallTarget(prev); + if (target && target->doesNotReturn()) + return false; + } + } + } + + return true; +} + +StatsTracker::StatsTracker(Executor &_executor, std::string _objectFilename, + bool _updateMinDistToUncovered) + : executor(_executor), + objectFilename(_objectFilename), + statsFile(0), + istatsFile(0), + startWallTime(util::getWallTime()), + numBranches(0), + fullBranches(0), + partialBranches(0), + updateMinDistToUncovered(_updateMinDistToUncovered) { + KModule *km = executor.kmodule; + + sys::Path module(objectFilename); + if (!sys::Path(objectFilename).isAbsolute()) { + sys::Path current = sys::Path::GetCurrentDirectory(); + current.appendComponent(objectFilename); + if (current.exists()) + objectFilename = current.c_str(); + } + + if (OutputIStats) + theStatisticManager->useIndexedStats(km->infos->getMaxID()); + + for (std::vector<KFunction*>::iterator it = km->functions.begin(), + ie = km->functions.end(); it != ie; ++it) { + KFunction *kf = *it; + kf->trackCoverage = 1; + + for (unsigned i=0; i<kf->numInstructions; ++i) { + KInstruction *ki = kf->instructions[i]; + + if (OutputIStats) { + unsigned id = ki->info->id; + theStatisticManager->setIndex(id); + if (kf->trackCoverage && instructionIsCoverable(ki->inst)) + ++stats::uncoveredInstructions; + } + + if (kf->trackCoverage) { + if (BranchInst *bi = dyn_cast<BranchInst>(ki->inst)) + if (!bi->isUnconditional()) + numBranches++; + } + } + } + + if (OutputStats) { + statsFile = executor.interpreterHandler->openOutputFile("run.stats"); + assert(statsFile && "unable to open statistics trace file"); + writeStatsHeader(); + writeStatsLine(); + + executor.addTimer(new WriteStatsTimer(this), StatsWriteInterval); + + if (updateMinDistToUncovered) + executor.addTimer(new UpdateReachableTimer(this), UncoveredUpdateInterval); + } + + if (OutputIStats) { + istatsFile = executor.interpreterHandler->openOutputFile("run.istats"); + assert(istatsFile && "unable to open istats file"); + + executor.addTimer(new WriteIStatsTimer(this), IStatsWriteInterval); + } +} + +StatsTracker::~StatsTracker() { + if (statsFile) + delete statsFile; + if (istatsFile) + delete istatsFile; +} + +void StatsTracker::done() { + if (statsFile) + writeStatsLine(); + if (OutputIStats) + writeIStats(); +} + +void StatsTracker::stepInstruction(ExecutionState &es) { + if (OutputIStats) { + if (TrackInstructionTime) { + static sys::TimeValue lastNowTime(0,0),lastUserTime(0,0); + + if (lastUserTime.seconds()==0 && lastUserTime.nanoseconds()==0) { + sys::TimeValue sys(0,0); + sys::Process::GetTimeUsage(lastNowTime,lastUserTime,sys); + } else { + sys::TimeValue now(0,0),user(0,0),sys(0,0); + sys::Process::GetTimeUsage(now,user,sys); + sys::TimeValue delta = user - lastUserTime; + sys::TimeValue deltaNow = now - lastNowTime; + stats::instructionTime += delta.usec(); + stats::instructionRealTime += deltaNow.usec(); + lastUserTime = user; + lastNowTime = now; + } + } + + Instruction *inst = es.pc->inst; + const InstructionInfo &ii = *es.pc->info; + StackFrame &sf = es.stack.back(); + theStatisticManager->setIndex(ii.id); + if (UseCallPaths) + theStatisticManager->setContext(&sf.callPathNode->statistics); + + if (es.instsSinceCovNew) + ++es.instsSinceCovNew; + + if (sf.kf->trackCoverage && instructionIsCoverable(inst)) { + if (!theStatisticManager->getIndexedValue(stats::coveredInstructions, ii.id)) { + // Checking for actual stoppoints avoids inconsistencies due + // to line number propogation. + if (isa<DbgStopPointInst>(inst)) + es.coveredLines[&ii.file].insert(ii.line); + es.coveredNew = true; + es.instsSinceCovNew = 1; + ++stats::coveredInstructions; + stats::uncoveredInstructions += (uint64_t)-1; + } + } + } +} + +/// + +/* Should be called _after_ the es->pushFrame() */ +void StatsTracker::framePushed(ExecutionState &es, StackFrame *parentFrame) { + if (OutputIStats) { + StackFrame &sf = es.stack.back(); + + if (UseCallPaths) { + CallPathNode *parent = parentFrame ? parentFrame->callPathNode : 0; + CallPathNode *cp = callPathManager.getCallPath(parent, + sf.caller ? sf.caller->inst : 0, + sf.kf->function); + sf.callPathNode = cp; + cp->count++; + } + + if (updateMinDistToUncovered) { + uint64_t minDistAtRA = 0; + if (parentFrame) + minDistAtRA = parentFrame->minDistToUncoveredOnReturn; + + sf.minDistToUncoveredOnReturn = sf.caller ? + computeMinDistToUncovered(sf.caller, minDistAtRA) : 0; + } + } +} + +/* Should be called _after_ the es->popFrame() */ +void StatsTracker::framePopped(ExecutionState &es) { + // XXX remove me? +} + + +void StatsTracker::markBranchVisited(ExecutionState *visitedTrue, + ExecutionState *visitedFalse) { + if (OutputIStats) { + unsigned id = theStatisticManager->getIndex(); + uint64_t hasTrue = theStatisticManager->getIndexedValue(stats::trueBranches, id); + uint64_t hasFalse = theStatisticManager->getIndexedValue(stats::falseBranches, id); + if (visitedTrue && !hasTrue) { + visitedTrue->coveredNew = true; + visitedTrue->instsSinceCovNew = 1; + ++stats::trueBranches; + if (hasFalse) { ++fullBranches; --partialBranches; } + else ++partialBranches; + hasTrue = 1; + } + if (visitedFalse && !hasFalse) { + visitedFalse->coveredNew = true; + visitedFalse->instsSinceCovNew = 1; + ++stats::falseBranches; + if (hasTrue) { ++fullBranches; --partialBranches; } + else ++partialBranches; + } + } +} + +void StatsTracker::writeStatsHeader() { + *statsFile << "('Instructions'," + << "'FullBranches'," + << "'PartialBranches'," + << "'NumBranches'," + << "'UserTime'," + << "'NumStates'," + << "'MallocUsage'," + << "'NumQueries'," + << "'NumQueryConstructs'," + << "'NumObjects'," + << "'WallTime'," + << "'CoveredInstructions'," + << "'UncoveredInstructions'," + << "'QueryTime'," + << "'SolverTime'," + << "'CexCacheTime'," + << "'ForkTime'," + << "'ResolveTime'," + << ")\n"; + statsFile->flush(); +} + +double StatsTracker::elapsed() { + return util::getWallTime() - startWallTime; +} + +void StatsTracker::writeStatsLine() { + *statsFile << "(" << stats::instructions + << "," << fullBranches + << "," << partialBranches + << "," << numBranches + << "," << util::getUserTime() + << "," << executor.states.size() + << "," << sys::Process::GetTotalMemoryUsage() + << "," << stats::queries + << "," << stats::queryConstructs + << "," << 0 // was numObjects + << "," << elapsed() + << "," << stats::coveredInstructions + << "," << stats::uncoveredInstructions + << "," << stats::queryTime / 1000000. + << "," << stats::solverTime / 1000000. + << "," << stats::cexCacheTime / 1000000. + << "," << stats::forkTime / 1000000. + << "," << stats::resolveTime / 1000000. + << ")\n"; + statsFile->flush(); +} + +void StatsTracker::updateStateStatistics(uint64_t addend) { + for (std::set<ExecutionState*>::iterator it = executor.states.begin(), + ie = executor.states.end(); it != ie; ++it) { + ExecutionState &state = **it; + const InstructionInfo &ii = *state.pc->info; + theStatisticManager->incrementIndexedValue(stats::states, ii.id, addend); + if (UseCallPaths) + state.stack.back().callPathNode->statistics.incrementValue(stats::states, addend); + } +} + +void StatsTracker::writeIStats() { + Module *m = executor.kmodule->module; + uint64_t istatsMask = 0; + std::ostream &of = *istatsFile; + + of.seekp(0, std::ios::end); + unsigned istatsSize = of.tellp(); + of.seekp(0); + + of << "version: 1\n"; + of << "creator: klee\n"; + of << "pid: " << sys::Process::GetCurrentUserId() << "\n"; + of << "cmd: " << m->getModuleIdentifier() << "\n\n"; + of << "\n"; + + StatisticManager &sm = *theStatisticManager; + unsigned nStats = sm.getNumStatistics(); + + // Max is 13, sadly + istatsMask |= 1<<sm.getStatisticID("Queries"); + istatsMask |= 1<<sm.getStatisticID("QueriesValid"); + istatsMask |= 1<<sm.getStatisticID("QueriesInvalid"); + istatsMask |= 1<<sm.getStatisticID("QueryTime"); + istatsMask |= 1<<sm.getStatisticID("ResolveTime"); + istatsMask |= 1<<sm.getStatisticID("Instructions"); + istatsMask |= 1<<sm.getStatisticID("InstructionTimes"); + istatsMask |= 1<<sm.getStatisticID("InstructionRealTimes"); + istatsMask |= 1<<sm.getStatisticID("Forks"); + istatsMask |= 1<<sm.getStatisticID("CoveredInstructions"); + istatsMask |= 1<<sm.getStatisticID("UncoveredInstructions"); + istatsMask |= 1<<sm.getStatisticID("States"); + istatsMask |= 1<<sm.getStatisticID("MinDistToUncovered"); + + of << "positions: instr line\n"; + + for (unsigned i=0; i<nStats; i++) { + if (istatsMask & (1<<i)) { + Statistic &s = sm.getStatistic(i); + of << "event: " << s.getShortName() << " : " + << s.getName() << "\n"; + } + } + + of << "events: "; + for (unsigned i=0; i<nStats; i++) { + if (istatsMask & (1<<i)) + of << sm.getStatistic(i).getShortName() << " "; + } + of << "\n"; + + // set state counts, decremented after we process so that we don't + // have to zero all records each time. + if (istatsMask & (1<<stats::states.getID())) + updateStateStatistics(1); + + std::string sourceFile = ""; + + CallSiteSummaryTable callSiteStats; + if (UseCallPaths) + callPathManager.getSummaryStatistics(callSiteStats); + + of << "ob=" << objectFilename << "\n"; + + for (Module::iterator fnIt = m->begin(), fn_ie = m->end(); + fnIt != fn_ie; ++fnIt) { + if (!fnIt->isDeclaration()) { + of << "fn=" << fnIt->getName() << "\n"; + for (Function::iterator bbIt = fnIt->begin(), bb_ie = fnIt->end(); + bbIt != bb_ie; ++bbIt) { + for (BasicBlock::iterator it = bbIt->begin(), ie = bbIt->end(); + it != it; ++it) { + Instruction *instr = &*it; + const InstructionInfo &ii = executor.kmodule->infos->getInfo(instr); + unsigned index = ii.id; + if (ii.file!=sourceFile) { + of << "fl=" << ii.file << "\n"; + sourceFile = ii.file; + } + of << ii.assemblyLine << " "; + of << ii.line << " "; + for (unsigned i=0; i<nStats; i++) + if (istatsMask&(1<<i)) + of << sm.getIndexedValue(sm.getStatistic(i), index) << " "; + of << "\n"; + + if (UseCallPaths && + (isa<CallInst>(instr) || isa<InvokeInst>(instr))) { + CallSiteSummaryTable::iterator it = callSiteStats.find(instr); + if (it!=callSiteStats.end()) { + for (std::map<llvm::Function*, CallSiteInfo>::iterator + fit = it->second.begin(), fie = it->second.end(); + fit != fie; ++fit) { + Function *f = fit->first; + CallSiteInfo &csi = fit->second; + const InstructionInfo &fii = + executor.kmodule->infos->getFunctionInfo(f); + + if (fii.file!="" && fii.file!=sourceFile) + of << "cfl=" << fii.file << "\n"; + of << "cfn=" << f->getName() << "\n"; + of << "calls=" << csi.count << " "; + of << fii.assemblyLine << " "; + of << fii.line << "\n"; + + of << ii.assemblyLine << " "; + of << ii.line << " "; + for (unsigned i=0; i<nStats; i++) { + if (istatsMask&(1<<i)) { + Statistic &s = sm.getStatistic(i); + uint64_t value; + + // Hack, ignore things that don't make sense on + // call paths. + if (&s == &stats::uncoveredInstructions) { + value = 0; + } else { + value = csi.statistics.getValue(s); + } + + of << value << " "; + } + } + of << "\n"; + } + } + } + } + } + } + } + + if (istatsMask & (1<<stats::states.getID())) + updateStateStatistics((uint64_t)-1); + + // Clear then end of the file if necessary (no truncate op?). + unsigned pos = of.tellp(); + for (unsigned i=pos; i<istatsSize; ++i) + of << '\n'; + + of.flush(); +} + +/// + +typedef std::map<Instruction*, std::vector<Function*> > calltargets_ty; + +static calltargets_ty callTargets; +static std::map<Function*, std::vector<Instruction*> > functionCallers; +static std::map<Function*, unsigned> functionShortestPath; + +static std::vector<Instruction*> getSuccs(Instruction *i) { + BasicBlock *bb = i->getParent(); + std::vector<Instruction*> res; + + if (i==bb->getTerminator()) { + for (succ_iterator it = succ_begin(bb), ie = succ_end(bb); it != ie; ++it) + res.push_back(it->begin()); + } else { + res.push_back(++BasicBlock::iterator(i)); + } + + return res; +} + +uint64_t klee::computeMinDistToUncovered(const KInstruction *ki, + uint64_t minDistAtRA) { + StatisticManager &sm = *theStatisticManager; + if (minDistAtRA==0) { // unreachable on return, best is local + return sm.getIndexedValue(stats::minDistToUncovered, + ki->info->id); + } else { + uint64_t minDistLocal = sm.getIndexedValue(stats::minDistToUncovered, + ki->info->id); + uint64_t distToReturn = sm.getIndexedValue(stats::minDistToReturn, + ki->info->id); + + if (distToReturn==0) { // return unreachable, best is local + return minDistLocal; + } else if (!minDistLocal) { // no local reachable + return distToReturn + minDistAtRA; + } else { + return std::min(minDistLocal, distToReturn + minDistAtRA); + } + } +} + +void StatsTracker::computeReachableUncovered() { + KModule *km = executor.kmodule; + Module *m = km->module; + static bool init = true; + const InstructionInfoTable &infos = *km->infos; + StatisticManager &sm = *theStatisticManager; + + if (init) { + init = false; + + // Compute call targets. It would be nice to use alias information + // instead of assuming all indirect calls hit all escaping + // functions, eh? + for (Module::iterator fnIt = m->begin(), fn_ie = m->end(); + fnIt != fn_ie; ++fnIt) { + for (Function::iterator bbIt = fnIt->begin(), bb_ie = fnIt->end(); + bbIt != bb_ie; ++bbIt) { + for (BasicBlock::iterator it = bbIt->begin(), ie = bbIt->end(); + it != it; ++it) { + if (isa<CallInst>(it) || isa<InvokeInst>(it)) { + if (isa<InlineAsm>(it->getOperand(0))) { + // We can never call through here so assume no targets + // (which should be correct anyhow). + callTargets.insert(std::make_pair(it, + std::vector<Function*>())); + } else if (Function *target = getDirectCallTarget(it)) { + callTargets[it].push_back(target); + } else { + callTargets[it] = + std::vector<Function*>(km->escapingFunctions.begin(), + km->escapingFunctions.end()); + } + } + } + } + } + + // Compute function callers as reflexion of callTargets. + for (calltargets_ty::iterator it = callTargets.begin(), + ie = callTargets.end(); it != ie; ++it) + for (std::vector<Function*>::iterator fit = it->second.begin(), + fie = it->second.end(); fit != fie; ++fit) + functionCallers[*fit].push_back(it->first); + + // Initialize minDistToReturn to shortest paths through + // functions. 0 is unreachable. + std::vector<Instruction *> instructions; + for (Module::iterator fnIt = m->begin(), fn_ie = m->end(); + fnIt != fn_ie; ++fnIt) { + if (fnIt->isDeclaration()) { + if (fnIt->doesNotReturn()) { + functionShortestPath[fnIt] = 0; + } else { + functionShortestPath[fnIt] = 1; // whatever + } + } else { + functionShortestPath[fnIt] = 0; + } + + // Not sure if I should bother to preorder here. XXX I should. + for (Function::iterator bbIt = fnIt->begin(), bb_ie = fnIt->end(); + bbIt != bb_ie; ++bbIt) { + for (BasicBlock::iterator it = bbIt->begin(), ie = bbIt->end(); + it != it; ++it) { + instructions.push_back(it); + unsigned id = infos.getInfo(it).id; + sm.setIndexedValue(stats::minDistToReturn, + id, + isa<ReturnInst>(it) || isa<UnwindInst>(it)); + } + } + } + + std::reverse(instructions.begin(), instructions.end()); + + // I'm so lazy it's not even worklisted. + bool changed; + do { + changed = false; + for (std::vector<Instruction*>::iterator it = instructions.begin(), + ie = instructions.end(); it != ie; ++it) { + Instruction *inst = *it; + unsigned bestThrough = 0; + + if (isa<CallInst>(inst) || isa<InvokeInst>(inst)) { + std::vector<Function*> &targets = callTargets[inst]; + for (std::vector<Function*>::iterator fnIt = targets.begin(), + ie = targets.end(); fnIt != ie; ++fnIt) { + uint64_t dist = functionShortestPath[*fnIt]; + if (dist) { + dist = 1+dist; // count instruction itself + if (bestThrough==0 || dist<bestThrough) + bestThrough = dist; + } + } + } else { + bestThrough = 1; + } + + if (bestThrough) { + unsigned id = infos.getInfo(*it).id; + uint64_t best, cur = best = sm.getIndexedValue(stats::minDistToReturn, id); + std::vector<Instruction*> succs = getSuccs(*it); + for (std::vector<Instruction*>::iterator it2 = succs.begin(), + ie = succs.end(); it2 != ie; ++it2) { + uint64_t dist = sm.getIndexedValue(stats::minDistToReturn, + infos.getInfo(*it2).id); + if (dist) { + uint64_t val = bestThrough + dist; + if (best==0 || val<best) + best = val; + } + } + if (best != cur) { + sm.setIndexedValue(stats::minDistToReturn, id, best); + changed = true; + + // Update shortest path if this is the entry point. + Function *f = inst->getParent()->getParent(); + if (inst==f->begin()->begin()) + functionShortestPath[f] = best; + } + } + } + } while (changed); + } + + // compute minDistToUncovered, 0 is unreachable + std::vector<Instruction *> instructions; + for (Module::iterator fnIt = m->begin(), fn_ie = m->end(); + fnIt != fn_ie; ++fnIt) { + // Not sure if I should bother to preorder here. + for (Function::iterator bbIt = fnIt->begin(), bb_ie = fnIt->end(); + bbIt != bb_ie; ++bbIt) { + for (BasicBlock::iterator it = bbIt->begin(), ie = bbIt->end(); + it != it; ++it) { + unsigned id = infos.getInfo(it).id; + instructions.push_back(&*it); + sm.setIndexedValue(stats::minDistToUncovered, + id, + sm.getIndexedValue(stats::uncoveredInstructions, id)); + } + } + } + + std::reverse(instructions.begin(), instructions.end()); + + // I'm so lazy it's not even worklisted. + bool changed; + do { + changed = false; + for (std::vector<Instruction*>::iterator it = instructions.begin(), + ie = instructions.end(); it != ie; ++it) { + Instruction *inst = *it; + uint64_t best, cur = best = sm.getIndexedValue(stats::minDistToUncovered, + infos.getInfo(inst).id); + unsigned bestThrough = 0; + + if (isa<CallInst>(inst) || isa<InvokeInst>(inst)) { + std::vector<Function*> &targets = callTargets[inst]; + for (std::vector<Function*>::iterator fnIt = targets.begin(), + ie = targets.end(); fnIt != ie; ++fnIt) { + uint64_t dist = functionShortestPath[*fnIt]; + if (dist) { + dist = 1+dist; // count instruction itself + if (bestThrough==0 || dist<bestThrough) + bestThrough = dist; + } + + if (!(*fnIt)->isDeclaration()) { + uint64_t calleeDist = sm.getIndexedValue(stats::minDistToUncovered, + infos.getFunctionInfo(*fnIt).id); + if (calleeDist) { + calleeDist = 1+calleeDist; // count instruction itself + if (best==0 || calleeDist<best) + best = calleeDist; + } + } + } + } else { + bestThrough = 1; + } + + if (bestThrough) { + std::vector<Instruction*> succs = getSuccs(inst); + for (std::vector<Instruction*>::iterator it2 = succs.begin(), + ie = succs.end(); it2 != ie; ++it2) { + uint64_t dist = sm.getIndexedValue(stats::minDistToUncovered, + infos.getInfo(*it2).id); + if (dist) { + uint64_t val = bestThrough + dist; + if (best==0 || val<best) + best = val; + } + } + } + + if (best != cur) { + sm.setIndexedValue(stats::minDistToUncovered, + infos.getInfo(inst).id, + best); + changed = true; + } + } + } while (changed); + + for (std::set<ExecutionState*>::iterator it = executor.states.begin(), + ie = executor.states.end(); it != ie; ++it) { + ExecutionState *es = *it; + uint64_t currentFrameMinDist = 0; + for (ExecutionState::stack_ty::iterator sfIt = es->stack.begin(), + sf_ie = es->stack.end(); sfIt != sf_ie; ++sfIt) { + ExecutionState::stack_ty::iterator next = sfIt + 1; + KInstIterator kii; + + if (next==es->stack.end()) { + kii = es->pc; + } else { + kii = next->caller; + ++kii; + } + + sfIt->minDistToUncoveredOnReturn = currentFrameMinDist; + + currentFrameMinDist = computeMinDistToUncovered(kii, currentFrameMinDist); + } + } +} diff --git a/lib/Core/StatsTracker.h b/lib/Core/StatsTracker.h new file mode 100644 index 00000000..9d22b389 --- /dev/null +++ b/lib/Core/StatsTracker.h @@ -0,0 +1,93 @@ +//===-- StatsTracker.h ------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_STATSTRACKER_H +#define KLEE_STATSTRACKER_H + +#include "CallPathManager.h" + +#include <iostream> +#include <set> + +namespace llvm { + class BranchInst; + class Function; + class Instruction; +} + +namespace klee { + class ExecutionState; + class Executor; + class InstructionInfoTable; + class InterpreterHandler; + class KInstruction; + class StackFrame; + + class StatsTracker { + friend class WriteStatsTimer; + friend class WriteIStatsTimer; + + Executor &executor; + std::string objectFilename; + + std::ostream *statsFile, *istatsFile; + double startWallTime; + + unsigned numBranches; + unsigned fullBranches, partialBranches; + + CallPathManager callPathManager; + + bool updateMinDistToUncovered; + + public: + static bool useStatistics(); + + private: + void updateStateStatistics(uint64_t addend); + void writeStatsHeader(); + void writeStatsLine(); + void writeIStats(); + + public: + StatsTracker(Executor &_executor, std::string _objectFilename, + bool _updateMinDistToUncovered); + ~StatsTracker(); + + // called after a new StackFrame has been pushed (for callpath tracing) + void framePushed(ExecutionState &es, StackFrame *parentFrame); + + // called after a StackFrame has been popped + void framePopped(ExecutionState &es); + + // called when some side of a branch has been visited. it is + // imperative that this be called when the statistics index is at + // the index for the branch itself. + void markBranchVisited(ExecutionState *visitedTrue, + ExecutionState *visitedFalse); + + // called when execution is done and stats files should be flushed + void done(); + + // process stats for a single instruction step, es is the state + // about to be stepped + void stepInstruction(ExecutionState &es); + + /// Return time in seconds since execution start. + double elapsed(); + + void computeReachableUncovered(); + }; + + uint64_t computeMinDistToUncovered(const KInstruction *ki, + uint64_t minDistAtRA); + +} + +#endif diff --git a/lib/Core/TimingSolver.cpp b/lib/Core/TimingSolver.cpp new file mode 100644 index 00000000..70e42836 --- /dev/null +++ b/lib/Core/TimingSolver.cpp @@ -0,0 +1,147 @@ +//===-- TimingSolver.cpp --------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TimingSolver.h" + +#include "klee/ExecutionState.h" +#include "klee/Solver.h" +#include "klee/Statistics.h" + +#include "CoreStats.h" + +#include "llvm/System/Process.h" + +using namespace klee; +using namespace llvm; + +/***/ + +bool TimingSolver::evaluate(const ExecutionState& state, ref<Expr> expr, + Solver::Validity &result) { + // Fast path, to avoid timer and OS overhead. + if (expr.isConstant()) { + result = expr.getConstantValue() ? Solver::True : Solver::False; + return true; + } + + sys::TimeValue now(0,0),user(0,0),delta(0,0),sys(0,0); + sys::Process::GetTimeUsage(now,user,sys); + + if (simplifyExprs) + expr = state.constraints.simplifyExpr(expr); + + bool success = solver->evaluate(Query(state.constraints, expr), result); + + sys::Process::GetTimeUsage(delta,user,sys); + delta -= now; + stats::solverTime += delta.usec(); + state.queryCost += delta.usec()/1000000.; + + return success; +} + +bool TimingSolver::mustBeTrue(const ExecutionState& state, ref<Expr> expr, + bool &result) { + // Fast path, to avoid timer and OS overhead. + if (expr.isConstant()) { + result = expr.getConstantValue() ? true : false; + return true; + } + + sys::TimeValue now(0,0),user(0,0),delta(0,0),sys(0,0); + sys::Process::GetTimeUsage(now,user,sys); + + if (simplifyExprs) + expr = state.constraints.simplifyExpr(expr); + + bool success = solver->mustBeTrue(Query(state.constraints, expr), result); + + sys::Process::GetTimeUsage(delta,user,sys); + delta -= now; + stats::solverTime += delta.usec(); + state.queryCost += delta.usec()/1000000.; + + return success; +} + +bool TimingSolver::mustBeFalse(const ExecutionState& state, ref<Expr> expr, + bool &result) { + return mustBeTrue(state, Expr::createNot(expr), result); +} + +bool TimingSolver::mayBeTrue(const ExecutionState& state, ref<Expr> expr, + bool &result) { + bool res; + if (!mustBeFalse(state, expr, res)) + return false; + result = !res; + return true; +} + +bool TimingSolver::mayBeFalse(const ExecutionState& state, ref<Expr> expr, + bool &result) { + bool res; + if (!mustBeTrue(state, expr, res)) + return false; + result = !res; + return true; +} + +bool TimingSolver::getValue(const ExecutionState& state, ref<Expr> expr, + ref<Expr> &result) { + // Fast path, to avoid timer and OS overhead. + if (expr.isConstant()) { + result = expr; + return true; + } + + sys::TimeValue now(0,0),user(0,0),delta(0,0),sys(0,0); + sys::Process::GetTimeUsage(now,user,sys); + + if (simplifyExprs) + expr = state.constraints.simplifyExpr(expr); + + bool success = solver->getValue(Query(state.constraints, expr), result); + + sys::Process::GetTimeUsage(delta,user,sys); + delta -= now; + stats::solverTime += delta.usec(); + state.queryCost += delta.usec()/1000000.; + + return success; +} + +bool +TimingSolver::getInitialValues(const ExecutionState& state, + const std::vector<const Array*> + &objects, + std::vector< std::vector<unsigned char> > + &result) { + if (objects.empty()) + return true; + + sys::TimeValue now(0,0),user(0,0),delta(0,0),sys(0,0); + sys::Process::GetTimeUsage(now,user,sys); + + bool success = solver->getInitialValues(Query(state.constraints, + ref<Expr>(0, Expr::Bool)), + objects, result); + + sys::Process::GetTimeUsage(delta,user,sys); + delta -= now; + stats::solverTime += delta.usec(); + state.queryCost += delta.usec()/1000000.; + + return success; +} + +std::pair< ref<Expr>, ref<Expr> > +TimingSolver::getRange(const ExecutionState& state, ref<Expr> expr) { + return solver->getRange(Query(state.constraints, expr)); +} diff --git a/lib/Core/TimingSolver.h b/lib/Core/TimingSolver.h new file mode 100644 index 00000000..875216d9 --- /dev/null +++ b/lib/Core/TimingSolver.h @@ -0,0 +1,70 @@ +//===-- TimingSolver.h ------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_TIMINGSOLVER_H +#define KLEE_TIMINGSOLVER_H + +#include "klee/Expr.h" +#include "klee/Solver.h" + +#include <vector> + +namespace klee { + class ExecutionState; + class Solver; + class STPSolver; + + /// TimingSolver - A simple class which wraps a solver and handles + /// tracking the statistics that we care about. + class TimingSolver { + public: + Solver *solver; + STPSolver *stpSolver; + bool simplifyExprs; + + public: + /// TimingSolver - Construct a new timing solver. + /// + /// \param _simplifyExprs - Whether expressions should be + /// simplified (via the constraint manager interface) prior to + /// querying. + TimingSolver(Solver *_solver, STPSolver *_stpSolver, + bool _simplifyExprs = true) + : solver(_solver), stpSolver(_stpSolver), simplifyExprs(_simplifyExprs) {} + ~TimingSolver() { + delete solver; + } + + void setTimeout(double t) { + stpSolver->setTimeout(t); + } + + bool evaluate(const ExecutionState&, ref<Expr>, Solver::Validity &result); + + bool mustBeTrue(const ExecutionState&, ref<Expr>, bool &result); + + bool mustBeFalse(const ExecutionState&, ref<Expr>, bool &result); + + bool mayBeTrue(const ExecutionState&, ref<Expr>, bool &result); + + bool mayBeFalse(const ExecutionState&, ref<Expr>, bool &result); + + bool getValue(const ExecutionState &, ref<Expr> expr, ref<Expr> &result); + + bool getInitialValues(const ExecutionState&, + const std::vector<const Array*> &objects, + std::vector< std::vector<unsigned char> > &result); + + virtual std::pair< ref<Expr>, ref<Expr> > + getRange(const ExecutionState&, ref<Expr> query); + }; + +} + +#endif diff --git a/lib/Core/UserSearcher.cpp b/lib/Core/UserSearcher.cpp new file mode 100644 index 00000000..1aff9e5e --- /dev/null +++ b/lib/Core/UserSearcher.cpp @@ -0,0 +1,175 @@ +//===-- UserSearcher.cpp --------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Common.h" + +#include "UserSearcher.h" + +#include "Searcher.h" +#include "Executor.h" + +#include "llvm/Support/CommandLine.h" + +using namespace llvm; +using namespace klee; + +namespace { + cl::opt<bool> + UseRandomSearch("use-random-search"); + + cl::opt<bool> + UseInterleavedRS("use-interleaved-RS"); + + cl::opt<bool> + UseInterleavedNURS("use-interleaved-NURS"); + + cl::opt<bool> + UseInterleavedMD2UNURS("use-interleaved-MD2U-NURS"); + + cl::opt<bool> + UseInterleavedInstCountNURS("use-interleaved-icnt-NURS"); + + cl::opt<bool> + UseInterleavedCPInstCountNURS("use-interleaved-cpicnt-NURS"); + + cl::opt<bool> + UseInterleavedQueryCostNURS("use-interleaved-query-cost-NURS"); + + cl::opt<bool> + UseInterleavedCovNewNURS("use-interleaved-covnew-NURS"); + + cl::opt<bool> + UseNonUniformRandomSearch("use-non-uniform-random-search"); + + cl::opt<bool> + UseRandomPathSearch("use-random-path"); + + cl::opt<WeightedRandomSearcher::WeightType> + WeightType("weight-type", cl::desc("Set the weight type for --use-non-uniform-random-search"), + cl::values(clEnumValN(WeightedRandomSearcher::Depth, "none", "use (2^depth)"), + clEnumValN(WeightedRandomSearcher::InstCount, "icnt", "use current pc exec count"), + clEnumValN(WeightedRandomSearcher::CPInstCount, "cpicnt", "use current pc exec count"), + clEnumValN(WeightedRandomSearcher::QueryCost, "query-cost", "use query cost"), + clEnumValN(WeightedRandomSearcher::MinDistToUncovered, "md2u", "use min dist to uncovered"), + clEnumValN(WeightedRandomSearcher::CoveringNew, "covnew", "use min dist to uncovered + coveringNew flag"), + clEnumValEnd)); + + cl::opt<bool> + UseMerge("use-merge", + cl::desc("Enable support for klee_merge() (experimental)")); + + cl::opt<bool> + UseBumpMerge("use-bump-merge", + cl::desc("Enable support for klee_merge() (extra experimental)")); + + cl::opt<bool> + UseIterativeDeepeningTimeSearch("use-iterative-deepening-time-search", + cl::desc("(experimental)")); + + cl::opt<bool> + UseBatchingSearch("use-batching-search", + cl::desc("Use batching searcher (keep running selected state for N instructions/time, see --batch-instructions and --batch-time")); + + cl::opt<unsigned> + BatchInstructions("batch-instructions", + cl::desc("Number of instructions to batch when using --use-batching-search"), + cl::init(10000)); + + cl::opt<double> + BatchTime("batch-time", + cl::desc("Amount of time to batch when using --use-batching-search"), + cl::init(5.0)); +} + +bool klee::userSearcherRequiresMD2U() { + return (WeightType==WeightedRandomSearcher::MinDistToUncovered || + WeightType==WeightedRandomSearcher::CoveringNew || + UseInterleavedMD2UNURS || + UseInterleavedCovNewNURS || + UseInterleavedInstCountNURS || + UseInterleavedCPInstCountNURS || + UseInterleavedQueryCostNURS); +} + +// FIXME: Remove. +bool klee::userSearcherRequiresBranchSequences() { + return false; +} + +Searcher *klee::constructUserSearcher(Executor &executor) { + Searcher *searcher = 0; + + if (UseRandomPathSearch) { + searcher = new RandomPathSearcher(executor); + } else if (UseNonUniformRandomSearch) { + searcher = new WeightedRandomSearcher(executor, WeightType); + } else if (UseRandomSearch) { + searcher = new RandomSearcher(); + } else { + searcher = new DFSSearcher(); + } + + if (UseInterleavedNURS || UseInterleavedMD2UNURS || UseInterleavedRS || + UseInterleavedCovNewNURS || UseInterleavedInstCountNURS || + UseInterleavedCPInstCountNURS || UseInterleavedQueryCostNURS) { + std::vector<Searcher *> s; + s.push_back(searcher); + + if (UseInterleavedNURS) + s.push_back(new WeightedRandomSearcher(executor, + WeightedRandomSearcher::Depth)); + if (UseInterleavedMD2UNURS) + s.push_back(new WeightedRandomSearcher(executor, + WeightedRandomSearcher::MinDistToUncovered)); + + if (UseInterleavedCovNewNURS) + s.push_back(new WeightedRandomSearcher(executor, + WeightedRandomSearcher::CoveringNew)); + + if (UseInterleavedInstCountNURS) + s.push_back(new WeightedRandomSearcher(executor, + WeightedRandomSearcher::InstCount)); + + if (UseInterleavedCPInstCountNURS) + s.push_back(new WeightedRandomSearcher(executor, + WeightedRandomSearcher::CPInstCount)); + + if (UseInterleavedQueryCostNURS) + s.push_back(new WeightedRandomSearcher(executor, + WeightedRandomSearcher::QueryCost)); + + if (UseInterleavedRS) + s.push_back(new RandomSearcher()); + + searcher = new InterleavedSearcher(s); + } + + if (UseBatchingSearch) { + searcher = new BatchingSearcher(searcher, BatchTime, BatchInstructions); + } + + if (UseMerge) { + assert(!UseBumpMerge); + searcher = new MergingSearcher(executor, searcher); + } else if (UseBumpMerge) { + searcher = new BumpMergingSearcher(executor, searcher); + } + + if (UseIterativeDeepeningTimeSearch) { + searcher = new IterativeDeepeningTimeSearcher(searcher); + } + + std::ostream &os = executor.getHandler().getInfoStream(); + + os << "BEGIN searcher description\n"; + searcher->printName(os); + os << "END searcher description\n"; + + return searcher; +} diff --git a/lib/Core/UserSearcher.h b/lib/Core/UserSearcher.h new file mode 100644 index 00000000..9571bf5b --- /dev/null +++ b/lib/Core/UserSearcher.h @@ -0,0 +1,25 @@ +//===-- UserSearcher.h ------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_USERSEARCHER_H +#define KLEE_USERSEARCHER_H + +namespace klee { + class Executor; + class Searcher; + + // XXX gross, should be on demand? + bool userSearcherRequiresMD2U(); + + bool userSearcherRequiresBranchSequences(); + + Searcher *constructUserSearcher(Executor &executor); +} + +#endif diff --git a/lib/Expr/Constraints.cpp b/lib/Expr/Constraints.cpp new file mode 100644 index 00000000..e9c376f4 --- /dev/null +++ b/lib/Expr/Constraints.cpp @@ -0,0 +1,155 @@ +//===-- Constraints.cpp ---------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Constraints.h" + +#include "klee/util/ExprPPrinter.h" +#include "klee/util/ExprVisitor.h" + +#include <iostream> +#include <map> + +using namespace klee; + +class ExprReplaceVisitor : public ExprVisitor { +private: + ref<Expr> src, dst; + +public: + ExprReplaceVisitor(ref<Expr> _src, ref<Expr> _dst) : src(_src), dst(_dst) {} + + Action visitExpr(const Expr &e) { + if (e == *src.get()) { + return Action::changeTo(dst); + } else { + return Action::doChildren(); + } + } + + Action visitExprPost(const Expr &e) { + if (e == *src.get()) { + return Action::changeTo(dst); + } else { + return Action::doChildren(); + } + } +}; + +class ExprReplaceVisitor2 : public ExprVisitor { +private: + const std::map< ref<Expr>, ref<Expr> > &replacements; + +public: + ExprReplaceVisitor2(const std::map< ref<Expr>, ref<Expr> > &_replacements) + : ExprVisitor(true), + replacements(_replacements) {} + + Action visitExprPost(const Expr &e) { + std::map< ref<Expr>, ref<Expr> >::const_iterator it = + replacements.find(ref<Expr>((Expr*) &e)); + if (it!=replacements.end()) { + return Action::changeTo(it->second); + } else { + return Action::doChildren(); + } + } +}; + +bool ConstraintManager::rewriteConstraints(ExprVisitor &visitor) { + ConstraintManager::constraints_ty old; + bool changed = false; + + constraints.swap(old); + for (ConstraintManager::constraints_ty::iterator + it = old.begin(), ie = old.end(); it != ie; ++it) { + ref<Expr> &ce = *it; + ref<Expr> e = visitor.visit(ce); + + if (e!=ce) { + addConstraintInternal(e); // enable further reductions + changed = true; + } else { + constraints.push_back(ce); + } + } + + return changed; +} + +void ConstraintManager::simplifyForValidConstraint(ref<Expr> e) { + // XXX +} + +ref<Expr> ConstraintManager::simplifyExpr(ref<Expr> e) const { + if (e.isConstant()) + return e; + + std::map< ref<Expr>, ref<Expr> > equalities; + + for (ConstraintManager::constraints_ty::const_iterator + it = constraints.begin(), ie = constraints.end(); it != ie; ++it) { + if (const EqExpr *ee = dyn_ref_cast<EqExpr>(*it)) { + if (ee->left.isConstant()) { + equalities.insert(std::make_pair(ee->right, + ee->left)); + } else { + equalities.insert(std::make_pair(*it, + ref<Expr>(1,Expr::Bool))); + } + } else { + equalities.insert(std::make_pair(*it, + ref<Expr>(1,Expr::Bool))); + } + } + + return ExprReplaceVisitor2(equalities).visit(e); +} + +void ConstraintManager::addConstraintInternal(ref<Expr> e) { + // rewrite any known equalities + + // XXX should profile the effects of this and the overhead. + // traversing the constraints looking for equalities is hardly the + // slowest thing we do, but it is probably nicer to have a + // ConstraintSet ADT which efficiently remembers obvious patterns + // (byte-constant comparison). + + switch (e.getKind()) { + case Expr::Constant: + assert(e.getConstantValue() && "attempt to add invalid (false) constraint"); + break; + + // split to enable finer grained independence and other optimizations + case Expr::And: { + BinaryExpr *be = static_ref_cast<BinaryExpr>(e); + addConstraintInternal(be->left); + addConstraintInternal(be->right); + break; + } + + case Expr::Eq: { + BinaryExpr *be = static_ref_cast<BinaryExpr>(e); + if (be->left.isConstant()) { + ExprReplaceVisitor visitor(be->right, be->left); + rewriteConstraints(visitor); + } + constraints.push_back(e); + break; + } + + default: + constraints.push_back(e); + break; + } +} + +void ConstraintManager::addConstraint(ref<Expr> e) { + e = simplifyExpr(e); + addConstraintInternal(e); +} diff --git a/lib/Expr/Expr.cpp b/lib/Expr/Expr.cpp new file mode 100644 index 00000000..55b9a0a4 --- /dev/null +++ b/lib/Expr/Expr.cpp @@ -0,0 +1,1122 @@ +//===-- Expr.cpp ----------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Expr.h" + + +#include "klee/Machine.h" +// FIXME: This shouldn't be here. +//#include "klee/Memory.h" +#include "llvm/Type.h" +#include "llvm/DerivedTypes.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Streams.h" +// FIXME: We shouldn't need this once fast constant support moves into +// Core. If we need to do arithmetic, we probably want to use APInt. +#include "klee/Internal/Support/IntEvaluation.h" + +#include "klee/util/ExprPPrinter.h" + +using namespace klee; +using namespace llvm; + +namespace { + cl::opt<bool> + ConstArrayOpt("const-array-opt", + cl::init(false), + cl::desc("Enable various optimizations involving all-constant arrays.")); +} + +/***/ + +unsigned Expr::count = 0; + +ref<Expr> Expr::createTempRead(const Array *array, Expr::Width w) { + UpdateList ul(array, true, 0); + + switch (w) { + case Expr::Bool: + return ZExtExpr::create(ReadExpr::create(ul, + ref<Expr>(0,kMachinePointerType)), + Expr::Bool); + case Expr::Int8: + return ReadExpr::create(ul, + ref<Expr>(0,kMachinePointerType)); + case Expr::Int16: + return ConcatExpr::create(ReadExpr::create(ul, + ref<Expr>(1,kMachinePointerType)), + ReadExpr::create(ul, + ref<Expr>(0,kMachinePointerType))); + case Expr::Int32: + return ConcatExpr::create4(ReadExpr::create(ul, + ref<Expr>(3,kMachinePointerType)), + ReadExpr::create(ul, + ref<Expr>(2,kMachinePointerType)), + ReadExpr::create(ul, + ref<Expr>(1,kMachinePointerType)), + ReadExpr::create(ul, + ref<Expr>(0,kMachinePointerType))); + case Expr::Int64: + return ConcatExpr::create8(ReadExpr::create(ul, + ref<Expr>(7,kMachinePointerType)), + ReadExpr::create(ul, + ref<Expr>(6,kMachinePointerType)), + ReadExpr::create(ul, + ref<Expr>(5,kMachinePointerType)), + ReadExpr::create(ul, + ref<Expr>(4,kMachinePointerType)), + ReadExpr::create(ul, + ref<Expr>(3,kMachinePointerType)), + ReadExpr::create(ul, + ref<Expr>(2,kMachinePointerType)), + ReadExpr::create(ul, + ref<Expr>(1,kMachinePointerType)), + ReadExpr::create(ul, + ref<Expr>(0,kMachinePointerType))); + default: assert(0 && "invalid width"); + } +} + +// returns 0 if b is structurally equal to *this +int Expr::compare(const Expr &b) const { + if (this == &b) return 0; + + Kind ak = getKind(), bk = b.getKind(); + if (ak!=bk) + return (ak < bk) ? -1 : 1; + + if (hashValue != b.hashValue) + return (hashValue < b.hashValue) ? -1 : 1; + + if (int res = compareContents(b)) + return res; + + unsigned aN = getNumKids(); + for (unsigned i=0; i<aN; i++) + if (int res = getKid(i).compare(b.getKid(i))) + return res; + + return 0; +} + +void Expr::printKind(std::ostream &os, Kind k) { + switch(k) { +#define X(C) case C: os << #C; break + X(Constant); + X(NotOptimized); + X(Read); + X(Select); + X(Concat); + X(Extract); + X(ZExt); + X(SExt); + X(Add); + X(Sub); + X(Mul); + X(UDiv); + X(SDiv); + X(URem); + X(SRem); + X(And); + X(Or); + X(Xor); + X(Shl); + X(LShr); + X(AShr); + X(Eq); + X(Ne); + X(Ult); + X(Ule); + X(Ugt); + X(Uge); + X(Slt); + X(Sle); + X(Sgt); + X(Sge); +#undef X + default: + assert(0 && "invalid kind"); + } +} + +//////// +// +// Simple hash functions for various kinds of Exprs +// +/////// + +unsigned Expr::computeHash() { + unsigned res = getKind() * Expr::MAGIC_HASH_CONSTANT; + + int n = getNumKids(); + for (int i = 0; i < n; i++) { + res <<= 1; + res ^= getKid(i).hash() * Expr::MAGIC_HASH_CONSTANT; + } + + hashValue = res; + return hashValue; +} + +unsigned ConstantExpr::computeHash() { + hashValue = Expr::hashConstant(asUInt64, width); + return hashValue; +} + +unsigned CastExpr::computeHash() { + unsigned res = getWidth() * Expr::MAGIC_HASH_CONSTANT; + hashValue = res ^ src.hash() * Expr::MAGIC_HASH_CONSTANT; + return hashValue; +} + +unsigned ExtractExpr::computeHash() { + unsigned res = offset * Expr::MAGIC_HASH_CONSTANT; + res ^= getWidth() * Expr::MAGIC_HASH_CONSTANT; + hashValue = res ^ expr.hash() * Expr::MAGIC_HASH_CONSTANT; + return hashValue; +} + +unsigned ReadExpr::computeHash() { + unsigned res = index.hash() * Expr::MAGIC_HASH_CONSTANT; + res ^= updates.hash(); + hashValue = res; + return hashValue; +} + +uint64_t Expr::getConstantValue() const { + assert(getKind() == Constant); + return static_cast<const ConstantExpr*>(this)->asUInt64; +} + +ref<Expr> Expr::createFromKind(Kind k, std::vector<CreateArg> args) { + unsigned numArgs = args.size(); + + switch(k) { + case NotOptimized: + assert(numArgs == 1 && args[0].isExpr() && + "invalid args array for given opcode"); + return NotOptimizedExpr::create(args[0].expr); + + case Select: + assert(numArgs == 3 && args[0].isExpr() && + args[1].isExpr() && args[2].isExpr() && + "invalid args array for Select opcode"); + return SelectExpr::create(args[0].expr, + args[1].expr, + args[2].expr); + + case Concat: { + assert(numArgs == 2 && args[0].isExpr() && args[1].isExpr() && + "invalid args array for Concat opcode"); + + return ConcatExpr::create(args[0].expr, args[1].expr); + } + +#define CAST_EXPR_CASE(T) \ + case T: \ + assert(numArgs == 2 && \ + args[0].isExpr() && args[1].isWidth() && \ + "invalid args array for given opcode"); \ + return T ## Expr::create(args[0].expr, args[1].width); \ + +#define BINARY_EXPR_CASE(T) \ + case T: \ + assert(numArgs == 2 && \ + args[0].isExpr() && args[1].isExpr() && \ + "invalid args array for given opcode"); \ + return T ## Expr::create(args[0].expr, args[1].expr); \ + + CAST_EXPR_CASE(ZExt); + CAST_EXPR_CASE(SExt); + + BINARY_EXPR_CASE(Add); + BINARY_EXPR_CASE(Sub); + BINARY_EXPR_CASE(Mul); + BINARY_EXPR_CASE(UDiv); + BINARY_EXPR_CASE(SDiv); + BINARY_EXPR_CASE(URem); + BINARY_EXPR_CASE(SRem); + BINARY_EXPR_CASE(And); + BINARY_EXPR_CASE(Or); + BINARY_EXPR_CASE(Xor); + BINARY_EXPR_CASE(Shl); + BINARY_EXPR_CASE(LShr); + BINARY_EXPR_CASE(AShr); + + BINARY_EXPR_CASE(Eq); + BINARY_EXPR_CASE(Ne); + BINARY_EXPR_CASE(Ult); + BINARY_EXPR_CASE(Ule); + BINARY_EXPR_CASE(Ugt); + BINARY_EXPR_CASE(Uge); + BINARY_EXPR_CASE(Slt); + BINARY_EXPR_CASE(Sle); + BINARY_EXPR_CASE(Sgt); + BINARY_EXPR_CASE(Sge); + + case Constant: + case Extract: + case Read: + default: + assert(0 && "invalid kind"); + } + +} + + +void Expr::printWidth(std::ostream &os, Width width) { + switch(width) { + case Expr::Bool: os << "Expr::Bool"; break; + case Expr::Int8: os << "Expr::Int8"; break; + case Expr::Int16: os << "Expr::Int16"; break; + case Expr::Int32: os << "Expr::Int32"; break; + case Expr::Int64: os << "Expr::Int64"; break; + default: os << "<invalid type: " << (unsigned) width << ">"; + } +} + +Expr::Width Expr::getWidthForLLVMType(const llvm::Type *t) { + switch (t->getTypeID()) { + case llvm::Type::IntegerTyID: { + Width w = cast<IntegerType>(t)->getBitWidth(); + + // should remove this limitation soon + if (w == 1 || w == 8 || w == 16 || w == 32 || w == 64) + return w; + else { + assert(0 && "XXX arbitrary bit widths unsupported"); + abort(); + } + } + case llvm::Type::FloatTyID: return Expr::Int32; + case llvm::Type::DoubleTyID: return Expr::Int64; + case llvm::Type::X86_FP80TyID: return Expr::Int64; // XXX: needs to be fixed + case llvm::Type::PointerTyID: return kMachinePointerType; + default: + cerr << "non-primitive type argument to Expr::getTypeForLLVMType()\n"; + abort(); + } +} + +ref<Expr> Expr::createImplies(ref<Expr> hyp, ref<Expr> conc) { + return OrExpr::create(Expr::createNot(hyp), conc); +} + +ref<Expr> Expr::createIsZero(ref<Expr> e) { + return EqExpr::create(e, ConstantExpr::create(0, e.getWidth())); +} + +ref<Expr> Expr::createCoerceToPointerType(ref<Expr> e) { + return ZExtExpr::create(e, kMachinePointerType); +} + +ref<Expr> Expr::createNot(ref<Expr> e) { + return createIsZero(e); +} + +ref<Expr> Expr::createPointer(uint64_t v) { + return ConstantExpr::create(v, kMachinePointerType); +} + +Expr* Expr::createConstant(uint64_t val, Width w) { + Expr *r = new ConstantExpr(val, w); + r->computeHash(); + return r; +} + +void Expr::print(std::ostream &os) const { + const ref<Expr> tmp((Expr*)this); + ExprPPrinter::printOne(os, "", tmp); +} + +/***/ + +ref<ConstantExpr> ConstantExpr::fromMemory(void *address, Width width) { + switch (width) { + case Expr::Bool: return ConstantExpr::create(*(( uint8_t*) address), width); + case Expr::Int8: return ConstantExpr::create(*(( uint8_t*) address), width); + case Expr::Int16: return ConstantExpr::create(*((uint16_t*) address), width); + case Expr::Int32: return ConstantExpr::create(*((uint32_t*) address), width); + case Expr::Int64: return ConstantExpr::create(*((uint64_t*) address), width); + default: assert(0 && "invalid type"); + } +} + +void ConstantExpr::toMemory(void *address) { + switch (width) { + case Expr::Bool: *(( uint8_t*) address) = asUInt64; break; + case Expr::Int8: *(( uint8_t*) address) = asUInt64; break; + case Expr::Int16: *((uint16_t*) address) = asUInt64; break; + case Expr::Int32: *((uint32_t*) address) = asUInt64; break; + case Expr::Int64: *((uint64_t*) address) = asUInt64; break; + default: assert(0 && "invalid type"); + } +} + +/***/ + +ref<Expr> NotOptimizedExpr::create(ref<Expr> src) { + return NotOptimizedExpr::alloc(src); +} + +ref<Expr> ReadExpr::create(const UpdateList &ul, ref<Expr> index) { + // rollback index when possible... + + // XXX this doesn't really belong here... there are basically two + // cases, one is rebuild, where we want to optimistically try various + // optimizations when the index has changed, and the other is + // initial creation, where we expect the ObjectState to have constructed + // a smart UpdateList so it is not worth rescanning. + + const UpdateNode *un = ul.head; + for (; un; un=un->next) { + ref<Expr> cond = EqExpr::create(index, un->index); + + if (cond.isConstant()) { + if (cond.getConstantValue()) + return un->value; + } else { + break; + } + } + + return ReadExpr::alloc(ul, index); +} + +int ReadExpr::compareContents(const Expr &b) const { + return updates.compare(static_cast<const ReadExpr&>(b).updates); +} + +ref<Expr> SelectExpr::create(ref<Expr> c, ref<Expr> t, ref<Expr> f) { + Expr::Width kt = t.getWidth(); + + assert(c.getWidth()==Bool && "type mismatch"); + assert(kt==f.getWidth() && "type mismatch"); + + if (c.isConstant()) { + return c.getConstantValue() ? t : f; + } else if (t==f) { + return t; + } else if (kt==Expr::Bool) { // c ? t : f <=> (c and t) or (not c and f) + if (t.isConstant()) { + if (t.getConstantValue()) { + return OrExpr::create(c, f); + } else { + return AndExpr::create(Expr::createNot(c), f); + } + } else if (f.isConstant()) { + if (f.getConstantValue()) { + return OrExpr::create(Expr::createNot(c), t); + } else { + return AndExpr::create(c, t); + } + } + } + + return SelectExpr::alloc(c, t, f); +} + +/***/ + + +ref<Expr> ConcatExpr::create(const ref<Expr> &l, const ref<Expr> &r) { + Expr::Width w = l.getWidth() + r.getWidth(); + + /* Constant folding */ + if (l.getKind() == Expr::Constant && r.getKind() == Expr::Constant) { + // XXX: should fix this constant limitation soon + assert(w <= 64 && "ConcatExpr::create(): don't support concats describing constants greater than 64 bits yet"); + + uint64_t res = (l.getConstantValue() << r.getWidth()) + r.getConstantValue(); + return ConstantExpr::create(res, w); + } + + // Merge contiguous Extracts + if (l.getKind() == Expr::Extract && r.getKind() == Expr::Extract) { + const ExtractExpr* ee_left = static_ref_cast<ExtractExpr>(l); + const ExtractExpr* ee_right = static_ref_cast<ExtractExpr>(r); + if (ee_left->expr == ee_right->expr && + ee_right->offset + ee_right->width == ee_left->offset) { + return ExtractExpr::create(ee_left->expr, ee_right->offset, w); + } + } + + return ConcatExpr::alloc(l, r); +} + +/// Shortcut to concat N kids. The chain returned is unbalanced to the right +ref<Expr> ConcatExpr::createN(unsigned n_kids, const ref<Expr> kids[]) { + assert(n_kids > 0); + if (n_kids == 1) + return kids[0]; + + ref<Expr> r = ConcatExpr::create(kids[n_kids-2], kids[n_kids-1]); + for (int i=n_kids-3; i>=0; i--) + r = ConcatExpr::create(kids[i], r); + return r; +} + +/// Shortcut to concat 4 kids. The chain returned is unbalanced to the right +ref<Expr> ConcatExpr::create4(const ref<Expr> &kid1, const ref<Expr> &kid2, + const ref<Expr> &kid3, const ref<Expr> &kid4) { + return ConcatExpr::create(kid1, ConcatExpr::create(kid2, ConcatExpr::create(kid3, kid4))); +} + +/// Shortcut to concat 8 kids. The chain returned is unbalanced to the right +ref<Expr> ConcatExpr::create8(const ref<Expr> &kid1, const ref<Expr> &kid2, + const ref<Expr> &kid3, const ref<Expr> &kid4, + const ref<Expr> &kid5, const ref<Expr> &kid6, + const ref<Expr> &kid7, const ref<Expr> &kid8) { + return ConcatExpr::create(kid1, ConcatExpr::create(kid2, ConcatExpr::create(kid3, + ConcatExpr::create(kid4, ConcatExpr::create4(kid5, kid6, kid7, kid8))))); +} + +/***/ + +ref<Expr> ExtractExpr::create(ref<Expr> expr, unsigned off, Width w) { + unsigned kw = expr.getWidth(); + assert(w > 0 && off + w <= kw && "invalid extract"); + + if (w == kw) + return expr; + else if (expr.isConstant()) { + return ConstantExpr::create(ints::trunc(expr.getConstantValue() >> off, w, kw), w); + } + else + // Extract(Concat) + if (ConcatExpr *ce = dyn_ref_cast<ConcatExpr>(expr)) { + // if the extract skips the right side of the concat + if (off >= ce->getRight().getWidth()) + return ExtractExpr::create(ce->getLeft(), off - ce->getRight().getWidth(), w); + + // if the extract skips the left side of the concat + if (off + w <= ce->getRight().getWidth()) + return ExtractExpr::create(ce->getRight(), off, w); + + // E(C(x,y)) = C(E(x), E(y)) + return ConcatExpr::create(ExtractExpr::create(ce->getKid(0), 0, w - ce->getKid(1).getWidth() + off), + ExtractExpr::create(ce->getKid(1), off, ce->getKid(1).getWidth() - off)); + } + + return ExtractExpr::alloc(expr, off, w); +} + + +ref<Expr> ExtractExpr::createByteOff(ref<Expr> expr, unsigned offset, Width bits) { + return ExtractExpr::create(expr, 8*offset, bits); +} + +/***/ + +ref<Expr> ZExtExpr::create(const ref<Expr> &e, Width w) { + unsigned kBits = e.getWidth(); + if (w == kBits) { + return e; + } else if (w < kBits) { // trunc + return ExtractExpr::createByteOff(e, 0, w); + } else { + if (e.isConstant()) { + return ConstantExpr::create(ints::zext(e.getConstantValue(), w, kBits), + w); + } + + return ZExtExpr::alloc(e, w); + } +} + +ref<Expr> SExtExpr::create(const ref<Expr> &e, Width w) { + unsigned kBits = e.getWidth(); + if (w == kBits) { + return e; + } else if (w < kBits) { // trunc + return ExtractExpr::createByteOff(e, 0, w); + } else { + if (e.isConstant()) { + return ConstantExpr::create(ints::sext(e.getConstantValue(), w, kBits), + w); + } + + return SExtExpr::alloc(e, w); + } +} + +/***/ + +static ref<Expr> AndExpr_create(Expr *l, Expr *r); +static ref<Expr> XorExpr_create(Expr *l, Expr *r); + +static ref<Expr> EqExpr_createPartial(Expr *l, const ref<Expr> &cr); +static ref<Expr> AndExpr_createPartialR(const ref<Expr> &cl, Expr *r); +static ref<Expr> SubExpr_createPartialR(const ref<Expr> &cl, Expr *r); +static ref<Expr> XorExpr_createPartialR(const ref<Expr> &cl, Expr *r); + +static ref<Expr> AddExpr_createPartialR(const ref<Expr> &cl, Expr *r) { + assert(cl.isConstant() && "non-constant passed in place of constant"); + uint64_t value = cl.getConstantValue(); + Expr::Width type = cl.getWidth(); + + if (type==Expr::Bool) { + return XorExpr_createPartialR(cl, r); + } else if (!value) { + return r; + } else { + Expr::Kind rk = r->getKind(); + if (rk==Expr::Add && r->getKid(0).isConstant()) { // A + (B+c) == (A+B) + c + return AddExpr::create(AddExpr::create(cl, r->getKid(0)), + r->getKid(1)); + } else if (rk==Expr::Sub && r->getKid(0).isConstant()) { // A + (B-c) == (A+B) - c + return SubExpr::create(AddExpr::create(cl, r->getKid(0)), + r->getKid(1)); + } else { + return AddExpr::alloc(cl, r); + } + } +} +static ref<Expr> AddExpr_createPartial(Expr *l, const ref<Expr> &cr) { + return AddExpr_createPartialR(cr, l); +} +static ref<Expr> AddExpr_create(Expr *l, Expr *r) { + Expr::Width type = l->getWidth(); + + if (type == Expr::Bool) { + return XorExpr_create(l, r); + } else { + Expr::Kind lk = l->getKind(), rk = r->getKind(); + if (lk==Expr::Add && l->getKid(0).isConstant()) { // (k+a)+b = k+(a+b) + return AddExpr::create(l->getKid(0), + AddExpr::create(l->getKid(1), r)); + } else if (lk==Expr::Sub && l->getKid(0).isConstant()) { // (k-a)+b = k+(b-a) + return AddExpr::create(l->getKid(0), + SubExpr::create(r, l->getKid(1))); + } else if (rk==Expr::Add && r->getKid(0).isConstant()) { // a + (k+b) = k+(a+b) + return AddExpr::create(r->getKid(0), + AddExpr::create(l, r->getKid(1))); + } else if (rk==Expr::Sub && r->getKid(0).isConstant()) { // a + (k-b) = k+(a-b) + return AddExpr::create(r->getKid(0), + SubExpr::create(l, r->getKid(1))); + } else { + return AddExpr::alloc(l, r); + } + } +} + +static ref<Expr> SubExpr_createPartialR(const ref<Expr> &cl, Expr *r) { + assert(cl.isConstant() && "non-constant passed in place of constant"); + Expr::Width type = cl.getWidth(); + + if (type==Expr::Bool) { + return XorExpr_createPartialR(cl, r); + } else { + Expr::Kind rk = r->getKind(); + if (rk==Expr::Add && r->getKid(0).isConstant()) { // A - (B+c) == (A-B) - c + return SubExpr::create(SubExpr::create(cl, r->getKid(0)), + r->getKid(1)); + } else if (rk==Expr::Sub && r->getKid(0).isConstant()) { // A - (B-c) == (A-B) + c + return AddExpr::create(SubExpr::create(cl, r->getKid(0)), + r->getKid(1)); + } else { + return SubExpr::alloc(cl, r); + } + } +} +static ref<Expr> SubExpr_createPartial(Expr *l, const ref<Expr> &cr) { + assert(cr.isConstant() && "non-constant passed in place of constant"); + uint64_t value = cr.getConstantValue(); + Expr::Width width = cr.getWidth(); + uint64_t nvalue = ints::sub(0, value, width); + + return AddExpr_createPartial(l, ConstantExpr::create(nvalue, width)); +} +static ref<Expr> SubExpr_create(Expr *l, Expr *r) { + Expr::Width type = l->getWidth(); + + if (type == Expr::Bool) { + return XorExpr_create(l, r); + } else if (*l==*r) { + return ref<Expr>(0, type); + } else { + Expr::Kind lk = l->getKind(), rk = r->getKind(); + if (lk==Expr::Add && l->getKid(0).isConstant()) { // (k+a)-b = k+(a-b) + return AddExpr::create(l->getKid(0), + SubExpr::create(l->getKid(1), r)); + } else if (lk==Expr::Sub && l->getKid(0).isConstant()) { // (k-a)-b = k-(a+b) + return SubExpr::create(l->getKid(0), + AddExpr::create(l->getKid(1), r)); + } else if (rk==Expr::Add && r->getKid(0).isConstant()) { // a - (k+b) = (a-c) - k + return SubExpr::create(SubExpr::create(l, r->getKid(1)), + r->getKid(0)); + } else if (rk==Expr::Sub && r->getKid(0).isConstant()) { // a - (k-b) = (a+b) - k + return SubExpr::create(AddExpr::create(l, r->getKid(1)), + r->getKid(0)); + } else { + return SubExpr::alloc(l, r); + } + } +} + +static ref<Expr> MulExpr_createPartialR(const ref<Expr> &cl, Expr *r) { + assert(cl.isConstant() && "non-constant passed in place of constant"); + uint64_t value = cl.getConstantValue(); + Expr::Width type = cl.getWidth(); + + if (type == Expr::Bool) { + return AndExpr_createPartialR(cl, r); + } else if (value == 1) { + return r; + } else if (!value) { + return cl; + } else { + return MulExpr::alloc(cl, r); + } +} +static ref<Expr> MulExpr_createPartial(Expr *l, const ref<Expr> &cr) { + return MulExpr_createPartialR(cr, l); +} +static ref<Expr> MulExpr_create(Expr *l, Expr *r) { + Expr::Width type = l->getWidth(); + + if (type == Expr::Bool) { + return AndExpr::alloc(l, r); + } else { + return MulExpr::alloc(l, r); + } +} + +static ref<Expr> AndExpr_createPartial(Expr *l, const ref<Expr> &cr) { + assert(cr.isConstant() && "non-constant passed in place of constant"); + uint64_t value = cr.getConstantValue(); + Expr::Width width = cr.getWidth();; + + if (value==ints::sext(1, width, 1)) { + return l; + } else if (!value) { + return cr; + } else { + return AndExpr::alloc(l, cr); + } +} +static ref<Expr> AndExpr_createPartialR(const ref<Expr> &cl, Expr *r) { + return AndExpr_createPartial(r, cl); +} +static ref<Expr> AndExpr_create(Expr *l, Expr *r) { + return AndExpr::alloc(l, r); +} + +static ref<Expr> OrExpr_createPartial(Expr *l, const ref<Expr> &cr) { + assert(cr.isConstant() && "non-constant passed in place of constant"); + uint64_t value = cr.getConstantValue(); + Expr::Width width = cr.getWidth(); + + if (value == ints::sext(1, width, 1)) { + return cr; + } else if (!value) { + return l; + } else { + return OrExpr::alloc(l, cr); + } +} +static ref<Expr> OrExpr_createPartialR(const ref<Expr> &cl, Expr *r) { + return OrExpr_createPartial(r, cl); +} +static ref<Expr> OrExpr_create(Expr *l, Expr *r) { + return OrExpr::alloc(l, r); +} + +static ref<Expr> XorExpr_createPartialR(const ref<Expr> &cl, Expr *r) { + assert(cl.isConstant() && "non-constant passed in place of constant"); + uint64_t value = cl.getConstantValue(); + Expr::Width type = cl.getWidth(); + + if (type==Expr::Bool) { + if (value) { + return EqExpr_createPartial(r, ConstantExpr::create(0, Expr::Bool)); + } else { + return r; + } + } else if (!value) { + return r; + } else { + return XorExpr::alloc(cl, r); + } +} + +static ref<Expr> XorExpr_createPartial(Expr *l, const ref<Expr> &cr) { + return XorExpr_createPartialR(cr, l); +} +static ref<Expr> XorExpr_create(Expr *l, Expr *r) { + return XorExpr::alloc(l, r); +} + +static ref<Expr> UDivExpr_create(const ref<Expr> &l, const ref<Expr> &r) { + if (l.getWidth() == Expr::Bool) { // r must be 1 + return l; + } else{ + return UDivExpr::alloc(l, r); + } +} + +static ref<Expr> SDivExpr_create(const ref<Expr> &l, const ref<Expr> &r) { + if (l.getWidth() == Expr::Bool) { // r must be 1 + return l; + } else{ + return SDivExpr::alloc(l, r); + } +} + +static ref<Expr> URemExpr_create(const ref<Expr> &l, const ref<Expr> &r) { + if (l.getWidth() == Expr::Bool) { // r must be 1 + return ConstantExpr::create(0, Expr::Bool); + } else{ + return URemExpr::alloc(l, r); + } +} + +static ref<Expr> SRemExpr_create(const ref<Expr> &l, const ref<Expr> &r) { + if (l.getWidth() == Expr::Bool) { // r must be 1 + return ConstantExpr::create(0, Expr::Bool); + } else{ + return SRemExpr::alloc(l, r); + } +} + +static ref<Expr> ShlExpr_create(const ref<Expr> &l, const ref<Expr> &r) { + if (l.getWidth() == Expr::Bool) { // l & !r + return AndExpr::create(l, Expr::createNot(r)); + } else{ + return ShlExpr::alloc(l, r); + } +} + +static ref<Expr> LShrExpr_create(const ref<Expr> &l, const ref<Expr> &r) { + if (l.getWidth() == Expr::Bool) { // l & !r + return AndExpr::create(l, Expr::createNot(r)); + } else{ + return LShrExpr::alloc(l, r); + } +} + +static ref<Expr> AShrExpr_create(const ref<Expr> &l, const ref<Expr> &r) { + if (l.getWidth() == Expr::Bool) { // l + return l; + } else{ + return AShrExpr::alloc(l, r); + } +} + +#define BCREATE_R(_e_op, _op, partialL, partialR) \ +ref<Expr> _e_op ::create(const ref<Expr> &l, const ref<Expr> &r) { \ + assert(l.getWidth()==r.getWidth() && "type mismatch"); \ + if (l.isConstant()) { \ + if (r.isConstant()) { \ + Expr::Width width = l.getWidth(); \ + uint64_t val = ints::_op(l.getConstantValue(), \ + r.getConstantValue(), width); \ + return ConstantExpr::create(val, width); \ + } else { \ + return _e_op ## _createPartialR(l, r.get()); \ + } \ + } else if (r.isConstant()) { \ + return _e_op ## _createPartial(l.get(), r); \ + } \ + return _e_op ## _create(l.get(), r.get()); \ +} + +#define BCREATE(_e_op, _op) \ +ref<Expr> _e_op ::create(const ref<Expr> &l, const ref<Expr> &r) { \ + assert(l.getWidth()==r.getWidth() && "type mismatch"); \ + if (l.isConstant()) { \ + if (r.isConstant()) { \ + Expr::Width width = l.getWidth(); \ + uint64_t val = ints::_op(l.getConstantValue(), \ + r.getConstantValue(), width); \ + return ConstantExpr::create(val, width); \ + } \ + } \ + return _e_op ## _create(l, r); \ +} + +BCREATE_R(AddExpr, add, AddExpr_createPartial, AddExpr_createPartialR) +BCREATE_R(SubExpr, sub, SubExpr_createPartial, SubExpr_createPartialR) +BCREATE_R(MulExpr, mul, MulExpr_createPartial, MulExpr_createPartialR) +BCREATE_R(AndExpr, land, AndExpr_createPartial, AndExpr_createPartialR) +BCREATE_R(OrExpr, lor, OrExpr_createPartial, OrExpr_createPartialR) +BCREATE_R(XorExpr, lxor, XorExpr_createPartial, XorExpr_createPartialR) +BCREATE(UDivExpr, udiv) +BCREATE(SDivExpr, sdiv) +BCREATE(URemExpr, urem) +BCREATE(SRemExpr, srem) +BCREATE(ShlExpr, shl) +BCREATE(LShrExpr, lshr) +BCREATE(AShrExpr, ashr) + +#define CMPCREATE(_e_op, _op) \ +ref<Expr> _e_op ::create(const ref<Expr> &l, const ref<Expr> &r) { \ + assert(l.getWidth()==r.getWidth() && "type mismatch"); \ + if (l.isConstant()) { \ + if (r.isConstant()) { \ + Expr::Width width = l.getWidth(); \ + uint64_t val = ints::_op(l.getConstantValue(), \ + r.getConstantValue(), width); \ + return ConstantExpr::create(val, Expr::Bool); \ + } \ + } \ + return _e_op ## _create(l, r); \ +} + +#define CMPCREATE_T(_e_op, _op, _reflexive_e_op, partialL, partialR) \ +ref<Expr> _e_op ::create(const ref<Expr> &l, const ref<Expr> &r) { \ + assert(l.getWidth()==r.getWidth() && "type mismatch"); \ + if (l.isConstant()) { \ + if (r.isConstant()) { \ + Expr::Width width = l.getWidth(); \ + uint64_t val = ints::_op(l.getConstantValue(), \ + r.getConstantValue(), width); \ + return ConstantExpr::create(val, Expr::Bool); \ + } else { \ + return partialR(l, r.get()); \ + } \ + } else if (r.isConstant()) { \ + return partialL(l.get(), r); \ + } else { \ + return _e_op ## _create(l.get(), r.get()); \ + } \ +} + + +static ref<Expr> EqExpr_create(const ref<Expr> &l, const ref<Expr> &r) { + if (l == r) { + return ref<Expr>(1, Expr::Bool); + } else { + return EqExpr::alloc(l, r); + } +} + + +/// Tries to optimize EqExpr cl == rd, where cl is a ConstantExpr and +/// rd a ReadExpr. If rd is a read into an all-constant array, +/// returns a disjunction of equalities on the index. Otherwise, +/// returns the initial equality expression. +static ref<Expr> TryConstArrayOpt(const ref<Expr> &cl, + ReadExpr *rd) { + assert(cl.isConstant() && "constant expression required"); + assert(rd->getKind() == Expr::Read && "read expression required"); + + uint64_t ct = cl.getConstantValue(); + ref<Expr> first_idx_match; + + // number of positions in the array that contain value ct + unsigned matches = 0; + + //llvm::cerr << "Size updates/root: " << rd->updates.getSize() << " / " << (rd->updates.root)->size << "\n"; + + // for now, just assume standard "flushing" of a concrete array, + // where the concrete array has one update for each index, in order + bool all_const = true; + if (rd->updates.getSize() == rd->updates.root->size) { + unsigned k = rd->updates.getSize(); + for (const UpdateNode *un = rd->updates.head; un; un = un->next) { + assert(k > 0); + k--; + + ref<Expr> idx = un->index; + ref<Expr> val = un->value; + if (!idx.isConstant() || !val.isConstant()) { + all_const = false; + //llvm::cerr << "Idx or val not constant\n"; + break; + } + else { + if (idx.getConstantValue() != k) { + all_const = false; + //llvm::cerr << "Wrong constant\n"; + break; + } + if (val.getConstantValue() == ct) { + matches++; + if (matches == 1) + first_idx_match = un->index; + } + } + } + } + else all_const = false; + + if (all_const && matches <= 100) { + // apply optimization + //llvm::cerr << "\n\n=== Applying const array optimization ===\n\n"; + + if (matches == 0) + return ref<Expr>(0, Expr::Bool); + + ref<Expr> res = EqExpr::create(first_idx_match, rd->index); + if (matches == 1) + return res; + + for (const UpdateNode *un = rd->updates.head; un; un = un->next) { + if (un->index != first_idx_match && un->value.getConstantValue() == ct) { + ref<Expr> curr_eq = EqExpr::create(un->index, rd->index); + res = OrExpr::create(curr_eq, res); + } + } + + return res; + } + + return EqExpr_create(cl, ref<Expr>(rd)); +} + + +static ref<Expr> EqExpr_createPartialR(const ref<Expr> &cl, Expr *r) { + assert(cl.isConstant() && "non-constant passed in place of constant"); + uint64_t value = cl.getConstantValue(); + Expr::Width width = cl.getWidth(); + + Expr::Kind rk = r->getKind(); + if (width == Expr::Bool) { + if (value) { + return r; + } else { + // 0 != ... + + if (rk == Expr::Eq) { + const EqExpr *ree = static_ref_cast<EqExpr>(r); + + // eliminate double negation + if (ree->left.isConstant() && + ree->left.getWidth()==Expr::Bool) { + assert(!ree->left.getConstantValue()); + return ree->right; + } + } else if (rk == Expr::Or) { + const OrExpr *roe = static_ref_cast<OrExpr>(r); + + // transform not(or(a,b)) to and(not a, not b) + return AndExpr::create(Expr::createNot(roe->left), + Expr::createNot(roe->right)); + } + } + } else if (rk == Expr::SExt) { + // (sext(a,T)==c) == (a==c) + const SExtExpr *see = static_ref_cast<SExtExpr>(r); + Expr::Width fromBits = see->src.getWidth(); + uint64_t trunc = bits64::truncateToNBits(value, fromBits); + + // pathological check, make sure it is possible to + // sext to this value *from any value* + if (value == ints::sext(trunc, width, fromBits)) { + return EqExpr::create(see->src, ConstantExpr::create(trunc, fromBits)); + } else { + return ConstantExpr::create(0, Expr::Bool); + } + } else if (rk == Expr::ZExt) { + // (zext(a,T)==c) == (a==c) + const ZExtExpr *zee = static_ref_cast<ZExtExpr>(r); + Expr::Width fromBits = zee->src.getWidth(); + uint64_t trunc = bits64::truncateToNBits(value, fromBits); + + // pathological check, make sure it is possible to + // zext to this value *from any value* + if (value == ints::zext(trunc, width, fromBits)) { + return EqExpr::create(zee->src, ConstantExpr::create(trunc, fromBits)); + } else { + return ConstantExpr::create(0, Expr::Bool); + } + } else if (rk==Expr::Add) { + const AddExpr *ae = static_ref_cast<AddExpr>(r); + if (ae->left.isConstant()) { + // c0 = c1 + b => c0 - c1 = b + return EqExpr_createPartialR(SubExpr::create(cl, ae->left), + ae->right.get()); + } + } else if (rk==Expr::Sub) { + const SubExpr *se = static_ref_cast<SubExpr>(r); + if (se->left.isConstant()) { + // c0 = c1 - b => c1 - c0 = b + return EqExpr_createPartialR(SubExpr::create(se->left, cl), + se->right.get()); + } + } else if (rk == Expr::Read && ConstArrayOpt) { + return TryConstArrayOpt(cl, static_cast<ReadExpr*>(r)); + } + + return EqExpr_create(cl, r); +} + +static ref<Expr> EqExpr_createPartial(Expr *l, const ref<Expr> &cr) { + return EqExpr_createPartialR(cr, l); +} + +ref<Expr> NeExpr::create(const ref<Expr> &l, const ref<Expr> &r) { + return EqExpr::create(ConstantExpr::create(0, Expr::Bool), + EqExpr::create(l, r)); +} + +ref<Expr> UgtExpr::create(const ref<Expr> &l, const ref<Expr> &r) { + return UltExpr::create(r, l); +} +ref<Expr> UgeExpr::create(const ref<Expr> &l, const ref<Expr> &r) { + return UleExpr::create(r, l); +} + +ref<Expr> SgtExpr::create(const ref<Expr> &l, const ref<Expr> &r) { + return SltExpr::create(r, l); +} +ref<Expr> SgeExpr::create(const ref<Expr> &l, const ref<Expr> &r) { + return SleExpr::create(r, l); +} + +static ref<Expr> UltExpr_create(const ref<Expr> &l, const ref<Expr> &r) { + Expr::Width t = l.getWidth(); + if (t == Expr::Bool) { // !l && r + return AndExpr::create(Expr::createNot(l), r); + } else { + if (r.isConstant()) { + uint64_t value = r.getConstantValue(); + if (value <= 8) { + ref<Expr> res(0,Expr::Bool); + for (unsigned i=0; i<value; i++) { + res = OrExpr::create(EqExpr::create(l, ref<Expr>(i,t)), res); + } + // llvm::cerr << l << "<" << r << " <=> " << res << "\n"; + return res; + } + } + return UltExpr::alloc(l, r); + } +} + +static ref<Expr> UleExpr_create(const ref<Expr> &l, const ref<Expr> &r) { + if (l.getWidth() == Expr::Bool) { // !(l && !r) + return OrExpr::create(Expr::createNot(l), r); + } else { + return UleExpr::alloc(l, r); + } +} + +static ref<Expr> SltExpr_create(const ref<Expr> &l, const ref<Expr> &r) { + if (l.getWidth() == Expr::Bool) { // l && !r + return AndExpr::create(l, Expr::createNot(r)); + } else { + return SltExpr::alloc(l, r); + } +} + +static ref<Expr> SleExpr_create(const ref<Expr> &l, const ref<Expr> &r) { + if (l.getWidth() == Expr::Bool) { // !(!l && r) + return OrExpr::create(l, Expr::createNot(r)); + } else { + return SleExpr::alloc(l, r); + } +} + +CMPCREATE_T(EqExpr, eq, EqExpr, EqExpr_createPartial, EqExpr_createPartialR) +CMPCREATE(UltExpr, ult) +CMPCREATE(UleExpr, ule) +CMPCREATE(SltExpr, slt) +CMPCREATE(SleExpr, sle) diff --git a/lib/Expr/ExprEvaluator.cpp b/lib/Expr/ExprEvaluator.cpp new file mode 100644 index 00000000..102387e1 --- /dev/null +++ b/lib/Expr/ExprEvaluator.cpp @@ -0,0 +1,74 @@ +//===-- ExprEvaluator.cpp -------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/util/ExprEvaluator.h" + +using namespace klee; + +ExprVisitor::Action ExprEvaluator::evalRead(const UpdateList &ul, + unsigned index) { + for (const UpdateNode *un=ul.head; un; un=un->next) { + ref<Expr> ui = visit(un->index); + + if (ui.isConstant()) { + if (ui.getConstantValue() == index) + return Action::changeTo(visit(un->value)); + } else { + // update index is unknown, so may or may not be index, we + // cannot guarantee value. we can rewrite to read at this + // version though (mostly for debugging). + + UpdateList fwd(ul.root, un, 0); + return Action::changeTo(ReadExpr::create(fwd, + ref<Expr>(index,Expr::Int32))); + } + } + + return Action::changeTo(getInitialValue(*ul.root, index)); +} + +ExprVisitor::Action ExprEvaluator::visitRead(const ReadExpr &re) { + ref<Expr> v = visit(re.index); + + if (v.isConstant()) { + return evalRead(re.updates, v.getConstantValue()); + } else { + return Action::doChildren(); + } +} + +// we need to check for div by zero during partial evaluation, +// if this occurs then simply ignore the 0 divisor and use the +// original expression. +ExprVisitor::Action ExprEvaluator::protectedDivOperation(const BinaryExpr &e) { + ref<Expr> kids[2] = { visit(e.left), + visit(e.right) }; + + if (kids[1].isConstant() && !kids[1].getConstantValue()) + kids[1] = e.right; + + if (kids[0]!=e.left || kids[1]!=e.right) { + return Action::changeTo(e.rebuild(kids)); + } else { + return Action::skipChildren(); + } +} + +ExprVisitor::Action ExprEvaluator::visitUDiv(const UDivExpr &e) { + return protectedDivOperation(e); +} +ExprVisitor::Action ExprEvaluator::visitSDiv(const SDivExpr &e) { + return protectedDivOperation(e); +} +ExprVisitor::Action ExprEvaluator::visitURem(const URemExpr &e) { + return protectedDivOperation(e); +} +ExprVisitor::Action ExprEvaluator::visitSRem(const SRemExpr &e) { + return protectedDivOperation(e); +} diff --git a/lib/Expr/ExprPPrinter.cpp b/lib/Expr/ExprPPrinter.cpp new file mode 100644 index 00000000..dc7f4f64 --- /dev/null +++ b/lib/Expr/ExprPPrinter.cpp @@ -0,0 +1,478 @@ +//===-- ExprPPrinter.cpp - ----------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/util/ExprPPrinter.h" + +#include "klee/Constraints.h" + +#include "llvm/Support/CommandLine.h" + +#include <map> +#include <vector> +#include <iostream> +#include <sstream> +#include <iomanip> + +using namespace klee; + +namespace { + llvm::cl::opt<bool> + PCWidthAsArg("pc-width-as-arg", llvm::cl::init(true)); + + llvm::cl::opt<bool> + PCAllWidths("pc-all-widths", llvm::cl::init(false)); + + llvm::cl::opt<bool> + PCPrefixWidth("pc-prefix-width", llvm::cl::init(true)); + + llvm::cl::opt<bool> + PCMultibyteReads("pc-multibyte-reads", llvm::cl::init(true)); + + llvm::cl::opt<bool> + PCAllConstWidths("pc-all-const-widths", llvm::cl::init(false)); +} + +/// PrintContext - Helper class for storing extra information for +/// the pretty printer. +class PrintContext { +private: + std::ostream &os; + std::stringstream ss; + std::string newline; + +public: + /// Number of characters on the current line. + unsigned pos; + +public: + PrintContext(std::ostream &_os) : os(_os), newline("\n"), pos(0) {} + + void setNewline(const std::string &_newline) { + newline = _newline; + } + + void breakLine(unsigned indent=0) { + os << newline; + if (indent) + os << std::setw(indent) << ' '; + pos = indent; + } + + /// write - Output a string to the stream and update the + /// position. The stream should not have any newlines. + void write(const std::string &s) { + os << s; + pos += s.length(); + } + + template <typename T> + PrintContext &operator<<(T elt) { + ss.str(""); + ss << elt; + write(ss.str()); + return *this; + } +}; + +class PPrinter : public ExprPPrinter { + std::map<ref<Expr>, unsigned> bindings; + std::map<const UpdateNode*, unsigned> updateBindings; + std::set< ref<Expr> > couldPrint, shouldPrint; + std::set<const UpdateNode*> couldPrintUpdates, shouldPrintUpdates; + std::ostream &os; + unsigned counter; + unsigned updateCounter; + bool hasScan; + std::string newline; + + /// shouldPrintWidth - Predicate for whether this expression should + /// be printed with its width. + bool shouldPrintWidth(ref<Expr> e) { + if (PCAllWidths) + return true; + return e.getWidth() != Expr::Bool; + } + + bool isVerySimple(const ref<Expr> &e) { + return e.isConstant() || bindings.find(e)!=bindings.end(); + } + + bool isVerySimpleUpdate(const UpdateNode *un) { + return !un || updateBindings.find(un)!=updateBindings.end(); + } + + + // document me! + bool isSimple(const ref<Expr> &e) { + if (isVerySimple(e)) { + return true; + } else if (const ReadExpr *re = dyn_ref_cast<ReadExpr>(e)) { + return isVerySimple(re->index) && isVerySimpleUpdate(re->updates.head); + } else { + Expr *ep = e.get(); + for (unsigned i=0; i<ep->getNumKids(); i++) + if (!isVerySimple(ep->getKid(i))) + return false; + return true; + } + } + + bool hasSimpleKids(const Expr *ep) { + for (unsigned i=0; i<ep->getNumKids(); i++) + if (!isSimple(ep->getKid(i))) + return false; + return true; + } + + void scanUpdate(const UpdateNode *un) { + if (un) { + if (couldPrintUpdates.insert(un).second) { + scanUpdate(un->next); + scan1(un->index); + scan1(un->value); + } else { + shouldPrintUpdates.insert(un); + } + } + } + + void scan1(const ref<Expr> &e) { + if (!e.isConstant()) { + if (couldPrint.insert(e).second) { + Expr *ep = e.get(); + for (unsigned i=0; i<ep->getNumKids(); i++) + scan1(ep->getKid(i)); + if (const ReadExpr *re = dyn_ref_cast<ReadExpr>(e)) + scanUpdate(re->updates.head); + } else { + shouldPrint.insert(e); + } + } + } + + void printUpdateList(const UpdateList &updates, PrintContext &PC) { + const UpdateNode *head = updates.head; + + // Special case empty list. + if (!head) { + if (updates.isRooted) { + PC << "arr" << updates.root->id; + } else { + PC << "[]"; + } + return; + } + + // FIXME: Explain this breaking policy. + bool openedList = false, nextShouldBreak = false; + unsigned outerIndent = PC.pos; + unsigned middleIndent = 0; + for (const UpdateNode *un = head; un; un = un->next) { + // We are done if we hit the cache. + std::map<const UpdateNode*, unsigned>::iterator it = + updateBindings.find(un); + if (it!=updateBindings.end()) { + if (openedList) + PC << "] @ "; + PC << "U" << it->second; + return; + } else if (!hasScan || shouldPrintUpdates.count(un)) { + if (openedList) + PC << "] @"; + if (un != head) + PC.breakLine(outerIndent); + PC << "U" << updateCounter << ":"; + updateBindings.insert(std::make_pair(un, updateCounter++)); + openedList = nextShouldBreak = false; + } + + if (!openedList) { + openedList = 1; + PC << '['; + middleIndent = PC.pos; + } else { + PC << ','; + printSeparator(PC, !nextShouldBreak, middleIndent); + } + //PC << "(="; + //unsigned innerIndent = PC.pos; + print(un->index, PC); + //printSeparator(PC, isSimple(un->index), innerIndent); + PC << "="; + print(un->value, PC); + //PC << ')'; + + nextShouldBreak = !(un->index.isConstant() && un->value.isConstant()); + } + + if (openedList) + PC << ']'; + + if (updates.isRooted) + PC << " @ arr" << updates.root->id; + } + + void printWidth(PrintContext &PC, ref<Expr> e) { + if (!shouldPrintWidth(e)) + return; + + if (PCWidthAsArg) { + PC << ' '; + if (PCPrefixWidth) + PC << 'w'; + } + + PC << e.getWidth(); + } + + /// hasOrderedReads - True iff all children are reads with + /// consecutive offsets according to the given \arg stride. + bool hasOrderedReads(const Expr *ep, int stride) { + const ReadExpr *base = dyn_ref_cast<ReadExpr>(ep->getKid(0)); + if (!base) + return false; + + // Get stride expr in proper index width. + Expr::Width idxWidth = base->index.getWidth(); + ref<Expr> strideExpr(stride, idxWidth), offset(0, idxWidth); + for (unsigned i=1; i<ep->getNumKids(); ++i) { + const ReadExpr *re = dyn_ref_cast<ReadExpr>(ep->getKid(i)); + if (!re) + return false; + + // Check if the index follows the stride. + // FIXME: How aggressive should this be simplified. The + // canonicalizing builder is probably the right choice, but this + // is yet another area where we would really prefer it to be + // global or else use static methods. + offset = AddExpr::create(offset, strideExpr); + if (SubExpr::create(re->index, base->index) != offset) + return false; + } + + return true; + } + + /// hasAllByteReads - True iff all children are byte level reads. + bool hasAllByteReads(const Expr *ep) { + for (unsigned i=0; i<ep->getNumKids(); ++i) { + const ReadExpr *re = dyn_ref_cast<ReadExpr>(ep->getKid(i)); + if (!re || re->getWidth() != Expr::Int8) + return false; + } + return true; + } + + void printRead(const ReadExpr *re, PrintContext &PC, unsigned indent) { + print(re->index, PC); + printSeparator(PC, isVerySimple(re->index), indent); + printUpdateList(re->updates, PC); + } + + void printExtract(const ExtractExpr *ee, PrintContext &PC, unsigned indent) { + PC << ee->offset << ' '; + print(ee->expr, PC); + } + + void printExpr(const Expr *ep, PrintContext &PC, unsigned indent, bool printConstWidth=false) { + bool simple = hasSimpleKids(ep); + + print(ep->getKid(0), PC); + for (unsigned i=1; i<ep->getNumKids(); i++) { + printSeparator(PC, simple, indent); + print(ep->getKid(i), PC, printConstWidth); + } + } + +public: + PPrinter(std::ostream &_os) : os(_os), newline("\n") { + reset(); + } + + void setNewline(const std::string &_newline) { + newline = _newline; + } + + void reset() { + counter = 0; + updateCounter = 0; + hasScan = false; + bindings.clear(); + updateBindings.clear(); + couldPrint.clear(); + shouldPrint.clear(); + couldPrintUpdates.clear(); + shouldPrintUpdates.clear(); + } + + void scan(const ref<Expr> &e) { + hasScan = true; + scan1(e); + } + + void print(const ref<Expr> &e, unsigned level=0) { + PrintContext PC(os); + PC.pos = level; + print(e, PC); + } + + void printConst(const ref<Expr> &e, PrintContext &PC, bool printWidth) { + assert(e.isConstant()); + + if (e.getWidth() == Expr::Bool) + PC << (e.getConstantValue() ? "true" : "false"); + else { + if (PCAllConstWidths) + printWidth = true; + + if (printWidth) + PC << "(w" << e.getWidth() << " "; + + PC << e.getConstantValue(); + + if (printWidth) + PC << ")"; + } + } + + void print(const ref<Expr> &e, PrintContext &PC, bool printConstWidth=false) { + if (e.isConstant()) + printConst(e, PC, printConstWidth); + else { + std::map<ref<Expr>, unsigned>::iterator it = bindings.find(e); + if (it!=bindings.end()) { + PC << 'N' << it->second; + } else { + if (!hasScan || shouldPrint.count(e)) { + PC << 'N' << counter << ':'; + bindings.insert(std::make_pair(e, counter++)); + } + + // Detect Not. + // FIXME: This should be in common code. + if (const EqExpr *ee = dyn_ref_cast<EqExpr>(e)) { + if (ee->left == ref<Expr>(false, Expr::Bool)) { + PC << "(Not"; + printWidth(PC, e); + PC << ' '; + // FIXME: This is a boom if right is a constant. + print(ee->right, PC); + PC << ')'; + return; + } + } + + // Detect multibyte reads. + // FIXME: Hrm. One problem with doing this is that we are + // masking the sharing of the indices which aren't + // visible. Need to think if this matters... probably not + // because if they are offset reads then its either constant, + // or they are (base + offset) and base will get printed with + // a declaration. + if (PCMultibyteReads && e.getKind() == Expr::Concat) { + const Expr *ep = e.get(); + if (hasAllByteReads(ep)) { + bool isMSB = hasOrderedReads(ep, 1); + if (isMSB || hasOrderedReads(ep, -1)) { + PC << "(Read" << (isMSB ? "MSB" : "LSB"); + printWidth(PC, e); + PC << ' '; + unsigned firstIdx = isMSB ? 0 : ep->getNumKids()-1; + printRead(static_ref_cast<ReadExpr>(ep->getKid(firstIdx)), + PC, PC.pos); + PC << ')'; + return; + } + } + } + + PC << '(' << e.getKind(); + printWidth(PC, e); + PC << ' '; + + // Indent at first argument and dispatch to appropriate print + // routine for exprs which require special handling. + unsigned indent = PC.pos; + if (const ReadExpr *re = dyn_ref_cast<ReadExpr>(e)) { + printRead(re, PC, indent); + } else if (const ExtractExpr *ee = dyn_ref_cast<ExtractExpr>(e)) { + printExtract(ee, PC, indent); + } else if (e.getKind() == Expr::Concat || e.getKind() == Expr::SExt) + printExpr(e.get(), PC, indent, true); + else + printExpr(e.get(), PC, indent); + PC << ")"; + } + } + } + + /* Public utility functions */ + + void printSeparator(PrintContext &PC, bool simple, unsigned indent) { + if (simple) { + PC << ' '; + } else { + PC.breakLine(indent); + } + } +}; + +ExprPPrinter *klee::ExprPPrinter::create(std::ostream &os) { + return new PPrinter(os); +} + +void ExprPPrinter::printOne(std::ostream &os, + const char *message, + const ref<Expr> &e) { + PPrinter p(os); + p.scan(e); + + // FIXME: Need to figure out what to do here. Probably print as a + // "forward declaration" with whatever syntax we pick for that. + PrintContext PC(os); + PC << message << ": "; + p.print(e, PC); + PC.breakLine(); +} + +void ExprPPrinter::printConstraints(std::ostream &os, + const ConstraintManager &constraints) { + printQuery(os, constraints, ref<Expr>(false, Expr::Bool)); +} + +void ExprPPrinter::printQuery(std::ostream &os, + const ConstraintManager &constraints, + const ref<Expr> &q) { + PPrinter p(os); + + for (ConstraintManager::const_iterator it = constraints.begin(), + ie = constraints.end(); it != ie; ++it) + p.scan(*it); + p.scan(q); + + PrintContext PC(os); + PC << "(query ["; + + // Ident at constraint list; + unsigned indent = PC.pos; + for (ConstraintManager::const_iterator it = constraints.begin(), + ie = constraints.end(); it != ie;) { + p.print(*it, PC); + ++it; + if (it != ie) + PC.breakLine(indent); + } + PC << ']'; + + p.printSeparator(PC, constraints.empty(), indent-1); + p.print(q, PC); + + PC << ')'; + PC.breakLine(); +} diff --git a/lib/Expr/ExprUtil.cpp b/lib/Expr/ExprUtil.cpp new file mode 100644 index 00000000..f74b519f --- /dev/null +++ b/lib/Expr/ExprUtil.cpp @@ -0,0 +1,127 @@ +//===-- ExprUtil.cpp ------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/util/ExprUtil.h" +#include "klee/util/ExprHashMap.h" + +#include "klee/Expr.h" + +#include "klee/util/ExprVisitor.h" + +#include <set> + +using namespace klee; + +void klee::findReads(ref<Expr> e, + bool visitUpdates, + std::vector< ref<ReadExpr> > &results) { + // Invariant: \forall_{i \in stack} !i.isConstant() && i \in visited + std::vector< ref<Expr> > stack; + ExprHashSet visited; + std::set<const UpdateNode *> updates; + + if (!e.isConstant()) { + visited.insert(e); + stack.push_back(e); + } + + while (!stack.empty()) { + ref<Expr> top = stack.back(); + stack.pop_back(); + + if (ReadExpr *re = dyn_ref_cast<ReadExpr>(top)) { + // We memoized so can just add to list without worrying about + // repeats. + results.push_back(re); + + if (!re->index.isConstant() && + visited.insert(re->index).second) + stack.push_back(re->index); + + if (visitUpdates) { + // XXX this is probably suboptimal. We want to avoid a potential + // explosion traversing update lists which can be quite + // long. However, it seems silly to hash all of the update nodes + // especially since we memoize all the expr results anyway. So + // we take a simple approach of memoizing the results for the + // head, which often will be shared among multiple nodes. + if (updates.insert(re->updates.head).second) { + for (const UpdateNode *un=re->updates.head; un; un=un->next) { + if (!un->index.isConstant() && + visited.insert(un->index).second) + stack.push_back(un->index); + if (!un->value.isConstant() && + visited.insert(un->value).second) + stack.push_back(un->value); + } + } + } + } else if (!top.isConstant()) { + Expr *e = top.get(); + for (unsigned i=0; i<e->getNumKids(); i++) { + ref<Expr> k = e->getKid(i); + if (!k.isConstant() && + visited.insert(k).second) + stack.push_back(k); + } + } + } +} + +/// + +namespace klee { + +class SymbolicObjectFinder : public ExprVisitor { +protected: + Action visitRead(const ReadExpr &re) { + const UpdateList &ul = re.updates; + + // XXX should we memo better than what ExprVisitor is doing for us? + for (const UpdateNode *un=ul.head; un; un=un->next) { + visit(un->index); + visit(un->value); + } + + if (ul.isRooted) + if (results.insert(ul.root).second) + objects.push_back(ul.root); + + return Action::doChildren(); + } + +public: + std::set<const Array*> results; + std::vector<const Array*> &objects; + + SymbolicObjectFinder(std::vector<const Array*> &_objects) + : objects(_objects) {} +}; + +} + +template<typename InputIterator> +void klee::findSymbolicObjects(InputIterator begin, + InputIterator end, + std::vector<const Array*> &results) { + SymbolicObjectFinder of(results); + for (; begin!=end; ++begin) + of.visit(*begin); +} + +void klee::findSymbolicObjects(ref<Expr> e, + std::vector<const Array*> &results) { + findSymbolicObjects(&e, &e+1, results); +} + +typedef std::vector< ref<Expr> >::iterator A; +template void klee::findSymbolicObjects<A>(A, A, std::vector<const Array*> &); + +typedef std::set< ref<Expr> >::iterator B; +template void klee::findSymbolicObjects<B>(B, B, std::vector<const Array*> &); diff --git a/lib/Expr/ExprVisitor.cpp b/lib/Expr/ExprVisitor.cpp new file mode 100644 index 00000000..b15cdffa --- /dev/null +++ b/lib/Expr/ExprVisitor.cpp @@ -0,0 +1,253 @@ +//===-- ExprVisitor.cpp ---------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Expr.h" +#include "klee/util/ExprVisitor.h" + +#include "llvm/Support/CommandLine.h" + +namespace { + llvm::cl::opt<bool> + UseVisitorHash("use-visitor-hash", + llvm::cl::desc("Use hash-consing during expr visitation."), + llvm::cl::init(true)); +} + +using namespace klee; + +ref<Expr> ExprVisitor::visit(const ref<Expr> &e) { + if (!UseVisitorHash || e.isConstant()) { + return visitActual(e); + } else { + visited_ty::iterator it = visited.find(e); + + if (it!=visited.end()) { + return it->second; + } else { + ref<Expr> res = visitActual(e); + visited.insert(std::make_pair(e, res)); + return res; + } + } +} + +ref<Expr> ExprVisitor::visitActual(const ref<Expr> &e) { + if (e.isConstant()) { + return e; + } else { + Expr &ep = *e.get(); + + Action res = visitExpr(ep); + switch(res.kind) { + case Action::DoChildren: + // continue with normal action + break; + case Action::SkipChildren: + return e; + case Action::ChangeTo: + return res.argument; + } + + switch(ep.getKind()) { + case Expr::NotOptimized: res = visitNotOptimized(static_cast<NotOptimizedExpr&>(ep)); break; + case Expr::Read: res = visitRead(static_cast<ReadExpr&>(ep)); break; + case Expr::Select: res = visitSelect(static_cast<SelectExpr&>(ep)); break; + case Expr::Concat: res = visitConcat(static_cast<ConcatExpr&>(ep)); break; + case Expr::Extract: res = visitExtract(static_cast<ExtractExpr&>(ep)); break; + case Expr::ZExt: res = visitZExt(static_cast<ZExtExpr&>(ep)); break; + case Expr::SExt: res = visitSExt(static_cast<SExtExpr&>(ep)); break; + case Expr::Add: res = visitAdd(static_cast<AddExpr&>(ep)); break; + case Expr::Sub: res = visitSub(static_cast<SubExpr&>(ep)); break; + case Expr::Mul: res = visitMul(static_cast<MulExpr&>(ep)); break; + case Expr::UDiv: res = visitUDiv(static_cast<UDivExpr&>(ep)); break; + case Expr::SDiv: res = visitSDiv(static_cast<SDivExpr&>(ep)); break; + case Expr::URem: res = visitURem(static_cast<URemExpr&>(ep)); break; + case Expr::SRem: res = visitSRem(static_cast<SRemExpr&>(ep)); break; + case Expr::And: res = visitAnd(static_cast<AndExpr&>(ep)); break; + case Expr::Or: res = visitOr(static_cast<OrExpr&>(ep)); break; + case Expr::Xor: res = visitXor(static_cast<XorExpr&>(ep)); break; + case Expr::Shl: res = visitShl(static_cast<ShlExpr&>(ep)); break; + case Expr::LShr: res = visitLShr(static_cast<LShrExpr&>(ep)); break; + case Expr::AShr: res = visitAShr(static_cast<AShrExpr&>(ep)); break; + case Expr::Eq: res = visitEq(static_cast<EqExpr&>(ep)); break; + case Expr::Ne: res = visitNe(static_cast<NeExpr&>(ep)); break; + case Expr::Ult: res = visitUlt(static_cast<UltExpr&>(ep)); break; + case Expr::Ule: res = visitUle(static_cast<UleExpr&>(ep)); break; + case Expr::Ugt: res = visitUgt(static_cast<UgtExpr&>(ep)); break; + case Expr::Uge: res = visitUge(static_cast<UgeExpr&>(ep)); break; + case Expr::Slt: res = visitSlt(static_cast<SltExpr&>(ep)); break; + case Expr::Sle: res = visitSle(static_cast<SleExpr&>(ep)); break; + case Expr::Sgt: res = visitSgt(static_cast<SgtExpr&>(ep)); break; + case Expr::Sge: res = visitSge(static_cast<SgeExpr&>(ep)); break; + case Expr::Constant: + default: + assert(0 && "invalid expression kind"); + } + + switch(res.kind) { + case Action::DoChildren: { + bool rebuild = false; + ref<Expr> e(&ep), kids[8]; + unsigned count = ep.getNumKids(); + for (unsigned i=0; i<count; i++) { + ref<Expr> kid = ep.getKid(i); + kids[i] = visit(kid); + if (kids[i] != kid) + rebuild = true; + } + if (rebuild) { + e = ep.rebuild(kids); + if (recursive) + e = visit(e); + } + if (!e.isConstant()) { + res = visitExprPost(*e.get()); + if (res.kind==Action::ChangeTo) + e = res.argument; + } + return e; + } + case Action::SkipChildren: + return e; + case Action::ChangeTo: + return res.argument; + default: + assert(0 && "invalid kind"); + } + } +} + +ExprVisitor::Action ExprVisitor::visitExpr(const Expr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitExprPost(const Expr&) { + return Action::skipChildren(); +} + +ExprVisitor::Action ExprVisitor::visitNotOptimized(const NotOptimizedExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitRead(const ReadExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitSelect(const SelectExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitConcat(const ConcatExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitExtract(const ExtractExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitZExt(const ZExtExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitSExt(const SExtExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitAdd(const AddExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitSub(const SubExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitMul(const MulExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitUDiv(const UDivExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitSDiv(const SDivExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitURem(const URemExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitSRem(const SRemExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitAnd(const AndExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitOr(const OrExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitXor(const XorExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitShl(const ShlExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitLShr(const LShrExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitAShr(const AShrExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitEq(const EqExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitNe(const NeExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitUlt(const UltExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitUle(const UleExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitUgt(const UgtExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitUge(const UgeExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitSlt(const SltExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitSle(const SleExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitSgt(const SgtExpr&) { + return Action::doChildren(); +} + +ExprVisitor::Action ExprVisitor::visitSge(const SgeExpr&) { + return Action::doChildren(); +} + diff --git a/lib/Expr/Lexer.cpp b/lib/Expr/Lexer.cpp new file mode 100644 index 00000000..77e25f62 --- /dev/null +++ b/lib/Expr/Lexer.cpp @@ -0,0 +1,261 @@ +//===-- Lexer.cpp ---------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "expr/Lexer.h" + +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Streams.h" + +#include <iomanip> +#include <iostream> +#include <string.h> + +using namespace llvm; +using namespace klee; +using namespace klee::expr; + +/// + +const char *Token::getKindName() const { + switch (kind) { + default: + case Unknown: return "Unknown"; + case Arrow: return "Arrow"; + case At: return "At"; + case Colon: return "Colon"; + case Comma: return "Comma"; + case Comment: return "Comment"; + case EndOfFile: return "EndOfFile"; + case Equals: return "Equals"; + case Identifier: return "Identifier"; + case KWFalse: return "KWFalse"; + case KWQuery: return "KWQuery"; + case KWReserved: return "KWReserved"; + case KWTrue: return "KWTrue"; + case KWWidth: return "KWWidth"; + case LBrace: return "LBrace"; + case LParen: return "LParen"; + case LSquare: return "LSquare"; + case Number: return "Number"; + case RBrace: return "RBrace"; + case RParen: return "RParen"; + case RSquare: return "RSquare"; + case Semicolon: return "Semicolon"; + } +} + +void Token::dump() { + llvm::cerr << "(Token \"" << getKindName() << "\" " + << (void*) start << " " << length << " " + << line << " " << column << ")"; +} + +/// + +static inline bool isInternalIdentifierChar(int Char) { + return isalnum(Char) || Char == '_' || Char == '.'; +} + +Lexer::Lexer(const llvm::MemoryBuffer *MB) + : BufferPos(MB->getBufferStart()), BufferEnd(MB->getBufferEnd()), + LineNumber(1), ColumnNumber(0) { +} + +Lexer::~Lexer() { +} + +int Lexer::PeekNextChar() { + if (BufferPos == BufferEnd) + return -1; + return *BufferPos; +} + +int Lexer::GetNextChar() { + if (BufferPos == BufferEnd) + return -1; + + // Handle DOS/Mac newlines here, by stripping duplicates and by + // returning '\n' for both. + char Result = *BufferPos++; + if (Result == '\n' || Result == '\r') { + if (BufferPos != BufferEnd && *BufferPos == ('\n' + '\r' - Result)) + ++BufferPos; + Result = '\n'; + } + + if (Result == '\n') { + ++LineNumber; + ColumnNumber = 0; + } else { + ++ColumnNumber; + } + + return Result; +} + +Token &Lexer::SetTokenKind(Token &Result, Token::Kind k) { + Result.kind = k; + Result.length = BufferPos - Result.start; + return Result; +} + +static bool isReservedKW(const char *Str, unsigned N) { + unsigned i; + + // Check for i[0-9]+ + if (N>1 && Str[0] == 'i') { + for (i=1; i<N; ++i) + if (!isdigit(Str[i])) + break; + if (i==N) + return true; + } + + // Check for fp[0-9]+([.].*)?$ + if (N>3 && Str[0]=='f' && Str[1]=='p' && isdigit(Str[2])) { + for (i=3; i<N; ++i) + if (!isdigit(Str[i])) + break; + if (i==N || Str[i]=='.') + return true; + } + + return false; +} +static bool isWidthKW(const char *Str, unsigned N) { + if (N<2 || Str[0] != 'w') + return false; + for (unsigned i=1; i<N; ++i) + if (!isdigit(Str[i])) + return false; + return true; +} +Token &Lexer::SetIdentifierTokenKind(Token &Result) { + unsigned Length = BufferPos - Result.start; + switch (Length) { + case 3: + if (memcmp("def", Result.start, 3) == 0) + return SetTokenKind(Result, Token::KWReserved); + if (memcmp("var", Result.start, 3) == 0) + return SetTokenKind(Result, Token::KWReserved); + break; + + case 4: + if (memcmp("true", Result.start, 4) == 0) + return SetTokenKind(Result, Token::KWTrue); + break; + + case 5: + if (memcmp("array", Result.start, 5) == 0) + return SetTokenKind(Result, Token::KWReserved); + if (memcmp("false", Result.start, 5) == 0) + return SetTokenKind(Result, Token::KWFalse); + if (memcmp("query", Result.start, 5) == 0) + return SetTokenKind(Result, Token::KWQuery); + break; + + case 6: + if (memcmp("define", Result.start, 6) == 0) + return SetTokenKind(Result, Token::KWReserved); + break; + + case 7: + if (memcmp("declare", Result.start, 7) == 0) + return SetTokenKind(Result, Token::KWReserved); + break; + } + + if (isReservedKW(Result.start, Length)) + return SetTokenKind(Result, Token::KWReserved); + if (isWidthKW(Result.start, Length)) + return SetTokenKind(Result, Token::KWWidth); + + return SetTokenKind(Result, Token::Identifier); +} + +void Lexer::SkipToEndOfLine() { + for (;;) { + int Char = GetNextChar(); + if (Char == -1 || Char =='\n') + break; + } +} + +Token &Lexer::LexNumber(Token &Result) { + while (isalnum(PeekNextChar()) || PeekNextChar()=='_') + GetNextChar(); + return SetTokenKind(Result, Token::Number); +} + +Token &Lexer::LexIdentifier(Token &Result) { + while (isInternalIdentifierChar(PeekNextChar())) + GetNextChar(); + + // Recognize keywords specially. + return SetIdentifierTokenKind(Result); +} + +Token &Lexer::Lex(Token &Result) { + Result.kind = Token::Unknown; + Result.length = 0; + Result.start = BufferPos; + + // Skip whitespace. + while (isspace(PeekNextChar())) + GetNextChar(); + + Result.start = BufferPos; + Result.line = LineNumber; + Result.column = ColumnNumber; + int Char = GetNextChar(); + switch (Char) { + case -1: return SetTokenKind(Result, Token::EndOfFile); + + case '(': return SetTokenKind(Result, Token::LParen); + case ')': return SetTokenKind(Result, Token::RParen); + case ',': return SetTokenKind(Result, Token::Comma); + case ':': return SetTokenKind(Result, Token::Colon); + case ';': return SetTokenKind(Result, Token::Semicolon); + case '=': return SetTokenKind(Result, Token::Equals); + case '@': return SetTokenKind(Result, Token::At); + case '[': return SetTokenKind(Result, Token::LSquare); + case ']': return SetTokenKind(Result, Token::RSquare); + case '{': return SetTokenKind(Result, Token::LBrace); + case '}': return SetTokenKind(Result, Token::RBrace); + + case '#': + SkipToEndOfLine(); + return SetTokenKind(Result, Token::Comment); + + case '+': { + if (isdigit(PeekNextChar())) + return LexNumber(Result); + else + return SetTokenKind(Result, Token::Unknown); + } + + case '-': { + int Next = PeekNextChar(); + if (Next == '>') + return GetNextChar(), SetTokenKind(Result, Token::Arrow); + else if (isdigit(Next)) + return LexNumber(Result); + else + return SetTokenKind(Result, Token::Unknown); + break; + } + + default: + if (isdigit(Char)) + return LexNumber(Result); + else if (isalpha(Char) || Char == '_') + return LexIdentifier(Result); + return SetTokenKind(Result, Token::Unknown); + } +} diff --git a/lib/Expr/Makefile b/lib/Expr/Makefile new file mode 100644 index 00000000..b80569b3 --- /dev/null +++ b/lib/Expr/Makefile @@ -0,0 +1,16 @@ +#===-- lib/Expr/Makefile -----------------------------------*- Makefile -*--===# +# +# The KLEE Symbolic Virtual Machine +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +LEVEL=../.. + +LIBRARYNAME=kleaverExpr +DONT_BUILD_RELINKED=1 +BUILD_ARCHIVE=1 + +include $(LEVEL)/Makefile.common diff --git a/lib/Expr/Parser.cpp b/lib/Expr/Parser.cpp new file mode 100644 index 00000000..f5708384 --- /dev/null +++ b/lib/Expr/Parser.cpp @@ -0,0 +1,1310 @@ +//===-- Parser.cpp --------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "expr/Parser.h" + +#include "expr/Lexer.h" + +#include "klee/Constraints.h" +#include "klee/Solver.h" +#include "klee/util/ExprPPrinter.h" + +#include "llvm/ADT/APInt.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Streams.h" + +#include <cassert> +#include <iostream> +#include <map> + +using namespace llvm; +using namespace klee; +using namespace klee::expr; + +namespace { + /// ParseResult - Represent a possibly invalid parse result. + template<typename T> + struct ParseResult { + bool IsValid; + T Value; + + public: + ParseResult() : IsValid(false) {} + ParseResult(T _Value) : IsValid(true), Value(_Value) {} + ParseResult(bool _IsValid, T _Value) : IsValid(_IsValid), Value(_Value) {} + + bool isValid() { + return IsValid; + } + T get() { + assert(IsValid && "get() on invalid ParseResult!"); + return Value; + } + }; + + typedef ParseResult<Decl*> DeclResult; + typedef ParseResult<ExprHandle> ExprResult; + typedef ParseResult<Expr::Width> TypeResult; + typedef ParseResult<VersionHandle> VersionResult; + + /// NumberOrExprResult - Represent a number or expression. This is used to + /// wrap an expression production which may be a number, but for + /// which the type width is unknown. + class NumberOrExprResult { + Token AsNumber; + ExprResult AsExpr; + bool IsNumber; + + public: + NumberOrExprResult() : IsNumber(false) {} + explicit NumberOrExprResult(Token _AsNumber) : AsNumber(_AsNumber), + IsNumber(true) {} + explicit NumberOrExprResult(ExprResult _AsExpr) : AsExpr(_AsExpr), + IsNumber(false) {} + + bool isNumber() const { return IsNumber; } + const Token &getNumber() const { + assert(IsNumber && "Invalid accessor call."); + return AsNumber; + } + const ExprResult &getExpr() const { + assert(!IsNumber && "Invalid accessor call."); + return AsExpr; + } + }; + + /// ParserImpl - Parser implementation. + class ParserImpl : public Parser { + typedef std::map<const std::string, const Identifier*> IdentifierTabTy; + typedef std::map<const Identifier*, ExprHandle> ExprSymTabTy; + typedef std::map<const Identifier*, VersionHandle> VersionSymTabTy; + + const std::string Filename; + const MemoryBuffer *TheMemoryBuffer; + Lexer TheLexer; + unsigned MaxErrors; + unsigned NumErrors; + + // FIXME: Use LLVM symbol tables? + IdentifierTabTy IdentifierTab; + + std::map<const Identifier*, const ArrayDecl*> ArraySymTab; + ExprSymTabTy ExprSymTab; + VersionSymTabTy VersionSymTab; + + /// Tok - The currently lexed token. + Token Tok; + + /// ParenLevel - The current depth of matched '(' tokens. + unsigned ParenLevel; + /// SquareLevel - The current depth of matched '[' tokens. + unsigned SquareLevel; + + /* Core parsing functionality */ + + const Identifier *GetOrCreateIdentifier(const Token &Tok); + + void GetNextNonCommentToken() { + do { + TheLexer.Lex(Tok); + } while (Tok.kind == Token::Comment); + } + + /// ConsumeToken - Consume the current 'peek token' and lex the next one. + void ConsumeToken() { + assert(Tok.kind != Token::LParen && Tok.kind != Token::RParen); + GetNextNonCommentToken(); + } + + /// ConsumeExpectedToken - Check that the current token is of the + /// expected kind and consume it. + void ConsumeExpectedToken(Token::Kind k) { + assert(Tok.kind == k && "Unexpected token!"); + GetNextNonCommentToken(); + } + + void ConsumeLParen() { + ++ParenLevel; + ConsumeExpectedToken(Token::LParen); + } + + void ConsumeRParen() { + if (ParenLevel) // Cannot go below zero. + --ParenLevel; + ConsumeExpectedToken(Token::RParen); + } + + void ConsumeLSquare() { + ++SquareLevel; + ConsumeExpectedToken(Token::LSquare); + } + + void ConsumeRSquare() { + if (SquareLevel) // Cannot go below zero. + --SquareLevel; + ConsumeExpectedToken(Token::RSquare); + } + + void ConsumeAnyToken() { + switch (Tok.kind) { + case Token::LParen: return ConsumeLParen(); + case Token::RParen: return ConsumeRParen(); + case Token::LSquare: return ConsumeLSquare(); + case Token::RSquare: return ConsumeRSquare(); + default: + return ConsumeToken(); + } + } + + /* Utility functions */ + + /// SkipUntilRParen - Scan forward to the next token following an + /// rparen at the given level, or EOF, whichever is first. + void SkipUntilRParen(unsigned Level) { + // FIXME: I keep wavering on whether it is an error to call this + // with the current token an rparen. In most cases this should + // have been handled differently (error reported, + // whatever). Audit & resolve. + assert(Level <= ParenLevel && + "Refusing to skip until rparen at higher level."); + while (Tok.kind != Token::EndOfFile) { + if (Tok.kind == Token::RParen && ParenLevel == Level) { + ConsumeRParen(); + break; + } + ConsumeAnyToken(); + } + } + + /// SkipUntilRParen - Scan forward until reaching an rparen token + /// at the current level (or EOF). + void SkipUntilRParen() { + SkipUntilRParen(ParenLevel); + } + + /// ExpectRParen - Utility method to close an sexp. This expects to + /// eat an rparen, and emits a diagnostic and skips to the next one + /// (or EOF) if it cannot. + void ExpectRParen(const char *Msg) { + if (Tok.kind == Token::EndOfFile) { + // FIXME: Combine with Msg + Error("expected ')' but found end-of-file.", Tok); + } else if (Tok.kind != Token::RParen) { + Error(Msg, Tok); + SkipUntilRParen(); + } else { + ConsumeRParen(); + } + } + + /// SkipUntilRSquare - Scan forward to the next token following an + /// rsquare at the given level, or EOF, whichever is first. + void SkipUntilRSquare(unsigned Level) { + // FIXME: I keep wavering on whether it is an error to call this + // with the current token an rparen. In most cases this should + // have been handled differently (error reported, + // whatever). Audit & resolve. + assert(Level <= ParenLevel && + "Refusing to skip until rparen at higher level."); + while (Tok.kind != Token::EndOfFile) { + if (Tok.kind == Token::RSquare && ParenLevel == Level) { + ConsumeRSquare(); + break; + } + ConsumeAnyToken(); + } + } + + /// SkipUntilRSquare - Scan forward until reaching an rsquare token + /// at the current level (or EOF). + void SkipUntilRSquare() { + SkipUntilRSquare(ParenLevel); + } + + /// ExpectRSquare - Utility method to close an array. This expects + /// to eat an rparen, and emits a diagnostic and skips to the next + /// one (or EOF) if it cannot. + void ExpectRSquare(const char *Msg) { + if (Tok.kind == Token::EndOfFile) { + // FIXME: Combine with Msg + Error("expected ']' but found end-of-file.", Tok); + } else if (Tok.kind != Token::RSquare) { + Error(Msg, Tok); + SkipUntilRSquare(); + } else { + ConsumeRSquare(); + } + } + + /*** Grammar productions ****/ + + /* Top level decls */ + + DeclResult ParseArrayDecl(); + DeclResult ParseExprVarDecl(); + DeclResult ParseVersionVarDecl(); + DeclResult ParseCommandDecl(); + + /* Commands */ + + DeclResult ParseQueryCommand(); + + /* Etc. */ + + NumberOrExprResult ParseNumberOrExpr(); + + ExprResult ParseExpr(TypeResult ExpectedType); + ExprResult ParseParenExpr(TypeResult ExpectedType); + ExprResult ParseUnaryParenExpr(const Token &Name, + unsigned Kind, bool IsFixed, + Expr::Width ResTy); + ExprResult ParseBinaryParenExpr(const Token &Name, + unsigned Kind, bool IsFixed, + Expr::Width ResTy); + ExprResult ParseSelectParenExpr(const Token &Name, Expr::Width ResTy); + ExprResult ParseConcatParenExpr(const Token &Name, Expr::Width ResTy); + ExprResult ParseExtractParenExpr(const Token &Name, Expr::Width ResTy); + ExprResult ParseAnyReadParenExpr(const Token &Name, + unsigned Kind, + Expr::Width ResTy); + void ParseMatchedBinaryArgs(const Token &Name, + TypeResult ExpectType, + ExprResult &LHS, ExprResult &RHS); + ExprResult ParseNumber(Expr::Width Width); + ExprResult ParseNumberToken(Expr::Width Width, const Token &Tok); + + VersionResult ParseVersionSpecifier(); + VersionResult ParseVersion(); + + TypeResult ParseTypeSpecifier(); + + /*** Diagnostics ***/ + + void Error(const char *Message, const Token &At); + void Error(const char *Message) { Error(Message, Tok); } + + public: + ParserImpl(const std::string _Filename, + const MemoryBuffer *MB) : Filename(_Filename), + TheMemoryBuffer(MB), + TheLexer(MB), + MaxErrors(~0u), + NumErrors(0) {} + + /// Initialize - Initialize the parsing state. This must be called + /// prior to the start of parsing. + void Initialize() { + ParenLevel = SquareLevel = 0; + + ConsumeAnyToken(); + } + + /* Parser interface implementation */ + + virtual Decl *ParseTopLevelDecl(); + + virtual void SetMaxErrors(unsigned N) { + MaxErrors = N; + } + + virtual unsigned GetNumErrors() const { + return NumErrors; + } + }; +} + +const Identifier *ParserImpl::GetOrCreateIdentifier(const Token &Tok) { + // FIXME: Make not horribly inefficient please. + assert(Tok.kind == Token::Identifier && "Expected only identifier tokens."); + std::string Name(Tok.start, Tok.length); + IdentifierTabTy::iterator it = IdentifierTab.find(Name); + if (it != IdentifierTab.end()) + return it->second; + + Identifier *I = new Identifier(Name); + IdentifierTab.insert(std::make_pair(Name, I)); + + return I; +} + +Decl *ParserImpl::ParseTopLevelDecl() { + // Repeat until success or EOF. + while (Tok.kind != Token::EndOfFile) { + // Only handle commands for now. + if (Tok.kind == Token::LParen) { + DeclResult Res = ParseCommandDecl(); + if (Res.isValid()) + return Res.get(); + } else { + Error("expected '(' token."); + ConsumeAnyToken(); + } + } + + return 0; +} + +/// ParseCommandDecl - Parse a command declaration. The lexer should +/// be positioned at the opening '('. +/// +/// command = '(' name ... ')' +DeclResult ParserImpl::ParseCommandDecl() { + ConsumeLParen(); + + if (!Tok.isKeyword()) { + Error("malformed command."); + SkipUntilRParen(); + return DeclResult(); + } + + switch (Tok.kind) { + case Token::KWQuery: + return ParseQueryCommand(); + + default: + Error("malformed command (unexpected keyword)."); + SkipUntilRParen(); + return DeclResult(); + } +} + +/// ParseQueryCommand - Parse query command. The lexer should be +/// positioned at the 'query' keyword. +/// +/// 'query' expressions-list expression [expression-list [array-list]] +DeclResult ParserImpl::ParseQueryCommand() { + // FIXME: We need a command for this. Or something. + ExprSymTab.clear(); + VersionSymTab.clear(); + + std::vector<ExprHandle> Constraints; + ConsumeExpectedToken(Token::KWQuery); + if (Tok.kind != Token::LSquare) { + Error("malformed query, expected constraint list."); + SkipUntilRParen(); + return DeclResult(); + } + + ConsumeExpectedToken(Token::LSquare); + // FIXME: Should avoid reading past unbalanced parens here. + while (Tok.kind != Token::RSquare) { + if (Tok.kind == Token::EndOfFile) { + Error("unexpected end of file."); + return new QueryCommand(Constraints.begin(), Constraints.end(), + ref<Expr>(false, Expr::Bool)); + } + + ExprResult Res = ParseExpr(TypeResult(Expr::Bool)); + if (Res.isValid()) + Constraints.push_back(Res.get()); + } + + ConsumeRSquare(); + + ExprResult Res = ParseExpr(TypeResult()); + if (!Res.isValid()) // Error emitted by ParseExpr. + Res = ExprResult(ref<Expr>(0, Expr::Bool)); + + ExpectRParen("unexpected argument to 'query'."); + return new QueryCommand(Constraints.begin(), Constraints.end(), + Res.get()); +} + +/// ParseNumberOrExpr - Parse an expression whose type cannot be +/// predicted. +NumberOrExprResult ParserImpl::ParseNumberOrExpr() { + if (Tok.kind == Token::Number){ + Token Num = Tok; + ConsumeToken(); + return NumberOrExprResult(Num); + } else { + return NumberOrExprResult(ParseExpr(TypeResult())); + } +} + +/// ParseExpr - Parse an expression with the given \arg +/// ExpectedType. \arg ExpectedType can be invalid if the type cannot +/// be inferred from the context. +/// +/// expr = false | true +/// expr = <constant> +/// expr = <identifier> +/// expr = [<identifier>:] paren-expr +ExprResult ParserImpl::ParseExpr(TypeResult ExpectedType) { + // FIXME: Is it right to need to do this here? + if (Tok.kind == Token::EndOfFile) { + Error("unexpected end of file."); + return ExprResult(); + } + + if (Tok.kind == Token::KWFalse || Tok.kind == Token::KWTrue) { + bool Value = Tok.kind == Token::KWTrue; + ConsumeToken(); + return ExprResult(ref<Expr>(Value, Expr::Bool)); + } + + if (Tok.kind == Token::Number) { + if (!ExpectedType.isValid()) { + Error("cannot infer type of number."); + ConsumeToken(); + return ExprResult(); + } + + return ParseNumber(ExpectedType.get()); + } + + const Identifier *Label = 0; + if (Tok.kind == Token::Identifier) { + Token LTok = Tok; + Label = GetOrCreateIdentifier(Tok); + ConsumeToken(); + + if (Tok.kind != Token::Colon) { + ExprSymTabTy::iterator it = ExprSymTab.find(Label); + + if (it == ExprSymTab.end()) { + Error("invalid expression label reference.", LTok); + return ExprResult(); + } + + return it->second; + } + + ConsumeToken(); + if (ExprSymTab.count(Label)) { + Error("duplicate expression label definition.", LTok); + Label = 0; + } + } + + Token Start = Tok; + ExprResult Res = ParseParenExpr(ExpectedType); + if (!Res.isValid()) { + // If we know the type, define the identifier just so we don't get + // use-of-undef errors. + // FIXME: Maybe we should let the symbol table map to invalid + // entries? + if (Label && ExpectedType.isValid()) + ExprSymTab.insert(std::make_pair(Label, + ref<Expr>(0, ExpectedType.get()))); + return Res; + } else if (ExpectedType.isValid()) { + // Type check result. + if (Res.get().getWidth() != ExpectedType.get()) { + // FIXME: Need more info, and range + Error("expression has incorrect type.", Start); + return ExprResult(); + } + } + + if (Label) + ExprSymTab.insert(std::make_pair(Label, Res.get())); + return Res; +} + +// Additional kinds for macro forms. +enum MacroKind { + eMacroKind_Not = Expr::LastKind + 1, // false == x + eMacroKind_Neg, // 0 - x + eMacroKind_ReadLSB, // Multibyte read + eMacroKind_ReadMSB, // Multibyte write + eMacroKind_Concat, // Magic concatenation syntax + eMacroKind_LastMacroKind = eMacroKind_ReadMSB +}; + +/// LookupExprInfo - Return information on the named token, if it is +/// recognized. +/// +/// \param Kind [out] - The Expr::Kind or MacroKind of the identifier. +/// \param IsFixed [out] - True if the given kinds result and +/// (expression) arguments are all of the same width. +/// \param NumArgs [out] - The number of expression arguments for this +/// kind. -1 indicates the kind is variadic or has non-expression +/// arguments. +/// \return True if the token is a valid kind or macro name. +static bool LookupExprInfo(const Token &Tok, unsigned &Kind, + bool &IsFixed, int &NumArgs) { +#define SetOK(kind, isfixed, numargs) (Kind=kind, IsFixed=isfixed,\ + NumArgs=numargs, true) + assert(Tok.kind == Token::Identifier && "Unexpected token."); + + switch (Tok.length) { + case 2: + if (memcmp(Tok.start, "Eq", 2) == 0) + return SetOK(Expr::Eq, false, 2); + if (memcmp(Tok.start, "Ne", 2) == 0) + return SetOK(Expr::Ne, false, 2); + + if (memcmp(Tok.start, "Or", 2) == 0) + return SetOK(Expr::Or, true, 2); + break; + + case 3: + if (memcmp(Tok.start, "Add", 3) == 0) + return SetOK(Expr::Add, true, 2); + if (memcmp(Tok.start, "Sub", 3) == 0) + return SetOK(Expr::Sub, true, 2); + if (memcmp(Tok.start, "Mul", 3) == 0) + return SetOK(Expr::Mul, true, 2); + + if (memcmp(Tok.start, "And", 3) == 0) + return SetOK(Expr::And, true, 2); + if (memcmp(Tok.start, "Shl", 3) == 0) + return SetOK(Expr::Shl, true, 2); + if (memcmp(Tok.start, "Xor", 3) == 0) + return SetOK(Expr::Xor, true, 2); + + if (memcmp(Tok.start, "Not", 3) == 0) + return SetOK(eMacroKind_Not, true, 1); + if (memcmp(Tok.start, "Neg", 3) == 0) + return SetOK(eMacroKind_Neg, true, 1); + if (memcmp(Tok.start, "Ult", 3) == 0) + return SetOK(Expr::Ult, false, 2); + if (memcmp(Tok.start, "Ule", 3) == 0) + return SetOK(Expr::Ule, false, 2); + if (memcmp(Tok.start, "Ugt", 3) == 0) + return SetOK(Expr::Ugt, false, 2); + if (memcmp(Tok.start, "Uge", 3) == 0) + return SetOK(Expr::Uge, false, 2); + if (memcmp(Tok.start, "Slt", 3) == 0) + return SetOK(Expr::Slt, false, 2); + if (memcmp(Tok.start, "Sle", 3) == 0) + return SetOK(Expr::Sle, false, 2); + if (memcmp(Tok.start, "Sgt", 3) == 0) + return SetOK(Expr::Sgt, false, 2); + if (memcmp(Tok.start, "Sge", 3) == 0) + return SetOK(Expr::Sge, false, 2); + break; + + case 4: + if (memcmp(Tok.start, "Read", 4) == 0) + return SetOK(Expr::Read, true, -1); + if (memcmp(Tok.start, "AShr", 4) == 0) + return SetOK(Expr::AShr, true, 2); + if (memcmp(Tok.start, "LShr", 4) == 0) + return SetOK(Expr::LShr, true, 2); + + if (memcmp(Tok.start, "UDiv", 4) == 0) + return SetOK(Expr::UDiv, true, 2); + if (memcmp(Tok.start, "SDiv", 4) == 0) + return SetOK(Expr::SDiv, true, 2); + if (memcmp(Tok.start, "URem", 4) == 0) + return SetOK(Expr::URem, true, 2); + if (memcmp(Tok.start, "SRem", 4) == 0) + return SetOK(Expr::SRem, true, 2); + + if (memcmp(Tok.start, "SExt", 4) == 0) + return SetOK(Expr::SExt, false, 1); + if (memcmp(Tok.start, "ZExt", 4) == 0) + return SetOK(Expr::ZExt, false, 1); + break; + + case 6: + if (memcmp(Tok.start, "Concat", 6) == 0) + return SetOK(eMacroKind_Concat, false, -1); + if (memcmp(Tok.start, "Select", 6) == 0) + return SetOK(Expr::Select, false, 3); + break; + + case 7: + if (memcmp(Tok.start, "Extract", 7) == 0) + return SetOK(Expr::Extract, false, -1); + if (memcmp(Tok.start, "ReadLSB", 7) == 0) + return SetOK(eMacroKind_ReadLSB, true, -1); + if (memcmp(Tok.start, "ReadMSB", 7) == 0) + return SetOK(eMacroKind_ReadMSB, true, -1); + break; + } + + return false; +#undef SetOK +} + +/// ParseParenExpr - Parse a parenthesized expression with the given +/// \arg ExpectedType. \arg ExpectedType can be invalid if the type +/// cannot be inferred from the context. +/// +/// paren-expr = '(' type number ')' +/// paren-expr = '(' identifier [type] expr+ ') +/// paren-expr = '(' ('Read' | 'ReadMSB' | 'ReadLSB') type expr update-list ')' +ExprResult ParserImpl::ParseParenExpr(TypeResult FIXME_UNUSED) { + if (Tok.kind != Token::LParen) { + Error("unexpected token."); + ConsumeAnyToken(); + return ExprResult(); + } + + ConsumeLParen(); + + // Check for coercion case (w32 11). + if (Tok.kind == Token::KWWidth) { + TypeResult ExpectedType = ParseTypeSpecifier(); + + if (Tok.kind != Token::Number) { + Error("coercion can only apply to a number."); + SkipUntilRParen(); + return ExprResult(); + } + + // Make sure this was a type specifier we support. + ExprResult Res; + if (ExpectedType.isValid()) + Res = ParseNumber(ExpectedType.get()); + else + ConsumeToken(); + + ExpectRParen("unexpected argument in coercion."); + return Res; + } + + if (Tok.kind != Token::Identifier) { + Error("unexpected token, expected expression."); + SkipUntilRParen(); + return ExprResult(); + } + + Token Name = Tok; + ConsumeToken(); + + // FIXME: Use invalid type (i.e. width==0)? + Token TypeTok = Tok; + bool HasType = TypeTok.kind == Token::KWWidth; + TypeResult Type = HasType ? ParseTypeSpecifier() : Expr::Bool; + + // FIXME: For now just skip to rparen on error. It might be nice + // to try and actually parse the child nodes though for error + // messages & better recovery? + if (!Type.isValid()) { + SkipUntilRParen(); + return ExprResult(); + } + Expr::Width ResTy = Type.get(); + + unsigned ExprKind; + bool IsFixed; + int NumArgs; + if (!LookupExprInfo(Name, ExprKind, IsFixed, NumArgs)) { + // FIXME: For now just skip to rparen on error. It might be nice + // to try and actually parse the child nodes though for error + // messages & better recovery? + Error("unknown expression kind.", Name); + SkipUntilRParen(); + return ExprResult(); + } + + // See if we have to parse this form specially. + if (NumArgs == -1) { + switch (ExprKind) { + case eMacroKind_Concat: + return ParseConcatParenExpr(Name, ResTy); + + case Expr::Extract: + return ParseExtractParenExpr(Name, ResTy); + + case eMacroKind_ReadLSB: + case eMacroKind_ReadMSB: + case Expr::Read: + return ParseAnyReadParenExpr(Name, ExprKind, ResTy); + + default: + Error("internal error, unimplemented special form.", Name); + SkipUntilRParen(); + return ExprResult(ref<Expr>(0, ResTy)); + } + } + + switch (NumArgs) { + case 1: + return ParseUnaryParenExpr(Name, ExprKind, IsFixed, ResTy); + case 2: + return ParseBinaryParenExpr(Name, ExprKind, IsFixed, ResTy); + case 3: + if (ExprKind == Expr::Select) + return ParseSelectParenExpr(Name, ResTy); + default: + assert(0 && "Invalid argument kind (number of args)."); + return ExprResult(); + } +} + +ExprResult ParserImpl::ParseUnaryParenExpr(const Token &Name, + unsigned Kind, bool IsFixed, + Expr::Width ResTy) { + if (Tok.kind == Token::RParen) { + Error("unexpected end of arguments.", Name); + ConsumeRParen(); + return ref<Expr>(0, ResTy); + } + + ExprResult Arg = ParseExpr(IsFixed ? ResTy : TypeResult()); + if (!Arg.isValid()) + Arg = ref<Expr>(0, ResTy); + + ExpectRParen("unexpected argument in unary expression."); + ExprHandle E = Arg.get(); + switch (Kind) { + case eMacroKind_Not: + return EqExpr::alloc(ref<Expr>(0, E.getWidth()), E); + case eMacroKind_Neg: + return SubExpr::alloc(ref<Expr>(0, E.getWidth()), E); + case Expr::SExt: + // FIXME: Type check arguments. + return SExtExpr::alloc(E, ResTy); + case Expr::ZExt: + // FIXME: Type check arguments. + return ZExtExpr::alloc(E, ResTy); + default: + Error("internal error, unhandled kind.", Name); + return ref<Expr>(0, ResTy); + } +} + +/// ParseMatchedBinaryArgs - Parse a pair of arguments who are +/// expected to be of the same type. Upon return, if both LHS and RHS +/// are valid then they are guaranteed to have the same type. +/// +/// Name - The name token of the expression, for diagnostics. +/// ExpectType - The expected type of the arguments, if known. +void ParserImpl::ParseMatchedBinaryArgs(const Token &Name, + TypeResult ExpectType, + ExprResult &LHS, ExprResult &RHS) { + if (Tok.kind == Token::RParen) { + Error("unexpected end of arguments.", Name); + ConsumeRParen(); + return; + } + + // Avoid NumberOrExprResult overhead and give more precise + // diagnostics when we know the type. + if (ExpectType.isValid()) { + LHS = ParseExpr(ExpectType); + if (Tok.kind == Token::RParen) { + Error("unexpected end of arguments.", Name); + ConsumeRParen(); + return; + } + RHS = ParseExpr(ExpectType); + } else { + NumberOrExprResult LHS_NOE = ParseNumberOrExpr(); + + if (Tok.kind == Token::RParen) { + Error("unexpected end of arguments.", Name); + ConsumeRParen(); + return; + } + + if (LHS_NOE.isNumber()) { + NumberOrExprResult RHS_NOE = ParseNumberOrExpr(); + + if (RHS_NOE.isNumber()) { + Error("ambiguous arguments to expression.", Name); + } else { + RHS = RHS_NOE.getExpr(); + if (RHS.isValid()) + LHS = ParseNumberToken(RHS.get().getWidth(), LHS_NOE.getNumber()); + } + } else { + LHS = LHS_NOE.getExpr(); + if (!LHS.isValid()) { + // FIXME: Should suppress ambiguity warnings here. + RHS = ParseExpr(TypeResult()); + } else { + RHS = ParseExpr(LHS.get().getWidth()); + } + } + } + + ExpectRParen("unexpected argument to expression."); +} + +ExprResult ParserImpl::ParseBinaryParenExpr(const Token &Name, + unsigned Kind, bool IsFixed, + Expr::Width ResTy) { + ExprResult LHS, RHS; + ParseMatchedBinaryArgs(Name, IsFixed ? TypeResult(ResTy) : TypeResult(), + LHS, RHS); + if (!LHS.isValid() || !RHS.isValid()) + return ref<Expr>(0, ResTy); + + ref<Expr> LHS_E = LHS.get(), RHS_E = RHS.get(); + assert(LHS_E.getWidth() == RHS_E.getWidth() && "Mismatched types!"); + + switch (Kind) { + case Expr::Add: return AddExpr::alloc(LHS_E, RHS_E); + case Expr::Sub: return SubExpr::alloc(LHS_E, RHS_E); + case Expr::Mul: return MulExpr::alloc(LHS_E, RHS_E); + case Expr::UDiv: return UDivExpr::alloc(LHS_E, RHS_E); + case Expr::SDiv: return SDivExpr::alloc(LHS_E, RHS_E); + case Expr::URem: return URemExpr::alloc(LHS_E, RHS_E); + case Expr::SRem: return SRemExpr::alloc(LHS_E, RHS_E); + + case Expr::AShr: return AShrExpr::alloc(LHS_E, RHS_E); + case Expr::LShr: return LShrExpr::alloc(LHS_E, RHS_E); + case Expr::Shl: return AndExpr::alloc(LHS_E, RHS_E); + + case Expr::And: return AndExpr::alloc(LHS_E, RHS_E); + case Expr::Or: return OrExpr::alloc(LHS_E, RHS_E); + case Expr::Xor: return XorExpr::alloc(LHS_E, RHS_E); + + case Expr::Eq: return EqExpr::alloc(LHS_E, RHS_E); + case Expr::Ne: return NeExpr::alloc(LHS_E, RHS_E); + case Expr::Ult: return UltExpr::alloc(LHS_E, RHS_E); + case Expr::Ule: return UleExpr::alloc(LHS_E, RHS_E); + case Expr::Ugt: return UgtExpr::alloc(LHS_E, RHS_E); + case Expr::Uge: return UgeExpr::alloc(LHS_E, RHS_E); + case Expr::Slt: return SltExpr::alloc(LHS_E, RHS_E); + case Expr::Sle: return SleExpr::alloc(LHS_E, RHS_E); + case Expr::Sgt: return SgtExpr::alloc(LHS_E, RHS_E); + case Expr::Sge: return SgeExpr::alloc(LHS_E, RHS_E); + default: + Error("FIXME: unhandled kind.", Name); + return ref<Expr>(0, ResTy); + } +} + +ExprResult ParserImpl::ParseSelectParenExpr(const Token &Name, + Expr::Width ResTy) { + // FIXME: Why does this need to be here? + if (Tok.kind == Token::RParen) { + Error("unexpected end of arguments.", Name); + ConsumeRParen(); + return ref<Expr>(0, ResTy); + } + + ExprResult Cond = ParseExpr(Expr::Bool); + ExprResult LHS, RHS; + ParseMatchedBinaryArgs(Name, ResTy, LHS, RHS); + if (!Cond.isValid() || !LHS.isValid() || !RHS.isValid()) + return ref<Expr>(0, ResTy); + return SelectExpr::alloc(Cond.get(), LHS.get(), RHS.get()); +} + + +// need to decide if we want to allow n-ary Concat expressions in the +// language +ExprResult ParserImpl::ParseConcatParenExpr(const Token &Name, + Expr::Width ResTy) { + std::vector<ExprHandle> Kids; + + unsigned Width = 0; + while (Tok.kind != Token::RParen) { + ExprResult E = ParseExpr(TypeResult()); + + // Skip to end of expr on error. + if (!E.isValid()) { + SkipUntilRParen(); + return ref<Expr>(0, ResTy); + } + + Kids.push_back(E.get()); + Width += E.get().getWidth(); + } + + ConsumeRParen(); + + if (Width != ResTy) { + Error("concat does not match expected result size."); + return ref<Expr>(0, ResTy); + } + + return ConcatExpr::createN(Kids.size(), &Kids[0]); +} + +ExprResult ParserImpl::ParseExtractParenExpr(const Token &Name, + Expr::Width ResTy) { + // FIXME: Pull out parse constant integer expression. + ExprResult OffsetExpr = ParseNumber(Expr::Int32); + ExprResult Child = ParseExpr(TypeResult()); + + ExpectRParen("unexpected argument to expression."); + + if (!OffsetExpr.isValid() || !Child.isValid()) + return ref<Expr>(0, ResTy); + + assert(OffsetExpr.get().isConstant() && "ParseNumber returned non-constant."); + unsigned Offset = (unsigned) OffsetExpr.get().getConstantValue(); + + if (Offset + ResTy > Child.get().getWidth()) { + Error("extract out-of-range of child expression.", Name); + return ref<Expr>(0, ResTy); + } + + return ExtractExpr::alloc(Child.get(), Offset, ResTy); +} + +ExprResult ParserImpl::ParseAnyReadParenExpr(const Token &Name, + unsigned Kind, + Expr::Width ResTy) { + NumberOrExprResult Index = ParseNumberOrExpr(); + VersionResult Array = ParseVersionSpecifier(); + ExpectRParen("unexpected argument in read expression."); + + if (!Array.isValid()) + return ref<Expr>(0, ResTy); + + // FIXME: Need generic way to get array width. Needs to work with + // anonymous arrays. + Expr::Width ArrayDomainType = Expr::Int32; + Expr::Width ArrayRangeType = Expr::Int8; + + // Coerce number to correct type. + ExprResult IndexExpr; + if (Index.isNumber()) + IndexExpr = ParseNumberToken(ArrayDomainType, Index.getNumber()); + else + IndexExpr = Index.getExpr(); + + if (!IndexExpr.isValid()) + return ref<Expr>(0, ResTy); + else if (IndexExpr.get().getWidth() != ArrayDomainType) { + Error("index width does not match array domain."); + return ref<Expr>(0, ResTy); + } + + // FIXME: Check range width. + + switch (Kind) { + default: + assert(0 && "Invalid kind."); + return ref<Expr>(0, ResTy); + case eMacroKind_ReadLSB: + case eMacroKind_ReadMSB: { + unsigned NumReads = ResTy / ArrayRangeType; + if (ResTy != NumReads*ArrayRangeType) { + Error("invalid ordered read (not multiple of range type).", Name); + return ref<Expr>(0, ResTy); + } + std::vector<ExprHandle> Kids; + Kids.reserve(NumReads); + ExprHandle Index = IndexExpr.get(); + for (unsigned i=0; i<NumReads; ++i) { + // FIXME: using folding here + ExprHandle OffsetIndex = AddExpr::create(IndexExpr.get(), + ref<Expr>(i, ArrayDomainType)); + Kids.push_back(ReadExpr::alloc(Array.get(), OffsetIndex)); + } + if (Kind == eMacroKind_ReadLSB) + std::reverse(Kids.begin(), Kids.end()); + return ConcatExpr::createN(NumReads, &Kids[0]); + } + case Expr::Read: + return ReadExpr::alloc(Array.get(), IndexExpr.get()); + } +} + +/// version-specifier = <identifier> +/// version-specifier = [<identifier>:] [ version ] +VersionResult ParserImpl::ParseVersionSpecifier() { + const Identifier *Label = 0; + if (Tok.kind == Token::Identifier) { + Token LTok = Tok; + Label = GetOrCreateIdentifier(Tok); + ConsumeToken(); + + // FIXME: hack: add array declarations and ditch this. + if (memcmp(Label->Name.c_str(), "arr", 3) == 0) { + // Declare or create array. + const ArrayDecl *&A = ArraySymTab[Label]; + if (!A) { + // Array = new ArrayDecl(Label, 0, 32, 8); + unsigned id = atoi(&Label->Name.c_str()[3]); + Array *root = new Array(0, id, 0); + // Create update list mapping of name -> array. + VersionSymTab.insert(std::make_pair(Label, + UpdateList(root, true, NULL))); + } + } + + if (Tok.kind != Token::Colon) { + VersionSymTabTy::iterator it = VersionSymTab.find(Label); + + if (it == VersionSymTab.end()) { + Error("invalid update list label reference.", LTok); + return VersionResult(false, + UpdateList(0, true, NULL)); + } + + return it->second; + } + + ConsumeToken(); + if (VersionSymTab.count(Label)) { + Error("duplicate update list label definition.", LTok); + Label = 0; + } + } + + Token Start = Tok; + VersionResult Res = ParseVersion(); + // Define update list to avoid use-of-undef errors. + if (!Res.isValid()) + Res = VersionResult(false, + UpdateList(0, true, NULL)); + + if (Label) + VersionSymTab.insert(std::make_pair(Label, Res.get())); + return Res; +} + +/// version - '[' update-list? ']' ['@' version-specifier] +/// update-list - empty +/// update-list - lhs '=' rhs [',' update-list] +VersionResult ParserImpl::ParseVersion() { + if (Tok.kind != Token::LSquare) + return VersionResult(false, UpdateList(0, false, NULL)); + + std::vector< std::pair<NumberOrExprResult, NumberOrExprResult> > Writes; + ConsumeLSquare(); + for (;;) { + // FIXME: Type check exprs. + + // FIXME: We need to do this (the above) anyway just to handle + // implicit constants correctly. + NumberOrExprResult LHS = ParseNumberOrExpr(); + + if (Tok.kind != Token::Equals) { + Error("expected '='.", Tok); + break; + } + + ConsumeToken(); + NumberOrExprResult RHS = ParseNumberOrExpr(); + + Writes.push_back(std::make_pair(LHS, RHS)); + + if (Tok.kind == Token::Comma) + ConsumeToken(); + else + break; + } + ExpectRSquare("expected close of update list"); + + VersionHandle Base(0, false, NULL); + + // Anonymous array case. + if (Tok.kind != Token::At) { + Array *root = new Array(0, 0, 0); + Base = UpdateList(root, false, NULL); + } else { + ConsumeToken(); + + VersionResult BaseRes = ParseVersionSpecifier(); + if (!BaseRes.isValid()) + return BaseRes; + + Base = BaseRes.get(); + } + + Expr::Width ArrayDomainType = Expr::Int32; + Expr::Width ArrayRangeType = Expr::Int8; + + for (std::vector< std::pair<NumberOrExprResult, NumberOrExprResult> >::reverse_iterator + it = Writes.rbegin(), ie = Writes.rend(); it != ie; ++it) { + ExprResult LHS, RHS; + // FIXME: This can be factored into common helper for coercing a + // NumberOrExpr into an Expr. + if (it->first.isNumber()) { + LHS = ParseNumberToken(ArrayDomainType, it->first.getNumber()); + } else { + LHS = it->first.getExpr(); + if (LHS.isValid() && LHS.get().getWidth() != ArrayDomainType) { + // FIXME: bad token location. We should maybe try and know the + // array up-front? + Error("invalid value in write index (doesn't match domain).", Tok); + LHS = ExprResult(); + } + } + + if (it->second.isNumber()) { + RHS = ParseNumberToken(ArrayRangeType, it->second.getNumber()); + } else { + RHS = it->second.getExpr(); + if (RHS.isValid() && RHS.get().getWidth() != ArrayRangeType) { + // FIXME: bad token location. We should maybe try and know the + // array up-front? + Error("invalid value in write assignment (doesn't match range).", Tok); + RHS = ExprResult(); + } + } + + if (LHS.isValid() && RHS.isValid()) + Base.extend(LHS.get(), RHS.get()); + } + + return Base; +} + +/// ParseNumber - Parse a number of the given type. +ExprResult ParserImpl::ParseNumber(Expr::Width Type) { + ExprResult Res = ParseNumberToken(Type, Tok); + ConsumeExpectedToken(Token::Number); + return Res; +} + +/// ParseNumberToken - Parse a number of the given type from the given +/// token. +ExprResult ParserImpl::ParseNumberToken(Expr::Width Type, const Token &Tok) { + const char *S = Tok.start; + unsigned N = Tok.length; + unsigned Radix = 10, RadixBits = 4; + bool HasMinus = false; + + // Detect +/- (a number token cannot have both). + if (S[0] == '+') { + ++S; + --N; + } else if (S[0] == '-') { + HasMinus = true; + ++S; + --N; + } + + // Detect 0[box]. + if ((Tok.length >= 2 && S[0] == '0') && + (S[1] == 'b' || S[1] == 'o' || S[1] == 'x')) { + if (S[1] == 'b') { + Radix = 2; + RadixBits = 1; + } else if (S[1] == 'o') { + Radix = 8; + RadixBits = 3; + } else { + Radix = 16; + RadixBits = 4; + } + S += 2; + N -= 2; + + // Diagnose 0[box] with no trailing digits. + if (!N) { + Error("invalid numeric token (no digits).", Tok); + return ref<Expr>(0, Type); + } + } + + // This is a simple but slow way to handle overflow. + APInt Val(std::max(64U, RadixBits * N), 0); + APInt RadixVal(Val.getBitWidth(), Radix); + APInt DigitVal(Val.getBitWidth(), 0); + for (unsigned i=0; i<N; ++i) { + unsigned Digit, Char = S[i]; + + if (Char == '_') + continue; + + if ('0' <= Char && Char <= '9') + Digit = Char - '0'; + else if ('a' <= Char && Char <= 'z') + Digit = Char - 'a' + 10; + else if ('A' <= Char && Char <= 'Z') + Digit = Char - 'A' + 10; + else { + Error("invalid character in numeric token.", Tok); + return ref<Expr>(0, Type); + } + + if (Digit >= Radix) { + Error("invalid character in numeric token (out of range).", Tok); + return ref<Expr>(0, Type); + } + + DigitVal = Digit; + Val = Val * RadixVal + DigitVal; + } + + // FIXME: Actually do the check for overflow. + if (HasMinus) + Val = -Val; + + return ExprResult(ref<Expr>(Val.trunc(Type).getZExtValue(), Type)); +} + +/// ParseTypeSpecifier - Parse a type specifier. +/// +/// type = w[0-9]+ +TypeResult ParserImpl::ParseTypeSpecifier() { + assert(Tok.kind == Token::KWWidth && "Unexpected token."); + + // FIXME: Need APInt technically. + Token TypeTok = Tok; + int width = atoi(std::string(Tok.start+1,Tok.length-1).c_str()); + ConsumeToken(); + + // FIXME: We should impose some sort of maximum just for sanity? + return TypeResult(width); +} + +void ParserImpl::Error(const char *Message, const Token &At) { + ++NumErrors; + if (MaxErrors && NumErrors >= MaxErrors) + return; + + llvm::cerr << Filename + << ":" << At.line << ":" << At.column + << ": error: " << Message << "\n"; + + // Skip carat diagnostics on EOF token. + if (At.kind == Token::EndOfFile) + return; + + // Simple caret style diagnostics. + const char *LineBegin = At.start, *LineEnd = At.start, + *BufferBegin = TheMemoryBuffer->getBufferStart(), + *BufferEnd = TheMemoryBuffer->getBufferEnd(); + + // Run line pointers forward and back. + while (LineBegin > BufferBegin && + LineBegin[-1] != '\r' && LineBegin[-1] != '\n') + --LineBegin; + while (LineEnd < BufferEnd && + LineEnd[0] != '\r' && LineEnd[0] != '\n') + ++LineEnd; + + // Show the line. + llvm::cerr << std::string(LineBegin, LineEnd) << "\n"; + + // Show the caret or squiggly, making sure to print back spaces the + // same. + for (const char *S=LineBegin; S != At.start; ++S) + llvm::cerr << (isspace(*S) ? *S : ' '); + if (At.length > 1) { + for (unsigned i=0; i<At.length; ++i) + llvm::cerr << '~'; + } else + llvm::cerr << '^'; + llvm::cerr << '\n'; +} + +// AST API +// FIXME: Move out of parser. + +Decl::Decl() {} + +void QueryCommand::dump() { + // FIXME: This is masking the difference between an actual query and + // a query decl. + ExprPPrinter::printQuery(std::cerr, + ConstraintManager(Constraints), + Query); +} + +// Public parser API + +Parser::Parser() { +} + +Parser::~Parser() { +} + +Parser *Parser::Create(const std::string Filename, + const MemoryBuffer *MB) { + ParserImpl *P = new ParserImpl(Filename, MB); + P->Initialize(); + return P; +} diff --git a/lib/Expr/Updates.cpp b/lib/Expr/Updates.cpp new file mode 100644 index 00000000..b2ceeaf1 --- /dev/null +++ b/lib/Expr/Updates.cpp @@ -0,0 +1,126 @@ +//===-- Updates.cpp -------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Expr.h" + +#include <cassert> + +using namespace klee; + +/// + +UpdateNode::UpdateNode(const UpdateNode *_next, + const ref<Expr> &_index, + const ref<Expr> &_value) + : refCount(0), + stpArray(0), + next(_next), + index(_index), + value(_value) { + assert(_value.getWidth() == Expr::Int8 && "Update value should be 8-bit wide."); + computeHash(); + if (next) { + ++next->refCount; + size = 1 + next->size; + } + else size = 1; +} + +extern "C" void vc_DeleteExpr(void*); + +UpdateNode::~UpdateNode() { + // XXX gross + if (stpArray) + ::vc_DeleteExpr(stpArray); +} + +int UpdateNode::compare(const UpdateNode &b) const { + if (int i = index.compare(b.index)) + return i; + return value.compare(b.value); +} + +unsigned UpdateNode::computeHash() { + hashValue = index.hash() ^ value.hash(); + if (next) + hashValue ^= next->hash(); + return hashValue; +} + +/// + +UpdateList::UpdateList(const Array *_root, bool _isRooted, + const UpdateNode *_head) + : root(_root), + head(_head), + isRooted(_isRooted) { + if (head) ++head->refCount; +} + +UpdateList::UpdateList(const UpdateList &b) + : root(b.root), + head(b.head), + isRooted(b.isRooted) { + if (head) ++head->refCount; +} + +UpdateList::~UpdateList() { + // We need to be careful and avoid recursion here. We do this in + // cooperation with the private dtor of UpdateNode which does not + // recursively free its tail. + while (head && --head->refCount==0) { + const UpdateNode *n = head->next; + delete head; + head = n; + } +} + +UpdateList &UpdateList::operator=(const UpdateList &b) { + if (b.head) ++b.head->refCount; + if (head && --head->refCount==0) delete head; + root = b.root; + head = b.head; + isRooted = b.isRooted; + return *this; +} + +void UpdateList::extend(const ref<Expr> &index, const ref<Expr> &value) { + if (head) --head->refCount; + head = new UpdateNode(head, index, value); + ++head->refCount; +} + +int UpdateList::compare(const UpdateList &b) const { + // use object id to increase determinism + if (root->id != b.root->id) + return root->id < b.root->id ? -1 : 1; + + if (getSize() < b.getSize()) return -1; + else if (getSize() > b.getSize()) return 1; + + // XXX build comparison into update, make fast + const UpdateNode *an=head, *bn=b.head; + for (; an && bn; an=an->next,bn=bn->next) { + if (an==bn) { // exploit shared list structure + return 0; + } else { + if (int res = an->compare(*bn)) + return res; + } + } + assert(!an && !bn); + return 0; +} + +unsigned UpdateList::hash() const { + unsigned res = root->id * Expr::MAGIC_HASH_CONSTANT; + if (head) + res ^= head->hash(); + return res; +} diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 00000000..e12fef50 --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,15 @@ +#===-- lib/Makefile ----------------------------------------*- Makefile -*--===# +# +# The KLEE Symbolic Virtual Machine +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +LEVEL=.. + +PARALLEL_DIRS=Basic Support Expr Solver Module Core + +include $(LEVEL)/Makefile.common + diff --git a/lib/Module/Checks.cpp b/lib/Module/Checks.cpp new file mode 100644 index 00000000..ca4eeb44 --- /dev/null +++ b/lib/Module/Checks.cpp @@ -0,0 +1,68 @@ +//===-- Checks.cpp --------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Passes.h" + +#include "llvm/Constants.h" +#include "llvm/DerivedTypes.h" +#include "llvm/Function.h" +#include "llvm/InstrTypes.h" +#include "llvm/Instruction.h" +#include "llvm/Instructions.h" +#include "llvm/IntrinsicInst.h" +#include "llvm/Module.h" +#include "llvm/Pass.h" +#include "llvm/Type.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Target/TargetData.h" + +using namespace llvm; +using namespace klee; + +char DivCheckPass::ID; + +bool DivCheckPass::runOnModule(Module &M) { + Function *divZeroCheckFunction = 0; + + bool moduleChanged = false; + + for (Module::iterator f = M.begin(), fe = M.end(); f != fe; ++f) { + for (Function::iterator b = f->begin(), be = f->end(); b != be; ++b) { + for (BasicBlock::iterator i = b->begin(), ie = b->end(); i != ie; ++i) { + if (BinaryOperator* binOp = dyn_cast<BinaryOperator>(i)) { + // find all [s|u][div|mod] instructions + Instruction::BinaryOps opcode = binOp->getOpcode(); + if (opcode == Instruction::SDiv || opcode == Instruction::UDiv || + opcode == Instruction::SRem || opcode == Instruction::URem) { + + CastInst *denominator = + CastInst::CreateIntegerCast(i->getOperand(1), + (Type*)Type::Int64Ty, + false, /* sign doesn't matter */ + "int_cast_to_i64", + i); + + // Lazily bind the function to avoid always importing it. + if (!divZeroCheckFunction) { + Constant *fc = M.getOrInsertFunction("klee_div_zero_check", + Type::VoidTy, + Type::Int64Ty, NULL); + divZeroCheckFunction = cast<Function>(fc); + } + + CallInst::Create(divZeroCheckFunction, denominator, "", &*i); + moduleChanged = true; + } + } + } + } + } + return moduleChanged; +} diff --git a/lib/Module/InstructionInfoTable.cpp b/lib/Module/InstructionInfoTable.cpp new file mode 100644 index 00000000..82874406 --- /dev/null +++ b/lib/Module/InstructionInfoTable.cpp @@ -0,0 +1,196 @@ +//===-- InstructionInfoTable.cpp ------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Internal/Module/InstructionInfoTable.h" + +#include "llvm/Function.h" +#include "llvm/Instructions.h" +#include "llvm/IntrinsicInst.h" +#include "llvm/Linker.h" +#include "llvm/Module.h" +#include "llvm/Assembly/AsmAnnotationWriter.h" +#include "llvm/Support/CFG.h" +#include "llvm/Support/InstIterator.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Analysis/ValueTracking.h" + +#include <map> +#include <iostream> +#include <fstream> +#include <sstream> +#include <string> + +using namespace llvm; +using namespace klee; + +class InstructionToLineAnnotator : public llvm::AssemblyAnnotationWriter { +public: + void emitInstructionAnnot(const Instruction *i, llvm::raw_ostream &os) { + os << "%%%" << (uintptr_t) i; + } +}; + +static void buildInstructionToLineMap(Module *m, + std::map<const Instruction*, unsigned> &out) { + InstructionToLineAnnotator a; + std::ostringstream buffer; + m->print(buffer, &a); + std::string str = buffer.str(); + const char *s; + + unsigned line = 1; + for (s=str.c_str(); *s; s++) { + if (*s=='\n') { + line++; + if (s[1]=='%' && s[2]=='%' && s[3]=='%') { + s += 4; + char *end; + unsigned long long value = strtoull(s, &end, 10); + if (end!=s) { + out.insert(std::make_pair((const Instruction*) value, line)); + } + s = end; + } + } + } +} + +static std::string getDSPIPath(DbgStopPointInst *dspi) { + std::string dir, file; + bool res = GetConstantStringInfo(dspi->getDirectory(), dir); + assert(res && "GetConstantStringInfo failed"); + res = GetConstantStringInfo(dspi->getFileName(), file); + assert(res && "GetConstantStringInfo failed"); + if (dir.empty()) { + return file; + } else if (*dir.rbegin() == '/') { + return dir + file; + } else { + return dir + "/" + file; + } +} + +InstructionInfoTable::InstructionInfoTable(Module *m) + : dummyString(""), dummyInfo(0, dummyString, 0, 0) { + unsigned id = 0; + std::map<const Instruction*, unsigned> lineTable; + buildInstructionToLineMap(m, lineTable); + + for (Module::iterator fnIt = m->begin(), fn_ie = m->end(); + fnIt != fn_ie; ++fnIt) { + const std::string *initialFile = &dummyString; + unsigned initialLine = 0; + + // It may be better to look for the closest stoppoint to the entry + // following the CFG, but it is not clear that it ever matters in + // practice. + for (inst_iterator it = inst_begin(fnIt), ie = inst_end(fnIt); + it != ie; ++it) { + if (DbgStopPointInst *dspi = dyn_cast<DbgStopPointInst>(&*it)) { + initialFile = internString(getDSPIPath(dspi)); + initialLine = dspi->getLine(); + break; + } + } + + typedef std::map<BasicBlock*, std::pair<const std::string*,unsigned> > + sourceinfo_ty; + sourceinfo_ty sourceInfo; + for (llvm::Function::iterator bbIt = fnIt->begin(), bbie = fnIt->end(); + bbIt != bbie; ++bbIt) { + std::pair<sourceinfo_ty::iterator, bool> + res = sourceInfo.insert(std::make_pair(bbIt, + std::make_pair(initialFile, + initialLine))); + if (!res.second) + continue; + + std::vector<BasicBlock*> worklist; + worklist.push_back(bbIt); + + do { + BasicBlock *bb = worklist.back(); + worklist.pop_back(); + + sourceinfo_ty::iterator si = sourceInfo.find(bb); + assert(si != sourceInfo.end()); + const std::string *file = si->second.first; + unsigned line = si->second.second; + + for (BasicBlock::iterator it = bb->begin(), ie = bb->end(); + it != ie; ++it) { + Instruction *instr = it; + unsigned assemblyLine = 0; + std::map<const Instruction*, unsigned>::const_iterator ltit = + lineTable.find(instr); + if (ltit!=lineTable.end()) + assemblyLine = ltit->second; + if (DbgStopPointInst *dspi = dyn_cast<DbgStopPointInst>(instr)) { + file = internString(getDSPIPath(dspi)); + line = dspi->getLine(); + } + infos.insert(std::make_pair(instr, + InstructionInfo(id++, + *file, + line, + assemblyLine))); + } + + for (succ_iterator it = succ_begin(bb), ie = succ_end(bb); + it != ie; ++it) { + if (sourceInfo.insert(std::make_pair(*it, + std::make_pair(file, line))).second) + worklist.push_back(*it); + } + } while (!worklist.empty()); + } + } +} + +InstructionInfoTable::~InstructionInfoTable() { + for (std::set<const std::string *, ltstr>::iterator + it = internedStrings.begin(), ie = internedStrings.end(); + it != ie; ++it) + delete *it; +} + +const std::string *InstructionInfoTable::internString(std::string s) { + std::set<const std::string *, ltstr>::iterator it = internedStrings.find(&s); + if (it==internedStrings.end()) { + std::string *interned = new std::string(s); + internedStrings.insert(interned); + return interned; + } else { + return *it; + } +} + +unsigned InstructionInfoTable::getMaxID() const { + return infos.size(); +} + +const InstructionInfo & +InstructionInfoTable::getInfo(const Instruction *inst) const { + std::map<const llvm::Instruction*, InstructionInfo>::const_iterator it = + infos.find(inst); + if (it==infos.end()) { + return dummyInfo; + } else { + return it->second; + } +} + +const InstructionInfo & +InstructionInfoTable::getFunctionInfo(const Function *f) const { + if (f->isDeclaration()) { + return dummyInfo; + } else { + return getInfo(f->begin()->begin()); + } +} diff --git a/lib/Module/IntrinsicCleaner.cpp b/lib/Module/IntrinsicCleaner.cpp new file mode 100644 index 00000000..e59b7ff6 --- /dev/null +++ b/lib/Module/IntrinsicCleaner.cpp @@ -0,0 +1,119 @@ +//===-- IntrinsicCleaner.cpp ----------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Passes.h" + +#include "llvm/Constants.h" +#include "llvm/DerivedTypes.h" +#include "llvm/Function.h" +#include "llvm/InstrTypes.h" +#include "llvm/Instruction.h" +#include "llvm/Instructions.h" +#include "llvm/IntrinsicInst.h" +#include "llvm/Module.h" +#include "llvm/Pass.h" +#include "llvm/Type.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Target/TargetData.h" + +using namespace llvm; + +namespace klee { + +char IntrinsicCleanerPass::ID; + +bool IntrinsicCleanerPass::runOnModule(Module &M) { + bool dirty = false; + for (Module::iterator f = M.begin(), fe = M.end(); f != fe; ++f) + for (Function::iterator b = f->begin(), be = f->end(); b != be; ++b) + dirty |= runOnBasicBlock(*b); + return dirty; +} + +bool IntrinsicCleanerPass::runOnBasicBlock(BasicBlock &b) { + bool dirty = false; + + for (BasicBlock::iterator i = b.begin(), ie = b.end(); i != ie;) { + IntrinsicInst *ii = dyn_cast<IntrinsicInst>(&*i); + // increment now since LowerIntrinsic deletion makes iterator invalid. + ++i; + if(ii) { + switch (ii->getIntrinsicID()) { + case Intrinsic::vastart: + case Intrinsic::vaend: + break; + + // Lower vacopy so that object resolution etc is handled by + // normal instructions. FIXME: This is broken for non-x86_32. + case Intrinsic::vacopy: { // (dst, src) -> *((i8**) dst) = *((i8**) src) + Value *dst = ii->getOperand(1); + Value *src = ii->getOperand(2); + Type *i8pp = PointerType::getUnqual(PointerType::getUnqual(Type::Int8Ty)); + Value *castedDst = CastInst::CreatePointerCast(dst, i8pp, "vacopy.cast.dst", ii); + Value *castedSrc = CastInst::CreatePointerCast(src, i8pp, "vacopy.cast.src", ii); + Value *load = new LoadInst(castedSrc, "vacopy.read", ii); + new StoreInst(load, castedDst, false, ii); + ii->removeFromParent(); + delete ii; + break; + } + + case Intrinsic::dbg_stoppoint: { + // We can remove this stoppoint if the next instruction is + // sure to be another stoppoint. This is nice for cleanliness + // but also important for switch statements where it can allow + // the targets to be joined. + bool erase = false; + if (isa<DbgStopPointInst>(i) || + isa<UnreachableInst>(i)) { + erase = true; + } else if (isa<BranchInst>(i) || + isa<SwitchInst>(i)) { + BasicBlock *bb = i->getParent(); + erase = true; + for (succ_iterator it=succ_begin(bb), ie=succ_end(bb); + it!=ie; ++it) { + if (!isa<DbgStopPointInst>(it->getFirstNonPHI())) { + erase = false; + break; + } + } + } + + if (erase) { + ii->eraseFromParent(); + dirty = true; + } + break; + } + + case Intrinsic::dbg_region_start: + case Intrinsic::dbg_region_end: + case Intrinsic::dbg_func_start: + case Intrinsic::dbg_declare: + // Remove these regardless of lower intrinsics flag. This can + // be removed once IntrinsicLowering is fixed to not have bad + // caches. + ii->eraseFromParent(); + dirty = true; + break; + + default: + if (LowerIntrinsics) + IL->LowerIntrinsicCall(ii); + dirty = true; + break; + } + } + } + + return dirty; +} +} diff --git a/lib/Module/KInstruction.cpp b/lib/Module/KInstruction.cpp new file mode 100644 index 00000000..799620c6 --- /dev/null +++ b/lib/Module/KInstruction.cpp @@ -0,0 +1,19 @@ +//===-- KInstruction.cpp --------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Internal/Module/KInstruction.h" + +using namespace llvm; +using namespace klee; + +/***/ + +KInstruction::~KInstruction() { + delete[] operands; +} diff --git a/lib/Module/KModule.cpp b/lib/Module/KModule.cpp new file mode 100644 index 00000000..5d88fbda --- /dev/null +++ b/lib/Module/KModule.cpp @@ -0,0 +1,506 @@ +//===-- KModule.cpp -------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// FIXME: This does not belong here. +#include "../Core/Common.h" + +#include "klee/Internal/Module/KModule.h" + +#include "Passes.h" + +#include "klee/Interpreter.h" +#include "klee/Internal/Module/Cell.h" +#include "klee/Internal/Module/KInstruction.h" +#include "klee/Internal/Module/InstructionInfoTable.h" +#include "klee/Internal/Support/ModuleUtil.h" + +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/Instructions.h" +#include "llvm/Module.h" +#include "llvm/PassManager.h" +#include "llvm/ValueSymbolTable.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/System/Path.h" +#include "llvm/Target/TargetData.h" +#include "llvm/Transforms/Scalar.h" + +#include <sstream> + +using namespace llvm; +using namespace klee; + +namespace { + enum SwitchImplType { + eSwitchTypeSimple, + eSwitchTypeLLVM, + eSwitchTypeInternal + }; + + cl::list<std::string> + MergeAtExit("merge-at-exit"); + + cl::opt<bool> + NoTruncateSourceLines("no-truncate-source-lines", + cl::desc("Don't truncate long lines in the output source")); + + cl::opt<bool> + OutputSource("output-source", + cl::desc("Write the assembly for the final transformed source"), + cl::init(true)); + + cl::opt<bool> + OutputModule("output-module", + cl::desc("Write the bitcode for the final transformed module"), + cl::init(false)); + + cl::opt<SwitchImplType> + SwitchType("switch-type", cl::desc("Select the implementation of switch"), + cl::values(clEnumValN(eSwitchTypeSimple, "simple", + "lower to ordered branches"), + clEnumValN(eSwitchTypeLLVM, "llvm", + "lower using LLVM"), + clEnumValN(eSwitchTypeInternal, "internal", + "execute switch internally"), + clEnumValEnd), + cl::init(eSwitchTypeInternal)); + + cl::opt<bool> + DebugPrintEscapingFunctions("debug-print-escaping-functions", + cl::desc("Print functions whose address is taken.")); +} + +KModule::KModule(Module *_module) + : module(_module), + targetData(new TargetData(module)), + dbgStopPointFn(0), + kleeMergeFn(0), + infos(0), + constantTable(0) { +} + +KModule::~KModule() { + delete[] constantTable; + delete infos; + + for (std::vector<KFunction*>::iterator it = functions.begin(), + ie = functions.end(); it != ie; ++it) + delete *it; + + delete targetData; + delete module; +} + +/***/ + +namespace llvm { +extern void Optimize(Module*); +} + +// what a hack +static Function *getStubFunctionForCtorList(Module *m, + GlobalVariable *gv, + std::string name) { + assert(!gv->isDeclaration() && !gv->hasInternalLinkage() && + "do not support old LLVM style constructor/destructor lists"); + + std::vector<const Type*> nullary; + + Function *fn = Function::Create(FunctionType::get(Type::VoidTy, + nullary, false), + GlobalVariable::InternalLinkage, + name, + m); + BasicBlock *bb = BasicBlock::Create("entry", fn); + + // From lli: + // Should be an array of '{ int, void ()* }' structs. The first value is + // the init priority, which we ignore. + ConstantArray *arr = dyn_cast<ConstantArray>(gv->getInitializer()); + if (arr) { + for (unsigned i=0; i<arr->getNumOperands(); i++) { + ConstantStruct *cs = cast<ConstantStruct>(arr->getOperand(i)); + assert(cs->getNumOperands()==2 && "unexpected element in ctor initializer list"); + + Constant *fp = cs->getOperand(1); + if (!fp->isNullValue()) { + if (llvm::ConstantExpr *ce = dyn_cast<llvm::ConstantExpr>(fp)) + fp = ce->getOperand(0); + + if (Function *f = dyn_cast<Function>(fp)) { + CallInst::Create(f, "", bb); + } else { + assert(0 && "unable to get function pointer from ctor initializer list"); + } + } + } + } + + ReturnInst::Create(bb); + + return fn; +} + +static void injectStaticConstructorsAndDestructors(Module *m) { + GlobalVariable *ctors = m->getNamedGlobal("llvm.global_ctors"); + GlobalVariable *dtors = m->getNamedGlobal("llvm.global_dtors"); + + if (ctors || dtors) { + Function *mainFn = m->getFunction("main"); + assert(mainFn && "unable to find main function"); + + if (ctors) + CallInst::Create(getStubFunctionForCtorList(m, ctors, "klee.ctor_stub"), + "", mainFn->begin()->begin()); + if (dtors) { + Function *dtorStub = getStubFunctionForCtorList(m, dtors, "klee.dtor_stub"); + for (Function::iterator it = mainFn->begin(), ie = mainFn->end(); + it != ie; ++it) { + if (isa<ReturnInst>(it->getTerminator())) + CallInst::Create(dtorStub, "", it->getTerminator()); + } + } + } +} + +static void forceImport(Module *m, const char *name, const Type *retType, ...) { + // If module lacks an externally visible symbol for the name then we + // need to create one. We have to look in the symbol table because + // we want to check everything (global variables, functions, and + // aliases). + + Value *v = m->getValueSymbolTable().lookup(name); + GlobalValue *gv = dyn_cast_or_null<GlobalValue>(v); + + if (!gv || gv->hasInternalLinkage()) { + va_list ap; + + va_start(ap, retType); + std::vector<const Type *> argTypes; + while (const Type *t = va_arg(ap, const Type*)) + argTypes.push_back(t); + va_end(ap); + + m->getOrInsertFunction(name, FunctionType::get(retType, argTypes, false)); + } +} + +void KModule::prepare(const Interpreter::ModuleOptions &opts, + InterpreterHandler *ih) { + if (!MergeAtExit.empty()) { + Function *mergeFn = module->getFunction("klee_merge"); + if (!mergeFn) { + const llvm::FunctionType *Ty = + FunctionType::get(Type::VoidTy, std::vector<const Type*>(), false); + mergeFn = Function::Create(Ty, GlobalVariable::ExternalLinkage, + "klee_merge", + module); + } + + for (cl::list<std::string>::iterator it = MergeAtExit.begin(), + ie = MergeAtExit.end(); it != ie; ++it) { + std::string &name = *it; + Function *f = module->getFunction(name); + if (!f) { + klee_error("cannot insert merge-at-exit for: %s (cannot find)", + name.c_str()); + } else if (f->isDeclaration()) { + klee_error("cannot insert merge-at-exit for: %s (external)", + name.c_str()); + } + + BasicBlock *exit = BasicBlock::Create("exit", f); + PHINode *result = 0; + if (f->getReturnType() != Type::VoidTy) + result = PHINode::Create(f->getReturnType(), "retval", exit); + CallInst::Create(mergeFn, "", exit); + ReturnInst::Create(result, exit); + + llvm::cerr << "KLEE: adding klee_merge at exit of: " << name << "\n"; + for (llvm::Function::iterator bbit = f->begin(), bbie = f->end(); + bbit != bbie; ++bbit) { + if (&*bbit != exit) { + Instruction *i = bbit->getTerminator(); + if (i->getOpcode()==Instruction::Ret) { + if (result) { + result->addIncoming(i->getOperand(0), bbit); + } + i->eraseFromParent(); + BranchInst::Create(exit, bbit); + } + } + } + } + } + + // Inject checks prior to optimization... we also perform the + // invariant transformations that we will end up doing later so that + // optimize is seeing what is as close as possible to the final + // module. + PassManager pm; + pm.add(new RaiseAsmPass()); + if (opts.CheckDivZero) pm.add(new DivCheckPass()); + // FIXME: This false here is to work around a bug in + // IntrinsicLowering which caches values which may eventually be + // deleted (via RAUW). This can be removed once LLVM fixes this + // issue. + pm.add(new IntrinsicCleanerPass(*targetData, false)); + pm.run(*module); + + if (opts.Optimize) + Optimize(module); + + // Force importing functions required by intrinsic lowering. Kind of + // unfortunate clutter when we don't need them but we won't know + // that until after all linking and intrinsic lowering is + // done. After linking and passes we just try to manually trim these + // by name. We only add them if such a function doesn't exist to + // avoid creating stale uses. + + forceImport(module, "memcpy", PointerType::getUnqual(Type::Int8Ty), + PointerType::getUnqual(Type::Int8Ty), + PointerType::getUnqual(Type::Int8Ty), + targetData->getIntPtrType(), (Type*) 0); + forceImport(module, "memmove", PointerType::getUnqual(Type::Int8Ty), + PointerType::getUnqual(Type::Int8Ty), + PointerType::getUnqual(Type::Int8Ty), + targetData->getIntPtrType(), (Type*) 0); + forceImport(module, "memset", PointerType::getUnqual(Type::Int8Ty), + PointerType::getUnqual(Type::Int8Ty), + Type::Int32Ty, + targetData->getIntPtrType(), (Type*) 0); + + // FIXME: Missing force import for various math functions. + + // FIXME: Find a way that we can test programs without requiring + // this to be linked in, it makes low level debugging much more + // annoying. + llvm::sys::Path path(opts.LibraryDir); + path.appendComponent("libintrinsic.bca"); + module = linkWithLibrary(module, path.c_str()); + + // Needs to happen after linking (since ctors/dtors can be modified) + // and optimization (since global optimization can rewrite lists). + injectStaticConstructorsAndDestructors(module); + + // Finally, run the passes that maintain invariants we expect during + // interpretation. We run the intrinsic cleaner just in case we + // linked in something with intrinsics but any external calls are + // going to be unresolved. We really need to handle the intrinsics + // directly I think? + PassManager pm3; + pm3.add(createCFGSimplificationPass()); + switch(SwitchType) { + case eSwitchTypeInternal: break; + case eSwitchTypeSimple: pm3.add(new LowerSwitchPass()); break; + case eSwitchTypeLLVM: pm3.add(createLowerSwitchPass()); break; + default: klee_error("invalid --switch-type"); + } + pm3.add(new IntrinsicCleanerPass(*targetData)); + pm3.add(new PhiCleanerPass()); + pm3.run(*module); + + // For cleanliness see if we can discard any of the functions we + // forced to import. + Function *f; + f = module->getFunction("memcpy"); + if (f && f->use_empty()) f->eraseFromParent(); + f = module->getFunction("memmove"); + if (f && f->use_empty()) f->eraseFromParent(); + f = module->getFunction("memset"); + if (f && f->use_empty()) f->eraseFromParent(); + + + // Write out the .ll assembly file. We truncate long lines to work + // around a kcachegrind parsing bug (it puts them on new lines), so + // that source browsing works. + if (OutputSource) { + std::ostream *os = ih->openOutputFile("assembly.ll"); + assert(os && os->good() && "unable to open source output"); + + // We have an option for this in case the user wants a .ll they + // can compile. + if (NoTruncateSourceLines) { + *os << *module; + } else { + bool truncated = false; + std::stringstream buffer; + buffer << *module; + std::string string = buffer.str(); + const char *position = string.c_str(); + + for (;;) { + const char *end = index(position, '\n'); + if (!end) { + *os << position; + break; + } else { + unsigned count = (end - position) + 1; + if (count<255) { + os->write(position, count); + } else { + os->write(position, 254); + *os << "\n"; + truncated = true; + } + position = end+1; + } + } + } + + delete os; + } + + if (OutputModule) { + std::ostream *f = ih->openOutputFile("final.bc"); + WriteBitcodeToFile(module, *f); + delete f; + } + + dbgStopPointFn = module->getFunction("llvm.dbg.stoppoint"); + kleeMergeFn = module->getFunction("klee_merge"); + + /* Build shadow structures */ + + infos = new InstructionInfoTable(module); + + for (Module::iterator it = module->begin(), ie = module->end(); + it != ie; ++it) { + if (it->isDeclaration()) + continue; + + KFunction *kf = new KFunction(it, this); + + for (unsigned i=0; i<kf->numInstructions; ++i) { + KInstruction *ki = kf->instructions[i]; + ki->info = &infos->getInfo(ki->inst); + } + + functions.push_back(kf); + functionMap.insert(std::make_pair(it, kf)); + } + + /* Compute various interesting properties */ + + for (std::vector<KFunction*>::iterator it = functions.begin(), + ie = functions.end(); it != ie; ++it) { + KFunction *kf = *it; + if (functionEscapes(kf->function)) + escapingFunctions.insert(kf->function); + } + + if (DebugPrintEscapingFunctions && !escapingFunctions.empty()) { + llvm::cerr << "KLEE: escaping functions: ["; + for (std::set<Function*>::iterator it = escapingFunctions.begin(), + ie = escapingFunctions.end(); it != ie; ++it) { + llvm::cerr << (*it)->getName() << ", "; + } + llvm::cerr << "]\n"; + } +} + +KConstant* KModule::getKConstant(Constant *c) { + std::map<llvm::Constant*, KConstant*>::iterator it = constantMap.find(c); + if (it != constantMap.end()) + return it->second; + return NULL; +} + +unsigned KModule::getConstantID(Constant *c, KInstruction* ki) { + KConstant *kc = getKConstant(c); + if (kc) + return kc->id; + + unsigned id = constants.size(); + kc = new KConstant(c, id, ki); + constantMap.insert(std::make_pair(c, kc)); + constants.push_back(c); + return id; +} + +/***/ + +KConstant::KConstant(llvm::Constant* _ct, unsigned _id, KInstruction* _ki) { + ct = _ct; + id = _id; + ki = _ki; +} + +/***/ + +KFunction::KFunction(llvm::Function *_function, + KModule *km) + : function(_function), + numArgs(function->arg_size()), + numInstructions(0), + trackCoverage(true) { + for (llvm::Function::iterator bbit = function->begin(), + bbie = function->end(); bbit != bbie; ++bbit) { + BasicBlock *bb = bbit; + basicBlockEntry[bb] = numInstructions; + numInstructions += bb->size(); + } + + instructions = new KInstruction*[numInstructions]; + + std::map<Instruction*, unsigned> registerMap; + + // The first arg_size() registers are reserved for formals. + unsigned rnum = numArgs; + for (llvm::Function::iterator bbit = function->begin(), + bbie = function->end(); bbit != bbie; ++bbit) { + for (llvm::BasicBlock::iterator it = bbit->begin(), ie = bbit->end(); + it != ie; ++it) + registerMap[it] = rnum++; + } + numRegisters = rnum; + + unsigned i = 0; + for (llvm::Function::iterator bbit = function->begin(), + bbie = function->end(); bbit != bbie; ++bbit) { + for (llvm::BasicBlock::iterator it = bbit->begin(), ie = bbit->end(); + it != ie; ++it) { + KInstruction *ki; + + switch(it->getOpcode()) { + case Instruction::GetElementPtr: + ki = new KGEPInstruction(); break; + default: + ki = new KInstruction(); break; + } + + unsigned numOperands = it->getNumOperands(); + ki->inst = it; + ki->operands = new int[numOperands]; + ki->dest = registerMap[it]; + for (unsigned j=0; j<numOperands; j++) { + Value *v = it->getOperand(j); + + if (Instruction *inst = dyn_cast<Instruction>(v)) { + ki->operands[j] = registerMap[inst]; + } else if (Argument *a = dyn_cast<Argument>(v)) { + ki->operands[j] = a->getArgNo(); + } else if (isa<BasicBlock>(v) || isa<InlineAsm>(v)) { + ki->operands[j] = -1; + } else { + assert(isa<Constant>(v)); + Constant *c = cast<Constant>(v); + ki->operands[j] = -(km->getConstantID(c, ki) + 2); + } + } + + instructions[i++] = ki; + } + } +} + +KFunction::~KFunction() { + for (unsigned i=0; i<numInstructions; ++i) + delete instructions[i]; + delete[] instructions; +} diff --git a/lib/Module/LowerSwitch.cpp b/lib/Module/LowerSwitch.cpp new file mode 100644 index 00000000..a1b887f3 --- /dev/null +++ b/lib/Module/LowerSwitch.cpp @@ -0,0 +1,134 @@ +//===-- LowerSwitch.cpp - Eliminate Switch instructions -------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Derived from LowerSwitch.cpp in LLVM, heavily modified by piotrek +// to get rid of the binary search transform, as it was creating +// multiple paths through the program (i.e., extra paths that didn't +// exist in the original program). +// +//===----------------------------------------------------------------------===// + +#include "Passes.h" +#include <algorithm> + +using namespace llvm; + +namespace klee { + +char LowerSwitchPass::ID = 0; + +// The comparison function for sorting the switch case values in the vector. +struct SwitchCaseCmp { + bool operator () (const LowerSwitchPass::SwitchCase& C1, + const LowerSwitchPass::SwitchCase& C2) { + + const ConstantInt* CI1 = cast<const ConstantInt>(C1.value); + const ConstantInt* CI2 = cast<const ConstantInt>(C2.value); + return CI1->getValue().slt(CI2->getValue()); + } +}; + +bool LowerSwitchPass::runOnFunction(Function &F) { + bool changed = false; + + for (Function::iterator I = F.begin(), E = F.end(); I != E; ) { + BasicBlock *cur = I++; // Advance over block so we don't traverse new blocks + + if (SwitchInst *SI = dyn_cast<SwitchInst>(cur->getTerminator())) { + changed = true; + processSwitchInst(SI); + } + } + + return changed; +} + +// switchConvert - Convert the switch statement into a linear scan +// through all the case values +void LowerSwitchPass::switchConvert(CaseItr begin, CaseItr end, + Value* value, BasicBlock* origBlock, + BasicBlock* defaultBlock) +{ + BasicBlock *curHead = defaultBlock; + Function *F = origBlock->getParent(); + + // iterate through all the cases, creating a new BasicBlock for each + for (CaseItr it = begin; it < end; ++it) { + BasicBlock *newBlock = BasicBlock::Create("NodeBlock"); + Function::iterator FI = origBlock; + F->getBasicBlockList().insert(++FI, newBlock); + + ICmpInst *cmpInst = new ICmpInst(ICmpInst::ICMP_EQ, + value, + it->value, + "Case Comparison"); + + newBlock->getInstList().push_back(cmpInst); + BranchInst::Create(it->block, curHead, cmpInst, newBlock); + + // If there were any PHI nodes in this successor, rewrite one entry + // from origBlock to come from newBlock. + for (BasicBlock::iterator bi = it->block->begin(); isa<PHINode>(bi); ++bi) { + PHINode* PN = cast<PHINode>(bi); + + int blockIndex = PN->getBasicBlockIndex(origBlock); + assert(blockIndex != -1 && "Switch didn't go to this successor??"); + PN->setIncomingBlock((unsigned)blockIndex, newBlock); + } + + curHead = newBlock; + } + + // Branch to our shiny new if-then stuff... + BranchInst::Create(curHead, origBlock); +} + +// processSwitchInst - Replace the specified switch instruction with a sequence +// of chained if-then instructions. +// +void LowerSwitchPass::processSwitchInst(SwitchInst *SI) { + BasicBlock *origBlock = SI->getParent(); + BasicBlock *defaultBlock = SI->getDefaultDest(); + Function *F = origBlock->getParent(); + Value *switchValue = SI->getOperand(0); + + // Create a new, empty default block so that the new hierarchy of + // if-then statements go to this and the PHI nodes are happy. + BasicBlock* newDefault = BasicBlock::Create("newDefault"); + + F->getBasicBlockList().insert(defaultBlock, newDefault); + BranchInst::Create(defaultBlock, newDefault); + + // If there is an entry in any PHI nodes for the default edge, make sure + // to update them as well. + for (BasicBlock::iterator I = defaultBlock->begin(); isa<PHINode>(I); ++I) { + PHINode *PN = cast<PHINode>(I); + int BlockIdx = PN->getBasicBlockIndex(origBlock); + assert(BlockIdx != -1 && "Switch didn't go to this successor??"); + PN->setIncomingBlock((unsigned)BlockIdx, newDefault); + } + + CaseVector cases; + for (unsigned i = 1; i < SI->getNumSuccessors(); ++i) + cases.push_back(SwitchCase(SI->getSuccessorValue(i), + SI->getSuccessor(i))); + + // reverse cases, as switchConvert constructs a chain of + // basic blocks by appending to the front. if we reverse, + // the if comparisons will happen in the same order + // as the cases appear in the switch + std::reverse(cases.begin(), cases.end()); + + switchConvert(cases.begin(), cases.end(), switchValue, origBlock, newDefault); + + // We are now done with the switch instruction, so delete it + origBlock->getInstList().erase(SI); +} + +} diff --git a/lib/Module/Makefile b/lib/Module/Makefile new file mode 100755 index 00000000..bfd7c469 --- /dev/null +++ b/lib/Module/Makefile @@ -0,0 +1,16 @@ +#===-- lib/Module/Makefile ---------------------------------*- Makefile -*--===# +# +# The KLEE Symbolic Virtual Machine +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +LEVEL=../.. + +LIBRARYNAME=kleeModule +DONT_BUILD_RELINKED=1 +BUILD_ARCHIVE=1 + +include $(LEVEL)/Makefile.common diff --git a/lib/Module/ModuleUtil.cpp b/lib/Module/ModuleUtil.cpp new file mode 100644 index 00000000..d86b9d48 --- /dev/null +++ b/lib/Module/ModuleUtil.cpp @@ -0,0 +1,101 @@ +//===-- ModuleUtil.cpp ----------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Internal/Support/ModuleUtil.h" + +#include "llvm/Function.h" +#include "llvm/Instructions.h" +#include "llvm/IntrinsicInst.h" +#include "llvm/Linker.h" +#include "llvm/Module.h" +#include "llvm/Assembly/AsmAnnotationWriter.h" +#include "llvm/Support/CFG.h" +#include "llvm/Support/InstIterator.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Analysis/ValueTracking.h" + +#include <map> +#include <iostream> +#include <fstream> +#include <sstream> +#include <string> + +using namespace llvm; +using namespace klee; + +Module *klee::linkWithLibrary(Module *module, + const std::string &libraryName) { + try { + Linker linker("klee", module, false); + + llvm::sys::Path libraryPath(libraryName); + bool native = false; + + if (linker.LinkInFile(libraryPath, native)) { + assert(0 && "linking in library failed!"); + } + + return linker.releaseModule(); + } catch (...) { + assert(0 && "error during linking"); + } +} + +Function *klee::getDirectCallTarget(const Instruction *i) { + assert(isa<CallInst>(i) || isa<InvokeInst>(i)); + + Value *v = i->getOperand(0); + if (Function *f = dyn_cast<Function>(v)) { + return f; + } else if (llvm::ConstantExpr *ce = dyn_cast<llvm::ConstantExpr>(v)) { + if (ce->getOpcode()==Instruction::BitCast) + if (Function *f = dyn_cast<Function>(ce->getOperand(0))) + return f; + + // NOTE: This assert may fire, it isn't necessarily a problem and + // can be disabled, I just wanted to know when and if it happened. + assert(0 && "FIXME: Unresolved direct target for a constant expression."); + } + + return 0; +} + +static bool valueIsOnlyCalled(const Value *v) { + for (Value::use_const_iterator it = v->use_begin(), ie = v->use_end(); + it != ie; ++it) { + if (const Instruction *instr = dyn_cast<Instruction>(*it)) { + if (instr->getOpcode()==0) continue; // XXX function numbering inst + if (!isa<CallInst>(instr) && !isa<InvokeInst>(instr)) return false; + + // Make sure that the value is only the target of this call and + // not an argument. + for (unsigned i=1,e=instr->getNumOperands(); i!=e; ++i) + if (instr->getOperand(i)==v) + return false; + } else if (const llvm::ConstantExpr *ce = + dyn_cast<llvm::ConstantExpr>(*it)) { + if (ce->getOpcode()==Instruction::BitCast) + if (valueIsOnlyCalled(ce)) + continue; + return false; + } else if (const GlobalAlias *ga = dyn_cast<GlobalAlias>(*it)) { + // XXX what about v is bitcast of aliasee? + if (v==ga->getAliasee() && !valueIsOnlyCalled(ga)) + return false; + } else { + return false; + } + } + + return true; +} + +bool klee::functionEscapes(const Function *f) { + return !valueIsOnlyCalled(f); +} diff --git a/lib/Module/Optimize.cpp b/lib/Module/Optimize.cpp new file mode 100644 index 00000000..83e67292 --- /dev/null +++ b/lib/Module/Optimize.cpp @@ -0,0 +1,272 @@ +// FIXME: This file is a bastard child of opt.cpp and llvm-ld's +// Optimize.cpp. This stuff should live in common code. + + +//===- Optimize.cpp - Optimize a complete program -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements all optimization of the linked module for llvm-ld. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Module.h" +#include "llvm/PassManager.h" +#include "llvm/Analysis/Passes.h" +#include "llvm/Analysis/LoopPass.h" +#include "llvm/Analysis/Verifier.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/System/DynamicLibrary.h" +#include "llvm/Target/TargetData.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Support/PassNameParser.h" +#include "llvm/Support/PluginLoader.h" +#include <iostream> +using namespace llvm; + +#if 0 +// Pass Name Options as generated by the PassNameParser +static cl::list<const PassInfo*, bool, PassNameParser> + OptimizationList(cl::desc("Optimizations available:")); +#endif + +// Don't verify at the end +static cl::opt<bool> DontVerify("disable-verify", cl::ReallyHidden); + +static cl::opt<bool> DisableInline("disable-inlining", + cl::desc("Do not run the inliner pass")); + +static cl::opt<bool> +DisableOptimizations("disable-opt", + cl::desc("Do not run any optimization passes")); + +static cl::opt<bool> DisableInternalize("disable-internalize", + cl::desc("Do not mark all symbols as internal")); + +static cl::opt<bool> VerifyEach("verify-each", + cl::desc("Verify intermediate results of all passes")); + +static cl::alias ExportDynamic("export-dynamic", + cl::aliasopt(DisableInternalize), + cl::desc("Alias for -disable-internalize")); + +static cl::opt<bool> Strip("strip-all", + cl::desc("Strip all symbol info from executable")); + +static cl::alias A0("s", cl::desc("Alias for --strip-all"), + cl::aliasopt(Strip)); + +static cl::opt<bool> StripDebug("strip-debug", + cl::desc("Strip debugger symbol info from executable")); + +static cl::alias A1("S", cl::desc("Alias for --strip-debug"), + cl::aliasopt(StripDebug)); + +// A utility function that adds a pass to the pass manager but will also add +// a verifier pass after if we're supposed to verify. +static inline void addPass(PassManager &PM, Pass *P) { + // Add the pass to the pass manager... + PM.add(P); + + // If we are verifying all of the intermediate steps, add the verifier... + if (VerifyEach) + PM.add(createVerifierPass()); +} + +namespace llvm { + + +static void AddStandardCompilePasses(PassManager &PM) { + PM.add(createVerifierPass()); // Verify that input is correct + + addPass(PM, createLowerSetJmpPass()); // Lower llvm.setjmp/.longjmp + + // If the -strip-debug command line option was specified, do it. + if (StripDebug) + addPass(PM, createStripSymbolsPass(true)); + + if (DisableOptimizations) return; + + addPass(PM, createRaiseAllocationsPass()); // call %malloc -> malloc inst + addPass(PM, createCFGSimplificationPass()); // Clean up disgusting code + addPass(PM, createPromoteMemoryToRegisterPass());// Kill useless allocas + addPass(PM, createGlobalOptimizerPass()); // Optimize out global vars + addPass(PM, createGlobalDCEPass()); // Remove unused fns and globs + addPass(PM, createIPConstantPropagationPass());// IP Constant Propagation + addPass(PM, createDeadArgEliminationPass()); // Dead argument elimination + addPass(PM, createInstructionCombiningPass()); // Clean up after IPCP & DAE + addPass(PM, createCFGSimplificationPass()); // Clean up after IPCP & DAE + + addPass(PM, createPruneEHPass()); // Remove dead EH info + addPass(PM, createFunctionAttrsPass()); // Deduce function attrs + + if (!DisableInline) + addPass(PM, createFunctionInliningPass()); // Inline small functions + addPass(PM, createArgumentPromotionPass()); // Scalarize uninlined fn args + + addPass(PM, createSimplifyLibCallsPass()); // Library Call Optimizations + addPass(PM, createInstructionCombiningPass()); // Cleanup for scalarrepl. + addPass(PM, createJumpThreadingPass()); // Thread jumps. + addPass(PM, createCFGSimplificationPass()); // Merge & remove BBs + addPass(PM, createScalarReplAggregatesPass()); // Break up aggregate allocas + addPass(PM, createInstructionCombiningPass()); // Combine silly seq's + addPass(PM, createCondPropagationPass()); // Propagate conditionals + + addPass(PM, createTailCallEliminationPass()); // Eliminate tail calls + addPass(PM, createCFGSimplificationPass()); // Merge & remove BBs + addPass(PM, createReassociatePass()); // Reassociate expressions + addPass(PM, createLoopRotatePass()); + addPass(PM, createLICMPass()); // Hoist loop invariants + addPass(PM, createLoopUnswitchPass()); // Unswitch loops. + addPass(PM, createLoopIndexSplitPass()); // Index split loops. + // FIXME : Removing instcombine causes nestedloop regression. + addPass(PM, createInstructionCombiningPass()); + addPass(PM, createIndVarSimplifyPass()); // Canonicalize indvars + addPass(PM, createLoopDeletionPass()); // Delete dead loops + addPass(PM, createLoopUnrollPass()); // Unroll small loops + addPass(PM, createInstructionCombiningPass()); // Clean up after the unroller + addPass(PM, createGVNPass()); // Remove redundancies + addPass(PM, createMemCpyOptPass()); // Remove memcpy / form memset + addPass(PM, createSCCPPass()); // Constant prop with SCCP + + // Run instcombine after redundancy elimination to exploit opportunities + // opened up by them. + addPass(PM, createInstructionCombiningPass()); + addPass(PM, createCondPropagationPass()); // Propagate conditionals + + addPass(PM, createDeadStoreEliminationPass()); // Delete dead stores + addPass(PM, createAggressiveDCEPass()); // Delete dead instructions + addPass(PM, createCFGSimplificationPass()); // Merge & remove BBs + addPass(PM, createStripDeadPrototypesPass()); // Get rid of dead prototypes + addPass(PM, createDeadTypeEliminationPass()); // Eliminate dead types + addPass(PM, createConstantMergePass()); // Merge dup global constants +} + +/// Optimize - Perform link time optimizations. This will run the scalar +/// optimizations, any loaded plugin-optimization modules, and then the +/// inter-procedural optimizations if applicable. +void Optimize(Module* M) { + + // Instantiate the pass manager to organize the passes. + PassManager Passes; + + // If we're verifying, start off with a verification pass. + if (VerifyEach) + Passes.add(createVerifierPass()); + + // Add an appropriate TargetData instance for this module... + addPass(Passes, new TargetData(M)); + + // DWD - Run the opt standard pass list as well. + AddStandardCompilePasses(Passes); + + if (!DisableOptimizations) { + // Now that composite has been compiled, scan through the module, looking + // for a main function. If main is defined, mark all other functions + // internal. + if (!DisableInternalize) + addPass(Passes, createInternalizePass(true)); + + // Propagate constants at call sites into the functions they call. This + // opens opportunities for globalopt (and inlining) by substituting function + // pointers passed as arguments to direct uses of functions. + addPass(Passes, createIPSCCPPass()); + + // Now that we internalized some globals, see if we can hack on them! + addPass(Passes, createGlobalOptimizerPass()); + + // Linking modules together can lead to duplicated global constants, only + // keep one copy of each constant... + addPass(Passes, createConstantMergePass()); + + // Remove unused arguments from functions... + addPass(Passes, createDeadArgEliminationPass()); + + // Reduce the code after globalopt and ipsccp. Both can open up significant + // simplification opportunities, and both can propagate functions through + // function pointers. When this happens, we often have to resolve varargs + // calls, etc, so let instcombine do this. + addPass(Passes, createInstructionCombiningPass()); + + if (!DisableInline) + addPass(Passes, createFunctionInliningPass()); // Inline small functions + + addPass(Passes, createPruneEHPass()); // Remove dead EH info + addPass(Passes, createGlobalOptimizerPass()); // Optimize globals again. + addPass(Passes, createGlobalDCEPass()); // Remove dead functions + + // If we didn't decide to inline a function, check to see if we can + // transform it to pass arguments by value instead of by reference. + addPass(Passes, createArgumentPromotionPass()); + + // The IPO passes may leave cruft around. Clean up after them. + addPass(Passes, createInstructionCombiningPass()); + addPass(Passes, createJumpThreadingPass()); // Thread jumps. + addPass(Passes, createScalarReplAggregatesPass()); // Break up allocas + + // Run a few AA driven optimizations here and now, to cleanup the code. + addPass(Passes, createFunctionAttrsPass()); // Add nocapture + addPass(Passes, createGlobalsModRefPass()); // IP alias analysis + + addPass(Passes, createLICMPass()); // Hoist loop invariants + addPass(Passes, createGVNPass()); // Remove redundancies + addPass(Passes, createMemCpyOptPass()); // Remove dead memcpy's + addPass(Passes, createDeadStoreEliminationPass()); // Nuke dead stores + + // Cleanup and simplify the code after the scalar optimizations. + addPass(Passes, createInstructionCombiningPass()); + + addPass(Passes, createJumpThreadingPass()); // Thread jumps. + addPass(Passes, createPromoteMemoryToRegisterPass()); // Cleanup jumpthread. + + // Delete basic blocks, which optimization passes may have killed... + addPass(Passes, createCFGSimplificationPass()); + + // Now that we have optimized the program, discard unreachable functions... + addPass(Passes, createGlobalDCEPass()); + } + + // If the -s or -S command line options were specified, strip the symbols out + // of the resulting program to make it smaller. -s and -S are GNU ld options + // that we are supporting; they alias -strip-all and -strip-debug. + if (Strip || StripDebug) + addPass(Passes, createStripSymbolsPass(StripDebug && !Strip)); + +#if 0 + // Create a new optimization pass for each one specified on the command line + std::auto_ptr<TargetMachine> target; + for (unsigned i = 0; i < OptimizationList.size(); ++i) { + const PassInfo *Opt = OptimizationList[i]; + if (Opt->getNormalCtor()) + addPass(Passes, Opt->getNormalCtor()()); + else + std::cerr << "llvm-ld: cannot create pass: " << Opt->getPassName() + << "\n"; + } +#endif + + // The user's passes may leave cruft around. Clean up after them them but + // only if we haven't got DisableOptimizations set + if (!DisableOptimizations) { + addPass(Passes, createInstructionCombiningPass()); + addPass(Passes, createCFGSimplificationPass()); + addPass(Passes, createAggressiveDCEPass()); + addPass(Passes, createGlobalDCEPass()); + } + + // Make sure everything is still good. + if (!DontVerify) + Passes.add(createVerifierPass()); + + // Run our queue of passes all at once now, efficiently. + Passes.run(*M); +} + +} diff --git a/lib/Module/Passes.h b/lib/Module/Passes.h new file mode 100644 index 00000000..23205f75 --- /dev/null +++ b/lib/Module/Passes.h @@ -0,0 +1,132 @@ +//===-- Passes.h ------------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_PASSES_H +#define KLEE_PASSES_H + +#include "llvm/Constants.h" +#include "llvm/Instructions.h" +#include "llvm/Module.h" +#include "llvm/Pass.h" +#include "llvm/CodeGen/IntrinsicLowering.h" + +namespace llvm { + class Function; + class Instruction; + class Module; + class TargetData; + class Type; +} + +namespace klee { + + /// RaiseAsmPass - This pass raises some common occurences of inline + /// asm which are used by glibc into normal LLVM IR. +class RaiseAsmPass : public llvm::ModulePass { + static char ID; + + llvm::Function *getIntrinsic(llvm::Module &M, + unsigned IID, + const llvm::Type **Tys, + unsigned NumTys); + llvm::Function *getIntrinsic(llvm::Module &M, + unsigned IID, + const llvm::Type *Ty0) { + return getIntrinsic(M, IID, &Ty0, 1); + } + + bool runOnInstruction(llvm::Module &M, llvm::Instruction *I); + +public: + RaiseAsmPass() : llvm::ModulePass((intptr_t) &ID) {} + + virtual bool runOnModule(llvm::Module &M); +}; + + // This is a module pass because it can add and delete module + // variables (via intrinsic lowering). +class IntrinsicCleanerPass : public llvm::ModulePass { + static char ID; + llvm::IntrinsicLowering *IL; + bool LowerIntrinsics; + + bool runOnBasicBlock(llvm::BasicBlock &b); +public: + IntrinsicCleanerPass(const llvm::TargetData &TD, + bool LI=true) + : llvm::ModulePass((intptr_t) &ID), + IL(new llvm::IntrinsicLowering(TD)), + LowerIntrinsics(LI) {} + ~IntrinsicCleanerPass() { delete IL; } + + virtual bool runOnModule(llvm::Module &M); +}; + + // performs two transformations which make interpretation + // easier and faster. + // + // 1) Ensure that all the PHI nodes in a basic block have + // the incoming block list in the same order. Thus the + // incoming block index only needs to be computed once + // for each transfer. + // + // 2) Ensure that no PHI node result is used as an argument to + // a subsequent PHI node in the same basic block. This allows + // the transfer to execute the instructions in order instead + // of in two passes. +class PhiCleanerPass : public llvm::FunctionPass { + static char ID; + +public: + PhiCleanerPass() : llvm::FunctionPass((intptr_t) &ID) {} + + virtual bool runOnFunction(llvm::Function &f); +}; + +class DivCheckPass : public llvm::ModulePass { + static char ID; +public: + DivCheckPass(): ModulePass((intptr_t) &ID) {} + virtual bool runOnModule(llvm::Module &M); +}; + +/// LowerSwitchPass - Replace all SwitchInst instructions with chained branch +/// instructions. Note that this cannot be a BasicBlock pass because it +/// modifies the CFG! +class LowerSwitchPass : public llvm::FunctionPass { +public: + static char ID; // Pass identification, replacement for typeid + LowerSwitchPass() : FunctionPass((intptr_t) &ID) {} + + virtual bool runOnFunction(llvm::Function &F); + + struct SwitchCase { + llvm ::Constant *value; + llvm::BasicBlock *block; + + SwitchCase() : value(0), block(0) { } + SwitchCase(llvm::Constant *v, llvm::BasicBlock *b) : + value(v), block(b) { } + }; + + typedef std::vector<SwitchCase> CaseVector; + typedef std::vector<SwitchCase>::iterator CaseItr; + +private: + void processSwitchInst(llvm::SwitchInst *SI); + void switchConvert(CaseItr begin, + CaseItr end, + llvm::Value *value, + llvm::BasicBlock *origBlock, + llvm::BasicBlock *defaultBlock); +}; + +} + +#endif diff --git a/lib/Module/PhiCleaner.cpp b/lib/Module/PhiCleaner.cpp new file mode 100644 index 00000000..3d8d7867 --- /dev/null +++ b/lib/Module/PhiCleaner.cpp @@ -0,0 +1,83 @@ +//===-- PhiCleaner.cpp ----------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Passes.h" + +#include <set> + +using namespace llvm; + +char klee::PhiCleanerPass::ID = 0; + +bool klee::PhiCleanerPass::runOnFunction(Function &f) { + bool changed = false; + + for (Function::iterator b = f.begin(), be = f.end(); b != be; ++b) { + BasicBlock::iterator it = b->begin(); + + if (it->getOpcode() == Instruction::PHI) { + PHINode *reference = cast<PHINode>(it); + + std::set<Value*> phis; + phis.insert(reference); + + unsigned numBlocks = reference->getNumIncomingValues(); + for (++it; isa<PHINode>(*it); ++it) { + PHINode *pi = cast<PHINode>(it); + + assert(numBlocks == pi->getNumIncomingValues()); + + // see if it is out of order + unsigned i; + for (i=0; i<numBlocks; i++) + if (pi->getIncomingBlock(i) != reference->getIncomingBlock(i)) + break; + + if (i!=numBlocks) { + std::vector<Value*> values; + values.reserve(numBlocks); + for (unsigned i=0; i<numBlocks; i++) + values[i] = pi->getIncomingValueForBlock(reference->getIncomingBlock(i)); + for (unsigned i=0; i<numBlocks; i++) { + pi->setIncomingBlock(i, reference->getIncomingBlock(i)); + pi->setIncomingValue(i, values[i]); + } + changed = true; + } + + // see if it uses any previously defined phi nodes + for (i=0; i<numBlocks; i++) { + Value *value = pi->getIncomingValue(i); + + if (phis.find(value) != phis.end()) { + // fix by making a "move" at the end of the incoming block + // to a new temporary, which is thus known not to be a phi + // result. we could be somewhat more efficient about this + // by sharing temps and by reordering phi instructions so + // this isn't completely necessary, but in the end this is + // just a pathological case which does not occur very + // often. + Instruction *tmp = + new BitCastInst(value, + value->getType(), + value->getName() + ".phiclean", + pi->getIncomingBlock(i)->getTerminator()); + pi->setIncomingValue(i, tmp); + } + + changed = true; + } + + phis.insert(pi); + } + } + } + + return changed; +} diff --git a/lib/Module/RaiseAsm.cpp b/lib/Module/RaiseAsm.cpp new file mode 100644 index 00000000..67fbf8ae --- /dev/null +++ b/lib/Module/RaiseAsm.cpp @@ -0,0 +1,69 @@ +//===-- RaiseAsm.cpp ------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Passes.h" + +#include "llvm/InlineAsm.h" + +using namespace llvm; +using namespace klee; + +char RaiseAsmPass::ID = 0; + +Function *RaiseAsmPass::getIntrinsic(llvm::Module &M, + unsigned IID, + const Type **Tys, + unsigned NumTys) { + return Intrinsic::getDeclaration(&M, (llvm::Intrinsic::ID) IID, Tys, NumTys); +} + +// FIXME: This should just be implemented as a patch to +// X86TargetAsmInfo.cpp, then everyone will benefit. +bool RaiseAsmPass::runOnInstruction(Module &M, Instruction *I) { + if (CallInst *ci = dyn_cast<CallInst>(I)) { + if (InlineAsm *ia = dyn_cast<InlineAsm>(ci->getCalledValue())) { + const std::string &as = ia->getAsmString(); + const std::string &cs = ia->getConstraintString(); + const llvm::Type *T = ci->getType(); + + // bswaps + if (ci->getNumOperands() == 2 && + T == ci->getOperand(1)->getType() && + ((T == llvm::Type::Int16Ty && + as == "rorw $$8, ${0:w}" && + cs == "=r,0,~{dirflag},~{fpsr},~{flags},~{cc}") || + (T == llvm::Type::Int32Ty && + as == "rorw $$8, ${0:w};rorl $$16, $0;rorw $$8, ${0:w}" && + cs == "=r,0,~{dirflag},~{fpsr},~{flags},~{cc}"))) { + llvm::Value *Arg0 = ci->getOperand(1); + Function *F = getIntrinsic(M, Intrinsic::bswap, Arg0->getType()); + ci->setOperand(0, F); + return true; + } + } + } + + return false; +} + +bool RaiseAsmPass::runOnModule(Module &M) { + bool changed = false; + + for (Module::iterator fi = M.begin(), fe = M.end(); fi != fe; ++fi) { + for (Function::iterator bi = fi->begin(), be = fi->end(); bi != be; ++bi) { + for (BasicBlock::iterator ii = bi->begin(), ie = bi->end(); ii != ie;) { + Instruction *i = ii; + ++ii; + changed |= runOnInstruction(M, i); + } + } + } + + return changed; +} diff --git a/lib/README.txt b/lib/README.txt new file mode 100644 index 00000000..1750d900 --- /dev/null +++ b/lib/README.txt @@ -0,0 +1,18 @@ +The klee and kleaver code is organized as follows: + +lib/Basic - Low level support for both klee and kleaver which should + be independent of LLVM. + +lib/Support - Higher level support, but only used by klee. This can + use LLVM facilities. + +lib/Expr - The core kleaver expression library. + +lib/Solver - The kleaver solver library. + +lib/Module - klee facilities for working with LLVM modules, including + the shadow module/instruction structures we use during + execution. + +lib/Core - The core symbolic virtual machine. + diff --git a/lib/Solver/CachingSolver.cpp b/lib/Solver/CachingSolver.cpp new file mode 100644 index 00000000..517e133b --- /dev/null +++ b/lib/Solver/CachingSolver.cpp @@ -0,0 +1,241 @@ +//===-- CachingSolver.cpp - Caching expression solver ---------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "klee/Solver.h" + +#include "klee/Constraints.h" +#include "klee/Expr.h" +#include "klee/IncompleteSolver.h" +#include "klee/SolverImpl.h" + +#include "SolverStats.h" + +#include <tr1/unordered_map> + +using namespace klee; + +class CachingSolver : public SolverImpl { +private: + ref<Expr> canonicalizeQuery(ref<Expr> originalQuery, + bool &negationUsed); + + void cacheInsert(const Query& query, + IncompleteSolver::PartialValidity result); + + bool cacheLookup(const Query& query, + IncompleteSolver::PartialValidity &result); + + struct CacheEntry { + CacheEntry(const ConstraintManager &c, ref<Expr> q) + : constraints(c), query(q) {} + + CacheEntry(const CacheEntry &ce) + : constraints(ce.constraints), query(ce.query) {} + + ConstraintManager constraints; + ref<Expr> query; + + bool operator==(const CacheEntry &b) const { + return constraints==b.constraints && *query.get()==*b.query.get(); + } + }; + + struct CacheEntryHash { + unsigned operator()(const CacheEntry &ce) const { + unsigned result = ce.query.hash(); + + for (ConstraintManager::constraint_iterator it = ce.constraints.begin(); + it != ce.constraints.end(); ++it) + result ^= it->hash(); + + return result; + } + }; + + typedef std::tr1::unordered_map<CacheEntry, + IncompleteSolver::PartialValidity, + CacheEntryHash> cache_map; + + Solver *solver; + cache_map cache; + +public: + CachingSolver(Solver *s) : solver(s) {} + ~CachingSolver() { cache.clear(); delete solver; } + + bool computeValidity(const Query&, Solver::Validity &result); + bool computeTruth(const Query&, bool &isValid); + bool computeValue(const Query& query, ref<Expr> &result) { + return solver->impl->computeValue(query, result); + } + bool computeInitialValues(const Query& query, + const std::vector<const Array*> &objects, + std::vector< std::vector<unsigned char> > &values, + bool &hasSolution) { + return solver->impl->computeInitialValues(query, objects, values, + hasSolution); + } +}; + +/** @returns the canonical version of the given query. The reference + negationUsed is set to true if the original query was negated in + the canonicalization process. */ +ref<Expr> CachingSolver::canonicalizeQuery(ref<Expr> originalQuery, + bool &negationUsed) { + ref<Expr> negatedQuery = Expr::createNot(originalQuery); + + // select the "smaller" query to the be canonical representation + if (originalQuery.compare(negatedQuery) < 0) { + negationUsed = false; + return originalQuery; + } else { + negationUsed = true; + return negatedQuery; + } +} + +/** @returns true on a cache hit, false of a cache miss. Reference + value result only valid on a cache hit. */ +bool CachingSolver::cacheLookup(const Query& query, + IncompleteSolver::PartialValidity &result) { + bool negationUsed; + ref<Expr> canonicalQuery = canonicalizeQuery(query.expr, negationUsed); + + CacheEntry ce(query.constraints, canonicalQuery); + cache_map::iterator it = cache.find(ce); + + if (it != cache.end()) { + result = (negationUsed ? + IncompleteSolver::negatePartialValidity(it->second) : + it->second); + return true; + } + + return false; +} + +/// Inserts the given query, result pair into the cache. +void CachingSolver::cacheInsert(const Query& query, + IncompleteSolver::PartialValidity result) { + bool negationUsed; + ref<Expr> canonicalQuery = canonicalizeQuery(query.expr, negationUsed); + + CacheEntry ce(query.constraints, canonicalQuery); + IncompleteSolver::PartialValidity cachedResult = + (negationUsed ? IncompleteSolver::negatePartialValidity(result) : result); + + cache.insert(std::make_pair(ce, cachedResult)); +} + +bool CachingSolver::computeValidity(const Query& query, + Solver::Validity &result) { + IncompleteSolver::PartialValidity cachedResult; + bool tmp, cacheHit = cacheLookup(query, cachedResult); + + if (cacheHit) { + ++stats::queryCacheHits; + + switch(cachedResult) { + case IncompleteSolver::MustBeTrue: + result = Solver::True; + return true; + case IncompleteSolver::MustBeFalse: + result = Solver::False; + return true; + case IncompleteSolver::TrueOrFalse: + result = Solver::Unknown; + return true; + case IncompleteSolver::MayBeTrue: { + if (!solver->impl->computeTruth(query, tmp)) + return false; + if (tmp) { + cacheInsert(query, IncompleteSolver::MustBeTrue); + result = Solver::True; + return true; + } else { + cacheInsert(query, IncompleteSolver::TrueOrFalse); + result = Solver::Unknown; + return true; + } + } + case IncompleteSolver::MayBeFalse: { + if (!solver->impl->computeTruth(query.negateExpr(), tmp)) + return false; + if (tmp) { + cacheInsert(query, IncompleteSolver::MustBeFalse); + result = Solver::False; + return true; + } else { + cacheInsert(query, IncompleteSolver::TrueOrFalse); + result = Solver::Unknown; + return true; + } + } + default: assert(0 && "unreachable"); + } + } + + ++stats::queryCacheMisses; + + if (!solver->impl->computeValidity(query, result)) + return false; + + switch (result) { + case Solver::True: + cachedResult = IncompleteSolver::MustBeTrue; break; + case Solver::False: + cachedResult = IncompleteSolver::MustBeFalse; break; + default: + cachedResult = IncompleteSolver::TrueOrFalse; break; + } + + cacheInsert(query, cachedResult); + return true; +} + +bool CachingSolver::computeTruth(const Query& query, + bool &isValid) { + IncompleteSolver::PartialValidity cachedResult; + bool cacheHit = cacheLookup(query, cachedResult); + + // a cached result of MayBeTrue forces us to check whether + // a False assignment exists. + if (cacheHit && cachedResult != IncompleteSolver::MayBeTrue) { + ++stats::queryCacheHits; + isValid = (cachedResult == IncompleteSolver::MustBeTrue); + return true; + } + + ++stats::queryCacheMisses; + + // cache miss: query solver + if (!solver->impl->computeTruth(query, isValid)) + return false; + + if (isValid) { + cachedResult = IncompleteSolver::MustBeTrue; + } else if (cacheHit) { + // We know a true assignment exists, and query isn't valid, so + // must be TrueOrFalse. + assert(cachedResult == IncompleteSolver::MayBeTrue); + cachedResult = IncompleteSolver::TrueOrFalse; + } else { + cachedResult = IncompleteSolver::MayBeFalse; + } + + cacheInsert(query, cachedResult); + return true; +} + +/// + +Solver *klee::createCachingSolver(Solver *_solver) { + return new Solver(new CachingSolver(_solver)); +} diff --git a/lib/Solver/CexCachingSolver.cpp b/lib/Solver/CexCachingSolver.cpp new file mode 100644 index 00000000..79bc985d --- /dev/null +++ b/lib/Solver/CexCachingSolver.cpp @@ -0,0 +1,313 @@ +//===-- CexCachingSolver.cpp ----------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Solver.h" + +#include "klee/Constraints.h" +#include "klee/Expr.h" +#include "klee/SolverImpl.h" +#include "klee/TimerStatIncrementer.h" +#include "klee/util/Assignment.h" +#include "klee/util/ExprUtil.h" +#include "klee/util/ExprVisitor.h" +#include "klee/Internal/ADT/MapOfSets.h" + +#include "SolverStats.h" + +#include "llvm/Support/CommandLine.h" + +using namespace klee; +using namespace llvm; + +namespace { + cl::opt<bool> + DebugCexCacheCheckBinding("debug-cex-cache-check-binding"); + + cl::opt<bool> + CexCacheTryAll("cex-cache-try-all", + cl::desc("try substituting all counterexamples before asking STP"), + cl::init(false)); + + cl::opt<bool> + CexCacheExperimental("cex-cache-exp", cl::init(false)); + +} + +/// + +typedef std::set< ref<Expr> > KeyType; + +struct AssignmentLessThan { + bool operator()(const Assignment *a, const Assignment *b) { + return a->bindings < b->bindings; + } +}; + + +class CexCachingSolver : public SolverImpl { + typedef std::set<Assignment*, AssignmentLessThan> assignmentsTable_ty; + + Solver *solver; + + MapOfSets<ref<Expr>, Assignment*> cache; + // memo table + assignmentsTable_ty assignmentsTable; + + bool searchForAssignment(KeyType &key, + Assignment *&result); + + bool lookupAssignment(const Query& query, Assignment *&result); + + bool getAssignment(const Query& query, Assignment *&result); + +public: + CexCachingSolver(Solver *_solver) : solver(_solver) {} + ~CexCachingSolver(); + + bool computeTruth(const Query&, bool &isValid); + bool computeValidity(const Query&, Solver::Validity &result); + bool computeValue(const Query&, ref<Expr> &result); + bool computeInitialValues(const Query&, + const std::vector<const Array*> &objects, + std::vector< std::vector<unsigned char> > &values, + bool &hasSolution); +}; + +/// + +struct NullAssignment { + bool operator()(Assignment *a) const { return !a; } +}; + +struct NonNullAssignment { + bool operator()(Assignment *a) const { return a!=0; } +}; + +struct NullOrSatisfyingAssignment { + KeyType &key; + + NullOrSatisfyingAssignment(KeyType &_key) : key(_key) {} + + bool operator()(Assignment *a) const { + return !a || a->satisfies(key.begin(), key.end()); + } +}; + +bool CexCachingSolver::searchForAssignment(KeyType &key, Assignment *&result) { + Assignment * const *lookup = cache.lookup(key); + if (lookup) { + result = *lookup; + return true; + } + + if (CexCacheTryAll) { + Assignment **lookup = cache.findSuperset(key, NonNullAssignment()); + if (!lookup) lookup = cache.findSubset(key, NullAssignment()); + if (lookup) { + result = *lookup; + return true; + } + for (assignmentsTable_ty::iterator it = assignmentsTable.begin(), + ie = assignmentsTable.end(); it != ie; ++it) { + Assignment *a = *it; + if (a->satisfies(key.begin(), key.end())) { + result = a; + return true; + } + } + } else { + // XXX which order? one is sure to be better + Assignment **lookup = cache.findSuperset(key, NonNullAssignment()); + if (!lookup) lookup = cache.findSubset(key, NullOrSatisfyingAssignment(key)); + if (lookup) { + result = *lookup; + return true; + } + } + + return false; +} + +bool CexCachingSolver::lookupAssignment(const Query &query, + Assignment *&result) { + KeyType key(query.constraints.begin(), query.constraints.end()); + ref<Expr> neg = Expr::createNot(query.expr); + if (neg.isConstant()) { + if (!neg.getConstantValue()) { + result = (Assignment*) 0; + return true; + } + } else { + key.insert(neg); + } + + return searchForAssignment(key, result); +} + +bool CexCachingSolver::getAssignment(const Query& query, Assignment *&result) { + KeyType key(query.constraints.begin(), query.constraints.end()); + ref<Expr> neg = Expr::createNot(query.expr); + if (neg.isConstant()) { + if (!neg.getConstantValue()) { + result = (Assignment*) 0; + return true; + } + } else { + key.insert(neg); + } + + if (!searchForAssignment(key, result)) { + // need to solve + + std::vector<const Array*> objects; + findSymbolicObjects(key.begin(), key.end(), objects); + + std::vector< std::vector<unsigned char> > values; + bool hasSolution; + if (!solver->impl->computeInitialValues(query, objects, values, + hasSolution)) + return false; + + Assignment *binding; + if (hasSolution) { + binding = new Assignment(objects, values); + + // memoization + std::pair<assignmentsTable_ty::iterator, bool> + res = assignmentsTable.insert(binding); + if (!res.second) { + delete binding; + binding = *res.first; + } + + if (DebugCexCacheCheckBinding) + assert(binding->satisfies(key.begin(), key.end())); + } else { + binding = (Assignment*) 0; + } + + result = binding; + cache.insert(key, binding); + } + + return true; +} + +/// + +CexCachingSolver::~CexCachingSolver() { + cache.clear(); + delete solver; + for (assignmentsTable_ty::iterator it = assignmentsTable.begin(), + ie = assignmentsTable.end(); it != ie; ++it) + delete *it; +} + +bool CexCachingSolver::computeValidity(const Query& query, + Solver::Validity &result) { + TimerStatIncrementer t(stats::cexCacheTime); + Assignment *a; + if (!getAssignment(query.withFalse(), a)) + return false; + assert(a && "computeValidity() must have assignment"); + ref<Expr> q = a->evaluate(query.expr); + assert(q.isConstant() && "assignment evaluation did not result in constant"); + + if (q.getConstantValue()) { + if (!getAssignment(query, a)) + return false; + result = !a ? Solver::True : Solver::Unknown; + } else { + if (!getAssignment(query.negateExpr(), a)) + return false; + result = !a ? Solver::False : Solver::Unknown; + } + + return true; +} + +bool CexCachingSolver::computeTruth(const Query& query, + bool &isValid) { + TimerStatIncrementer t(stats::cexCacheTime); + + // There is a small amount of redundancy here. We only need to know + // truth and do not really need to compute an assignment. This means + // that we could check the cache to see if we already know that + // state ^ query has no assignment. In that case, by the validity of + // state, we know that state ^ !query must have an assignment, and + // so query cannot be true (valid). This does get hits, but doesn't + // really seem to be worth the overhead. + + if (CexCacheExperimental) { + Assignment *a; + if (lookupAssignment(query.negateExpr(), a) && !a) + return false; + } + + Assignment *a; + if (!getAssignment(query, a)) + return false; + + isValid = !a; + + return true; +} + +bool CexCachingSolver::computeValue(const Query& query, + ref<Expr> &result) { + TimerStatIncrementer t(stats::cexCacheTime); + + Assignment *a; + if (!getAssignment(query.withFalse(), a)) + return false; + assert(a && "computeValue() must have assignment"); + result = a->evaluate(query.expr); + assert(result.isConstant() && + "assignment evaluation did not result in constant"); + return true; +} + +bool +CexCachingSolver::computeInitialValues(const Query& query, + const std::vector<const Array*> + &objects, + std::vector< std::vector<unsigned char> > + &values, + bool &hasSolution) { + TimerStatIncrementer t(stats::cexCacheTime); + Assignment *a; + if (!getAssignment(query, a)) + return false; + hasSolution = !!a; + + if (!a) + return true; + + // FIXME: We should use smarter assignment for result so we don't + // need redundant copy. + values = std::vector< std::vector<unsigned char> >(objects.size()); + for (unsigned i=0; i < objects.size(); ++i) { + const Array *os = objects[i]; + Assignment::bindings_ty::iterator it = a->bindings.find(os); + + if (it == a->bindings.end()) { + values[i] = std::vector<unsigned char>(os->size, 0); + } else { + values[i] = it->second; + } + } + + return true; +} + +/// + +Solver *klee::createCexCachingSolver(Solver *_solver) { + return new Solver(new CexCachingSolver(_solver)); +} diff --git a/lib/Solver/ConstantDivision.cpp b/lib/Solver/ConstantDivision.cpp new file mode 100644 index 00000000..c8f8f3d5 --- /dev/null +++ b/lib/Solver/ConstantDivision.cpp @@ -0,0 +1,146 @@ +//===-- ConstantDivision.cpp ----------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ConstantDivision.h" + +#include "klee/util/Bits.h" + +#include <algorithm> +#include <cassert> + +namespace klee { + +/* Macros and functions which define the basic bit-level operations + * needed to implement quick division operations. + * + * Based on Hacker's Delight (2003) by Henry S. Warren, Jr. + */ + +/* 32 -- number of bits in the integer type on this architecture */ + +/* 2^32 -- NUM_BITS=32 requires 64 bits to represent this unsigned value */ +#define TWO_TO_THE_32_U64 (1ULL << 32) + +/* 2e31 -- NUM_BITS=32 requires 64 bits to represent this signed value */ +#define TWO_TO_THE_31_S64 (1LL << 31) + +/* ABS(x) -- positive x */ +#define ABS(x) ( ((x)>0)?x:-(x) ) /* fails if x is the min value of its type */ + +/* XSIGN(x) -- -1 if x<0 and 0 otherwise */ +#define XSIGN(x) ( (x) >> 31 ) + +/* LOG2_CEIL(x) -- logarithm base 2 of x, rounded up */ +#define LOG2_CEIL(x) ( 32 - ldz(x - 1) ) + +/* ones(x) -- counts the number of bits in x with the value 1 */ +static uint32_t ones( register uint32_t x ) { + x -= ((x >> 1) & 0x55555555); + x = (((x >> 2) & 0x33333333) + (x & 0x33333333)); + x = (((x >> 4) + x) & 0x0f0f0f0f); + x += (x >> 8); + x += (x >> 16); + + return( x & 0x0000003f ); +} + +/* ldz(x) -- counts the number of leading zeroes in a 32-bit word */ +static uint32_t ldz( register uint32_t x ) { + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + + return 32 - ones(x); +} + +/* exp_base_2(n) -- 2^n computed as an integer */ +static uint32_t exp_base_2( register int32_t n ) { + register uint32_t x = ~n & (n - 32); + x = x >> 31; + return( x << n ); +} + +// A simple algorithm: Iterate over all contiguous regions of 1 bits +// in x starting with the lowest bits. +// +// For a particular range where x is 1 for bits [low,high) then: +// 1) if the range is just one bit, simple add it +// 2) if the range is more than one bit, replace with an add +// of the high bit and a subtract of the low bit. we apply +// one useful optimization: if we were going to add the bit +// below the one we wish to subtract, we simply change that +// add to a subtract instead of subtracting the low bit itself. +// Obviously we must take care when high==64. +void ComputeMultConstants64(uint64_t multiplicand, + uint64_t &add, uint64_t &sub) { + uint64_t x = multiplicand; + add = sub = 0; + + while (x) { + // Determine rightmost contiguous region of 1s. + unsigned low = bits64::indexOfRightmostBit(x); + uint64_t lowbit = 1LL << low; + uint64_t p = x + lowbit; + uint64_t q = bits64::isolateRightmostBit(p); + unsigned high = q ? bits64::indexOfSingleBit(q) : 64; + + if (high==low+1) { // Just one bit... + add |= lowbit; + } else { + // Rewrite as +(1<<high) - (1<<low). + + // Optimize +(1<<x) - (1<<(x+1)) to -(1<<x). + if (low && (add & (lowbit>>1))) { + add ^= lowbit>>1; + sub ^= lowbit>>1; + } else { + sub |= lowbit; + } + + if (high!=64) + add |= 1LL << high; + } + + x = p ^ q; + } + + assert(multiplicand == add - sub); +} + +void ComputeUDivConstants32(uint32_t d, uint32_t &mprime, uint32_t &sh1, + uint32_t &sh2) { + int32_t l = LOG2_CEIL( d ); /* signed so l-1 => -1 when l=0 (see sh2) */ + uint32_t mid = exp_base_2(l) - d; + + mprime = (TWO_TO_THE_32_U64 * mid / d) + 1; + sh1 = std::min( l, 1 ); + sh2 = std::max( l-1, 0 ); +} + +void ComputeSDivConstants32(int32_t d, int32_t &mprime, int32_t &dsign, + int32_t &shpost ) { + uint64_t abs_d = ABS( (int64_t)d ); /* use 64-bits in case d is INT32_MIN */ + + /* LOG2_CEIL works on 32-bits, so we cast abs_d. The only possible value + * outside the 32-bit rep. is 2^31. This is special cased to save computer + * time since 64-bit routines would be overkill. */ + int32_t l = std::max( 1U, LOG2_CEIL((uint32_t)abs_d) ); + if( abs_d == TWO_TO_THE_31_S64 ) l = 31; + + uint32_t mid = exp_base_2( l - 1 ); + uint64_t m = TWO_TO_THE_32_U64 * mid / abs_d + 1ULL; + + mprime = m - TWO_TO_THE_32_U64; /* implicit cast to 32-bits signed */ + dsign = XSIGN( d ); + shpost = l - 1; +} + +} diff --git a/lib/Solver/ConstantDivision.h b/lib/Solver/ConstantDivision.h new file mode 100644 index 00000000..9e3e9c95 --- /dev/null +++ b/lib/Solver/ConstantDivision.h @@ -0,0 +1,51 @@ +//===-- ConstantDivision.h --------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __UTIL_CONSTANTDIVISION_H__ +#define __UTIL_CONSTANTDIVISION_H__ + +#include <stdint.h> + +namespace klee { + +/// ComputeMultConstants64 - Compute add and sub such that add-sub==x, +/// while attempting to minimize the number of bits in add and sub +/// combined. +void ComputeMultConstants64(uint64_t x, uint64_t &add_out, + uint64_t &sub_out); + +/// Compute the constants to perform a quicker equivalent of a division of some +/// 32-bit unsigned integer n by a known constant d (also a 32-bit unsigned +/// integer). The constants to compute n/d without explicit division will be +/// stored in mprime, sh1, and sh2 (unsigned 32-bit integers). +/// +/// @param d - denominator (divisor) +/// +/// @param [out] mprime +/// @param [out] sh1 +/// @param [out] sh2 +void ComputeUDivConstants32(uint32_t d, uint32_t &mprime, uint32_t &sh1, + uint32_t &sh2); + +/// Compute the constants to perform a quicker equivalent of a division of some +/// 32-bit signed integer n by a known constant d (also a 32-bit signed +/// integer). The constants to compute n/d without explicit division will be +/// stored in mprime, dsign, and shpost (signed 32-bit integers). +/// +/// @param d - denominator (divisor) +/// +/// @param [out] mprime +/// @param [out] dsign +/// @param [out] shpost +void ComputeSDivConstants32(int32_t d, int32_t &mprime, int32_t &dsign, + int32_t &shpost); + +} + +#endif diff --git a/lib/Solver/FastCexSolver.cpp b/lib/Solver/FastCexSolver.cpp new file mode 100644 index 00000000..d2bc27c6 --- /dev/null +++ b/lib/Solver/FastCexSolver.cpp @@ -0,0 +1,959 @@ +//===-- FastCexSolver.cpp -------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Solver.h" + +#include "klee/Constraints.h" +#include "klee/Expr.h" +#include "klee/IncompleteSolver.h" +#include "klee/util/ExprEvaluator.h" +#include "klee/util/ExprRangeEvaluator.h" +#include "klee/util/ExprVisitor.h" +// FIXME: Use APInt. +#include "klee/Internal/Support/IntEvaluation.h" + +#include <iostream> +#include <sstream> +#include <cassert> +#include <map> +#include <vector> + +using namespace klee; + +/***/ + +//#define LOG +#ifdef LOG +std::ostream *theLog; +#endif + + // Hacker's Delight, pgs 58-63 +static uint64_t minOR(uint64_t a, uint64_t b, + uint64_t c, uint64_t d) { + uint64_t temp, m = ((uint64_t) 1)<<63; + while (m) { + if (~a & c & m) { + temp = (a | m) & -m; + if (temp <= b) { a = temp; break; } + } else if (a & ~c & m) { + temp = (c | m) & -m; + if (temp <= d) { c = temp; break; } + } + m >>= 1; + } + + return a | c; +} +static uint64_t maxOR(uint64_t a, uint64_t b, + uint64_t c, uint64_t d) { + uint64_t temp, m = ((uint64_t) 1)<<63; + + while (m) { + if (b & d & m) { + temp = (b - m) | (m - 1); + if (temp >= a) { b = temp; break; } + temp = (d - m) | (m -1); + if (temp >= c) { d = temp; break; } + } + m >>= 1; + } + + return b | d; +} +static uint64_t minAND(uint64_t a, uint64_t b, + uint64_t c, uint64_t d) { + uint64_t temp, m = ((uint64_t) 1)<<63; + while (m) { + if (~a & ~c & m) { + temp = (a | m) & -m; + if (temp <= b) { a = temp; break; } + temp = (c | m) & -m; + if (temp <= d) { c = temp; break; } + } + m >>= 1; + } + + return a & c; +} +static uint64_t maxAND(uint64_t a, uint64_t b, + uint64_t c, uint64_t d) { + uint64_t temp, m = ((uint64_t) 1)<<63; + while (m) { + if (b & ~d & m) { + temp = (b & ~m) | (m - 1); + if (temp >= a) { b = temp; break; } + } else if (~b & d & m) { + temp = (d & ~m) | (m - 1); + if (temp >= c) { d = temp; break; } + } + m >>= 1; + } + + return b & d; +} + +/// + +class ValueRange { +private: + uint64_t m_min, m_max; + +public: + ValueRange() : m_min(1),m_max(0) {} + ValueRange(uint64_t value) : m_min(value), m_max(value) {} + ValueRange(uint64_t _min, uint64_t _max) : m_min(_min), m_max(_max) {} + ValueRange(const ValueRange &b) : m_min(b.m_min), m_max(b.m_max) {} + + void print(std::ostream &os) const { + if (isFixed()) { + os << m_min; + } else { + os << "[" << m_min << "," << m_max << "]"; + } + } + + bool isEmpty() const { + return m_min>m_max; + } + bool contains(uint64_t value) const { + return this->intersects(ValueRange(value)); + } + bool intersects(const ValueRange &b) const { + return !this->set_intersection(b).isEmpty(); + } + + bool isFullRange(unsigned bits) { + return m_min==0 && m_max==bits64::maxValueOfNBits(bits); + } + + ValueRange set_intersection(const ValueRange &b) const { + return ValueRange(std::max(m_min,b.m_min), std::min(m_max,b.m_max)); + } + ValueRange set_union(const ValueRange &b) const { + return ValueRange(std::min(m_min,b.m_min), std::max(m_max,b.m_max)); + } + ValueRange set_difference(const ValueRange &b) const { + if (b.isEmpty() || b.m_min > m_max || b.m_max < m_min) { // no intersection + return *this; + } else if (b.m_min <= m_min && b.m_max >= m_max) { // empty + return ValueRange(1,0); + } else if (b.m_min <= m_min) { // one range out + // cannot overflow because b.m_max < m_max + return ValueRange(b.m_max+1, m_max); + } else if (b.m_max >= m_max) { + // cannot overflow because b.min > m_min + return ValueRange(m_min, b.m_min-1); + } else { + // two ranges, take bottom + return ValueRange(m_min, b.m_min-1); + } + } + ValueRange binaryAnd(const ValueRange &b) const { + // XXX + assert(!isEmpty() && !b.isEmpty() && "XXX"); + if (isFixed() && b.isFixed()) { + return ValueRange(m_min & b.m_min); + } else { + return ValueRange(minAND(m_min, m_max, b.m_min, b.m_max), + maxAND(m_min, m_max, b.m_min, b.m_max)); + } + } + ValueRange binaryAnd(uint64_t b) const { return binaryAnd(ValueRange(b)); } + ValueRange binaryOr(ValueRange b) const { + // XXX + assert(!isEmpty() && !b.isEmpty() && "XXX"); + if (isFixed() && b.isFixed()) { + return ValueRange(m_min | b.m_min); + } else { + return ValueRange(minOR(m_min, m_max, b.m_min, b.m_max), + maxOR(m_min, m_max, b.m_min, b.m_max)); + } + } + ValueRange binaryOr(uint64_t b) const { return binaryOr(ValueRange(b)); } + ValueRange binaryXor(ValueRange b) const { + if (isFixed() && b.isFixed()) { + return ValueRange(m_min ^ b.m_min); + } else { + uint64_t t = m_max | b.m_max; + while (!bits64::isPowerOfTwo(t)) + t = bits64::withoutRightmostBit(t); + return ValueRange(0, (t<<1)-1); + } + } + + ValueRange binaryShiftLeft(unsigned bits) const { + return ValueRange(m_min<<bits, m_max<<bits); + } + ValueRange binaryShiftRight(unsigned bits) const { + return ValueRange(m_min>>bits, m_max>>bits); + } + + ValueRange concat(const ValueRange &b, unsigned bits) const { + return binaryShiftLeft(bits).binaryOr(b); + } + ValueRange extract(uint64_t lowBit, uint64_t maxBit) const { + return binaryShiftRight(lowBit).binaryAnd(bits64::maxValueOfNBits(maxBit-lowBit)); + } + + ValueRange add(const ValueRange &b, unsigned width) const { + return ValueRange(0, bits64::maxValueOfNBits(width)); + } + ValueRange sub(const ValueRange &b, unsigned width) const { + return ValueRange(0, bits64::maxValueOfNBits(width)); + } + ValueRange mul(const ValueRange &b, unsigned width) const { + return ValueRange(0, bits64::maxValueOfNBits(width)); + } + ValueRange udiv(const ValueRange &b, unsigned width) const { + return ValueRange(0, bits64::maxValueOfNBits(width)); + } + ValueRange sdiv(const ValueRange &b, unsigned width) const { + return ValueRange(0, bits64::maxValueOfNBits(width)); + } + ValueRange urem(const ValueRange &b, unsigned width) const { + return ValueRange(0, bits64::maxValueOfNBits(width)); + } + ValueRange srem(const ValueRange &b, unsigned width) const { + return ValueRange(0, bits64::maxValueOfNBits(width)); + } + + // use min() to get value if true (XXX should we add a method to + // make code clearer?) + bool isFixed() const { return m_min==m_max; } + + bool operator==(const ValueRange &b) const { return m_min==b.m_min && m_max==b.m_max; } + bool operator!=(const ValueRange &b) const { return !(*this==b); } + + bool mustEqual(const uint64_t b) const { return m_min==m_max && m_min==b; } + bool mayEqual(const uint64_t b) const { return m_min<=b && m_max>=b; } + + bool mustEqual(const ValueRange &b) const { return isFixed() && b.isFixed() && m_min==b.m_min; } + bool mayEqual(const ValueRange &b) const { return this->intersects(b); } + + uint64_t min() const { + assert(!isEmpty() && "cannot get minimum of empty range"); + return m_min; + } + + uint64_t max() const { + assert(!isEmpty() && "cannot get maximum of empty range"); + return m_max; + } + + int64_t minSigned(unsigned bits) const { + assert((m_min>>bits)==0 && (m_max>>bits)==0 && + "range is outside given number of bits"); + + // if max allows sign bit to be set then it can be smallest value, + // otherwise since the range is not empty, min cannot have a sign + // bit + + uint64_t smallest = ((uint64_t) 1 << (bits-1)); + if (m_max >= smallest) { + return ints::sext(smallest, 64, bits); + } else { + return m_min; + } + } + + int64_t maxSigned(unsigned bits) const { + assert((m_min>>bits)==0 && (m_max>>bits)==0 && + "range is outside given number of bits"); + + uint64_t smallest = ((uint64_t) 1 << (bits-1)); + + // if max and min have sign bit then max is max, otherwise if only + // max has sign bit then max is largest signed integer, otherwise + // max is max + + if (m_min < smallest && m_max >= smallest) { + return smallest - 1; + } else { + return ints::sext(m_max, 64, bits); + } + } +}; + +inline std::ostream &operator<<(std::ostream &os, const ValueRange &vr) { + vr.print(os); + return os; +} + +// used to find all memory object ids and the maximum size of any +// object state that references them (for symbolic size). +class ObjectFinder : public ExprVisitor { +protected: + Action visitRead(const ReadExpr &re) { + addUpdates(re.updates); + return Action::doChildren(); + } + + // XXX nice if this information was cached somewhere, used by + // independence as well right? + void addUpdates(const UpdateList &ul) { + for (const UpdateNode *un=ul.head; un; un=un->next) { + visit(un->index); + visit(un->value); + } + + addObject(*ul.root); + } + +public: + void addObject(const Array& array) { + unsigned id = array.id; + std::map<unsigned,unsigned>::iterator it = results.find(id); + + // FIXME: Not 64-bit size clean. + if (it == results.end()) { + results[id] = (unsigned) array.size; + } else { + it->second = std::max(it->second, (unsigned) array.size); + } + } + +public: + std::map<unsigned, unsigned> results; +}; + +// XXX waste of space, rather have ByteValueRange +typedef ValueRange CexValueData; + +class CexObjectData { +public: + unsigned size; + CexValueData *values; + +public: + CexObjectData(unsigned _size) : size(_size), values(new CexValueData[size]) { + for (unsigned i=0; i<size; i++) + values[i] = ValueRange(0, 255); + } +}; + +class CexRangeEvaluator : public ExprRangeEvaluator<ValueRange> { +public: + std::map<unsigned, CexObjectData> &objectValues; + CexRangeEvaluator(std::map<unsigned, CexObjectData> &_objectValues) + : objectValues(_objectValues) {} + + ValueRange getInitialReadRange(const Array &os, ValueRange index) { + return ValueRange(0, 255); + } +}; + +class CexConstifier : public ExprEvaluator { +protected: + ref<Expr> getInitialValue(const Array& array, unsigned index) { + std::map<unsigned, CexObjectData>::iterator it = + objectValues.find(array.id); + assert(it != objectValues.end() && "missing object?"); + CexObjectData &cod = it->second; + + if (index >= cod.size) { + return ReadExpr::create(UpdateList(&array, true, 0), + ref<Expr>(index, Expr::Int32)); + } else { + CexValueData &cvd = cod.values[index]; + assert(cvd.min() == cvd.max() && "value is not fixed"); + return ref<Expr>(cvd.min(), Expr::Int8); + } + } + +public: + std::map<unsigned, CexObjectData> &objectValues; + CexConstifier(std::map<unsigned, CexObjectData> &_objectValues) + : objectValues(_objectValues) {} +}; + +class CexData { +public: + std::map<unsigned, CexObjectData> objectValues; + +public: + CexData(ObjectFinder &finder) { + for (std::map<unsigned,unsigned>::iterator it = finder.results.begin(), + ie = finder.results.end(); it != ie; ++it) { + objectValues.insert(std::pair<unsigned, CexObjectData>(it->first, + CexObjectData(it->second))); + } + } + ~CexData() { + for (std::map<unsigned, CexObjectData>::iterator it = objectValues.begin(), + ie = objectValues.end(); it != ie; ++it) + delete[] it->second.values; + } + + void forceExprToValue(ref<Expr> e, uint64_t value) { + forceExprToRange(e, CexValueData(value,value)); + } + + void forceExprToRange(ref<Expr> e, CexValueData range) { +#ifdef LOG + // *theLog << "force: " << e << " to " << range << "\n"; +#endif + switch (e.getKind()) { + case Expr::Constant: { + // rather a pity if the constant isn't in the range, but how can + // we use this? + break; + } + + // Special + + case Expr::NotOptimized: break; + + case Expr::Read: { + ReadExpr *re = static_ref_cast<ReadExpr>(e); + const Array *array = re->updates.root; + CexObjectData &cod = objectValues.find(array->id)->second; + + // XXX we need to respect the version here and object state chain + + if (re->index.isConstant() && + re->index.getConstantValue() < array->size) { + CexValueData &cvd = cod.values[re->index.getConstantValue()]; + CexValueData tmp = cvd.set_intersection(range); + + if (tmp.isEmpty()) { + if (range.isFixed()) // ranges conflict, if new one is fixed use that + cvd = range; + } else { + cvd = tmp; + } + } else { + // XXX fatal("XXX not implemented"); + } + + break; + } + + case Expr::Select: { + SelectExpr *se = static_ref_cast<SelectExpr>(e); + ValueRange cond = evalRangeForExpr(se->cond); + if (cond.isFixed()) { + if (cond.min()) { + forceExprToRange(se->trueExpr, range); + } else { + forceExprToRange(se->falseExpr, range); + } + } else { + // XXX imprecise... we have a choice here. One method is to + // simply force both sides into the specified range (since the + // condition is indetermined). This may lose in two ways, the + // first is that the condition chosen may limit further + // restrict the range in each of the children, however this is + // less of a problem as the range will be a superset of legal + // values. The other is if the condition ends up being forced + // by some other constraints, then we needlessly forced one + // side into the given range. + // + // The other method would be to force the condition to one + // side and force that side into the given range. This loses + // when we force the condition to an unsatisfiable value + // (either because the condition cannot be that, or the + // resulting range given that condition is not in the required + // range). + // + // Currently we just force both into the range. A hybrid would + // be to evaluate the ranges for each of the children... if + // one of the ranges happens to already be a subset of the + // required range then it may be preferable to force the + // condition to that side. + forceExprToRange(se->trueExpr, range); + forceExprToRange(se->falseExpr, range); + } + break; + } + + // XXX imprecise... the problem here is that extracting bits + // loses information about what bits are connected across the + // bytes. if a value can be 1 or 256 then either the top or + // lower byte is 0, but just extraction loses this information + // and will allow neither,one,or both to be 1. + // + // we can protect against this in a limited fashion by writing + // the extraction a byte at a time, then checking the evaluated + // value, isolating for that range, and continuing. + case Expr::Concat: { + ConcatExpr *ce = static_ref_cast<ConcatExpr>(e); + if (ce->is2ByteConcat()) { + forceExprToRange(ce->getKid(0), range.extract( 8, 16)); + forceExprToRange(ce->getKid(1), range.extract( 0, 8)); + } + else if (ce->is4ByteConcat()) { + forceExprToRange(ce->getKid(0), range.extract(24, 32)); + forceExprToRange(ce->getKid(1), range.extract(16, 24)); + forceExprToRange(ce->getKid(2), range.extract( 8, 16)); + forceExprToRange(ce->getKid(3), range.extract( 0, 8)); + } + else if (ce->is8ByteConcat()) { + forceExprToRange(ce->getKid(0), range.extract(56, 64)); + forceExprToRange(ce->getKid(1), range.extract(48, 56)); + forceExprToRange(ce->getKid(2), range.extract(40, 48)); + forceExprToRange(ce->getKid(3), range.extract(32, 40)); + forceExprToRange(ce->getKid(4), range.extract(24, 32)); + forceExprToRange(ce->getKid(5), range.extract(16, 24)); + forceExprToRange(ce->getKid(6), range.extract( 8, 16)); + forceExprToRange(ce->getKid(7), range.extract( 0, 8)); + } + + break; + } + + case Expr::Extract: { + // XXX + break; + } + + // Casting + + // Simply intersect the output range with the range of all + // possible outputs and then truncate to the desired number of + // bits. + + // For ZExt this simplifies to just intersection with the + // possible input range. + case Expr::ZExt: { + CastExpr *ce = static_ref_cast<CastExpr>(e); + unsigned inBits = ce->src.getWidth(); + ValueRange input = range.set_intersection(ValueRange(0, bits64::maxValueOfNBits(inBits))); + forceExprToRange(ce->src, input); + break; + } + // For SExt instead of doing the intersection we just take the output range + // minus the impossible values. This is nicer since it is a single interval. + case Expr::SExt: { + CastExpr *ce = static_ref_cast<CastExpr>(e); + unsigned inBits = ce->src.getWidth(); + unsigned outBits = ce->width; + ValueRange output = range.set_difference(ValueRange(1<<(inBits-1), + (bits64::maxValueOfNBits(outBits)- + bits64::maxValueOfNBits(inBits-1)-1))); + ValueRange input = output.binaryAnd(bits64::maxValueOfNBits(inBits)); + forceExprToRange(ce->src, input); + break; + } + + // Binary + + case Expr::And: { + BinaryExpr *be = static_ref_cast<BinaryExpr>(e); + if (be->getWidth()==Expr::Bool) { + if (range.isFixed()) { + ValueRange left = evalRangeForExpr(be->left); + ValueRange right = evalRangeForExpr(be->right); + + if (!range.min()) { + if (left.mustEqual(0) || right.mustEqual(0)) { + // all is well + } else { + // XXX heuristic, which order + + forceExprToValue(be->left, 0); + left = evalRangeForExpr(be->left); + + // see if that worked + if (!left.mustEqual(1)) + forceExprToValue(be->right, 0); + } + } else { + if (!left.mustEqual(1)) forceExprToValue(be->left, 1); + if (!right.mustEqual(1)) forceExprToValue(be->right, 1); + } + } + } else { + // XXX + } + break; + } + + case Expr::Or: { + BinaryExpr *be = static_ref_cast<BinaryExpr>(e); + if (be->getWidth()==Expr::Bool) { + if (range.isFixed()) { + ValueRange left = evalRangeForExpr(be->left); + ValueRange right = evalRangeForExpr(be->right); + + if (range.min()) { + if (left.mustEqual(1) || right.mustEqual(1)) { + // all is well + } else { + // XXX heuristic, which order? + + // force left to value we need + forceExprToValue(be->left, 1); + left = evalRangeForExpr(be->left); + + // see if that worked + if (!left.mustEqual(1)) + forceExprToValue(be->right, 1); + } + } else { + if (!left.mustEqual(0)) forceExprToValue(be->left, 0); + if (!right.mustEqual(0)) forceExprToValue(be->right, 0); + } + } + } else { + // XXX + } + break; + } + + case Expr::Xor: break; + + // Comparison + + case Expr::Eq: { + BinaryExpr *be = static_ref_cast<BinaryExpr>(e); + if (range.isFixed()) { + if (be->left.isConstant()) { + uint64_t value = be->left.getConstantValue(); + if (range.min()) { + forceExprToValue(be->right, value); + } else { + if (value==0) { + forceExprToRange(be->right, + CexValueData(1, + ints::sext(1, + be->right.getWidth(), + 1))); + } else { + // XXX heuristic / lossy, could be better to pick larger range? + forceExprToRange(be->right, CexValueData(0, value-1)); + } + } + } else { + // XXX what now + } + } + break; + } + + case Expr::Ult: { + BinaryExpr *be = static_ref_cast<BinaryExpr>(e); + + // XXX heuristic / lossy, what order if conflict + + if (range.isFixed()) { + ValueRange left = evalRangeForExpr(be->left); + ValueRange right = evalRangeForExpr(be->right); + + uint64_t maxValue = bits64::maxValueOfNBits(be->right.getWidth()); + + // XXX should deal with overflow (can lead to empty range) + + if (left.isFixed()) { + if (range.min()) { + forceExprToRange(be->right, CexValueData(left.min()+1, maxValue)); + } else { + forceExprToRange(be->right, CexValueData(0, left.min())); + } + } else if (right.isFixed()) { + if (range.min()) { + forceExprToRange(be->left, CexValueData(0, right.min()-1)); + } else { + forceExprToRange(be->left, CexValueData(right.min(), maxValue)); + } + } else { + // XXX ??? + } + } + break; + } + case Expr::Ule: { + BinaryExpr *be = static_ref_cast<BinaryExpr>(e); + + // XXX heuristic / lossy, what order if conflict + + if (range.isFixed()) { + ValueRange left = evalRangeForExpr(be->left); + ValueRange right = evalRangeForExpr(be->right); + + // XXX should deal with overflow (can lead to empty range) + + uint64_t maxValue = bits64::maxValueOfNBits(be->right.getWidth()); + if (left.isFixed()) { + if (range.min()) { + forceExprToRange(be->right, CexValueData(left.min(), maxValue)); + } else { + forceExprToRange(be->right, CexValueData(0, left.min()-1)); + } + } else if (right.isFixed()) { + if (range.min()) { + forceExprToRange(be->left, CexValueData(0, right.min())); + } else { + forceExprToRange(be->left, CexValueData(right.min()+1, maxValue)); + } + } else { + // XXX ??? + } + } + break; + } + + case Expr::Ne: + case Expr::Ugt: + case Expr::Uge: + case Expr::Sgt: + case Expr::Sge: + assert(0 && "invalid expressions (uncanonicalized"); + + default: + break; + } + } + + void fixValues() { + for (std::map<unsigned, CexObjectData>::iterator it = objectValues.begin(), + ie = objectValues.end(); it != ie; ++it) { + CexObjectData &cod = it->second; + for (unsigned i=0; i<cod.size; i++) { + CexValueData &cvd = cod.values[i]; + cvd = CexValueData(cvd.min() + (cvd.max()-cvd.min())/2); + } + } + } + + ValueRange evalRangeForExpr(ref<Expr> &e) { + CexRangeEvaluator ce(objectValues); + return ce.evaluate(e); + } + + bool exprMustBeValue(ref<Expr> e, uint64_t value) { + CexConstifier cc(objectValues); + ref<Expr> v = cc.visit(e); + if (!v.isConstant()) return false; + // XXX reenable once all reads and vars are fixed + // assert(v.isConstant() && "not all values have been fixed"); + return v.getConstantValue()==value; + } +}; + +/* *** */ + + +class FastCexSolver : public IncompleteSolver { +public: + FastCexSolver(); + ~FastCexSolver(); + + IncompleteSolver::PartialValidity computeTruth(const Query&); + bool computeValue(const Query&, ref<Expr> &result); + bool computeInitialValues(const Query&, + const std::vector<const Array*> &objects, + std::vector< std::vector<unsigned char> > &values, + bool &hasSolution); +}; + +FastCexSolver::FastCexSolver() { } + +FastCexSolver::~FastCexSolver() { } + +IncompleteSolver::PartialValidity +FastCexSolver::computeTruth(const Query& query) { +#ifdef LOG + std::ostringstream log; + theLog = &log; + // log << "------ start FastCexSolver::mustBeTrue ------\n"; + log << "-- QUERY --\n"; + unsigned i=0; + for (ConstraintManager::const_iterator it = query.constraints.begin(), + ie = query.constraints.end(); it != ie; ++it) + log << " C" << i++ << ": " << *it << ", \n"; + log << " Q : " << query.expr << "\n"; +#endif + + ObjectFinder of; + for (ConstraintManager::const_iterator it = query.constraints.begin(), + ie = query.constraints.end(); it != ie; ++it) + of.visit(*it); + of.visit(query.expr); + CexData cd(of); + + for (ConstraintManager::const_iterator it = query.constraints.begin(), + ie = query.constraints.end(); it != ie; ++it) + cd.forceExprToValue(*it, 1); + cd.forceExprToValue(query.expr, 0); + +#ifdef LOG + log << " -- ranges --\n"; + for (std::map<unsigned, CexObjectData>::iterator it = objectValues.begin(), + ie = objectValues.end(); it != ie; ++it) { + CexObjectData &cod = it->second; + log << " arr" << it->first << "[" << cod.size << "] = ["; + unsigned continueFrom=cod.size-1; + for (; continueFrom>0; continueFrom--) + if (cod.values[continueFrom-1]!=cod.values[continueFrom]) + break; + for (unsigned i=0; i<cod.size; i++) { + log << cod.values[i]; + if (i<cod.size-1) { + log << ", "; + if (i==continueFrom) { + log << "..."; + break; + } + } + } + log << "]\n"; + } +#endif + + // this could be done lazily of course + cd.fixValues(); + +#ifdef LOG + log << " -- fixed values --\n"; + for (std::map<unsigned, CexObjectData>::iterator it = objectValues.begin(), + ie = objectValues.end(); it != ie; ++it) { + CexObjectData &cod = it->second; + log << " arr" << it->first << "[" << cod.size << "] = ["; + unsigned continueFrom=cod.size-1; + for (; continueFrom>0; continueFrom--) + if (cod.values[continueFrom-1]!=cod.values[continueFrom]) + break; + for (unsigned i=0; i<cod.size; i++) { + log << cod.values[i]; + if (i<cod.size-1) { + log << ", "; + if (i==continueFrom) { + log << "..."; + break; + } + } + } + log << "]\n"; + } +#endif + + // check the result + + bool isGood = true; + + if (!cd.exprMustBeValue(query.expr, 0)) isGood = false; + + for (ConstraintManager::const_iterator it = query.constraints.begin(), + ie = query.constraints.end(); it != ie; ++it) + if (!cd.exprMustBeValue(*it, 1)) + isGood = false; + +#ifdef LOG + log << " -- evaluating result --\n"; + + i=0; + for (ConstraintManager::const_iterator it = query.constraints.begin(), + ie = query.constraints.end(); it != ie; ++it) { + bool res = cd.exprMustBeValue(*it, 1); + log << " C" << i++ << ": " << (res?"true":"false") << "\n"; + } + log << " Q : " + << (cd.exprMustBeValue(query.expr, 0)?"true":"false") << "\n"; + + log << "\n\n"; +#endif + + return isGood ? IncompleteSolver::MayBeFalse : IncompleteSolver::None; +} + +bool FastCexSolver::computeValue(const Query& query, ref<Expr> &result) { + ObjectFinder of; + for (ConstraintManager::const_iterator it = query.constraints.begin(), + ie = query.constraints.end(); it != ie; ++it) + of.visit(*it); + of.visit(query.expr); + CexData cd(of); + + for (ConstraintManager::const_iterator it = query.constraints.begin(), + ie = query.constraints.end(); it != ie; ++it) + cd.forceExprToValue(*it, 1); + + // this could be done lazily of course + cd.fixValues(); + + // check the result + for (ConstraintManager::const_iterator it = query.constraints.begin(), + ie = query.constraints.end(); it != ie; ++it) + if (!cd.exprMustBeValue(*it, 1)) + return false; + + CexConstifier cc(cd.objectValues); + ref<Expr> value = cc.visit(query.expr); + + if (value.isConstant()) { + result = value; + return true; + } else { + return false; + } +} + +bool +FastCexSolver::computeInitialValues(const Query& query, + const std::vector<const Array*> + &objects, + std::vector< std::vector<unsigned char> > + &values, + bool &hasSolution) { + ObjectFinder of; + for (ConstraintManager::const_iterator it = query.constraints.begin(), + ie = query.constraints.end(); it != ie; ++it) + of.visit(*it); + of.visit(query.expr); + for (unsigned i = 0; i != objects.size(); ++i) + of.addObject(*objects[i]); + CexData cd(of); + + for (ConstraintManager::const_iterator it = query.constraints.begin(), + ie = query.constraints.end(); it != ie; ++it) + cd.forceExprToValue(*it, 1); + cd.forceExprToValue(query.expr, 0); + + // this could be done lazily of course + cd.fixValues(); + + // check the result + for (ConstraintManager::const_iterator it = query.constraints.begin(), + ie = query.constraints.end(); it != ie; ++it) + if (!cd.exprMustBeValue(*it, 1)) + return false; + if (!cd.exprMustBeValue(query.expr, 0)) + return false; + + hasSolution = true; + CexConstifier cc(cd.objectValues); + for (unsigned i = 0; i != objects.size(); ++i) { + const Array *array = objects[i]; + std::vector<unsigned char> data; + data.reserve(array->size); + + for (unsigned i=0; i < array->size; i++) { + ref<Expr> value = + cc.visit(ReadExpr::create(UpdateList(array, true, 0), + ConstantExpr::create(i, + kMachinePointerType))); + + if (value.isConstant()) { + data.push_back(value.getConstantValue()); + } else { + // FIXME: When does this happen? + return false; + } + } + + values.push_back(data); + } + + return true; +} + + +Solver *klee::createFastCexSolver(Solver *s) { + return new Solver(new StagedSolverImpl(new FastCexSolver(), s)); +} diff --git a/lib/Solver/IncompleteSolver.cpp b/lib/Solver/IncompleteSolver.cpp new file mode 100644 index 00000000..f473f70b --- /dev/null +++ b/lib/Solver/IncompleteSolver.cpp @@ -0,0 +1,136 @@ +//===-- IncompleteSolver.cpp ----------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/IncompleteSolver.h" + +#include "klee/Constraints.h" + +using namespace klee; +using namespace llvm; + +/***/ + +IncompleteSolver::PartialValidity +IncompleteSolver::negatePartialValidity(PartialValidity pv) { + switch(pv) { + case MustBeTrue: return MustBeFalse; + case MustBeFalse: return MustBeTrue; + case MayBeTrue: return MayBeFalse; + case MayBeFalse: return MayBeTrue; + case TrueOrFalse: return TrueOrFalse; + default: assert(0 && "invalid partial validity"); + } +} + +IncompleteSolver::PartialValidity +IncompleteSolver::computeValidity(const Query& query) { + PartialValidity trueResult = computeTruth(query); + + if (trueResult == MustBeTrue) { + return MustBeTrue; + } else { + PartialValidity falseResult = computeTruth(query.negateExpr()); + + if (falseResult == MustBeTrue) { + return MustBeFalse; + } else { + bool trueCorrect = trueResult != None, + falseCorrect = falseResult != None; + + if (trueCorrect && falseCorrect) { + return TrueOrFalse; + } else if (trueCorrect) { // ==> trueResult == MayBeFalse + return MayBeFalse; + } else if (falseCorrect) { // ==> falseResult == MayBeFalse + return MayBeTrue; + } else { + return None; + } + } + } +} + +/***/ + +StagedSolverImpl::StagedSolverImpl(IncompleteSolver *_primary, + Solver *_secondary) + : primary(_primary), + secondary(_secondary) { +} + +StagedSolverImpl::~StagedSolverImpl() { + delete primary; + delete secondary; +} + +bool StagedSolverImpl::computeTruth(const Query& query, bool &isValid) { + IncompleteSolver::PartialValidity trueResult = primary->computeTruth(query); + + if (trueResult != IncompleteSolver::None) { + isValid = (trueResult == IncompleteSolver::MustBeTrue); + return true; + } + + return secondary->impl->computeTruth(query, isValid); +} + +bool StagedSolverImpl::computeValidity(const Query& query, + Solver::Validity &result) { + bool tmp; + + switch(primary->computeValidity(query)) { + case IncompleteSolver::MustBeTrue: + result = Solver::True; + break; + case IncompleteSolver::MustBeFalse: + result = Solver::False; + break; + case IncompleteSolver::TrueOrFalse: + result = Solver::Unknown; + break; + case IncompleteSolver::MayBeTrue: + if (!secondary->impl->computeTruth(query, tmp)) + return false; + result = tmp ? Solver::True : Solver::Unknown; + break; + case IncompleteSolver::MayBeFalse: + if (!secondary->impl->computeTruth(query.negateExpr(), tmp)) + return false; + result = tmp ? Solver::False : Solver::Unknown; + break; + default: + if (!secondary->impl->computeValidity(query, result)) + return false; + break; + } + + return true; +} + +bool StagedSolverImpl::computeValue(const Query& query, + ref<Expr> &result) { + if (primary->computeValue(query, result)) + return true; + + return secondary->impl->computeValue(query, result); +} + +bool +StagedSolverImpl::computeInitialValues(const Query& query, + const std::vector<const Array*> + &objects, + std::vector< std::vector<unsigned char> > + &values, + bool &hasSolution) { + if (primary->computeInitialValues(query, objects, values, hasSolution)) + return true; + + return secondary->impl->computeInitialValues(query, objects, values, + hasSolution); +} diff --git a/lib/Solver/IndependentSolver.cpp b/lib/Solver/IndependentSolver.cpp new file mode 100644 index 00000000..c966aff6 --- /dev/null +++ b/lib/Solver/IndependentSolver.cpp @@ -0,0 +1,314 @@ +//===-- IndependentSolver.cpp ---------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Solver.h" + +#include "klee/Expr.h" +#include "klee/Constraints.h" +#include "klee/SolverImpl.h" + +#include "klee/util/ExprUtil.h" + +#include "llvm/Support/Streams.h" + +#include <map> +#include <vector> + +using namespace klee; +using namespace llvm; + +template<class T> +class DenseSet { + typedef std::set<T> set_ty; + set_ty s; + +public: + DenseSet() {} + + void add(T x) { + s.insert(x); + } + void add(T start, T end) { + for (; start<end; start++) + s.insert(start); + } + + // returns true iff set is changed by addition + bool add(const DenseSet &b) { + bool modified = false; + for (typename set_ty::const_iterator it = b.s.begin(), ie = b.s.end(); + it != ie; ++it) { + if (modified || !s.count(*it)) { + modified = true; + s.insert(*it); + } + } + return modified; + } + + bool intersects(const DenseSet &b) { + for (typename set_ty::iterator it = s.begin(), ie = s.end(); + it != ie; ++it) + if (b.s.count(*it)) + return true; + return false; + } + + void print(std::ostream &os) const { + bool first = true; + os << "{"; + for (typename set_ty::iterator it = s.begin(), ie = s.end(); + it != ie; ++it) { + if (first) { + first = false; + } else { + os << ","; + } + os << *it; + } + os << "}"; + } +}; + +template<class T> +inline std::ostream &operator<<(std::ostream &os, const DenseSet<T> &dis) { + dis.print(os); + return os; +} + +class IndependentElementSet { + typedef std::map<const Array*, DenseSet<unsigned> > elements_ty; + elements_ty elements; + std::set<const Array*> wholeObjects; + +public: + IndependentElementSet() {} + IndependentElementSet(ref<Expr> e) { + std::vector< ref<ReadExpr> > reads; + findReads(e, /* visitUpdates= */ true, reads); + for (unsigned i = 0; i != reads.size(); ++i) { + ReadExpr *re = reads[i].get(); + if (re->updates.isRooted) { + const Array *array = re->updates.root; + if (!wholeObjects.count(array)) { + if (re->index.isConstant()) { + DenseSet<unsigned> &dis = elements[array]; + dis.add((unsigned) re->index.getConstantValue()); + } else { + elements_ty::iterator it2 = elements.find(array); + if (it2!=elements.end()) + elements.erase(it2); + wholeObjects.insert(array); + } + } + } + } + } + IndependentElementSet(const IndependentElementSet &ies) : + elements(ies.elements), + wholeObjects(ies.wholeObjects) {} + + IndependentElementSet &operator=(const IndependentElementSet &ies) { + elements = ies.elements; + wholeObjects = ies.wholeObjects; + return *this; + } + + void print(std::ostream &os) const { + os << "{"; + bool first = true; + for (std::set<const Array*>::iterator it = wholeObjects.begin(), + ie = wholeObjects.end(); it != ie; ++it) { + const Array *array = *it; + + if (first) { + first = false; + } else { + os << ", "; + } + + os << "MO" << array->id; + } + for (elements_ty::const_iterator it = elements.begin(), ie = elements.end(); + it != ie; ++it) { + const Array *array = it->first; + const DenseSet<unsigned> &dis = it->second; + + if (first) { + first = false; + } else { + os << ", "; + } + + os << "MO" << array->id << " : " << dis; + } + os << "}"; + } + + // more efficient when this is the smaller set + bool intersects(const IndependentElementSet &b) { + for (std::set<const Array*>::iterator it = wholeObjects.begin(), + ie = wholeObjects.end(); it != ie; ++it) { + const Array *array = *it; + if (b.wholeObjects.count(array) || + b.elements.find(array) != b.elements.end()) + return true; + } + for (elements_ty::iterator it = elements.begin(), ie = elements.end(); + it != ie; ++it) { + const Array *array = it->first; + if (b.wholeObjects.count(array)) + return true; + elements_ty::const_iterator it2 = b.elements.find(array); + if (it2 != b.elements.end()) { + if (it->second.intersects(it2->second)) + return true; + } + } + return false; + } + + // returns true iff set is changed by addition + bool add(const IndependentElementSet &b) { + bool modified = false; + for (std::set<const Array*>::const_iterator it = b.wholeObjects.begin(), + ie = b.wholeObjects.end(); it != ie; ++it) { + const Array *array = *it; + elements_ty::iterator it2 = elements.find(array); + if (it2!=elements.end()) { + modified = true; + elements.erase(it2); + wholeObjects.insert(array); + } else { + if (!wholeObjects.count(array)) { + modified = true; + wholeObjects.insert(array); + } + } + } + for (elements_ty::const_iterator it = b.elements.begin(), + ie = b.elements.end(); it != ie; ++it) { + const Array *array = it->first; + if (!wholeObjects.count(array)) { + elements_ty::iterator it2 = elements.find(array); + if (it2==elements.end()) { + modified = true; + elements.insert(*it); + } else { + if (it2->second.add(it->second)) + modified = true; + } + } + } + return modified; + } +}; + +inline std::ostream &operator<<(std::ostream &os, const IndependentElementSet &ies) { + ies.print(os); + return os; +} + +static +IndependentElementSet getIndependentConstraints(const Query& query, + std::vector< ref<Expr> > &result) { + IndependentElementSet eltsClosure(query.expr); + std::vector< std::pair<ref<Expr>, IndependentElementSet> > worklist; + + for (ConstraintManager::const_iterator it = query.constraints.begin(), + ie = query.constraints.end(); it != ie; ++it) + worklist.push_back(std::make_pair(*it, IndependentElementSet(*it))); + + // XXX This should be more efficient (in terms of low level copy stuff). + bool done = false; + do { + done = true; + std::vector< std::pair<ref<Expr>, IndependentElementSet> > newWorklist; + for (std::vector< std::pair<ref<Expr>, IndependentElementSet> >::iterator + it = worklist.begin(), ie = worklist.end(); it != ie; ++it) { + if (it->second.intersects(eltsClosure)) { + if (eltsClosure.add(it->second)) + done = false; + result.push_back(it->first); + } else { + newWorklist.push_back(*it); + } + } + worklist.swap(newWorklist); + } while (!done); + + if (0) { + std::set< ref<Expr> > reqset(result.begin(), result.end()); + llvm::cerr << "--\n"; + llvm::cerr << "Q: " << query.expr << "\n"; + llvm::cerr << "\telts: " << IndependentElementSet(query.expr) << "\n"; + int i = 0; + for (ConstraintManager::const_iterator it = query.constraints.begin(), + ie = query.constraints.end(); it != ie; ++it) { + llvm::cerr << "C" << i++ << ": " << *it; + llvm::cerr << " " << (reqset.count(*it) ? "(required)" : "(independent)") << "\n"; + llvm::cerr << "\telts: " << IndependentElementSet(*it) << "\n"; + } + llvm::cerr << "elts closure: " << eltsClosure << "\n"; + } + + return eltsClosure; +} + +class IndependentSolver : public SolverImpl { +private: + Solver *solver; + +public: + IndependentSolver(Solver *_solver) + : solver(_solver) {} + ~IndependentSolver() { delete solver; } + + bool computeTruth(const Query&, bool &isValid); + bool computeValidity(const Query&, Solver::Validity &result); + bool computeValue(const Query&, ref<Expr> &result); + bool computeInitialValues(const Query& query, + const std::vector<const Array*> &objects, + std::vector< std::vector<unsigned char> > &values, + bool &hasSolution) { + return solver->impl->computeInitialValues(query, objects, values, + hasSolution); + } +}; + +bool IndependentSolver::computeValidity(const Query& query, + Solver::Validity &result) { + std::vector< ref<Expr> > required; + IndependentElementSet eltsClosure = + getIndependentConstraints(query, required); + ConstraintManager tmp(required); + return solver->impl->computeValidity(Query(tmp, query.expr), + result); +} + +bool IndependentSolver::computeTruth(const Query& query, bool &isValid) { + std::vector< ref<Expr> > required; + IndependentElementSet eltsClosure = + getIndependentConstraints(query, required); + ConstraintManager tmp(required); + return solver->impl->computeTruth(Query(tmp, query.expr), + isValid); +} + +bool IndependentSolver::computeValue(const Query& query, ref<Expr> &result) { + std::vector< ref<Expr> > required; + IndependentElementSet eltsClosure = + getIndependentConstraints(query, required); + ConstraintManager tmp(required); + return solver->impl->computeValue(Query(tmp, query.expr), result); +} + +Solver *klee::createIndependentSolver(Solver *s) { + return new Solver(new IndependentSolver(s)); +} diff --git a/lib/Solver/Makefile b/lib/Solver/Makefile new file mode 100755 index 00000000..11d3d330 --- /dev/null +++ b/lib/Solver/Makefile @@ -0,0 +1,16 @@ +#===-- lib/Solver/Makefile ---------------------------------*- Makefile -*--===# +# +# The KLEE Symbolic Virtual Machine +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +LEVEL=../.. + +LIBRARYNAME=kleaverSolver +DONT_BUILD_RELINKED=1 +BUILD_ARCHIVE=1 + +include $(LEVEL)/Makefile.common diff --git a/lib/Solver/PCLoggingSolver.cpp b/lib/Solver/PCLoggingSolver.cpp new file mode 100644 index 00000000..4b787acb --- /dev/null +++ b/lib/Solver/PCLoggingSolver.cpp @@ -0,0 +1,134 @@ +//===-- PCLoggingSolver.cpp -----------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Solver.h" + +// FIXME: This should not be here. +#include "klee/ExecutionState.h" +#include "klee/Expr.h" +#include "klee/SolverImpl.h" +#include "klee/Statistics.h" +#include "klee/util/ExprPPrinter.h" +#include "klee/Internal/Support/QueryLog.h" +#include "klee/Internal/System/Time.h" + +#include "llvm/Support/CommandLine.h" + +#include <fstream> + +using namespace klee; +using namespace llvm; +using namespace klee::util; + +/// + +class PCLoggingSolver : public SolverImpl { + Solver *solver; + std::ofstream os; + ExprPPrinter *printer; + unsigned queryCount; + double startTime; + + void startQuery(const Query& query, const char *typeName) { + Statistic *S = theStatisticManager->getStatisticByName("Instructions"); + uint64_t instructions = S ? S->getValue() : 0; + os << "# Query " << queryCount++ << " -- " + << "Type: " << typeName << ", " + << "Instructions: " << instructions << "\n"; + printer->printQuery(os, query.constraints, query.expr); + + startTime = getWallTime(); + } + + void finishQuery(bool success) { + double delta = getWallTime() - startTime; + os << "# " << (success ? "OK" : "FAIL") << " -- " + << "Elapsed: " << delta << "\n"; + } + +public: + PCLoggingSolver(Solver *_solver, std::string path) + : solver(_solver), + os(path.c_str(), std::ios::trunc), + printer(ExprPPrinter::create(os)), + queryCount(0) { + } + ~PCLoggingSolver() { + delete printer; + delete solver; + } + + bool computeTruth(const Query& query, bool &isValid) { + startQuery(query, "Truth"); + bool success = solver->impl->computeTruth(query, isValid); + finishQuery(success); + if (success) + os << "# Is Valid: " << (isValid ? "true" : "false") << "\n"; + os << "\n"; + return success; + } + + bool computeValidity(const Query& query, Solver::Validity &result) { + startQuery(query, "Validity"); + bool success = solver->impl->computeValidity(query, result); + finishQuery(success); + if (success) + os << "# Validity: " << result << "\n"; + os << "\n"; + return success; + } + + bool computeValue(const Query& query, ref<Expr> &result) { + startQuery(query, "Value"); + bool success = solver->impl->computeValue(query, result); + finishQuery(success); + if (success) + os << "# Result: " << result << "\n"; + os << "\n"; + return success; + } + + bool computeInitialValues(const Query& query, + const std::vector<const Array*> &objects, + std::vector< std::vector<unsigned char> > &values, + bool &hasSolution) { + // FIXME: Add objects to output. + startQuery(query, "InitialValues"); + bool success = solver->impl->computeInitialValues(query, objects, + values, hasSolution); + finishQuery(success); + if (success) { + os << "# Solvable: " << (hasSolution ? "true" : "false") << "\n"; + if (hasSolution) { + std::vector< std::vector<unsigned char> >::iterator + values_it = values.begin(); + for (std::vector<const Array*>::const_iterator i = objects.begin(), + e = objects.end(); i != e; ++i, ++values_it) { + const Array *array = *i; + std::vector<unsigned char> &data = *values_it; + os << "# arr" << array->id << " = ["; + for (unsigned j = 0; j < array->size; j++) { + os << (int) data[j]; + if (j+1 < array->size) + os << ","; + } + os << "]\n"; + } + } + } + os << "\n"; + return success; + } +}; + +/// + +Solver *klee::createPCLoggingSolver(Solver *_solver, std::string path) { + return new Solver(new PCLoggingSolver(_solver, path)); +} diff --git a/lib/Solver/STPBuilder.cpp b/lib/Solver/STPBuilder.cpp new file mode 100644 index 00000000..33aee035 --- /dev/null +++ b/lib/Solver/STPBuilder.cpp @@ -0,0 +1,819 @@ +//===-- STPBuilder.cpp ----------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "STPBuilder.h" + +#include "klee/Expr.h" +#include "klee/Solver.h" +#include "klee/util/Bits.h" + +#include "ConstantDivision.h" +#include "SolverStats.h" + +#include "llvm/Support/CommandLine.h" + +#define vc_bvBoolExtract IAMTHESPAWNOFSATAN +// unclear return +#define vc_bvLeftShiftExpr IAMTHESPAWNOFSATAN +#define vc_bvRightShiftExpr IAMTHESPAWNOFSATAN +// bad refcnt'ng +#define vc_bvVar32LeftShiftExpr IAMTHESPAWNOFSATAN +#define vc_bvVar32RightShiftExpr IAMTHESPAWNOFSATAN +#define vc_bvVar32DivByPowOfTwoExpr IAMTHESPAWNOFSATAN +#define vc_bvCreateMemoryArray IAMTHESPAWNOFSATAN +#define vc_bvReadMemoryArray IAMTHESPAWNOFSATAN +#define vc_bvWriteToMemoryArray IAMTHESPAWNOFSATAN + +#include <algorithm> // max, min +#include <cassert> +#include <iostream> +#include <map> +#include <sstream> +#include <vector> + +using namespace klee; + +namespace { + llvm::cl::opt<bool> + UseConstructHash("use-construct-hash", + llvm::cl::desc("Use hash-consing during STP query construction."), + llvm::cl::init(true)); +} + +/// + +/***/ + +STPBuilder::STPBuilder(::VC _vc, bool _optimizeDivides) + : vc(_vc), optimizeDivides(_optimizeDivides) +{ + tempVars[0] = buildVar("__tmpInt8", 8); + tempVars[1] = buildVar("__tmpInt16", 16); + tempVars[2] = buildVar("__tmpInt32", 32); + tempVars[3] = buildVar("__tmpInt64", 64); +} + +STPBuilder::~STPBuilder() { +} + +/// + +/* Warning: be careful about what c_interface functions you use. Some of + them look like they cons memory but in fact don't, which is bad when + you call vc_DeleteExpr on them. */ + +::VCExpr STPBuilder::buildVar(const char *name, unsigned width) { + // XXX don't rebuild if this stuff cons's + ::Type t = (width==1) ? vc_boolType(vc) : vc_bvType(vc, width); + ::VCExpr res = vc_varExpr(vc, (char*) name, t); + vc_DeleteExpr(t); + return res; +} + +::VCExpr STPBuilder::buildArray(const char *name, unsigned indexWidth, unsigned valueWidth) { + // XXX don't rebuild if this stuff cons's + ::Type t1 = vc_bvType(vc, indexWidth); + ::Type t2 = vc_bvType(vc, valueWidth); + ::Type t = vc_arrayType(vc, t1, t2); + ::VCExpr res = vc_varExpr(vc, (char*) name, t); + vc_DeleteExpr(t); + vc_DeleteExpr(t2); + vc_DeleteExpr(t1); + return res; +} + +ExprHandle STPBuilder::getTempVar(Expr::Width w) { + switch (w) { + case Expr::Int8: return tempVars[0]; + case Expr::Int16: return tempVars[1]; + case Expr::Int32: return tempVars[2]; + case Expr::Int64: return tempVars[3]; + default: + assert(0 && "invalid type"); + } +} + +ExprHandle STPBuilder::getTrue() { + return vc_trueExpr(vc); +} +ExprHandle STPBuilder::getFalse() { + return vc_falseExpr(vc); +} +ExprHandle STPBuilder::bvOne(unsigned width) { + return bvConst32(width, 1); +} +ExprHandle STPBuilder::bvZero(unsigned width) { + return bvConst32(width, 0); +} +ExprHandle STPBuilder::bvMinusOne(unsigned width) { + return bvConst64(width, (int64_t) -1); +} +ExprHandle STPBuilder::bvConst32(unsigned width, uint32_t value) { + return vc_bvConstExprFromInt(vc, width, value); +} +ExprHandle STPBuilder::bvConst64(unsigned width, uint64_t value) { + return vc_bvConstExprFromLL(vc, width, value); +} + +ExprHandle STPBuilder::bvBoolExtract(ExprHandle expr, int bit) { + return vc_eqExpr(vc, bvExtract(expr, bit, bit), bvOne(1)); +} +ExprHandle STPBuilder::bvExtract(ExprHandle expr, unsigned top, unsigned bottom) { + return vc_bvExtract(vc, expr, top, bottom); +} +ExprHandle STPBuilder::eqExpr(ExprHandle a, ExprHandle b) { + return vc_eqExpr(vc, a, b); +} + +// logical right shift +ExprHandle STPBuilder::bvRightShift(ExprHandle expr, unsigned amount, unsigned shiftBits) { + unsigned width = vc_getBVLength(vc, expr); + unsigned shift = amount & ((1<<shiftBits) - 1); + + if (shift==0) { + return expr; + } else if (shift>=width) { + return bvZero(width); + } else { + return vc_bvConcatExpr(vc, + bvZero(shift), + bvExtract(expr, width - 1, shift)); + } +} + +// logical left shift +ExprHandle STPBuilder::bvLeftShift(ExprHandle expr, unsigned amount, unsigned shiftBits) { + unsigned width = vc_getBVLength(vc, expr); + unsigned shift = amount & ((1<<shiftBits) - 1); + + if (shift==0) { + return expr; + } else if (shift>=width) { + return bvZero(width); + } else { + // stp shift does "expr @ [0 x s]" which we then have to extract, + // rolling our own gives slightly smaller exprs + return vc_bvConcatExpr(vc, + bvExtract(expr, width - shift - 1, 0), + bvZero(shift)); + } +} + +// left shift by a variable amount on an expression of the specified width +ExprHandle STPBuilder::bvVarLeftShift(ExprHandle expr, ExprHandle amount, unsigned width) { + ExprHandle res = bvZero(width); + + int shiftBits = getShiftBits( width ); + + //get the shift amount (looking only at the bits appropriate for the given width) + ExprHandle shift = vc_bvExtract( vc, amount, shiftBits - 1, 0 ); + + //construct a big if-then-elif-elif-... with one case per possible shift amount + for( int i=width-1; i>=0; i-- ) { + res = vc_iteExpr(vc, + eqExpr(shift, bvConst32(shiftBits, i)), + bvLeftShift(expr, i, shiftBits), + res); + } + return res; +} + +// logical right shift by a variable amount on an expression of the specified width +ExprHandle STPBuilder::bvVarRightShift(ExprHandle expr, ExprHandle amount, unsigned width) { + ExprHandle res = bvZero(width); + + int shiftBits = getShiftBits( width ); + + //get the shift amount (looking only at the bits appropriate for the given width) + ExprHandle shift = vc_bvExtract( vc, amount, shiftBits - 1, 0 ); + + //construct a big if-then-elif-elif-... with one case per possible shift amount + for( int i=width-1; i>=0; i-- ) { + res = vc_iteExpr(vc, + eqExpr(shift, bvConst32(shiftBits, i)), + bvRightShift(expr, i, shiftBits), + res); + } + + return res; +} + +// arithmetic right shift by a variable amount on an expression of the specified width +ExprHandle STPBuilder::bvVarArithRightShift(ExprHandle expr, ExprHandle amount, unsigned width) { + int shiftBits = getShiftBits( width ); + + //get the shift amount (looking only at the bits appropriate for the given width) + ExprHandle shift = vc_bvExtract( vc, amount, shiftBits - 1, 0 ); + + //get the sign bit to fill with + ExprHandle signedBool = bvBoolExtract(expr, width-1); + + //start with the result if shifting by width-1 + ExprHandle res = constructAShrByConstant(expr, width-1, signedBool, shiftBits); + + //construct a big if-then-elif-elif-... with one case per possible shift amount + // XXX more efficient to move the ite on the sign outside all exprs? + // XXX more efficient to sign extend, right shift, then extract lower bits? + for( int i=width-2; i>=0; i-- ) { + res = vc_iteExpr(vc, + eqExpr(shift, bvConst32(shiftBits,i)), + constructAShrByConstant(expr, + i, + signedBool, + shiftBits), + res); + } + + return res; +} + +ExprHandle STPBuilder::constructAShrByConstant(ExprHandle expr, + unsigned amount, + ExprHandle isSigned, + unsigned shiftBits) { + unsigned width = vc_getBVLength(vc, expr); + unsigned shift = amount & ((1<<shiftBits) - 1); + + if (shift==0) { + return expr; + } else if (shift>=width-1) { + return vc_iteExpr(vc, isSigned, bvMinusOne(width), bvZero(width)); + } else { + return vc_iteExpr(vc, + isSigned, + ExprHandle(vc_bvConcatExpr(vc, + bvMinusOne(shift), + bvExtract(expr, width - 1, shift))), + bvRightShift(expr, shift, shiftBits)); + } +} + +ExprHandle STPBuilder::constructMulByConstant(ExprHandle expr, unsigned width, uint64_t x) { + unsigned shiftBits = getShiftBits(width); + uint64_t add, sub; + ExprHandle res = 0; + + // expr*x == expr*(add-sub) == expr*add - expr*sub + ComputeMultConstants64(x, add, sub); + + // legal, these would overflow completely + add = bits64::truncateToNBits(add, width); + sub = bits64::truncateToNBits(sub, width); + + for (int j=63; j>=0; j--) { + uint64_t bit = 1LL << j; + + if ((add&bit) || (sub&bit)) { + assert(!((add&bit) && (sub&bit)) && "invalid mult constants"); + ExprHandle op = bvLeftShift(expr, j, shiftBits); + + if (add&bit) { + if (res) { + res = vc_bvPlusExpr(vc, width, res, op); + } else { + res = op; + } + } else { + if (res) { + res = vc_bvMinusExpr(vc, width, res, op); + } else { + res = vc_bvUMinusExpr(vc, op); + } + } + } + } + + if (!res) + res = bvZero(width); + + return res; +} + +/* + * Compute the 32-bit unsigned integer division of n by a divisor d based on + * the constants derived from the constant divisor d. + * + * Returns n/d without doing explicit division. The cost is 2 adds, 3 shifts, + * and a (64-bit) multiply. + * + * @param n numerator (dividend) as an expression + * @param width number of bits used to represent the value + * @param d the divisor + * + * @return n/d without doing explicit division + */ +ExprHandle STPBuilder::constructUDivByConstant(ExprHandle expr_n, unsigned width, uint64_t d) { + assert(width==32 && "can only compute udiv constants for 32-bit division"); + + // Compute the constants needed to compute n/d for constant d w/o + // division by d. + uint32_t mprime, sh1, sh2; + ComputeUDivConstants32(d, mprime, sh1, sh2); + ExprHandle expr_sh1 = bvConst32( 32, sh1); + ExprHandle expr_sh2 = bvConst32( 32, sh2); + + // t1 = MULUH(mprime, n) = ( (uint64_t)mprime * (uint64_t)n ) >> 32 + ExprHandle expr_n_64 = vc_bvConcatExpr( vc, bvZero(32), expr_n ); //extend to 64 bits + ExprHandle t1_64bits = constructMulByConstant( expr_n_64, 64, (uint64_t)mprime ); + ExprHandle t1 = vc_bvExtract( vc, t1_64bits, 63, 32 ); //upper 32 bits + + // n/d = (((n - t1) >> sh1) + t1) >> sh2; + ExprHandle n_minus_t1 = vc_bvMinusExpr( vc, width, expr_n, t1 ); + ExprHandle shift_sh1 = bvVarRightShift( n_minus_t1, expr_sh1, 32 ); + ExprHandle plus_t1 = vc_bvPlusExpr( vc, width, shift_sh1, t1 ); + ExprHandle res = bvVarRightShift( plus_t1, expr_sh2, 32 ); + + return res; +} + +/* + * Compute the 32-bitnsigned integer division of n by a divisor d based on + * the constants derived from the constant divisor d. + * + * Returns n/d without doing explicit division. The cost is 3 adds, 3 shifts, + * a (64-bit) multiply, and an XOR. + * + * @param n numerator (dividend) as an expression + * @param width number of bits used to represent the value + * @param d the divisor + * + * @return n/d without doing explicit division + */ +ExprHandle STPBuilder::constructSDivByConstant(ExprHandle expr_n, unsigned width, uint64_t d) { + assert(width==32 && "can only compute udiv constants for 32-bit division"); + + // Compute the constants needed to compute n/d for constant d w/o division by d. + int32_t mprime, dsign, shpost; + ComputeSDivConstants32(d, mprime, dsign, shpost); + ExprHandle expr_dsign = bvConst32( 32, dsign); + ExprHandle expr_shpost = bvConst32( 32, shpost); + + // q0 = n + MULSH( mprime, n ) = n + (( (int64_t)mprime * (int64_t)n ) >> 32) + int64_t mprime_64 = (int64_t)mprime; + + ExprHandle expr_n_64 = vc_bvSignExtend( vc, expr_n, 64 ); + ExprHandle mult_64 = constructMulByConstant( expr_n_64, 64, mprime_64 ); + ExprHandle mulsh = vc_bvExtract( vc, mult_64, 63, 32 ); //upper 32-bits + ExprHandle n_plus_mulsh = vc_bvPlusExpr( vc, width, expr_n, mulsh ); + + // Improved variable arithmetic right shift: sign extend, shift, + // extract. + ExprHandle extend_npm = vc_bvSignExtend( vc, n_plus_mulsh, 64 ); + ExprHandle shift_npm = bvVarRightShift( extend_npm, expr_shpost, 64 ); + ExprHandle shift_shpost = vc_bvExtract( vc, shift_npm, 31, 0 ); //lower 32-bits + + // XSIGN(n) is -1 if n is negative, positive one otherwise + ExprHandle is_signed = bvBoolExtract( expr_n, 31 ); + ExprHandle neg_one = bvMinusOne(32); + ExprHandle xsign_of_n = vc_iteExpr( vc, is_signed, neg_one, bvZero(32) ); + + // q0 = (n_plus_mulsh >> shpost) - XSIGN(n) + ExprHandle q0 = vc_bvMinusExpr( vc, width, shift_shpost, xsign_of_n ); + + // n/d = (q0 ^ dsign) - dsign + ExprHandle q0_xor_dsign = vc_bvXorExpr( vc, q0, expr_dsign ); + ExprHandle res = vc_bvMinusExpr( vc, width, q0_xor_dsign, expr_dsign ); + + return res; +} + +::VCExpr STPBuilder::getInitialArray(const Array *root) { + if (root->stpInitialArray) { + return root->stpInitialArray; + } else { + char buf[32]; + sprintf(buf, "arr%d", root->id); + root->stpInitialArray = buildArray(buf, 32, 8); + return root->stpInitialArray; + } +} + +ExprHandle STPBuilder::getInitialRead(const Array *root, unsigned index) { + return vc_readExpr(vc, getInitialArray(root), bvConst32(32, index)); +} + +::VCExpr STPBuilder::getArrayForUpdate(const Array *root, + const UpdateNode *un) { + if (!un) { + return getInitialArray(root); + } else { + // FIXME: This really needs to be non-recursive. + if (!un->stpArray) + un->stpArray = vc_writeExpr(vc, + getArrayForUpdate(root, un->next), + construct(un->index, 0), + construct(un->value, 0)); + + return un->stpArray; + } +} + +/** if *width_out!=1 then result is a bitvector, + otherwise it is a bool */ +ExprHandle STPBuilder::construct(ref<Expr> e, int *width_out) { + if (!UseConstructHash || e.isConstant()) { + return constructActual(e, width_out); + } else { + ExprHashMap< std::pair<ExprHandle, unsigned> >::iterator it = + constructed.find(e); + if (it!=constructed.end()) { + if (width_out) + *width_out = it->second.second; + return it->second.first; + } else { + int width; + if (!width_out) width_out = &width; + ExprHandle res = constructActual(e, width_out); + constructed.insert(std::make_pair(e, std::make_pair(res, *width_out))); + return res; + } + } +} + + +/** if *width_out!=1 then result is a bitvector, + otherwise it is a bool */ +ExprHandle STPBuilder::constructActual(ref<Expr> e, int *width_out) { + int width; + if (!width_out) width_out = &width; + + ++stats::queryConstructs; + + switch(e.getKind()) { + + case Expr::Constant: { + uint64_t asUInt64 = e.getConstantValue(); + *width_out = e.getWidth(); + + if (*width_out > 64) + assert(0 && "constructActual: width > 64"); + + if (*width_out == 1) + return asUInt64 ? getTrue() : getFalse(); + else if (*width_out <= 32) + return bvConst32(*width_out, asUInt64); + else return bvConst64(*width_out, asUInt64); + } + + // Special + case Expr::NotOptimized: { + NotOptimizedExpr *noe = static_ref_cast<NotOptimizedExpr>(e); + return construct(noe->src, width_out); + } + + case Expr::Read: { + ReadExpr *re = static_ref_cast<ReadExpr>(e); + *width_out = 8; + return vc_readExpr(vc, + getArrayForUpdate(re->updates.root, re->updates.head), + construct(re->index, 0)); + } + + case Expr::Select: { + SelectExpr *se = static_ref_cast<SelectExpr>(e); + ExprHandle cond = construct(se->cond, 0); + ExprHandle tExpr = construct(se->trueExpr, width_out); + ExprHandle fExpr = construct(se->falseExpr, width_out); + return vc_iteExpr(vc, cond, tExpr, fExpr); + } + + case Expr::Concat: { + ConcatExpr *ce = static_ref_cast<ConcatExpr>(e); + unsigned numKids = ce->getNumKids(); + ExprHandle res = construct(ce->getKid(numKids-1), 0); + for (int i=numKids-2; i>=0; i--) { + res = vc_bvConcatExpr(vc, construct(ce->getKid(i), 0), res); + } + *width_out = ce->getWidth(); + return res; + } + + case Expr::Extract: { + ExtractExpr *ee = static_ref_cast<ExtractExpr>(e); + ExprHandle src = construct(ee->expr, width_out); + *width_out = ee->getWidth(); + if (*width_out==1) { + return bvBoolExtract(src, 0); + } else { + return vc_bvExtract(vc, src, ee->offset + *width_out - 1, ee->offset); + } + } + + // Casting + + case Expr::ZExt: { + int srcWidth; + CastExpr *ce = static_ref_cast<CastExpr>(e); + ExprHandle src = construct(ce->src, &srcWidth); + *width_out = ce->getWidth(); + if (srcWidth==1) { + return vc_iteExpr(vc, src, bvOne(*width_out), bvZero(*width_out)); + } else { + return vc_bvConcatExpr(vc, bvZero(*width_out-srcWidth), src); + } + } + + case Expr::SExt: { + int srcWidth; + CastExpr *ce = static_ref_cast<CastExpr>(e); + ExprHandle src = construct(ce->src, &srcWidth); + *width_out = ce->getWidth(); + if (srcWidth==1) { + return vc_iteExpr(vc, src, bvMinusOne(*width_out), bvZero(*width_out)); + } else { + return vc_bvSignExtend(vc, src, *width_out); + } + } + + // Arithmetic + + case Expr::Add: { + AddExpr *ae = static_ref_cast<AddExpr>(e); + ExprHandle left = construct(ae->left, width_out); + ExprHandle right = construct(ae->right, width_out); + assert(*width_out!=1 && "uncanonicalized add"); + return vc_bvPlusExpr(vc, *width_out, left, right); + } + + case Expr::Sub: { + SubExpr *se = static_ref_cast<SubExpr>(e); + ExprHandle left = construct(se->left, width_out); + ExprHandle right = construct(se->right, width_out); + assert(*width_out!=1 && "uncanonicalized sub"); + return vc_bvMinusExpr(vc, *width_out, left, right); + } + + case Expr::Mul: { + MulExpr *me = static_ref_cast<MulExpr>(e); + ExprHandle right = construct(me->right, width_out); + assert(*width_out!=1 && "uncanonicalized mul"); + + if (me->left.isConstant()) { + return constructMulByConstant(right, *width_out, me->left.getConstantValue()); + } else { + ExprHandle left = construct(me->left, width_out); + return vc_bvMultExpr(vc, *width_out, left, right); + } + } + + case Expr::UDiv: { + UDivExpr *de = static_ref_cast<UDivExpr>(e); + ExprHandle left = construct(de->left, width_out); + assert(*width_out!=1 && "uncanonicalized udiv"); + + if (de->right.isConstant()) { + uint64_t divisor = de->right.getConstantValue(); + + if (bits64::isPowerOfTwo(divisor)) { + return bvRightShift(left, + bits64::indexOfSingleBit(divisor), + getShiftBits(*width_out)); + } else if (optimizeDivides) { + if (*width_out == 32) //only works for 32-bit division + return constructUDivByConstant( left, *width_out, (uint32_t)divisor ); + } + } + + ExprHandle right = construct(de->right, width_out); + return vc_bvDivExpr(vc, *width_out, left, right); + } + + case Expr::SDiv: { + SDivExpr *de = static_ref_cast<SDivExpr>(e); + ExprHandle left = construct(de->left, width_out); + assert(*width_out!=1 && "uncanonicalized sdiv"); + + if (de->right.isConstant()) { + uint64_t divisor = de->right.getConstantValue(); + + if (optimizeDivides) { + if (*width_out == 32) //only works for 32-bit division + return constructSDivByConstant( left, *width_out, divisor); + } + } + + // XXX need to test for proper handling of sign, not sure I + // trust STP + ExprHandle right = construct(de->right, width_out); + return vc_sbvDivExpr(vc, *width_out, left, right); + } + + case Expr::URem: { + URemExpr *de = static_ref_cast<URemExpr>(e); + ExprHandle left = construct(de->left, width_out); + assert(*width_out!=1 && "uncanonicalized urem"); + + if (de->right.isConstant()) { + uint64_t divisor = de->right.getConstantValue(); + + if (bits64::isPowerOfTwo(divisor)) { + unsigned bits = bits64::indexOfSingleBit(divisor); + + // special case for modding by 1 or else we bvExtract -1:0 + if (bits == 0) { + return bvZero(*width_out); + } else { + return vc_bvConcatExpr(vc, + bvZero(*width_out - bits), + bvExtract(left, bits - 1, 0)); + } + } + + //use fast division to compute modulo without explicit division for constant divisor + if (optimizeDivides) { + if (*width_out == 32) { //only works for 32-bit division + ExprHandle quotient = constructUDivByConstant( left, *width_out, (uint32_t)divisor ); + ExprHandle quot_times_divisor = constructMulByConstant( quotient, *width_out, divisor ); + ExprHandle rem = vc_bvMinusExpr( vc, *width_out, left, quot_times_divisor ); + return rem; + } + } + } + + ExprHandle right = construct(de->right, width_out); + return vc_bvModExpr(vc, *width_out, left, right); + } + + case Expr::SRem: { + SRemExpr *de = static_ref_cast<SRemExpr>(e); + ExprHandle left = construct(de->left, width_out); + ExprHandle right = construct(de->right, width_out); + assert(*width_out!=1 && "uncanonicalized srem"); + +#if 0 //not faster per first benchmark + if (optimizeDivides) { + if (ConstantExpr *cre = de->right->asConstant()) { + uint64_t divisor = cre->asUInt64; + + //use fast division to compute modulo without explicit division for constant divisor + if( *width_out == 32 ) { //only works for 32-bit division + ExprHandle quotient = constructSDivByConstant( left, *width_out, divisor ); + ExprHandle quot_times_divisor = constructMulByConstant( quotient, *width_out, divisor ); + ExprHandle rem = vc_bvMinusExpr( vc, *width_out, left, quot_times_divisor ); + return rem; + } + } + } +#endif + + // XXX implement my fast path and test for proper handling of sign + return vc_sbvModExpr(vc, *width_out, left, right); + } + + // Binary + + case Expr::And: { + AndExpr *ae = static_ref_cast<AndExpr>(e); + ExprHandle left = construct(ae->left, width_out); + ExprHandle right = construct(ae->right, width_out); + if (*width_out==1) { + return vc_andExpr(vc, left, right); + } else { + return vc_bvAndExpr(vc, left, right); + } + } + case Expr::Or: { + OrExpr *oe = static_ref_cast<OrExpr>(e); + ExprHandle left = construct(oe->left, width_out); + ExprHandle right = construct(oe->right, width_out); + if (*width_out==1) { + return vc_orExpr(vc, left, right); + } else { + return vc_bvOrExpr(vc, left, right); + } + } + + case Expr::Xor: { + XorExpr *xe = static_ref_cast<XorExpr>(e); + ExprHandle left = construct(xe->left, width_out); + ExprHandle right = construct(xe->right, width_out); + + if (*width_out==1) { + // XXX check for most efficient? + return vc_iteExpr(vc, left, + ExprHandle(vc_notExpr(vc, right)), right); + } else { + return vc_bvXorExpr(vc, left, right); + } + } + + case Expr::Shl: { + ShlExpr *se = static_ref_cast<ShlExpr>(e); + ExprHandle left = construct(se->left, width_out); + assert(*width_out!=1 && "uncanonicalized shl"); + + if (se->right.isConstant()) { + return bvLeftShift(left, se->right.getConstantValue(), getShiftBits(*width_out)); + } else { + int shiftWidth; + ExprHandle amount = construct(se->right, &shiftWidth); + return bvVarLeftShift( left, amount, *width_out ); + } + } + + case Expr::LShr: { + LShrExpr *lse = static_ref_cast<LShrExpr>(e); + ExprHandle left = construct(lse->left, width_out); + unsigned shiftBits = getShiftBits(*width_out); + assert(*width_out!=1 && "uncanonicalized lshr"); + + if (lse->right.isConstant()) { + return bvRightShift(left, (unsigned) lse->right.getConstantValue(), shiftBits); + } else { + int shiftWidth; + ExprHandle amount = construct(lse->right, &shiftWidth); + return bvVarRightShift( left, amount, *width_out ); + } + } + + case Expr::AShr: { + AShrExpr *ase = static_ref_cast<AShrExpr>(e); + ExprHandle left = construct(ase->left, width_out); + assert(*width_out!=1 && "uncanonicalized ashr"); + + if (ase->right.isConstant()) { + unsigned shift = (unsigned) ase->right.getConstantValue(); + ExprHandle signedBool = bvBoolExtract(left, *width_out-1); + return constructAShrByConstant(left, shift, signedBool, getShiftBits(*width_out)); + } else { + int shiftWidth; + ExprHandle amount = construct(ase->right, &shiftWidth); + return bvVarArithRightShift( left, amount, *width_out ); + } + } + + // Comparison + + case Expr::Eq: { + EqExpr *ee = static_ref_cast<EqExpr>(e); + ExprHandle left = construct(ee->left, width_out); + ExprHandle right = construct(ee->right, width_out); + if (*width_out==1) { + if (ee->left.isConstant()) { + assert(!ee->left.getConstantValue() && "uncanonicalized eq"); + return vc_notExpr(vc, right); + } else { + return vc_iffExpr(vc, left, right); + } + } else { + *width_out = 1; + return vc_eqExpr(vc, left, right); + } + } + + case Expr::Ult: { + UltExpr *ue = static_ref_cast<UltExpr>(e); + ExprHandle left = construct(ue->left, width_out); + ExprHandle right = construct(ue->right, width_out); + assert(*width_out!=1 && "uncanonicalized ult"); + *width_out = 1; + return vc_bvLtExpr(vc, left, right); + } + + case Expr::Ule: { + UleExpr *ue = static_ref_cast<UleExpr>(e); + ExprHandle left = construct(ue->left, width_out); + ExprHandle right = construct(ue->right, width_out); + assert(*width_out!=1 && "uncanonicalized ule"); + *width_out = 1; + return vc_bvLeExpr(vc, left, right); + } + + case Expr::Slt: { + SltExpr *se = static_ref_cast<SltExpr>(e); + ExprHandle left = construct(se->left, width_out); + ExprHandle right = construct(se->right, width_out); + assert(*width_out!=1 && "uncanonicalized slt"); + *width_out = 1; + return vc_sbvLtExpr(vc, left, right); + } + + case Expr::Sle: { + SleExpr *se = static_ref_cast<SleExpr>(e); + ExprHandle left = construct(se->left, width_out); + ExprHandle right = construct(se->right, width_out); + assert(*width_out!=1 && "uncanonicalized sle"); + *width_out = 1; + return vc_sbvLeExpr(vc, left, right); + } + + // unused due to canonicalization +#if 0 + case Expr::Ne: + case Expr::Ugt: + case Expr::Uge: + case Expr::Sgt: + case Expr::Sge: +#endif + + default: + assert(0 && "unhandled Expr type"); + return vc_trueExpr(vc); + } +} diff --git a/lib/Solver/STPBuilder.h b/lib/Solver/STPBuilder.h new file mode 100644 index 00000000..6382bc1f --- /dev/null +++ b/lib/Solver/STPBuilder.h @@ -0,0 +1,125 @@ +//===-- STPBuilder.h --------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __UTIL_STPBUILDER_H__ +#define __UTIL_STPBUILDER_H__ + +#include "klee/util/ExprHashMap.h" +#include "klee/Config/config.h" + +#include <vector> +#include <map> + +#define Expr VCExpr +#include "stp/c_interface.h" + +#if ENABLE_STPLOG == 1 +#include "stp/stplog.h" +#endif +#undef Expr + +namespace klee { + class ExprHolder { + friend class ExprHandle; + ::VCExpr expr; + unsigned count; + + public: + ExprHolder(const ::VCExpr _expr) : expr(_expr), count(0) {} + ~ExprHolder() { + if (expr) vc_DeleteExpr(expr); + } + }; + + class ExprHandle { + ExprHolder *H; + + public: + ExprHandle() : H(new ExprHolder(0)) { H->count++; } + ExprHandle(::VCExpr _expr) : H(new ExprHolder(_expr)) { H->count++; } + ExprHandle(const ExprHandle &b) : H(b.H) { H->count++; } + ~ExprHandle() { if (--H->count == 0) delete H; } + + ExprHandle &operator=(const ExprHandle &b) { + if (--H->count == 0) delete H; + H = b.H; + H->count++; + return *this; + } + + operator bool () { return H->expr; } + operator ::VCExpr () { return H->expr; } + }; + +class STPBuilder { + ::VC vc; + ExprHandle tempVars[4]; + ExprHashMap< std::pair<ExprHandle, unsigned> > constructed; + + /// optimizeDivides - Rewrite division and reminders by constants + /// into multiplies and shifts. STP should probably handle this for + /// use. + bool optimizeDivides; + +private: + unsigned getShiftBits(unsigned amount) { + return (amount == 64) ? 6 : 5; + } + + ExprHandle bvOne(unsigned width); + ExprHandle bvZero(unsigned width); + ExprHandle bvMinusOne(unsigned width); + ExprHandle bvConst32(unsigned width, uint32_t value); + ExprHandle bvConst64(unsigned width, uint64_t value); + + ExprHandle bvBoolExtract(ExprHandle expr, int bit); + ExprHandle bvExtract(ExprHandle expr, unsigned top, unsigned bottom); + ExprHandle eqExpr(ExprHandle a, ExprHandle b); + + //logical left and right shift (not arithmetic) + ExprHandle bvLeftShift(ExprHandle expr, unsigned shift, unsigned shiftBits); + ExprHandle bvRightShift(ExprHandle expr, unsigned amount, unsigned shiftBits); + ExprHandle bvVarLeftShift(ExprHandle expr, ExprHandle amount, unsigned width); + ExprHandle bvVarRightShift(ExprHandle expr, ExprHandle amount, unsigned width); + ExprHandle bvVarArithRightShift(ExprHandle expr, ExprHandle amount, unsigned width); + + ExprHandle constructAShrByConstant(ExprHandle expr, unsigned shift, + ExprHandle isSigned, unsigned shiftBits); + ExprHandle constructMulByConstant(ExprHandle expr, unsigned width, uint64_t x); + ExprHandle constructUDivByConstant(ExprHandle expr_n, unsigned width, uint64_t d); + ExprHandle constructSDivByConstant(ExprHandle expr_n, unsigned width, uint64_t d); + + ::VCExpr getInitialArray(const Array *os); + ::VCExpr getArrayForUpdate(const Array *root, const UpdateNode *un); + + ExprHandle constructActual(ref<Expr> e, int *width_out); + ExprHandle construct(ref<Expr> e, int *width_out); + + ::VCExpr buildVar(const char *name, unsigned width); + ::VCExpr buildArray(const char *name, unsigned indexWidth, unsigned valueWidth); + +public: + STPBuilder(::VC _vc, bool _optimizeDivides=true); + ~STPBuilder(); + + ExprHandle getTrue(); + ExprHandle getFalse(); + ExprHandle getTempVar(Expr::Width w); + ExprHandle getInitialRead(const Array *os, unsigned index); + + ExprHandle construct(ref<Expr> e) { + ExprHandle res = construct(e, 0); + constructed.clear(); + return res; + } +}; + +} + +#endif diff --git a/lib/Solver/Solver.cpp b/lib/Solver/Solver.cpp new file mode 100644 index 00000000..24d3ef86 --- /dev/null +++ b/lib/Solver/Solver.cpp @@ -0,0 +1,643 @@ +//===-- Solver.cpp --------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Solver.h" +#include "klee/SolverImpl.h" + +#include "SolverStats.h" +#include "STPBuilder.h" + +#include "klee/Constraints.h" +#include "klee/Expr.h" +#include "klee/TimerStatIncrementer.h" +#include "klee/util/Assignment.h" +#include "klee/util/ExprPPrinter.h" +#include "klee/util/ExprUtil.h" +#include "klee/Internal/Support/Timer.h" + +#define vc_bvBoolExtract IAMTHESPAWNOFSATAN + +#include <cassert> +#include <map> +#include <vector> + +#include <sys/wait.h> +#include <sys/ipc.h> +#include <sys/shm.h> + +using namespace klee; + +/***/ + +const char *Solver::validity_to_str(Validity v) { + switch (v) { + default: return "Unknown"; + case True: return "True"; + case False: return "False"; + } +} + +Solver::~Solver() { + delete impl; +} + +SolverImpl::~SolverImpl() { +} + +bool Solver::evaluate(const Query& query, Validity &result) { + assert(query.expr.getWidth() == Expr::Bool && "Invalid expression type!"); + + // Maintain invariants implementation expect. + if (query.expr.isConstant()) { + result = query.expr.getConstantValue() ? True : False; + return true; + } + + return impl->computeValidity(query, result); +} + +bool SolverImpl::computeValidity(const Query& query, Solver::Validity &result) { + bool isTrue, isFalse; + if (!computeTruth(query, isTrue)) + return false; + if (isTrue) { + result = Solver::True; + } else { + if (!computeTruth(query.negateExpr(), isFalse)) + return false; + result = isFalse ? Solver::False : Solver::Unknown; + } + return true; +} + +bool Solver::mustBeTrue(const Query& query, bool &result) { + assert(query.expr.getWidth() == Expr::Bool && "Invalid expression type!"); + + // Maintain invariants implementation expect. + if (query.expr.isConstant()) { + result = query.expr.getConstantValue() ? true : false; + return true; + } + + return impl->computeTruth(query, result); +} + +bool Solver::mustBeFalse(const Query& query, bool &result) { + return mustBeTrue(query.negateExpr(), result); +} + +bool Solver::mayBeTrue(const Query& query, bool &result) { + bool res; + if (!mustBeFalse(query, res)) + return false; + result = !res; + return true; +} + +bool Solver::mayBeFalse(const Query& query, bool &result) { + bool res; + if (!mustBeTrue(query, res)) + return false; + result = !res; + return true; +} + +bool Solver::getValue(const Query& query, ref<Expr> &result) { + // Maintain invariants implementation expect. + if (query.expr.isConstant()) { + result = query.expr; + return true; + } + + return impl->computeValue(query, result); +} + +bool +Solver::getInitialValues(const Query& query, + const std::vector<const Array*> &objects, + std::vector< std::vector<unsigned char> > &values) { + bool hasSolution; + bool success = + impl->computeInitialValues(query, objects, values, hasSolution); + // FIXME: Propogate this out. + if (!hasSolution) + return false; + + return success; +} + +std::pair< ref<Expr>, ref<Expr> > Solver::getRange(const Query& query) { + ref<Expr> e = query.expr; + Expr::Width width = e.getWidth(); + uint64_t min, max; + + if (width==1) { + Solver::Validity result; + if (!evaluate(query, result)) + assert(0 && "computeValidity failed"); + switch (result) { + case Solver::True: + min = max = 1; break; + case Solver::False: + min = max = 0; break; + default: + min = 0, max = 1; break; + } + } else if (e.isConstant()) { + min = max = e.getConstantValue(); + } else { + // binary search for # of useful bits + uint64_t lo=0, hi=width, mid, bits=0; + while (lo<hi) { + mid = (lo+hi)/2; + bool res; + bool success = + mustBeTrue(query.withExpr( + EqExpr::create(LShrExpr::create(e, + ConstantExpr::create(mid, + width)), + ConstantExpr::create(0, width))), + res); + assert(success && "FIXME: Unhandled solver failure"); + if (res) { + hi = mid; + } else { + lo = mid+1; + } + + bits = lo; + } + + // could binary search for training zeros and offset + // min max but unlikely to be very useful + + // check common case + bool res = false; + bool success = + mayBeTrue(query.withExpr(EqExpr::create(e, ConstantExpr::create(0, + width))), + res); + assert(success && "FIXME: Unhandled solver failure"); + if (res) { + min = 0; + } else { + // binary search for min + lo=0, hi=bits64::maxValueOfNBits(bits); + while (lo<hi) { + mid = (lo+hi)/2; + bool res = false; + bool success = + mayBeTrue(query.withExpr(UleExpr::create(e, + ConstantExpr::create(mid, + width))), + res); + assert(success && "FIXME: Unhandled solver failure"); + if (res) { + hi = mid; + } else { + lo = mid+1; + } + } + + min = lo; + } + + // binary search for max + lo=min, hi=bits64::maxValueOfNBits(bits); + while (lo<hi) { + mid = (lo+hi)/2; + bool res; + bool success = + mustBeTrue(query.withExpr(UleExpr::create(e, + ConstantExpr::create(mid, + width))), + res); + assert(success && "FIXME: Unhandled solver failure"); + if (res) { + hi = mid; + } else { + lo = mid+1; + } + } + + max = lo; + } + + return std::make_pair(ConstantExpr::create(min, width), + ConstantExpr::create(max, width)); +} + +/***/ + +class ValidatingSolver : public SolverImpl { +private: + Solver *solver, *oracle; + +public: + ValidatingSolver(Solver *_solver, Solver *_oracle) + : solver(_solver), oracle(_oracle) {} + ~ValidatingSolver() { delete solver; } + + bool computeValidity(const Query&, Solver::Validity &result); + bool computeTruth(const Query&, bool &isValid); + bool computeValue(const Query&, ref<Expr> &result); + bool computeInitialValues(const Query&, + const std::vector<const Array*> &objects, + std::vector< std::vector<unsigned char> > &values, + bool &hasSolution); +}; + +bool ValidatingSolver::computeTruth(const Query& query, + bool &isValid) { + bool answer; + + if (!solver->impl->computeTruth(query, isValid)) + return false; + if (!oracle->impl->computeTruth(query, answer)) + return false; + + if (isValid != answer) + assert(0 && "invalid solver result (computeTruth)"); + + return true; +} + +bool ValidatingSolver::computeValidity(const Query& query, + Solver::Validity &result) { + Solver::Validity answer; + + if (!solver->impl->computeValidity(query, result)) + return false; + if (!oracle->impl->computeValidity(query, answer)) + return false; + + if (result != answer) + assert(0 && "invalid solver result (computeValidity)"); + + return true; +} + +bool ValidatingSolver::computeValue(const Query& query, + ref<Expr> &result) { + bool answer; + + if (!solver->impl->computeValue(query, result)) + return false; + // We don't want to compare, but just make sure this is a legal + // solution. + if (!oracle->impl->computeTruth(query.withExpr(NeExpr::create(query.expr, + result)), + answer)) + return false; + + if (answer) + assert(0 && "invalid solver result (computeValue)"); + + return true; +} + +bool +ValidatingSolver::computeInitialValues(const Query& query, + const std::vector<const Array*> + &objects, + std::vector< std::vector<unsigned char> > + &values, + bool &hasSolution) { + bool answer; + + if (!solver->impl->computeInitialValues(query, objects, values, + hasSolution)) + return false; + + if (hasSolution) { + // Assert the bindings as constraints, and verify that the + // conjunction of the actual constraints is satisfiable. + std::vector< ref<Expr> > bindings; + for (unsigned i = 0; i != values.size(); ++i) { + const Array *array = objects[i]; + for (unsigned j=0; j<array->size; j++) { + unsigned char value = values[i][j]; + bindings.push_back(EqExpr::create(ReadExpr::create(UpdateList(array, + true, 0), + ref<Expr>(j, Expr::Int32)), + ref<Expr>(value, Expr::Int8))); + } + } + ConstraintManager tmp(bindings); + ref<Expr> constraints = Expr::createNot(query.expr); + for (ConstraintManager::const_iterator it = query.constraints.begin(), + ie = query.constraints.end(); it != ie; ++it) + constraints = AndExpr::create(constraints, *it); + + if (!oracle->impl->computeTruth(Query(tmp, constraints), answer)) + return false; + if (!answer) + assert(0 && "invalid solver result (computeInitialValues)"); + } else { + if (!oracle->impl->computeTruth(query, answer)) + return false; + if (!answer) + assert(0 && "invalid solver result (computeInitialValues)"); + } + + return true; +} + +Solver *klee::createValidatingSolver(Solver *s, Solver *oracle) { + return new Solver(new ValidatingSolver(s, oracle)); +} + +/***/ + +class STPSolverImpl : public SolverImpl { +private: + /// The solver we are part of, for access to public information. + STPSolver *solver; + VC vc; + STPBuilder *builder; + double timeout; + bool useForkedSTP; + +public: + STPSolverImpl(STPSolver *_solver, bool _useForkedSTP); + ~STPSolverImpl(); + + char *getConstraintLog(const Query&); + void setTimeout(double _timeout) { timeout = _timeout; } + + bool computeTruth(const Query&, bool &isValid); + bool computeValue(const Query&, ref<Expr> &result); + bool computeInitialValues(const Query&, + const std::vector<const Array*> &objects, + std::vector< std::vector<unsigned char> > &values, + bool &hasSolution); +}; + +static unsigned char *shared_memory_ptr; +static const unsigned shared_memory_size = 1<<20; +static int shared_memory_id; + +static void stp_error_handler(const char* err_msg) { + fprintf(stderr, "error: STP Error: %s\n", err_msg); + abort(); +} + +STPSolverImpl::STPSolverImpl(STPSolver *_solver, bool _useForkedSTP) + : solver(_solver), + vc(vc_createValidityChecker()), + builder(new STPBuilder(vc)), + timeout(0.0), + useForkedSTP(_useForkedSTP) +{ + assert(vc && "unable to create validity checker"); + assert(builder && "unable to create STPBuilder"); + + vc_registerErrorHandler(::stp_error_handler); + + if (useForkedSTP) { + shared_memory_id = shmget(IPC_PRIVATE, shared_memory_size, IPC_CREAT | 0700); + assert(shared_memory_id>=0 && "shmget failed"); + shared_memory_ptr = (unsigned char*) shmat(shared_memory_id, NULL, 0); + assert(shared_memory_ptr!=(void*)-1 && "shmat failed"); + shmctl(shared_memory_id, IPC_RMID, NULL); + } +} + +STPSolverImpl::~STPSolverImpl() { + delete builder; + + vc_Destroy(vc); +} + +/***/ + +STPSolver::STPSolver(bool useForkedSTP) + : Solver(new STPSolverImpl(this, useForkedSTP)) +{ +} + +char *STPSolver::getConstraintLog(const Query &query) { + return static_cast<STPSolverImpl*>(impl)->getConstraintLog(query); +} + +void STPSolver::setTimeout(double timeout) { + static_cast<STPSolverImpl*>(impl)->setTimeout(timeout); +} + +/***/ + +char *STPSolverImpl::getConstraintLog(const Query &query) { + vc_push(vc); + for (std::vector< ref<Expr> >::const_iterator it = query.constraints.begin(), + ie = query.constraints.end(); it != ie; ++it) + vc_assertFormula(vc, builder->construct(*it)); + assert(query.expr == ref<Expr>(0, Expr::Bool) && + "Unexpected expression in query!"); + + char *buffer; + unsigned long length; + vc_printQueryStateToBuffer(vc, builder->getFalse(), + &buffer, &length, false); + vc_pop(vc); + + return buffer; +} + +bool STPSolverImpl::computeTruth(const Query& query, + bool &isValid) { + std::vector<const Array*> objects; + std::vector< std::vector<unsigned char> > values; + bool hasSolution; + + if (!computeInitialValues(query, objects, values, hasSolution)) + return false; + + isValid = !hasSolution; + return true; +} + +bool STPSolverImpl::computeValue(const Query& query, + ref<Expr> &result) { + std::vector<const Array*> objects; + std::vector< std::vector<unsigned char> > values; + bool hasSolution; + + // Find the object used in the expression, and compute an assignment + // for them. + findSymbolicObjects(query.expr, objects); + if (!computeInitialValues(query.withFalse(), objects, values, hasSolution)) + return false; + assert(hasSolution && "state has invalid constraint set"); + + // Evaluate the expression with the computed assignment. + Assignment a(objects, values); + result = a.evaluate(query.expr); + + return true; +} + +static void runAndGetCex(::VC vc, STPBuilder *builder, ::VCExpr q, + const std::vector<const Array*> &objects, + std::vector< std::vector<unsigned char> > &values, + bool &hasSolution) { + // XXX I want to be able to timeout here, safely + hasSolution = !vc_query(vc, q); + + if (hasSolution) { + values.reserve(objects.size()); + for (std::vector<const Array*>::const_iterator + it = objects.begin(), ie = objects.end(); it != ie; ++it) { + const Array *array = *it; + std::vector<unsigned char> data; + + data.reserve(array->size); + for (unsigned offset = 0; offset < array->size; offset++) { + ExprHandle counter = + vc_getCounterExample(vc, builder->getInitialRead(array, offset)); + unsigned char val = getBVUnsigned(counter); + data.push_back(val); + } + + values.push_back(data); + } + } +} + +static void stpTimeoutHandler(int x) { + _exit(52); +} + +static bool runAndGetCexForked(::VC vc, + STPBuilder *builder, + ::VCExpr q, + const std::vector<const Array*> &objects, + std::vector< std::vector<unsigned char> > + &values, + bool &hasSolution, + double timeout) { + unsigned char *pos = shared_memory_ptr; + unsigned sum = 0; + for (std::vector<const Array*>::const_iterator + it = objects.begin(), ie = objects.end(); it != ie; ++it) + sum += (*it)->size; + assert(sum<shared_memory_size && "not enough shared memory for counterexample"); + + fflush(stdout); + fflush(stderr); + int pid = fork(); + if (pid==-1) { + fprintf(stderr, "error: fork failed (for STP)"); + return false; + } + + if (pid == 0) { + if (timeout) { + ::alarm(0); /* Turn off alarm so we can safely set signal handler */ + ::signal(SIGALRM, stpTimeoutHandler); + ::alarm(std::max(1, (int)timeout)); + } + unsigned res = vc_query(vc, q); + if (!res) { + for (std::vector<const Array*>::const_iterator + it = objects.begin(), ie = objects.end(); it != ie; ++it) { + const Array *array = *it; + for (unsigned offset = 0; offset < array->size; offset++) { + ExprHandle counter = + vc_getCounterExample(vc, builder->getInitialRead(array, offset)); + *pos++ = getBVUnsigned(counter); + } + } + } + _exit(res); + } else { + int status; + int res = waitpid(pid, &status, 0); + + if (res<0) { + fprintf(stderr, "error: waitpid() for STP failed"); + return false; + } + + // From timed_run.py: It appears that linux at least will on + // "occasion" return a status when the process was terminated by a + // signal, so test signal first. + if (WIFSIGNALED(status) || !WIFEXITED(status)) { + fprintf(stderr, "error: STP did not return successfully"); + return false; + } + + int exitcode = WEXITSTATUS(status); + if (exitcode==0) { + hasSolution = true; + } else if (exitcode==1) { + hasSolution = false; + } else if (exitcode==52) { + fprintf(stderr, "error: STP timed out"); + return false; + } else { + fprintf(stderr, "error: STP did not return a recognized code"); + return false; + } + + if (hasSolution) { + values = std::vector< std::vector<unsigned char> >(objects.size()); + unsigned i=0; + for (std::vector<const Array*>::const_iterator + it = objects.begin(), ie = objects.end(); it != ie; ++it) { + const Array *array = *it; + std::vector<unsigned char> &data = values[i++]; + data.insert(data.begin(), pos, pos + array->size); + pos += array->size; + } + } + + return true; + } +} + +bool +STPSolverImpl::computeInitialValues(const Query &query, + const std::vector<const Array*> + &objects, + std::vector< std::vector<unsigned char> > + &values, + bool &hasSolution) { + TimerStatIncrementer t(stats::queryTime); + + vc_push(vc); + + for (ConstraintManager::const_iterator it = query.constraints.begin(), + ie = query.constraints.end(); it != ie; ++it) + vc_assertFormula(vc, builder->construct(*it)); + + ++stats::queries; + ++stats::queryCounterexamples; + + ExprHandle stp_e = builder->construct(query.expr); + + bool success; + if (useForkedSTP) { + success = runAndGetCexForked(vc, builder, stp_e, objects, values, + hasSolution, timeout); + } else { + runAndGetCex(vc, builder, stp_e, objects, values, hasSolution); + success = true; + } + + if (success) { + if (hasSolution) + ++stats::queriesInvalid; + else + ++stats::queriesValid; + } + + vc_pop(vc); + + return success; +} diff --git a/lib/Solver/SolverStats.cpp b/lib/Solver/SolverStats.cpp new file mode 100644 index 00000000..9d48792a --- /dev/null +++ b/lib/Solver/SolverStats.cpp @@ -0,0 +1,23 @@ +//===-- SolverStats.cpp ---------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SolverStats.h" + +using namespace klee; + +Statistic stats::cexCacheTime("CexCacheTime", "CCtime"); +Statistic stats::queries("Queries", "Q"); +Statistic stats::queriesInvalid("QueriesInvalid", "Qiv"); +Statistic stats::queriesValid("QueriesValid", "Qv"); +Statistic stats::queryCacheHits("QueryCacheHits", "QChits") ; +Statistic stats::queryCacheMisses("QueryCacheMisses", "QCmisses"); +Statistic stats::queryConstructTime("QueryConstructTime", "QBtime") ; +Statistic stats::queryConstructs("QueriesConstructs", "QB"); +Statistic stats::queryCounterexamples("QueriesCEX", "Qcex"); +Statistic stats::queryTime("QueryTime", "Qtime"); diff --git a/lib/Solver/SolverStats.h b/lib/Solver/SolverStats.h new file mode 100644 index 00000000..6fee7699 --- /dev/null +++ b/lib/Solver/SolverStats.h @@ -0,0 +1,32 @@ +//===-- SolverStats.h -------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_SOLVERSTATS_H +#define KLEE_SOLVERSTATS_H + +#include "klee/Statistic.h" + +namespace klee { +namespace stats { + + extern Statistic cexCacheTime; + extern Statistic queries; + extern Statistic queriesInvalid; + extern Statistic queriesValid; + extern Statistic queryCacheHits; + extern Statistic queryCacheMisses; + extern Statistic queryConstructTime; + extern Statistic queryConstructs; + extern Statistic queryCounterexamples; + extern Statistic queryTime; + +} +} + +#endif diff --git a/lib/Support/Makefile b/lib/Support/Makefile new file mode 100644 index 00000000..a1b46f3c --- /dev/null +++ b/lib/Support/Makefile @@ -0,0 +1,16 @@ +#===-- lib/Support/Makefile --------------------------------*- Makefile -*--===# +# +# The KLEE Symbolic Virtual Machine +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +LEVEL=../.. + +LIBRARYNAME=kleeSupport +DONT_BUILD_RELINKED=1 +BUILD_ARCHIVE=1 + +include $(LEVEL)/Makefile.common diff --git a/lib/Support/README.txt b/lib/Support/README.txt new file mode 100644 index 00000000..1ed6fcb4 --- /dev/null +++ b/lib/Support/README.txt @@ -0,0 +1,2 @@ +This directory holds basic support facilities (data structures, +utilities, etc.) used by klee. diff --git a/lib/Support/RNG.cpp b/lib/Support/RNG.cpp new file mode 100644 index 00000000..fef7e489 --- /dev/null +++ b/lib/Support/RNG.cpp @@ -0,0 +1,146 @@ +/* + A C-program for MT19937, with initialization improved 2002/1/26. + Coded by Takuji Nishimura and Makoto Matsumoto. + Modified to be a C++ class by Daniel Dunbar. + + Before using, initialize the state by using init_genrand(seed) + or init_by_array(init_key, key_length). + + Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The names of its contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + Any feedback is very welcome. + http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html + email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) +*/ + +#include "klee/Internal/ADT/RNG.h" + +using namespace klee; + +/* initializes mt[N] with a seed */ +RNG::RNG(unsigned int s) { + seed(s); +} + +void RNG::seed(unsigned int s) { + mt[0]= s & 0xffffffffUL; + for (mti=1; mti<N; mti++) { + mt[mti] = + (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti); + /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ + /* In the previous versions, MSBs of the seed affect */ + /* only MSBs of the array mt[]. */ + /* 2002/01/09 modified by Makoto Matsumoto */ + mt[mti] &= 0xffffffffUL; + /* for >32 bit machines */ + } +} + +/* generates a random number on [0,0xffffffff]-interval */ +unsigned int RNG::getInt32() { + unsigned int y; + static unsigned int mag01[2]={0x0UL, MATRIX_A}; + /* mag01[x] = x * MATRIX_A for x=0,1 */ + + if (mti >= N) { /* generate N words at one time */ + int kk; + + for (kk=0;kk<N-M;kk++) { + y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK); + mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL]; + } + for (;kk<N-1;kk++) { + y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK); + mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL]; + } + y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); + mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL]; + + mti = 0; + } + + y = mt[mti++]; + + /* Tempering */ + y ^= (y >> 11); + y ^= (y << 7) & 0x9d2c5680UL; + y ^= (y << 15) & 0xefc60000UL; + y ^= (y >> 18); + + return y; +} + +/* generates a random number on [0,0x7fffffff]-interval */ +int RNG::getInt31() { + return (int)(getInt32()>>1); +} + +/* generates a random number on [0,1]-real-interval */ +double RNG::getDoubleLR() { + return getInt32()*(1.0/4294967295.0); + /* divided by 2^32-1 */ +} + +/* generates a random number on [0,1)-real-interval */ +double RNG::getDoubleL() { + return getInt32()*(1.0/4294967296.0); + /* divided by 2^32 */ +} + +/* generates a random number on (0,1)-real-interval */ +double RNG::getDouble() { + return (((double)getInt32()) + 0.5)*(1.0/4294967296.0); + /* divided by 2^32 */ +} + +float RNG::getFloatLR() { + return getInt32()*(1.0f/4294967295.0f); + /* divided by 2^32-1 */ +} +float RNG::getFloatL() { + return getInt32()*(1.0f/4294967296.0f); + /* divided by 2^32 */ +} +float RNG::getFloat() { + return (getInt32() + 0.5f)*(1.0f/4294967296.0f); + /* divided by 2^32 */ +} + +bool RNG::getBool() { + unsigned bits = getInt32(); + bits ^= bits >> 16; + bits ^= bits >> 8; + bits ^= bits >> 4; + bits ^= bits >> 2; + bits ^= bits >> 1; + return bits&1; +} diff --git a/lib/Support/Time.cpp b/lib/Support/Time.cpp new file mode 100644 index 00000000..0ec8d9d7 --- /dev/null +++ b/lib/Support/Time.cpp @@ -0,0 +1,27 @@ +//===-- Time.cpp ----------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Internal/System/Time.h" + +#include "llvm/System/Process.h" + +using namespace llvm; +using namespace klee; + +double util::getUserTime() { + sys::TimeValue now(0,0),user(0,0),sys(0,0); + sys::Process::GetTimeUsage(now,user,sys); + return (user.seconds() + (double) user.nanoseconds() * 1e-9); +} + +double util::getWallTime() { + sys::TimeValue now(0,0),user(0,0),sys(0,0); + sys::Process::GetTimeUsage(now,user,sys); + return (now.seconds() + (double) now.nanoseconds() * 1e-9); +} diff --git a/lib/Support/Timer.cpp b/lib/Support/Timer.cpp new file mode 100644 index 00000000..cddb0707 --- /dev/null +++ b/lib/Support/Timer.cpp @@ -0,0 +1,27 @@ +//===-- Timer.cpp ---------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Internal/Support/Timer.h" + +#include "llvm/System/Process.h" + +using namespace klee; +using namespace llvm; + +WallTimer::WallTimer() { + sys::TimeValue now(0,0),user(0,0),sys(0,0); + sys::Process::GetTimeUsage(now,user,sys); + startMicroseconds = now.usec(); +} + +uint64_t WallTimer::check() { + sys::TimeValue now(0,0),user(0,0),sys(0,0); + sys::Process::GetTimeUsage(now,user,sys); + return now.usec() - startMicroseconds; +} diff --git a/lib/Support/TreeStream.cpp b/lib/Support/TreeStream.cpp new file mode 100644 index 00000000..0e8b86dd --- /dev/null +++ b/lib/Support/TreeStream.cpp @@ -0,0 +1,201 @@ +//===-- TreeStream.cpp ----------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Internal/ADT/TreeStream.h" + +#include <cassert> +#include <iostream> +#include <iomanip> +#include <fstream> +#include <iterator> +#include <map> + +#include <string.h> + +using namespace klee; + +/// + +TreeStreamWriter::TreeStreamWriter(const std::string &_path) + : lastID(0), + bufferCount(0), + path(_path), + output(new std::ofstream(path.c_str(), + std::ios::out | std::ios::binary)), + ids(1) { + if (!output->good()) { + delete output; + output = 0; + } +} + +TreeStreamWriter::~TreeStreamWriter() { + flush(); + if (output) + delete output; +} + +bool TreeStreamWriter::good() { + return !!output; +} + +TreeOStream TreeStreamWriter::open() { + return open(TreeOStream(*this, 0)); +} + +TreeOStream TreeStreamWriter::open(const TreeOStream &os) { + assert(output && os.writer==this); + flushBuffer(); + unsigned id = ids++; + output->write(reinterpret_cast<const char*>(&os.id), 4); + unsigned tag = id | (1<<31); + output->write(reinterpret_cast<const char*>(&tag), 4); + return TreeOStream(*this, id); +} + +void TreeStreamWriter::write(TreeOStream &os, const char *s, unsigned size) { +#if 1 + if (bufferCount && + (os.id!=lastID || size+bufferCount>bufferSize)) + flushBuffer(); + if (bufferCount) { // (os.id==lastID && size+bufferCount<=bufferSize) + memcpy(&buffer[bufferCount], s, size); + bufferCount += size; + } else if (size<bufferSize) { + lastID = os.id; + memcpy(buffer, s, size); + bufferCount = size; + } else { + output->write(reinterpret_cast<const char*>(&os.id), 4); + output->write(reinterpret_cast<const char*>(&size), 4); + output->write(buffer, size); + } +#else + output->write(reinterpret_cast<const char*>(&os.id), 4); + output->write(reinterpret_cast<const char*>(&size), 4); + output->write(s, size); +#endif +} + +void TreeStreamWriter::flushBuffer() { + if (bufferCount) { + output->write(reinterpret_cast<const char*>(&lastID), 4); + output->write(reinterpret_cast<const char*>(&bufferCount), 4); + output->write(buffer, bufferCount); + bufferCount = 0; + } +} + +void TreeStreamWriter::flush() { + flushBuffer(); + output->flush(); +} + +void TreeStreamWriter::readStream(TreeStreamID streamID, + std::vector<unsigned char> &out) { + assert(streamID>0 && streamID<ids); + flush(); + + std::ifstream is(path.c_str(), + std::ios::in | std::ios::binary); + assert(is.good()); +#if 0 + std::cout << "finding chain for: " << streamID << "\n"; +#endif + + std::map<unsigned,unsigned> parents; + std::vector<unsigned> roots; + for (;;) { + assert(is.good()); + unsigned id; + unsigned tag; + is.read(reinterpret_cast<char*>(&id), 4); + is.read(reinterpret_cast<char*>(&tag), 4); + if (tag&(1<<31)) { // fork + unsigned child = tag ^ (1<<31); + + if (child==streamID) { + roots.push_back(child); + while (id) { + roots.push_back(id); + std::map<unsigned, unsigned>::iterator it = parents.find(id); + assert(it!=parents.end()); + id = it->second; + } + break; + } else { + parents.insert(std::make_pair(child,id)); + } + } else { + unsigned size = tag; + while (size--) is.get(); + } + } +#if 0 + std::cout << "roots: "; + std::copy(roots.begin(), roots.end(), std::ostream_iterator<unsigned>(std::cout, " ")); + std::cout << "\n"; +#endif + is.seekg(0, std::ios::beg); + for (;;) { + unsigned id; + unsigned tag; + is.read(reinterpret_cast<char*>(&id), 4); + is.read(reinterpret_cast<char*>(&tag), 4); + if (!is.good()) break; + if (tag&(1<<31)) { // fork + unsigned child = tag ^ (1<<31); + if (id==roots.back() && roots.size()>1 && child==roots[roots.size()-2]) + roots.pop_back(); + } else { + unsigned size = tag; + if (id==roots.back()) { + while (size--) out.push_back(is.get()); + } else { + while (size--) is.get(); + } + } + } +} + +/// + +TreeOStream::TreeOStream() + : writer(0), + id(0) { +} + +TreeOStream::TreeOStream(TreeStreamWriter &_writer, unsigned _id) + : writer(&_writer), + id(_id) { +} + +TreeOStream::~TreeOStream() { +} + +unsigned TreeOStream::getID() const { + assert(writer); + return id; +} + +void TreeOStream::write(const char *buffer, unsigned size) { + assert(writer); + writer->write(*this, buffer, size); +} + +TreeOStream &TreeOStream::operator<<(const std::string &s) { + assert(writer); + write(s.c_str(), s.size()); + return *this; +} + +void TreeOStream::flush() { + assert(writer); + writer->flush(); +} diff --git a/runtime/Intrinsic/Makefile b/runtime/Intrinsic/Makefile new file mode 100644 index 00000000..ce4a34ba --- /dev/null +++ b/runtime/Intrinsic/Makefile @@ -0,0 +1,20 @@ +#===-- runtime/Intrinsic/Makefile --------------------------*- Makefile -*--===# +# +# The KLEE Symbolic Virtual Machine +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +LEVEL=../.. + +LIBRARYNAME=intrinsic +DONT_BUILD_RELINKED=1 +BUILD_ARCHIVE=1 +BYTECODE_LIBRARY=1 +# Don't strip debug info from the module. +DEBUG_RUNTIME=1 +NO_PEDANTIC=1 + +include $(LEVEL)/Makefile.common diff --git a/runtime/Intrinsic/klee_div_zero_check.c b/runtime/Intrinsic/klee_div_zero_check.c new file mode 100644 index 00000000..3fde1af5 --- /dev/null +++ b/runtime/Intrinsic/klee_div_zero_check.c @@ -0,0 +1,15 @@ +//===-- klee_div_zero_check.c ---------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <klee/klee.h> + +void klee_div_zero_check(long long z) { + if (z == 0) + klee_report_error(__FILE__, __LINE__, "divide by zero", "div.err"); +} diff --git a/runtime/Intrinsic/klee_int.c b/runtime/Intrinsic/klee_int.c new file mode 100644 index 00000000..88ec5026 --- /dev/null +++ b/runtime/Intrinsic/klee_int.c @@ -0,0 +1,17 @@ +//===-- klee_int.c --------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <assert.h> +#include <klee/klee.h> + +int klee_int(const char *name) { + int x; + klee_make_symbolic_name(&x, sizeof x, name); + return x; +} diff --git a/runtime/Intrinsic/klee_make_symbolic.c b/runtime/Intrinsic/klee_make_symbolic.c new file mode 100644 index 00000000..b9dec2a7 --- /dev/null +++ b/runtime/Intrinsic/klee_make_symbolic.c @@ -0,0 +1,14 @@ +//===-- klee_make_symbolic.c ----------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <klee/klee.h> + +void klee_make_symbolic(void *addr, unsigned nbytes) { + klee_make_symbolic_name(addr, nbytes, "unnamed"); +} diff --git a/runtime/Intrinsic/klee_range.c b/runtime/Intrinsic/klee_range.c new file mode 100644 index 00000000..59d1a05e --- /dev/null +++ b/runtime/Intrinsic/klee_range.c @@ -0,0 +1,33 @@ +//===-- klee_range.c ------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <assert.h> +#include <klee/klee.h> + +int klee_range(int start, int end, const char* name) { + int x; + + assert(start < end); + + if (start+1==end) { + return start; + } else { + klee_make_symbolic_name(&x, sizeof x, name); + + /* Make nicer constraint when simple... */ + if (start==0) { + klee_assume((unsigned) x < (unsigned) end); + } else { + klee_assume(start <= x); + klee_assume(x < end); + } + + return x; + } +} diff --git a/runtime/Intrinsic/memcpy.c b/runtime/Intrinsic/memcpy.c new file mode 100644 index 00000000..14b98ce6 --- /dev/null +++ b/runtime/Intrinsic/memcpy.c @@ -0,0 +1,19 @@ +//===-- memcpy.c ----------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <stdlib.h> + +void *memcpy(void *destaddr, void const *srcaddr, unsigned int len) { + char *dest = destaddr; + char const *src = srcaddr; + + while (len-- > 0) + *dest++ = *src++; + return destaddr; +} diff --git a/runtime/Intrinsic/memmove.c b/runtime/Intrinsic/memmove.c new file mode 100644 index 00000000..c6e1ada9 --- /dev/null +++ b/runtime/Intrinsic/memmove.c @@ -0,0 +1,28 @@ +//===-- memmove.c ---------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <stdlib.h> + +void *memmove(void *dst, const void *src, size_t count) { + char *a = dst; + const char *b = src; + + if (src == dst) + return dst; + + if (src>dst) { + while (count--) *a++ = *b++; + } else { + a+=count-1; + b+=count-1; + while (count--) *a-- = *b--; + } + + return dst; +} diff --git a/runtime/Intrinsic/mempcpy.c b/runtime/Intrinsic/mempcpy.c new file mode 100644 index 00000000..6327e748 --- /dev/null +++ b/runtime/Intrinsic/mempcpy.c @@ -0,0 +1,19 @@ +//===-- mempcpy.c ---------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <stdlib.h> + +void *mempcpy(void *destaddr, void const *srcaddr, unsigned int len) { + char *dest = destaddr; + char const *src = srcaddr; + + while (len-- > 0) + *dest++ = *src++; + return dest; +} diff --git a/runtime/Intrinsic/memset.c b/runtime/Intrinsic/memset.c new file mode 100644 index 00000000..ee9ecb87 --- /dev/null +++ b/runtime/Intrinsic/memset.c @@ -0,0 +1,17 @@ +//===-- memset.c ----------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <stdlib.h> + +void *memset(void * dst, int s, size_t count) { + char * a = dst; + while (count-- > 0) + *a++ = s; + return dst; +} diff --git a/runtime/Makefile b/runtime/Makefile new file mode 100755 index 00000000..24824d08 --- /dev/null +++ b/runtime/Makefile @@ -0,0 +1,26 @@ +#===-- runtime/Makefile ------------------------------------*- Makefile -*--===# +# +# The KLEE Symbolic Virtual Machine +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +# +# Relative path to the top of the source tree. +# +LEVEL=.. + +# +# List all of the subdirectories that we will compile. +# +PARALLEL_DIRS=Intrinsic klee-libc Runtest + +include $(LEVEL)/Makefile.config + +ifeq ($(ENABLE_POSIX_RUNTIME),1) +PARALLEL_DIRS += POSIX +endif + +include $(LEVEL)/Makefile.common diff --git a/runtime/POSIX/Makefile b/runtime/POSIX/Makefile new file mode 100644 index 00000000..9a42f5c0 --- /dev/null +++ b/runtime/POSIX/Makefile @@ -0,0 +1,20 @@ +#===-- runtime/POSIX/Makefile ------------------------------*- Makefile -*--===# +# +# The KLEE Symbolic Virtual Machine +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +LEVEL=../.. + +LIBRARYNAME=kleeRuntimePOSIX +DONT_BUILD_RELINKED=1 +BUILD_ARCHIVE=1 +BYTECODE_LIBRARY=1 +# Don't strip debug info from the module. +DEBUG_RUNTIME=1 +NO_PEDANTIC=1 + +include $(LEVEL)/Makefile.common diff --git a/runtime/POSIX/fd.c b/runtime/POSIX/fd.c new file mode 100644 index 00000000..367a8c4a --- /dev/null +++ b/runtime/POSIX/fd.c @@ -0,0 +1,1287 @@ +//===-- fd.c --------------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#define _LARGEFILE64_SOURCE +#include "fd.h" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/syscall.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> +#include <stdarg.h> +#include <assert.h> +#include <sys/vfs.h> +#include <unistd.h> +#include <dirent.h> +#include <sys/ioctl.h> +#include <sys/mtio.h> +#include <termios.h> +#include <sys/select.h> +#include <klee/klee.h> + +/* #define DEBUG */ + +void klee_warning(const char*); +void klee_warning_once(const char*); +int klee_get_errno(void); + +/* Returns pointer to the symbolic file structure is the pathname is symbolic */ +static exe_disk_file_t *__get_sym_file(const char *pathname) { + char c = pathname[0]; + unsigned i; + + if (c == 0 || pathname[1] != 0) + return NULL; + + for (i=0; i<__exe_fs.n_sym_files; ++i) { + if (c == 'A' + (char) i) { + exe_disk_file_t *df = &__exe_fs.sym_files[i]; + if (df->stat->st_ino == 0) + return NULL; + return df; + } + } + + return NULL; +} + +static void *__concretize_ptr(const void *p); +static size_t __concretize_size(size_t s); +static const char *__concretize_string(const char *s); + +/* Returns pointer to the file entry for a valid fd */ +static exe_file_t *__get_file(int fd) { + if (fd>=0 && fd<MAX_FDS) { + exe_file_t *f = &__exe_env.fds[fd]; + if (f->flags & eOpen) + return f; + } + + return 0; +} + +int access(const char *pathname, int mode) { + exe_disk_file_t *dfile = __get_sym_file(pathname); + + if (dfile) { + /* XXX we should check against stat values but we also need to + enforce in open and friends then. */ + return 0; + } else { + int r = syscall(__NR_access, __concretize_string(pathname), mode); + if (r == -1) + errno = klee_get_errno(); + return r; + } +} + +mode_t umask(mode_t mask) { + mode_t r = __exe_env.umask; + __exe_env.umask = mask & 0777; + return r; +} + + +/* Returns 1 if the process has the access rights specified by 'flags' + to the file with stat 's'. Returns 0 otherwise*/ +static int has_permission(int flags, struct stat64 *s) { + int write_access, read_access; + mode_t mode = s->st_mode; + + if (flags & O_RDONLY || flags & O_RDWR) + read_access = 1; + else read_access = 0; + + if (flags & O_WRONLY || flags & O_RDWR) + write_access = 1; + else write_access = 0; + + /* XXX: We don't worry about process uid and gid for now. + We allow access if any user has access to the file. */ +#if 0 + uid_t uid = s->st_uid; + uid_t euid = geteuid(); + gid_t gid = s->st_gid; + gid_t egid = getegid(); +#endif + + if (read_access && ((mode & S_IRUSR) | (mode & S_IRGRP) | (mode & S_IROTH))) + return 0; + + if (write_access && !((mode & S_IWUSR) | (mode & S_IWGRP) | (mode & S_IWOTH))) + return 0; + + return 1; +} + + +int __fd_open(const char *pathname, int flags, mode_t mode) { + exe_disk_file_t *df; + exe_file_t *f; + int fd; + + for (fd = 0; fd < MAX_FDS; ++fd) + if (!(__exe_env.fds[fd].flags & eOpen)) + break; + if (fd == MAX_FDS) { + errno = EMFILE; + return -1; + } + + f = &__exe_env.fds[fd]; + + /* Should be the case if file was available, but just in case. */ + memset(f, 0, sizeof *f); + + df = __get_sym_file(pathname); + if (df) { + /* XXX Should check access against mode / stat / possible + deletion. */ + f->dfile = df; + + if ((flags & O_CREAT) && (flags & O_EXCL)) { + errno = EEXIST; + return -1; + } + + if ((flags & O_TRUNC) && (flags & O_RDONLY)) { + /* The result of using O_TRUNC with O_RDONLY is undefined, so we + return error */ + fprintf(stderr, "Undefined call to open(): O_TRUNC | O_RDONLY\n"); + errno = EACCES; + return -1; + } + + if ((flags & O_EXCL) && !(flags & O_CREAT)) { + /* The result of using O_EXCL without O_CREAT is undefined, so + we return error */ + fprintf(stderr, "Undefined call to open(): O_EXCL w/o O_RDONLY\n"); + errno = EACCES; + return -1; + } + + if (!has_permission(flags, df->stat)) { + errno = EACCES; + return -1; + } + else + f->dfile->stat->st_mode = ((f->dfile->stat->st_mode & ~0777) | + (mode & ~__exe_env.umask)); + } else { + int os_fd = syscall(__NR_open, __concretize_string(pathname), flags, mode); + if (os_fd == -1) { + errno = klee_get_errno(); + return -1; + } + f->fd = os_fd; + } + + f->flags = eOpen; + if ((flags & O_ACCMODE) == O_RDONLY) { + f->flags |= eReadable; + } else if ((flags & O_ACCMODE) == O_WRONLY) { + f->flags |= eWriteable; + } else { /* XXX What actually happens here if != O_RDWR. */ + f->flags |= eReadable | eWriteable; + } + + return fd; +} + +int close(int fd) { + static int n_calls = 0; + exe_file_t *f; + int r = 0; + + n_calls++; + + f = __get_file(fd); + if (!f) { + errno = EBADF; + return -1; + } + + if (__exe_fs.max_failures && *__exe_fs.close_fail == n_calls) { + __exe_fs.max_failures--; + errno = EIO; + return -1; + } + +#if 0 + if (!f->dfile) { + /* if a concrete fd */ + r = syscall(__NR_close, f->fd); + } + else r = 0; +#endif + + memset(f, 0, sizeof *f); + + return r; +} + +ssize_t read(int fd, void *buf, size_t count) { + static int n_calls = 0; + exe_file_t *f; + + n_calls++; + + if (count == 0) + return 0; + + if (buf == NULL) { + errno = EFAULT; + return -1; + } + + f = __get_file(fd); + + if (!f) { + errno = EBADF; + return -1; + } + + if (__exe_fs.max_failures && *__exe_fs.read_fail == n_calls) { + __exe_fs.max_failures--; + errno = EIO; + return -1; + } + + if (!f->dfile) { + /* concrete file */ + int r; + buf = __concretize_ptr(buf); + count = __concretize_size(count); + /* XXX In terms of looking for bugs we really should do this check + before concretization, at least once the routine has been fixed + to properly work with symbolics. */ + klee_check_memory_access(buf, count); + if (f->fd == 0) + r = syscall(__NR_read, f->fd, buf, count); + else + r = syscall(__NR_pread64, f->fd, buf, count, (off64_t) f->off); + + if (r == -1) { + errno = klee_get_errno(); + return -1; + } + + if (f->fd != 0) + f->off += r; + return r; + } + else { + assert(f->off >= 0); + if (f->dfile->size < f->off) + return 0; + + /* symbolic file */ + if (f->off + count > f->dfile->size) { + count = f->dfile->size - f->off; + } + + memcpy(buf, f->dfile->contents + f->off, count); + f->off += count; + + return count; + } +} + + +ssize_t write(int fd, const void *buf, size_t count) { + static int n_calls = 0; + exe_file_t *f; + + n_calls++; + + f = __get_file(fd); + + if (!f) { + errno = EBADF; + return -1; + } + + if (__exe_fs.max_failures && *__exe_fs.write_fail == n_calls) { + __exe_fs.max_failures--; + errno = EIO; + return -1; + } + + if (!f->dfile) { + int r; + + buf = __concretize_ptr(buf); + count = __concretize_size(count); + /* XXX In terms of looking for bugs we really should do this check + before concretization, at least once the routine has been fixed + to properly work with symbolics. */ + klee_check_memory_access(buf, count); + if (f->fd == 1 || f->fd == 2) + r = syscall(__NR_write, f->fd, buf, count); + else r = syscall(__NR_pwrite64, f->fd, buf, count, (off64_t) f->off); + + if (r == -1) { + errno = klee_get_errno(); + return -1; + } + + assert(r >= 0); + if (f->fd != 1 && f->fd != 2) + f->off += r; + + return r; + } + else { + /* symbolic file */ + size_t actual_count = 0; + if (f->off + count <= f->dfile->size) + actual_count = count; + else { + if (__exe_env.save_all_writes) + assert(0); + else { + if (f->off < f->dfile->size) + actual_count = f->dfile->size - f->off; + } + } + + if (actual_count) + memcpy(f->dfile->contents + f->off, buf, actual_count); + + if (count != actual_count) + fprintf(stderr, "WARNING: write() ignores bytes.\n"); + + if (f->dfile == __exe_fs.sym_stdout) + __exe_fs.stdout_writes += actual_count; + + f->off += count; + return count; + } +} + + +off64_t __fd_lseek(int fd, off64_t offset, int whence) { + off64_t new_off; + exe_file_t *f = __get_file(fd); + + if (!f) { + errno = EBADF; + return -1; + } + + if (!f->dfile) { + /* We could always do SEEK_SET then whence, but this causes + troubles with directories since we play nasty tricks with the + offset, and the OS doesn't want us to randomly seek + directories. We could detect if it is a directory and correct + the offset, but really directories should only be SEEK_SET, so + this solves the problem. */ + if (whence == SEEK_SET) { + new_off = syscall(__NR_lseek, f->fd, (int) offset, SEEK_SET); + } else { + new_off = syscall(__NR_lseek, f->fd, (int) f->off, SEEK_SET); + + /* If we can't seek to start off, just return same error. + Probably ESPIPE. */ + if (new_off != -1) { + assert(new_off == f->off); + new_off = syscall(__NR_lseek, f->fd, (int) offset, whence); + } + } + + if (new_off == -1) { + errno = klee_get_errno(); + return -1; + } + + f->off = new_off; + return new_off; + } + + switch (whence) { + case SEEK_SET: new_off = offset; break; + case SEEK_CUR: new_off = f->off + offset; break; + case SEEK_END: new_off = f->dfile->size + offset; break; + default: { + errno = EINVAL; + return (off64_t) -1; + } + } + + if (new_off < 0) { + errno = EINVAL; + return (off64_t) -1; + } + + f->off = new_off; + return f->off; +} + +int __fd_stat(const char *path, struct stat64 *buf) { + exe_disk_file_t *dfile = __get_sym_file(path); + if (dfile) { + memcpy(buf, dfile->stat, sizeof(*dfile->stat)); + return 0; + } + + { + int r = syscall(__NR_stat64, __concretize_string(path), buf); + if (r == -1) + errno = klee_get_errno(); + return r; + } +} + +int __fd_lstat(const char *path, struct stat64 *buf) { + exe_disk_file_t *dfile = __get_sym_file(path); + if (dfile) { + memcpy(buf, dfile->stat, sizeof(*dfile->stat)); + return 0; + } + + { + int r = syscall(__NR_lstat64, __concretize_string(path), buf); + if (r == -1) + errno = klee_get_errno(); + return r; + } +} + +int chdir(const char *path) { + exe_disk_file_t *dfile = __get_sym_file(path); + + if (dfile) { + /* XXX incorrect */ + klee_warning("symbolic file, ignoring (ENOENT)"); + errno = ENOENT; + return -1; + } + + { + int r = syscall(__NR_chdir, __concretize_string(path)); + if (r == -1) + errno = klee_get_errno(); + return r; + } +} + +int fchdir(int fd) { + exe_file_t *f = __get_file(fd); + + if (!f) { + errno = EBADF; + return -1; + } + + if (f->dfile) { + klee_warning("symbolic file, ignoring (ENOENT)"); + errno = ENOENT; + return -1; + } else { + int r = syscall(__NR_fchdir, f->fd); + if (r == -1) + errno = klee_get_errno(); + return r; + } +} + +/* Sets mode and or errno and return appropriate result. */ +static int __df_chmod(exe_disk_file_t *df, mode_t mode) { + if (geteuid() == df->stat->st_uid) { + if (getgid() != df->stat->st_gid) + mode &= ~ S_ISGID; + df->stat->st_mode = ((df->stat->st_mode & ~07777) | + (mode & 07777)); + return 0; + } else { + errno = EPERM; + return -1; + } +} + +int chmod(const char *path, mode_t mode) { + static int n_calls = 0; + + exe_disk_file_t *dfile = __get_sym_file(path); + + n_calls++; + if (__exe_fs.max_failures && *__exe_fs.chmod_fail == n_calls) { + __exe_fs.max_failures--; + errno = EIO; + return -1; + } + + if (dfile) { + return __df_chmod(dfile, mode); + } else { + int r = syscall(__NR_chmod, __concretize_string(path), mode); + if (r == -1) + errno = klee_get_errno(); + return r; + } +} + +int fchmod(int fd, mode_t mode) { + static int n_calls = 0; + + exe_file_t *f = __get_file(fd); + + if (!f) { + errno = EBADF; + return -1; + } + + n_calls++; + if (__exe_fs.max_failures && *__exe_fs.fchmod_fail == n_calls) { + __exe_fs.max_failures--; + errno = EIO; + return -1; + } + + if (f->dfile) { + return __df_chmod(f->dfile, mode); + } else { + int r = syscall(__NR_fchmod, f->fd, mode); + if (r == -1) + errno = klee_get_errno(); + return r; + } +} + +static int __df_chown(exe_disk_file_t *df, uid_t owner, gid_t group) { + klee_warning("symbolic file, ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int chown(const char *path, uid_t owner, gid_t group) { + exe_disk_file_t *df = __get_sym_file(path); + + if (df) { + return __df_chown(df, owner, group); + } else { + int r = syscall(__NR_chown, __concretize_string(path), owner, group); + if (r == -1) + errno = klee_get_errno(); + return r; + } +} + +int fchown(int fd, uid_t owner, gid_t group) { + exe_file_t *f = __get_file(fd); + + if (!f) { + errno = EBADF; + return -1; + } + + if (f->dfile) { + return __df_chown(f->dfile, owner, group); + } else { + int r = syscall(__NR_fchown, fd, owner, group); + if (r == -1) + errno = klee_get_errno(); + return r; + } +} + +int lchown(const char *path, uid_t owner, gid_t group) { + /* XXX Ignores 'l' part */ + exe_disk_file_t *df = __get_sym_file(path); + + if (df) { + return __df_chown(df, owner, group); + } else { + int r = syscall(__NR_chown, __concretize_string(path), owner, group); + if (r == -1) + errno = klee_get_errno(); + return r; + } +} + +int __fd_fstat(int fd, struct stat64 *buf) { + exe_file_t *f = __get_file(fd); + + if (!f) { + errno = EBADF; + return -1; + } + + if (!f->dfile) { + int r = syscall(__NR_fstat64, f->fd, buf); + if (r == -1) + errno = klee_get_errno(); + return r; + } + + memcpy(buf, f->dfile->stat, sizeof(*f->dfile->stat)); + return 0; +} + +int __fd_ftruncate(int fd, off64_t length) { + static int n_calls = 0; + exe_file_t *f = __get_file(fd); + + n_calls++; + + if (!f) { + errno = EBADF; + return -1; + } + + if (__exe_fs.max_failures && *__exe_fs.ftruncate_fail == n_calls) { + __exe_fs.max_failures--; + errno = EIO; + return -1; + } + + if (f->dfile) { + klee_warning("symbolic file, ignoring (EIO)"); + errno = EIO; + return -1; + } else { + int r = syscall(__NR_ftruncate64, f->fd, length); + if (r == -1) + errno = klee_get_errno(); + return r; + } +} + +int __fd_getdents(unsigned int fd, struct dirent64 *dirp, unsigned int count) { + exe_file_t *f = __get_file(fd); + + if (!f) { + errno = EBADF; + return -1; + } + + if (f->dfile) { + klee_warning("symbolic file, ignoring (EINVAL)"); + errno = EINVAL; + return -1; + } else { + if ((unsigned) f->off < 4096u) { + /* Return our dirents */ + unsigned i, pad, bytes=0; + + /* What happens for bad offsets? */ + i = f->off / sizeof(*dirp); + if ((i * sizeof(*dirp) != f->off) || + i > __exe_fs.n_sym_files) { + errno = EINVAL; + return -1; + } + for (; i<__exe_fs.n_sym_files; ++i) { + exe_disk_file_t *df = &__exe_fs.sym_files[i]; + dirp->d_ino = df->stat->st_ino; + dirp->d_reclen = sizeof(*dirp); + dirp->d_type = IFTODT(df->stat->st_mode); + dirp->d_name[0] = 'A' + i; + dirp->d_name[1] = '\0'; + dirp->d_off = (i+1) * sizeof(*dirp); + bytes += dirp->d_reclen; + ++dirp; + } + + /* Fake jump to OS records by a "deleted" file. */ + pad = count>=4096 ? 4096 : count; + dirp->d_ino = 0; + dirp->d_reclen = pad - bytes; + dirp->d_type = DT_UNKNOWN; + dirp->d_name[0] = '\0'; + dirp->d_off = 4096; + bytes += dirp->d_reclen; + f->off = pad; + return bytes; + } else { + unsigned os_pos = f->off - 4096; + int res, s; + + /* For reasons which I really don't understand, if I don't + memset this then sometimes the kernel returns d_ino==0 for + some valid entries? Am I crazy? Can writeback possibly be + failing? + + Even more bizarre, interchanging the memset and the seek also + case strange behavior. Really should be debugged properly. */ + memset(dirp, 0, count); + s = syscall(__NR_lseek, f->fd, (int) os_pos, SEEK_SET); + assert(s != (off64_t) -1); + res = syscall(__NR_getdents64, f->fd, dirp, count); + if (res == -1) { + errno = klee_get_errno(); + } else { + int pos = 0; + + f->off = syscall(__NR_lseek, f->fd, 0, SEEK_CUR) + 4096; + + /* Patch offsets */ + + while (pos < res) { + struct dirent64 *dp = (struct dirent64*) ((char*) dirp + pos); + dp->d_off += 4096; + pos += dp->d_reclen; + } + } + return res; + } + } +} + +int ioctl(int fd, unsigned long request, ...) { + exe_file_t *f = __get_file(fd); + va_list ap; + void *buf; + +#if 0 + printf("In ioctl(%d, ...)\n", fd); +#endif + + if (!f) { + errno = EBADF; + return -1; + } + + va_start(ap, request); + buf = va_arg(ap, void*); + va_end(ap); + + if (f->dfile) { + struct stat *stat = (struct stat*) f->dfile->stat; + + switch (request) { + case TCGETS: { + struct termios *ts = buf; + + klee_warning_once("(TCGETS) symbolic file, incomplete model"); + + /* XXX need more data, this is ok but still not good enough */ + if (S_ISCHR(stat->st_mode)) { + /* Just copied from my system, munged to match what fields + uclibc thinks are there. */ + ts->c_iflag = 27906; + ts->c_oflag = 5; + ts->c_cflag = 1215; + ts->c_lflag = 35287; + ts->c_line = 0; + ts->c_cc[0] = '\x03'; + ts->c_cc[1] = '\x1c'; + ts->c_cc[2] = '\x7f'; + ts->c_cc[3] = '\x15'; + ts->c_cc[4] = '\x04'; + ts->c_cc[5] = '\x00'; + ts->c_cc[6] = '\x01'; + ts->c_cc[7] = '\xff'; + ts->c_cc[8] = '\x11'; + ts->c_cc[9] = '\x13'; + ts->c_cc[10] = '\x1a'; + ts->c_cc[11] = '\xff'; + ts->c_cc[12] = '\x12'; + ts->c_cc[13] = '\x0f'; + ts->c_cc[14] = '\x17'; + ts->c_cc[15] = '\x16'; + ts->c_cc[16] = '\xff'; + ts->c_cc[17] = '\x0'; + ts->c_cc[18] = '\x0'; + return 0; + } else { + errno = ENOTTY; + return -1; + } + } + case TCSETS: { + /* const struct termios *ts = buf; */ + klee_warning_once("(TCSETS) symbolic file, silently ignoring"); + if (S_ISCHR(stat->st_mode)) { + return 0; + } else { + errno = ENOTTY; + return -1; + } + } + case TCSETSW: { + /* const struct termios *ts = buf; */ + klee_warning_once("(TCSETSW) symbolic file, silently ignoring"); + if (fd==0) { + return 0; + } else { + errno = ENOTTY; + return -1; + } + } + case TCSETSF: { + /* const struct termios *ts = buf; */ + klee_warning_once("(TCSETSF) symbolic file, silently ignoring"); + if (S_ISCHR(stat->st_mode)) { + return 0; + } else { + errno = ENOTTY; + return -1; + } + } + case TIOCGWINSZ: { + struct winsize *ws = buf; + ws->ws_row = 24; + ws->ws_col = 80; + klee_warning_once("(TIOCGWINSZ) symbolic file, incomplete model"); + if (S_ISCHR(stat->st_mode)) { + return 0; + } else { + errno = ENOTTY; + return -1; + } + } + case TIOCSWINSZ: { + /* const struct winsize *ws = buf; */ + klee_warning_once("(TIOCSWINSZ) symbolic file, ignoring (EINVAL)"); + if (S_ISCHR(stat->st_mode)) { + errno = EINVAL; + return -1; + } else { + errno = ENOTTY; + return -1; + } + } + case FIONREAD: { + int *res = buf; + klee_warning_once("(FIONREAD) symbolic file, incomplete model"); + if (S_ISCHR(stat->st_mode)) { + if (f->off < f->dfile->size) { + *res = f->dfile->size - f->off; + } else { + *res = 0; + } + return 0; + } else { + errno = ENOTTY; + return -1; + } + } + case MTIOCGET: { + klee_warning("(MTIOCGET) symbolic file, ignoring (EINVAL)"); + errno = EINVAL; + return -1; + } + default: + klee_warning("symbolic file, ignoring (EINVAL)"); + errno = EINVAL; + return -1; + } + } else { + int r = syscall(__NR_ioctl, f->fd, request, buf ); + if (r == -1) + errno = klee_get_errno(); + return r; + } +} + +int fcntl(int fd, int cmd, ...) { + exe_file_t *f = __get_file(fd); + va_list ap; + unsigned arg; /* 32 bit assumption (int/ptr) */ + + if (!f) { + errno = EBADF; + return -1; + } + + if (cmd==F_GETFD || cmd==F_GETFL || cmd==F_GETOWN || cmd==F_GETSIG || + cmd==F_GETLEASE || cmd==F_NOTIFY) { + arg = 0; + } else { + va_start(ap, cmd); + arg = va_arg(ap, int); + va_end(ap); + } + + if (f->dfile) { + switch(cmd) { + case F_GETFD: { + int flags = 0; + if (f->flags & eCloseOnExec) + flags |= FD_CLOEXEC; + return flags; + } + case F_SETFD: { + f->flags &= ~eCloseOnExec; + if (arg & FD_CLOEXEC) + f->flags |= eCloseOnExec; + return 0; + } + case F_GETFL: { + /* XXX (CrC): This should return the status flags: O_APPEND, + O_ASYNC, O_DIRECT, O_NOATIME, O_NONBLOCK. As of now, we + discard these flags during open(). We should save them and + return them here. These same flags can be set by F_SETFL, + which we could also handle properly. + */ + return 0; + } + default: + klee_warning("symbolic file, ignoring (EINVAL)"); + errno = EINVAL; + return -1; + } + } else { + int r = syscall(__NR_fcntl, f->fd, cmd, arg ); + if (r == -1) + errno = klee_get_errno(); + return r; + } +} + +int __fd_statfs(const char *path, struct statfs *buf) { + exe_disk_file_t *dfile = __get_sym_file(path); + if (dfile) { + /* XXX incorrect */ + klee_warning("symbolic file, ignoring (ENOENT)"); + errno = ENOENT; + return -1; + } + + { + int r = syscall(__NR_statfs, __concretize_string(path), buf); + if (r == -1) + errno = klee_get_errno(); + return r; + } +} + +int fstatfs(int fd, struct statfs *buf) { + exe_file_t *f = __get_file(fd); + + if (!f) { + errno = EBADF; + return -1; + } + + if (f->dfile) { + klee_warning("symbolic file, ignoring (EBADF)"); + errno = EBADF; + return -1; + } else { + int r = syscall(__NR_fstatfs, f->fd, buf); + if (r == -1) + errno = klee_get_errno(); + return r; + } +} + +int fsync(int fd) { + exe_file_t *f = __get_file(fd); + + if (!f) { + errno = EBADF; + return -1; + } else if (f->dfile) { + return 0; + } else { + int r = syscall(__NR_fsync, f->fd); + if (r == -1) + errno = klee_get_errno(); + return r; + } +} + +int dup2(int oldfd, int newfd) { + exe_file_t *f = __get_file(oldfd); + + if (!f || !(newfd>=0 && newfd<MAX_FDS)) { + errno = EBADF; + return -1; + } else { + exe_file_t *f2 = &__exe_env.fds[newfd]; + if (f2->flags & eOpen) close(newfd); + + /* XXX Incorrect, really we need another data structure for open + files */ + *f2 = *f; + + f2->flags &= ~eCloseOnExec; + + /* I'm not sure it is wise, but we can get away with not dup'ng + the OS fd, since actually that will in many cases effect the + sharing of the open file (and the process should never have + access to it). */ + + return newfd; + } +} + +int dup(int oldfd) { + exe_file_t *f = __get_file(oldfd); + if (!f) { + errno = EBADF; + return -1; + } else { + int fd; + for (fd = 0; fd < MAX_FDS; ++fd) + if (!(__exe_env.fds[fd].flags & eOpen)) + break; + if (fd == MAX_FDS) { + errno = EMFILE; + return -1; + } else { + return dup2(oldfd, fd); + } + } +} + +int rmdir(const char *pathname) { + exe_disk_file_t *dfile = __get_sym_file(pathname); + if (dfile) { + /* XXX check access */ + if (S_ISDIR(dfile->stat->st_mode)) { + dfile->stat->st_ino = 0; + return 0; + } else { + errno = ENOTDIR; + return -1; + } + } + + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int unlink(const char *pathname) { + exe_disk_file_t *dfile = __get_sym_file(pathname); + if (dfile) { + /* XXX check access */ + if (S_ISREG(dfile->stat->st_mode)) { + dfile->stat->st_ino = 0; + return 0; + } else if (S_ISDIR(dfile->stat->st_mode)) { + errno = EISDIR; + return -1; + } else { + errno = EPERM; + return -1; + } + } + + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +ssize_t readlink(const char *path, char *buf, size_t bufsize) { + exe_disk_file_t *dfile = __get_sym_file(path); + if (dfile) { + /* XXX We need to get the sym file name really, but since we don't + handle paths anyway... */ + if (S_ISLNK(dfile->stat->st_mode)) { + buf[0] = path[0]; + if (bufsize>1) buf[1] = '.'; + if (bufsize>2) buf[2] = 'l'; + if (bufsize>3) buf[3] = 'n'; + if (bufsize>4) buf[4] = 'k'; + return (bufsize>5) ? 5 : bufsize; + } else { + errno = EINVAL; + return -1; + } + } else { + int r = syscall(__NR_readlink, path, buf, bufsize); + if (r == -1) + errno = klee_get_errno(); + return r; + } +} + +#undef FD_SET +#undef FD_CLR +#undef FD_ISSET +#undef FD_ZERO +#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) +#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) +#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) +#define FD_ZERO(p) memset((char *)(p), '\0', sizeof(*(p))) +int select(int nfds, fd_set *read, fd_set *write, + fd_set *except, struct timeval *timeout) { + fd_set in_read, in_write, in_except, os_read, os_write, os_except; + int i, count = 0, os_nfds = 0; + + if (read) { + in_read = *read; + FD_ZERO(read); + } else { + FD_ZERO(&in_read); + } + + if (write) { + in_write = *write; + FD_ZERO(write); + } else { + FD_ZERO(&in_write); + } + + if (except) { + in_except = *except; + FD_ZERO(except); + } else { + FD_ZERO(&in_except); + } + + FD_ZERO(&os_read); + FD_ZERO(&os_write); + FD_ZERO(&os_except); + + /* Check for symbolic stuff */ + for (i=0; i<nfds; i++) { + if (FD_ISSET(i, &in_read) || FD_ISSET(i, &in_write) || FD_ISSET(i, &in_except)) { + exe_file_t *f = __get_file(i); + if (!f) { + errno = EBADF; + return -1; + } else if (f->dfile) { + /* Operations on this fd will never block... */ + if (FD_ISSET(i, &in_read)) FD_SET(i, read); + if (FD_ISSET(i, &in_write)) FD_SET(i, write); + if (FD_ISSET(i, &in_except)) FD_SET(i, except); + ++count; + } else { + if (FD_ISSET(i, &in_read)) FD_SET(f->fd, &os_read); + if (FD_ISSET(i, &in_write)) FD_SET(f->fd, &os_write); + if (FD_ISSET(i, &in_except)) FD_SET(f->fd, &os_except); + if (f->fd >= os_nfds) os_nfds = f->fd + 1; + } + } + } + + if (os_nfds > 0) { + /* Never allow blocking select. This is broken but what else can + we do. */ + struct timeval tv = { 0, 0 }; + int r = syscall(__NR_select, os_nfds, + &os_read, &os_write, &os_except, &tv); + + if (r == -1) { + /* If no symbolic results, return error. Otherwise we will + silently ignore the OS error. */ + if (!count) { + errno = klee_get_errno(); + return -1; + } + } else { + count += r; + + /* Translate resulting sets back */ + for (i=0; i<nfds; i++) { + exe_file_t *f = __get_file(i); + if (f && !f->dfile) { + if (read && FD_ISSET(f->fd, &os_read)) FD_SET(i, read); + if (write && FD_ISSET(f->fd, &os_write)) FD_SET(i, write); + if (except && FD_ISSET(f->fd, &os_except)) FD_SET(i, except); + } + } + } + } + + return count; +} + +/*** Library functions ***/ + +char *getcwd(char *buf, size_t size) { + static int n_calls = 0; + int r; + + n_calls++; + + if (__exe_fs.max_failures && *__exe_fs.getcwd_fail == n_calls) { + __exe_fs.max_failures--; + errno = ERANGE; + return NULL; + } + + if (!buf) { + if (!size) + size = 1024; + buf = malloc(size); + } + + buf = __concretize_ptr(buf); + size = __concretize_size(size); + /* XXX In terms of looking for bugs we really should do this check + before concretization, at least once the routine has been fixed + to properly work with symbolics. */ + klee_check_memory_access(buf, size); + r = syscall(__NR_getcwd, buf, size); + if (r == -1) { + errno = klee_get_errno(); + return NULL; + } + + return buf; +} + +/*** Helper functions ***/ + +static void *__concretize_ptr(const void *p) { + /* XXX 32-bit assumption */ + char *pc = (char*) klee_get_value((unsigned) (long) p); + klee_assume(pc == p); + return pc; +} + +static size_t __concretize_size(size_t s) { + size_t sc = klee_get_value(s); + klee_assume(sc == s); + return sc; +} + +static const char *__concretize_string(const char *s) { + char *sc = __concretize_ptr(s); + unsigned i; + + for (i=0; ; ++i) { + char c = *sc; + if (!(i&(i-1))) { + if (!c) { + *sc++ = 0; + break; + } else if (c=='/') { + *sc++ = '/'; + } + } else { + char cc = (char) klee_get_value(c); + klee_assume(cc == c); + *sc++ = cc; + if (!cc) break; + } + } + + return s; +} + + + +/* Trivial model: + if path is "/" (basically no change) accept, otherwise reject +*/ +int chroot(const char *path) { + if (path[0] == '\0') { + errno = ENOENT; + return -1; + } + + if (path[0] == '/' && path[1] == '\0') { + return 0; + } + + klee_warning("ignoring (ENOENT)"); + errno = ENOENT; + return -1; +} diff --git a/runtime/POSIX/fd.h b/runtime/POSIX/fd.h new file mode 100644 index 00000000..f2780143 --- /dev/null +++ b/runtime/POSIX/fd.h @@ -0,0 +1,90 @@ +//===-- fd.h ---------------------------------------------------*- C++ -*--===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __EXE_FD__ +#define __EXE_FD__ + +#ifndef _LARGEFILE64_SOURCE +#error "_LARGEFILE64_SOURCE should be defined" +#endif +#include <sys/types.h> +#include <sys/statfs.h> +#include <dirent.h> + +typedef struct { + unsigned size; /* in bytes */ + char* contents; + struct stat64* stat; +} exe_disk_file_t; + +typedef enum { + eOpen = (1 << 0), + eCloseOnExec = (1 << 1), + eReadable = (1 << 2), + eWriteable = (1 << 3) +} exe_file_flag_t; + +typedef struct { + int fd; /* actual fd if not symbolic */ + unsigned flags; /* set of exe_file_flag_t values. fields + are only defined when flags at least + has eOpen. */ + off64_t off; /* offset */ + exe_disk_file_t* dfile; /* ptr to file on disk, if symbolic */ +} exe_file_t; + +typedef struct { + unsigned n_sym_files; /* number of symbolic input files, excluding stdin */ + exe_disk_file_t *sym_stdin, *sym_stdout; + unsigned stdout_writes; /* how many chars were written to stdout */ + exe_disk_file_t *sym_files; + /* --- */ + /* the maximum number of failures on one path; gets decremented after each failure */ + unsigned max_failures; + + /* Which read, write etc. call should fail */ + int *read_fail, *write_fail, *close_fail, *ftruncate_fail, *getcwd_fail; + int *chmod_fail, *fchmod_fail; +} exe_file_system_t; + +#define MAX_FDS 32 + +/* Note, if you change this structure be sure to update the + initialization code if necessary. New fields should almost + certainly be at the end. */ +typedef struct { + exe_file_t fds[MAX_FDS]; + mode_t umask; /* process umask */ + unsigned version; + /* If set, writes execute as expected. Otherwise, writes extending + the file size only change the contents up to the initial + size. The file offset is always incremented correctly. */ + int save_all_writes; +} exe_sym_env_t; + +extern exe_file_system_t __exe_fs; +extern exe_sym_env_t __exe_env; + +void klee_init_fds(unsigned n_files, unsigned file_length, + int sym_stdout_flag, int do_all_writes_flag, + unsigned max_failures); +void klee_init_env(int *argcPtr, char ***argvPtr); + +/* *** */ + +int __fd_open(const char *pathname, int flags, mode_t mode); +off64_t __fd_lseek(int fd, off64_t offset, int whence); +int __fd_stat(const char *path, struct stat64 *buf); +int __fd_lstat(const char *path, struct stat64 *buf); +int __fd_fstat(int fd, struct stat64 *buf); +int __fd_ftruncate(int fd, off64_t length); +int __fd_statfs(const char *path, struct statfs *buf); +int __fd_getdents(unsigned int fd, struct dirent64 *dirp, unsigned int count); + +#endif /* __EXE_FD__ */ diff --git a/runtime/POSIX/fd_32.c b/runtime/POSIX/fd_32.c new file mode 100644 index 00000000..4eea1805 --- /dev/null +++ b/runtime/POSIX/fd_32.c @@ -0,0 +1,196 @@ +//===-- fd_32.c -----------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#define _LARGEFILE64_SOURCE +#include "fd.h" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/syscall.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/vfs.h> +#include <fcntl.h> +#include <stdarg.h> +#include <assert.h> +#include <unistd.h> +#include <dirent.h> + +/***/ + +static void __stat64_to_stat(struct stat64 *a, struct stat *b) { + b->st_dev = a->st_dev; + b->st_ino = a->st_ino; + b->st_mode = a->st_mode; + b->st_nlink = a->st_nlink; + b->st_uid = a->st_uid; + b->st_gid = a->st_gid; + b->st_rdev = a->st_rdev; + b->st_size = a->st_size; + b->st_atime = a->st_atime; + b->st_mtime = a->st_mtime; + b->st_ctime = a->st_ctime; + b->st_blksize = a->st_blksize; + b->st_blocks = a->st_blocks; +} + +/***/ + +int open(const char *pathname, int flags, ...) { + mode_t mode = 0; + + if (flags & O_CREAT) { + /* get mode */ + va_list ap; + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + } + + return __fd_open(pathname, flags, mode); +} + +off_t lseek(int fd, off_t off, int whence) { + return (off_t) __fd_lseek(fd, off, whence); +} + +int __xstat(int vers, const char *path, struct stat *buf) { + struct stat64 tmp; + int res = __fd_stat(path, &tmp); + __stat64_to_stat(&tmp, buf); + return res; +} + +int stat(const char *path, struct stat *buf) { + struct stat64 tmp; + int res = __fd_stat(path, &tmp); + __stat64_to_stat(&tmp, buf); + return res; +} + +int __lxstat(int vers, const char *path, struct stat *buf) { + struct stat64 tmp; + int res = __fd_lstat(path, &tmp); + __stat64_to_stat(&tmp, buf); + return res; +} + +int lstat(const char *path, struct stat *buf) { + struct stat64 tmp; + int res = __fd_lstat(path, &tmp); + __stat64_to_stat(&tmp, buf); + return res; +} + +int __fxstat(int vers, int fd, struct stat *buf) { + struct stat64 tmp; + int res = __fd_fstat(fd, &tmp); + __stat64_to_stat(&tmp, buf); + return res; +} + +int fstat(int fd, struct stat *buf) { + struct stat64 tmp; + int res = __fd_fstat(fd, &tmp); + __stat64_to_stat(&tmp, buf); + return res; +} + +int ftruncate(int fd, off_t length) { + return __fd_ftruncate(fd, length); +} + +int statfs(const char *path, struct statfs *buf32) { +#if 0 + struct statfs64 buf; + + if (__fd_statfs(path, &buf) < 0) + return -1; + + buf32->f_type = buf.f_type; + buf32->f_bsize = buf.f_bsize; + buf32->f_blocks = buf.f_blocks; + buf32->f_bfree = buf.f_bfree; + buf32->f_bavail = buf.f_bavail; + buf32->f_files = buf.f_files; + buf32->f_ffree = buf.f_ffree; + buf32->f_fsid = buf.f_fsid; + buf32->f_namelen = buf.f_namelen; + + return 0; +#else + return __fd_statfs(path, buf32); +#endif +} + +/* Based on uclibc version. We use getdents64 and then rewrite the + results over themselves, as dirent32s. */ +ssize_t getdents(int fd, struct dirent *dirp, size_t nbytes) { + struct dirent64 *dp64 = (struct dirent64*) dirp; + ssize_t res = __fd_getdents(fd, dp64, nbytes); + + if (res>0) { + struct dirent64 *end = (struct dirent64*) ((char*) dp64 + res); + while (dp64 < end) { + struct dirent *dp = (struct dirent *) dp64; + unsigned name_len = (dp64->d_reclen - + (unsigned) &((struct dirent64*) 0)->d_name); + dp->d_ino = dp64->d_ino; + dp->d_off = dp64->d_off; + dp->d_reclen = dp64->d_reclen; + dp->d_type = dp64->d_type; + memmove(dp->d_name, dp64->d_name, name_len); + dp64 = (struct dirent64*) ((char*) dp64 + dp->d_reclen); + } + } + + return res; +} +int __getdents(unsigned int fd, struct dirent *dirp, unsigned int count) + __attribute__((alias("getdents"))); + +/* Forward to 64 versions (uclibc expects versions w/o asm specifier) */ + +int open64(const char *pathname, int flags, ...) __attribute__((weak)); +int open64(const char *pathname, int flags, ...) { + mode_t mode = 0; + + if (flags & O_CREAT) { + /* get mode */ + va_list ap; + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + } + + return __fd_open(pathname, flags, mode); +} + +off64_t lseek64(int fd, off64_t off, int whence) __attribute__((weak)); +off64_t lseek64(int fd, off64_t off, int whence) { + return __fd_lseek(fd, off, whence); +} + +int stat64(const char *path, struct stat64 *buf) __attribute__((weak)); +int stat64(const char *path, struct stat64 *buf) { + return __fd_stat(path, buf); +} + +int lstat64(const char *path, struct stat64 *buf) __attribute__((weak)); +int lstat64(const char *path, struct stat64 *buf) { + return __fd_lstat(path, buf); +} + +int fstat64(int fd, struct stat64 *buf) __attribute__((weak)); +int fstat64(int fd, struct stat64 *buf) { + return __fd_fstat(fd, buf); +} diff --git a/runtime/POSIX/fd_64.c b/runtime/POSIX/fd_64.c new file mode 100644 index 00000000..d0710caf --- /dev/null +++ b/runtime/POSIX/fd_64.c @@ -0,0 +1,90 @@ +//===-- fd_64.c -----------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#define _LARGEFILE64_SOURCE +#define _FILE_OFFSET_BITS 64 +#include "fd.h" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/syscall.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> +#include <stdarg.h> +#include <assert.h> +#include <sys/vfs.h> +#include <unistd.h> +#include <dirent.h> +#include <sys/ioctl.h> +#include <sys/mtio.h> +#include <termios.h> +#include <sys/select.h> +#include <klee/klee.h> + +/*** Forward to actual implementations ***/ + +int open(const char *pathname, int flags, ...) { + mode_t mode = 0; + + if (flags & O_CREAT) { + /* get mode */ + va_list ap; + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + } + + return __fd_open(pathname, flags, mode); +} + +off64_t lseek(int fd, off64_t offset, int whence) { + return __fd_lseek(fd, offset, whence); +} + +int __xstat(int vers, const char *path, struct stat *buf) { + return __fd_stat(path, (struct stat64*) buf); +} + +int stat(const char *path, struct stat *buf) { + return __fd_stat(path, (struct stat64*) buf); +} + +int __lxstat(int vers, const char *path, struct stat *buf) { + return __fd_lstat(path, (struct stat64*) buf); +} + +int lstat(const char *path, struct stat *buf) { + return __fd_lstat(path, (struct stat64*) buf); +} + +int __fxstat(int vers, int fd, struct stat *buf) { + return __fd_fstat(fd, (struct stat64*) buf); +} + +int fstat(int fd, struct stat *buf) { + return __fd_fstat(fd, (struct stat64*) buf); +} + +int ftruncate64(int fd, off64_t length) { + return __fd_ftruncate(fd, length); +} + +int statfs(const char *path, struct statfs *buf) __attribute__((weak)); +int statfs(const char *path, struct statfs *buf) { + return __fd_statfs(path, buf); +} + +int getdents64(unsigned int fd, struct dirent *dirp, unsigned int count) { + return __fd_getdents(fd, (struct dirent64*) dirp, count); +} +int __getdents64(unsigned int fd, struct dirent *dirp, unsigned int count) + __attribute__((alias("getdents64"))); diff --git a/runtime/POSIX/fd_init.c b/runtime/POSIX/fd_init.c new file mode 100644 index 00000000..61e49893 --- /dev/null +++ b/runtime/POSIX/fd_init.c @@ -0,0 +1,161 @@ +//===-- fd_init.c ---------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#define _LARGEFILE64_SOURCE +#define _FILE_OFFSET_BITS 64 +#include "fd.h" +#include <klee/klee.h> + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <sys/stat.h> +#include <sys/syscall.h> +#include <unistd.h> + + +exe_file_system_t __exe_fs; + +/* NOTE: It is important that these are statically initialized + correctly, since things that run before main may hit them given the + current way things are linked. */ + +/* XXX Technically these flags are initialized w.o.r. to the + environment we are actually running in. We could patch them in + klee_init_fds, but we still have the problem that uclibc calls + prior to main will get the wrong data. Not such a big deal since we + mostly care about sym case anyway. */ + + +exe_sym_env_t __exe_env = { + {{ 0, eOpen | eReadable, 0, 0}, + { 1, eOpen | eWriteable, 0, 0}, + { 2, eOpen | eWriteable, 0, 0}}, + 022, + 0, + 0 +}; + +static void __create_new_dfile(exe_disk_file_t *dfile, unsigned size, + const char *name, struct stat64 *defaults) { + struct stat64 *s = malloc(sizeof(*s)); + const char *sp; + char sname[64]; + for (sp=name; *sp; ++sp) + sname[sp-name] = *sp; + memcpy(&sname[sp-name], "-stat", 6); + + assert(size); + + dfile->size = size; + dfile->contents = malloc(dfile->size); + klee_make_symbolic_name(dfile->contents, dfile->size, name); + + klee_make_symbolic_name(s, sizeof(*s), sname); + + /* For broken tests */ + if (!klee_is_symbolic(s->st_ino) && + (s->st_ino & 0x7FFFFFFF) == 0) + s->st_ino = defaults->st_ino; + + /* Important since we copy this out through getdents, and readdir + will otherwise skip this entry. For same reason need to make sure + it fits in low bits. */ + klee_assume((s->st_ino & 0x7FFFFFFF) != 0); + + /* uclibc opendir uses this as its buffer size, try to keep + reasonable. */ + klee_assume((s->st_blksize & ~0xFFFF) == 0); + + klee_prefer_cex(s, !(s->st_mode & ~(S_IFMT | 0777))); + klee_prefer_cex(s, s->st_dev == defaults->st_dev); + klee_prefer_cex(s, s->st_rdev == defaults->st_rdev); + klee_prefer_cex(s, (s->st_mode&0700) == 0600); + klee_prefer_cex(s, (s->st_mode&0070) == 0020); + klee_prefer_cex(s, (s->st_mode&0007) == 0002); + klee_prefer_cex(s, (s->st_mode&S_IFMT) == S_IFREG); + klee_prefer_cex(s, s->st_nlink == 1); + klee_prefer_cex(s, s->st_uid == defaults->st_uid); + klee_prefer_cex(s, s->st_gid == defaults->st_gid); + klee_prefer_cex(s, s->st_blksize == 4096); + klee_prefer_cex(s, s->st_atime == defaults->st_atime); + klee_prefer_cex(s, s->st_mtime == defaults->st_mtime); + klee_prefer_cex(s, s->st_ctime == defaults->st_ctime); + + s->st_size = dfile->size; + s->st_blocks = 8; + dfile->stat = s; +} + +static unsigned __sym_uint32(const char *name) { + unsigned x; + klee_make_symbolic_name(&x, sizeof x, name); + return x; +} + +/* n_files: number of symbolic input files, excluding stdin + file_length: size in bytes of each symbolic file, including stdin + sym_stdout_flag: 1 if stdout should be symbolic, 0 otherwise + save_all_writes_flag: 1 if all writes are executed as expected, 0 if + writes past the initial file size are discarded + (file offset is always incremented) + max_failures: maximum number of system call failures */ +void klee_init_fds(unsigned n_files, unsigned file_length, + int sym_stdout_flag, int save_all_writes_flag, + unsigned max_failures) { + unsigned k; + char name[7] = "?-data"; + struct stat64 s; + + stat64(".", &s); + + __exe_fs.n_sym_files = n_files; + __exe_fs.sym_files = malloc(sizeof(*__exe_fs.sym_files) * n_files); + for (k=0; k < n_files; k++) { + name[0] = 'A' + k; + __create_new_dfile(&__exe_fs.sym_files[k], file_length, name, &s); + } + + /* setting symbolic stdin */ + if (file_length) { + __exe_fs.sym_stdin = malloc(sizeof(*__exe_fs.sym_stdin)); + __create_new_dfile(__exe_fs.sym_stdin, file_length, "stdin", &s); + __exe_env.fds[0].dfile = __exe_fs.sym_stdin; + } + else __exe_fs.sym_stdin = NULL; + + __exe_fs.max_failures = max_failures; + if (__exe_fs.max_failures) { + __exe_fs.read_fail = malloc(sizeof(*__exe_fs.read_fail)); + __exe_fs.write_fail = malloc(sizeof(*__exe_fs.write_fail)); + __exe_fs.close_fail = malloc(sizeof(*__exe_fs.close_fail)); + __exe_fs.ftruncate_fail = malloc(sizeof(*__exe_fs.ftruncate_fail)); + __exe_fs.getcwd_fail = malloc(sizeof(*__exe_fs.getcwd_fail)); + + klee_make_symbolic_name(__exe_fs.read_fail, sizeof(*__exe_fs.read_fail), "read_fail"); + klee_make_symbolic_name(__exe_fs.write_fail, sizeof(*__exe_fs.write_fail), "write_fail"); + klee_make_symbolic_name(__exe_fs.close_fail, sizeof(*__exe_fs.close_fail), "close_fail"); + klee_make_symbolic_name(__exe_fs.ftruncate_fail, sizeof(*__exe_fs.ftruncate_fail), "ftruncate_fail"); + klee_make_symbolic_name(__exe_fs.getcwd_fail, sizeof(*__exe_fs.getcwd_fail), "getcwd_fail"); + } + + /* setting symbolic stdout */ + if (sym_stdout_flag) { + __exe_fs.sym_stdout = malloc(sizeof(*__exe_fs.sym_stdout)); + __create_new_dfile(__exe_fs.sym_stdout, 1024, "stdout", &s); + __exe_env.fds[1].dfile = __exe_fs.sym_stdout; + __exe_fs.stdout_writes = 0; + } + else __exe_fs.sym_stdout = NULL; + + __exe_env.save_all_writes = save_all_writes_flag; + __exe_env.version = __sym_uint32("model_version"); + klee_assume(__exe_env.version == 1); +} diff --git a/runtime/POSIX/illegal.c b/runtime/POSIX/illegal.c new file mode 100644 index 00000000..469ea623 --- /dev/null +++ b/runtime/POSIX/illegal.c @@ -0,0 +1,70 @@ +//===-- illegal.c ---------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <signal.h> +#include <setjmp.h> +#include <sys/types.h> + +#include <klee/klee.h> + +void klee_warning(const char*); +void klee_warning_once(const char*); + +int kill(pid_t pid, int sig) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int _setjmp (struct __jmp_buf_tag __env[1]) __attribute__((weak)); +int _setjmp (struct __jmp_buf_tag __env[1]) { + klee_warning_once("ignoring"); + return 0; +} + +void longjmp(jmp_buf env, int val) { + klee_report_error(__FILE__, __LINE__, "longjmp unsupported", "xxx.err"); +} + +/* Macro so function name from klee_warning comes out correct. */ +#define __bad_exec() \ + (klee_warning("ignoring (EACCES)"),\ + errno = EACCES,\ + -1) + +/* This need to be weak because uclibc wants to define them as well, + but we will want to make sure a definition is around in case we + don't link with it. */ + +int execl(const char *path, const char *arg, ...) __attribute__((weak)); +int execlp(const char *file, const char *arg, ...) __attribute__((weak)); +int execle(const char *path, const char *arg, ...) __attribute__((weak)); +int execv(const char *path, char *const argv[]) __attribute__((weak)); +int execvp(const char *file, char *const argv[]) __attribute__((weak)); +int execve(const char *file, char *const argv[], char *const envp[]) __attribute__((weak)); + +int execl(const char *path, const char *arg, ...) { return __bad_exec(); } +int execlp(const char *file, const char *arg, ...) { return __bad_exec(); } +int execle(const char *path, const char *arg, ...) { return __bad_exec(); } +int execv(const char *path, char *const argv[]) { return __bad_exec(); } +int execvp(const char *file, char *const argv[]) { return __bad_exec(); } +int execve(const char *file, char *const argv[], char *const envp[]) { return __bad_exec(); } + +pid_t fork(void) { + klee_warning("ignoring (ENOMEM)"); + errno = ENOMEM; + return -1; +} + +pid_t vfork(void) { + return fork(); +} diff --git a/runtime/POSIX/klee_init_env.c b/runtime/POSIX/klee_init_env.c new file mode 100644 index 00000000..83e9fde6 --- /dev/null +++ b/runtime/POSIX/klee_init_env.c @@ -0,0 +1,181 @@ +//===-- klee_init_env.c ---------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/klee.h" +#define _LARGEFILE64_SOURCE +#include "fd.h" + +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <sys/syscall.h> +#include <unistd.h> + +static void __emit_error(const char *msg) { + klee_report_error(__FILE__, __LINE__, msg, "user.err"); +} + +/* Helper function that converts a string to an integer, and + terminates the program with an error message is the string is not a + proper number */ +static long int __str_to_int(char *s, const char *error_msg) { + long int res = 0; + char c; + + if (!*s) __emit_error(error_msg); + + while ((c = *s++)) { + if (c == '\0') { + break; + } else if (c>='0' && c<='9') { + res = res*10 + (c - '0'); + } else { + __emit_error(error_msg); + } + } + return res; +} + +static int __isprint(const char c) { + /* Assume ASCII */ + return (32 <= c && c <= 126); +} + +static int __streq(const char *a, const char *b) { + while (*a == *b) { + if (!*a) + return 1; + a++; + b++; + } + return 0; +} + +static char *__get_sym_str(int numChars, char *name) { + int i; + char *s = malloc(numChars+1); + klee_mark_global(s); + klee_make_symbolic_name(s, numChars+1, name); + + for (i=0; i<numChars; i++) + klee_prefer_cex(s, __isprint(s[i])); + + s[numChars] = '\0'; + return s; +} + +static void __add_arg(int *argc, char **argv, char *arg, int argcMax) { + if (*argc==argcMax) { + __emit_error("too many arguments for klee_init_env"); + } else { + argv[*argc] = arg; + (*argc)++; + } +} + +void klee_init_env(int* argcPtr, char*** argvPtr) { + int argc = *argcPtr; + char** argv = *argvPtr; + + int new_argc = 0, n_args; + char* new_argv[1024]; + unsigned max_len, min_argvs, max_argvs; + unsigned sym_files = 0, sym_file_len = 0; + int sym_stdout_flag = 0; + int save_all_writes_flag = 0; + int fd_fail = 0; + char** final_argv; + char sym_arg_name[5] = "arg"; + unsigned sym_arg_num = 0; + int k=0, i; + + sym_arg_name[4] = '\0'; + + while (k < argc) { + if (__streq(argv[k], "--sym-arg") || __streq(argv[k], "-sym-arg")) { + const char *msg = "--sym-arg expects an integer argument <max-len>"; + if (++k == argc) + __emit_error(msg); + + max_len = __str_to_int(argv[k++], msg); + sym_arg_name[3] = '0' + sym_arg_num++; + __add_arg(&new_argc, new_argv, + __get_sym_str(max_len, sym_arg_name), + 1024); + } + else if (__streq(argv[k], "--sym-args") || __streq(argv[k], "-sym-args")) { + const char *msg = + "--sym-args expects three integer arguments <min-argvs> <max-argvs> <max-len>"; + + if (k+3 >= argc) + __emit_error(msg); + + k++; + min_argvs = __str_to_int(argv[k++], msg); + max_argvs = __str_to_int(argv[k++], msg); + max_len = __str_to_int(argv[k++], msg); + + n_args = klee_range(min_argvs, max_argvs+1, "n_args"); + for (i=0; i < n_args; i++) { + sym_arg_name[3] = '0' + sym_arg_num++; + __add_arg(&new_argc, new_argv, + __get_sym_str(max_len, sym_arg_name), + 1024); + } + } + else if (__streq(argv[k], "--sym-files") || __streq(argv[k], "-sym-files")) { + const char* msg = "--sym-files expects two integer arguments <no-sym-files> <sym-file-len>"; + + if (k+2 >= argc) + __emit_error(msg); + + k++; + sym_files = __str_to_int(argv[k++], msg); + sym_file_len = __str_to_int(argv[k++], msg); + + } + else if (__streq(argv[k], "--sym-stdout") || __streq(argv[k], "-sym-stdout")) { + sym_stdout_flag = 1; + k++; + } + else if (__streq(argv[k], "--save-all-writes") || __streq(argv[k], "-save-all-writes")) { + save_all_writes_flag = 1; + k++; + } + else if (__streq(argv[k], "--fd-fail") || __streq(argv[k], "-fd-fail")) { + fd_fail = 1; + k++; + } + else if (__streq(argv[k], "--max-fail") || __streq(argv[k], "-max-fail")) { + const char *msg = "--max-fail expects an integer argument <max-failures>"; + if (++k == argc) + __emit_error(msg); + + fd_fail = __str_to_int(argv[k++], msg); + } + else { + /* simply copy arguments */ + __add_arg(&new_argc, new_argv, argv[k++], 1024); + } + } + + final_argv = (char**) malloc((new_argc+1) * sizeof(*final_argv)); + klee_mark_global(final_argv); + memcpy(final_argv, new_argv, new_argc * sizeof(*final_argv)); + final_argv[new_argc] = 0; + + *argcPtr = new_argc; + *argvPtr = final_argv; + + klee_init_fds(sym_files, sym_file_len, + sym_stdout_flag, save_all_writes_flag, + fd_fail); +} + diff --git a/runtime/POSIX/misc.c b/runtime/POSIX/misc.c new file mode 100644 index 00000000..12ff2f58 --- /dev/null +++ b/runtime/POSIX/misc.c @@ -0,0 +1,87 @@ +//===-- misc.c ------------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <assert.h> +#include <unistd.h> +#include <sys/syscall.h> +#include <errno.h> +#include <stdlib.h> +#include <klee/klee.h> +#include <string.h> + +#if 0 +#define MAX_SYM_ENV_SIZE 32 +typedef struct { + char name[MAX_SYM_ENV_SIZE]; + char *value; +} sym_env_var; + +static sym_env_var *__klee_sym_env = 0; +static unsigned __klee_sym_env_count = 0; +static unsigned __klee_sym_env_nvars = 0; +static unsigned __klee_sym_env_var_size = 0; +void __klee_init_environ(unsigned nvars, + unsigned var_size) { + assert(var_size); + __klee_sym_env = malloc(sizeof(*__klee_sym_env) * nvars); + assert(__klee_sym_env); + + __klee_sym_env_nvars = nvars; + __klee_sym_env_var_size = var_size; +} + +static unsigned __strlen(const char *s) { + const char *s2 = s; + while (*s2) ++s2; + return s2-s; +} + +extern char *__getenv(const char *name); +char *getenv(const char *name) { + char *res = __getenv(name); + + if (!__klee_sym_env_nvars) + return res; + + /* If it exists in the system environment fork and return the actual + result or 0. */ + if (res) { + return klee_range(0, 2, name) ? res : 0; + } else { + unsigned i, len = __strlen(name); + + if (len>=MAX_SYM_ENV_SIZE) { + /* Don't deal with strings to large to fit in our name. */ + return 0; + } else { + /* Check for existing entry */ + for (i=0; i<__klee_sym_env_count; ++i) + if (memcmp(__klee_sym_env[i].name, name, len+1)==0) + return __klee_sym_env[i].value; + + /* Otherwise create if room and we choose to */ + if (__klee_sym_env_count < __klee_sym_env_nvars) { + if (klee_range(0, 2, name)) { + char *s = malloc(__klee_sym_env_var_size+1); + klee_make_symbolic(s, __klee_sym_env_var_size+1); + s[__klee_sym_env_var_size] = '\0'; + + memcpy(__klee_sym_env[__klee_sym_env_count].name, name, len+1); + __klee_sym_env[__klee_sym_env_count].value = s; + ++__klee_sym_env_count; + + return s; + } + } + + return 0; + } + } +} +#endif diff --git a/runtime/POSIX/selinux.c b/runtime/POSIX/selinux.c new file mode 100644 index 00000000..38acba6c --- /dev/null +++ b/runtime/POSIX/selinux.c @@ -0,0 +1,80 @@ +//===-- selinux.c ---------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +/* Very basic SELinux support */ + +#include "klee/Config/config.h" + +#ifdef HAVE_SELINUX_SELINUX_H + +#include "klee/klee.h" + +#include <selinux/selinux.h> +#include <stdlib.h> +#include <errno.h> + +/* for now, assume we run on an SELinux machine */ +int exe_selinux = 1; + +/* NULL is the default policy behavior */ +security_context_t create_con = NULL; + + +int is_selinux_enabled() { + return exe_selinux; +} + + +/***/ + +int getfscreatecon(security_context_t *context) { + *context = create_con; + return 0; +} + + +int setfscreatecon(security_context_t context) { + if (context == NULL) { + create_con = context; + return 0; + } + + /* on my machine, setfscreatecon seems to incorrectly accept one + char strings.. Also, make sure mcstrans > 0.2.8 for replay + (important bug fixed) */ + if (context[0] != '\0' && context[1] == '\0') + klee_silent_exit(1); + + return -1; +} + +/***/ + +int setfilecon(const char *path, security_context_t con) { + if (con) + return 0; + + errno = ENOSPC; + return -1; +} + +int lsetfilecon(const char *path, security_context_t con) { + return setfilecon(path, con); +} + +int fsetfilecon(int fd, security_context_t con) { + return setfilecon("", con); +} + +/***/ + +void freecon(security_context_t con) {} +void freeconary(security_context_t *con) {} + +#endif diff --git a/runtime/POSIX/stubs.c b/runtime/POSIX/stubs.c new file mode 100644 index 00000000..cf64f26b --- /dev/null +++ b/runtime/POSIX/stubs.c @@ -0,0 +1,560 @@ +//===-- stubs.c -----------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <signal.h> +#include <time.h> +#include <utime.h> +#include <utmp.h> +#include <unistd.h> +#include <limits.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/times.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include "klee/Config/config.h" + +void klee_warning(const char*); +void klee_warning_once(const char*); + +/* Silent ignore */ + +int __syscall_rt_sigaction(int signum, const struct sigaction *act, + struct sigaction *oldact, size_t _something) + __attribute__((weak)); + +int __syscall_rt_sigaction(int signum, const struct sigaction *act, + struct sigaction *oldact, size_t _something) { + klee_warning_once("silently ignoring"); + return 0; +} + +int sigaction(int signum, const struct sigaction *act, + struct sigaction *oldact) __attribute__((weak)); + +int sigaction(int signum, const struct sigaction *act, + struct sigaction *oldact) { + klee_warning_once("silently ignoring"); + return 0; +} + +int sigprocmask(int how, const sigset_t *set, sigset_t *oldset) + __attribute__((weak)); +int sigprocmask(int how, const sigset_t *set, sigset_t *oldset) { + klee_warning_once("silently ignoring"); + return 0; +} + +/* Not even worth warning about these */ +int fdatasync(int fd) __attribute__((weak)); +int fdatasync(int fd) { + return 0; +} + +/* Not even worth warning about this */ +void sync(void) __attribute__((weak)); +void sync(void) { +} + +/* Error ignore */ + +extern int __fgetc_unlocked(FILE *f); +extern int __fputc_unlocked(int c, FILE *f); + +int __socketcall(int type, int *args) __attribute__((weak)); +int __socketcall(int type, int *args) { + klee_warning("ignoring (EAFNOSUPPORT)"); + errno = EAFNOSUPPORT; + return -1; +} + +int _IO_getc(FILE *f) __attribute__((weak)); +int _IO_getc(FILE *f) { + return __fgetc_unlocked(f); +} + +int _IO_putc(int c, FILE *f) __attribute__((weak)); +int _IO_putc(int c, FILE *f) { + return __fputc_unlocked(c, f); +} + +int mkdir(const char *pathname, mode_t mode) __attribute__((weak)); +int mkdir(const char *pathname, mode_t mode) { + klee_warning("ignoring (EIO)"); + errno = EIO; + return -1; +} + +int mkfifo(const char *pathname, mode_t mode) __attribute__((weak)); +int mkfifo(const char *pathname, mode_t mode) { + klee_warning("ignoring (EIO)"); + errno = EIO; + return -1; +} + +int mknod(const char *pathname, mode_t mode, dev_t dev) __attribute__((weak)); +int mknod(const char *pathname, mode_t mode, dev_t dev) { + klee_warning("ignoring (EIO)"); + errno = EIO; + return -1; +} + +int pipe(int filedes[2]) __attribute__((weak)); +int pipe(int filedes[2]) { + klee_warning("ignoring (ENFILE)"); + errno = ENFILE; + return -1; +} + +int link(const char *oldpath, const char *newpath) __attribute__((weak)); +int link(const char *oldpath, const char *newpath) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int symlink(const char *oldpath, const char *newpath) __attribute__((weak)); +int symlink(const char *oldpath, const char *newpath) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int rename(const char *oldpath, const char *newpath) __attribute__((weak)); +int rename(const char *oldpath, const char *newpath) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int nanosleep(const struct timespec *req, struct timespec *rem) __attribute__((weak)); +int nanosleep(const struct timespec *req, struct timespec *rem) { + return 0; +} + +/* XXX why can't I call this internally? */ +int clock_gettime(clockid_t clk_id, struct timespec *res) __attribute__((weak)); +int clock_gettime(clockid_t clk_id, struct timespec *res) { + /* Fake */ + struct timeval tv; + gettimeofday(&tv, NULL); + res->tv_sec = tv.tv_sec; + res->tv_nsec = tv.tv_usec * 1000; + return 0; +} + +int clock_settime(clockid_t clk_id, const struct timespec *res) __attribute__((weak)); +int clock_settime(clockid_t clk_id, const struct timespec *res) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +time_t time(time_t *t) { + struct timeval tv; + gettimeofday(&tv, NULL); + if (t) + *t = tv.tv_sec; + return tv.tv_sec; +} + +clock_t times(struct tms *buf) { + /* Fake */ + buf->tms_utime = 0; + buf->tms_stime = 0; + buf->tms_cutime = 0; + buf->tms_cstime = 0; + return 0; +} + +struct utmpx *getutxent(void) __attribute__((weak)); +struct utmpx *getutxent(void) { + return (struct utmpx*) getutent(); +} + +void setutxent(void) __attribute__((weak)); +void setutxent(void) { + setutent(); +} + +void endutxent(void) __attribute__((weak)); +void endutxent(void) { + endutent(); +} + +int utmpxname(const char *file) __attribute__((weak)); +int utmpxname(const char *file) { + utmpname(file); + return 0; +} + +int euidaccess(const char *pathname, int mode) __attribute__((weak)); +int euidaccess(const char *pathname, int mode) { + return access(pathname, mode); +} + +int eaccess(const char *pathname, int mode) __attribute__((weak)); +int eaccess(const char *pathname, int mode) { + return euidaccess(pathname, mode); +} + +int group_member (gid_t __gid) __attribute__((weak)); +int group_member (gid_t __gid) { + return ((__gid == getgid ()) || (__gid == getegid ())); +} + +int utime(const char *filename, const struct utimbuf *buf) __attribute__((weak)); +int utime(const char *filename, const struct utimbuf *buf) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int utimes(const char *filename, const struct timeval times[2]) __attribute__((weak)); +int utimes(const char *filename, const struct timeval times[2]) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int futimes(int fd, const struct timeval times[2]) __attribute__((weak)); +int futimes(int fd, const struct timeval times[2]) { + klee_warning("ignoring (EBADF)"); + errno = EBADF; + return -1; +} + +int strverscmp (__const char *__s1, __const char *__s2) { + return strcmp(__s1, __s2); /* XXX no doubt this is bad */ +} + +unsigned int gnu_dev_major(unsigned long long int __dev) __attribute__((weak)); +unsigned int gnu_dev_major(unsigned long long int __dev) { + return ((__dev >> 8) & 0xfff) | ((unsigned int) (__dev >> 32) & ~0xfff); +} + +unsigned int gnu_dev_minor(unsigned long long int __dev) __attribute__((weak)); +unsigned int gnu_dev_minor(unsigned long long int __dev) { + return (__dev & 0xff) | ((unsigned int) (__dev >> 12) & ~0xff); +} + +unsigned long long int gnu_dev_makedev(unsigned int __major, unsigned int __minor) __attribute__((weak)); +unsigned long long int gnu_dev_makedev(unsigned int __major, unsigned int __minor) { + return ((__minor & 0xff) | ((__major & 0xfff) << 8) + | (((unsigned long long int) (__minor & ~0xff)) << 12) + | (((unsigned long long int) (__major & ~0xfff)) << 32)); +} + +char *canonicalize_file_name (const char *name) __attribute__((weak)); +char *canonicalize_file_name (const char *name) { + char *res = malloc(PATH_MAX); + char *rp_res = realpath(name, res); + if (!rp_res) + free(res); + return rp_res; +} + +int getloadavg(double loadavg[], int nelem) __attribute__((weak)); +int getloadavg(double loadavg[], int nelem) { + klee_warning("ignoring (-1 result)"); + return -1; +} + +pid_t wait(int *status) __attribute__((weak)); +pid_t wait(int *status) { + klee_warning("ignoring (ECHILD)"); + errno = ECHILD; + return -1; +} + +pid_t wait3(int *status, int options, struct rusage *rusage) __attribute__((weak)); +pid_t wait3(int *status, int options, struct rusage *rusage) { + klee_warning("ignoring (ECHILD)"); + errno = ECHILD; + return -1; +} + +pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage) __attribute__((weak)); +pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage) { + klee_warning("ignoring (ECHILD)"); + errno = ECHILD; + return -1; +} + +pid_t waitpid(pid_t pid, int *status, int options) __attribute__((weak)); +pid_t waitpid(pid_t pid, int *status, int options) { + klee_warning("ignoring (ECHILD)"); + errno = ECHILD; + return -1; +} + +pid_t waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options) __attribute__((weak)); +pid_t waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options) { + klee_warning("ignoring (ECHILD)"); + errno = ECHILD; + return -1; +} + +/* ACL */ + +/* FIXME: We need autoconf magic for this. */ + +#ifdef HAVE_SYS_ACL_H + +#include <sys/acl.h> + +int acl_delete_def_file(const char *path_p) __attribute__((weak)); +int acl_delete_def_file(const char *path_p) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int acl_extended_file(const char path_p) __attribute__((weak)); +int acl_extended_file(const char path_p) { + klee_warning("ignoring (ENOENT)"); + errno = ENOENT; + return -1; +} + +int acl_entries(acl_t acl) __attribute__((weak)); +int acl_entries(acl_t acl) { + klee_warning("ignoring (EINVAL)"); + errno = EINVAL; + return -1; +} + +acl_t acl_from_mode(mode_t mode) __attribute__((weak)); +acl_t acl_from_mode(mode_t mode) { + klee_warning("ignoring (ENOMEM)"); + errno = ENOMEM; + return NULL; +} + +acl_t acl_get_fd(int fd) __attribute__((weak)); +acl_t acl_get_fd(int fd) { + klee_warning("ignoring (ENOMEM)"); + errno = ENOMEM; + return NULL; +} + +acl_t acl_get_file(const char *pathname, acl_type_t type) __attribute__((weak)); +acl_t acl_get_file(const char *pathname, acl_type_t type) { + klee_warning("ignoring (ENONMEM)"); + errno = ENOMEM; + return NULL; +} + +int acl_set_fd(int fd, acl_t acl) __attribute__((weak)); +int acl_set_fd(int fd, acl_t acl) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int acl_set_file(const char *path_p, acl_type_t type, acl_t acl) __attribute__((weak)); +int acl_set_file(const char *path_p, acl_type_t type, acl_t acl) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int acl_free(void *obj_p) __attribute__((weak)); +int acl_free(void *obj_p) { + klee_warning("ignoring (EINVAL)"); + errno = EINVAL; + return -1; +} + +#endif + +int mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data) __attribute__((weak)); +int mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int umount(const char *target) __attribute__((weak)); +int umount(const char *target) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int umount2(const char *target, int flags) __attribute__((weak)); +int umount2(const char *target, int flags) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int swapon(const char *path, int swapflags) __attribute__((weak)); +int swapon(const char *path, int swapflags) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int swapoff(const char *path) __attribute__((weak)); +int swapoff(const char *path) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int setgid(gid_t gid) __attribute__((weak)); +int setgid(gid_t gid) { + klee_warning("silently ignoring (returning 0)"); + return 0; +} + +int setgroups(size_t size, const gid_t *list) __attribute__((weak)); +int setgroups(size_t size, const gid_t *list) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int sethostname(const char *name, size_t len) __attribute__((weak)); +int sethostname(const char *name, size_t len) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int setpgid(pid_t pid, pid_t pgid) __attribute__((weak)); +int setpgid(pid_t pid, pid_t pgid) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int setpgrp(void) __attribute__((weak)); +int setpgrp(void) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int setpriority(__priority_which_t which, id_t who, int prio) __attribute__((weak)); +int setpriority(__priority_which_t which, id_t who, int prio) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int setresgid(gid_t rgid, gid_t egid, gid_t sgid) __attribute__((weak)); +int setresgid(gid_t rgid, gid_t egid, gid_t sgid) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int setresuid(uid_t ruid, uid_t euid, uid_t suid) __attribute__((weak)); +int setresuid(uid_t ruid, uid_t euid, uid_t suid) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int setrlimit(__rlimit_resource_t resource, const struct rlimit *rlim) __attribute__((weak)); +int setrlimit(__rlimit_resource_t resource, const struct rlimit *rlim) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int setrlimit64(__rlimit_resource_t resource, const struct rlimit64 *rlim) __attribute__((weak)); +int setrlimit64(__rlimit_resource_t resource, const struct rlimit64 *rlim) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +pid_t setsid(void) __attribute__((weak)); +pid_t setsid(void) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int settimeofday(const struct timeval *tv, const struct timezone *tz) __attribute__((weak)); +int settimeofday(const struct timeval *tv, const struct timezone *tz) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int setuid(uid_t uid) __attribute__((weak)); +int setuid(uid_t uid) { + klee_warning("silently ignoring (returning 0)"); + return 0; +} + +int reboot(int flag) __attribute__((weak)); +int reboot(int flag) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int mlock(const void *addr, size_t len) __attribute__((weak)); +int mlock(const void *addr, size_t len) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int munlock(const void *addr, size_t len) __attribute__((weak)); +int munlock(const void *addr, size_t len) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +int pause(void) __attribute__((weak)); +int pause(void) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +ssize_t readahead(int fd, off64_t *offset, size_t count) __attribute__((weak)); +ssize_t readahead(int fd, off64_t *offset, size_t count) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} + +void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset) __attribute__((weak)); +void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return (void*) -1; +} + +void *mmap64(void *start, size_t length, int prot, int flags, int fd, off64_t offset) __attribute__((weak)); +void *mmap64(void *start, size_t length, int prot, int flags, int fd, off64_t offset) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return (void*) -1; +} + +int munmap(void*start, size_t length) __attribute__((weak)); +int munmap(void*start, size_t length) { + klee_warning("ignoring (EPERM)"); + errno = EPERM; + return -1; +} diff --git a/runtime/POSIX/testing-dir/a b/runtime/POSIX/testing-dir/a new file mode 120000 index 00000000..dc1dc0cd --- /dev/null +++ b/runtime/POSIX/testing-dir/a @@ -0,0 +1 @@ +/dev/null \ No newline at end of file diff --git a/runtime/POSIX/testing-dir/b b/runtime/POSIX/testing-dir/b new file mode 120000 index 00000000..b9251ec6 --- /dev/null +++ b/runtime/POSIX/testing-dir/b @@ -0,0 +1 @@ +/dev/random \ No newline at end of file diff --git a/runtime/POSIX/testing-dir/c b/runtime/POSIX/testing-dir/c new file mode 100755 index 00000000..2b45f6a5 --- /dev/null +++ b/runtime/POSIX/testing-dir/c @@ -0,0 +1,2 @@ +#!/bin/sh +echo "Hello world!" diff --git a/runtime/POSIX/testing-dir/d b/runtime/POSIX/testing-dir/d new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/runtime/POSIX/testing-dir/d diff --git a/runtime/POSIX/testing-env b/runtime/POSIX/testing-env new file mode 100644 index 00000000..5a6e8eb8 --- /dev/null +++ b/runtime/POSIX/testing-env @@ -0,0 +1,27 @@ +# This file is sourced prior to running the testing environment, make +# sure to quote things. + +export TERM="xterm" +export SHELL="/bin/bash" +export LS_COLORS="no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.avi=01;35:*.fli=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.flac=01;35:*.mp3=01;35:*.mpc=01;35:*.ogg=01;35:*.wav=01;35:" +export PATH="/usr/local/bin:/usr/bin:/bin" +export COLORTERM="gnome-terminal" +export LC_ALL=C +export TABSIZE=8 +export COLUMNS=80 + +# 1 BLOCK_SIZE +# 2 COLUMNS +# 1 DF_BLOCK_SIZE +# 1 DU_BLOCK_SIZE +# 1 HOME +# 1 LS_BLOCK_SIZE +# 1 LS_COLORS +# 11 POSIXLY_CORRECT +# 1 QUOTING_STYLE +# 3 SHELL +# 4 SIMPLE_BACKUP_SUFFIX +# 1 TABSIZE +# 2 TERM +# 2 TIME_STYLE +# 4 TMPDIR diff --git a/runtime/Runtest/Makefile b/runtime/Runtest/Makefile new file mode 100644 index 00000000..dd21c4a9 --- /dev/null +++ b/runtime/Runtest/Makefile @@ -0,0 +1,19 @@ +#===-- runtime/Runtest/Makefile ----------------------------*- Makefile -*--===# +# +# The KLEE Symbolic Virtual Machine +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +LEVEL=../.. + +USEDLIBS=kleeBasic.a +LIBRARYNAME=kleeRuntest +SHARED_LIBRARY=1 +LINK_LIBS_IN_SHARED = 1 +DONT_BUILD_RELINKED = 1 +NO_PEDANTIC=1 + +include $(LEVEL)/Makefile.common diff --git a/runtime/Runtest/intrinsics.c b/runtime/Runtest/intrinsics.c new file mode 100644 index 00000000..1a2fd030 --- /dev/null +++ b/runtime/Runtest/intrinsics.c @@ -0,0 +1,154 @@ +//===-- intrinsics.c ------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +/* Straight C for linking simplicity */ + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/time.h> +#include <time.h> + +#include "klee/klee.h" + +#include "klee/Internal/ADT/BOut.h" + +static BOut *testData = 0; +static unsigned testPosition = 0; + +static unsigned char rand_byte(void) { + unsigned x = rand(); + x ^= x>>16; + x ^= x>>8; + return x & 0xFF; +} + +void klee_make_symbolic_name(void *array, unsigned nbytes, const char *name) { + static int rand_init = -1; + + if (rand_init == -1) { + if (getenv("KLEE_RANDOM")) { + struct timeval tv; + gettimeofday(&tv, 0); + rand_init = 1; + srand(tv.tv_sec ^ tv.tv_usec); + } else { + rand_init = 0; + } + } + + if (rand_init) { + if (!strcmp(name,"syscall_a0")) { + unsigned long long *v = array; + assert(nbytes == 8); + *v = rand() % 69; + } else { + char *c = array; + unsigned i; + for (i=0; i<nbytes; i++) + c[i] = rand_byte(); + } + return; + } + + if (!testData) { + char tmp[256]; + char *name = getenv("KLEE_RUNTEST"); + + if (!name) { + fprintf(stdout, "KLEE-RUNTIME: KLEE_RUNTEST not set, please enter .bout path: "); + fflush(stdout); + name = tmp; + if (!fgets(tmp, sizeof tmp, stdin) || !strlen(tmp)) { + fprintf(stderr, "KLEE-RUNTIME: cannot replay, no KLEE_RUNTEST or user input\n"); + exit(1); + } + tmp[strlen(tmp)-1] = '\0'; /* kill newline */ + } + testData = bOut_fromFile(name); + if (!testData) { + fprintf(stderr, "KLEE-RUNTIME: unable to open .bout file\n"); + exit(1); + } + } + + if (testPosition >= testData->numObjects) { + fprintf(stderr, "ERROR: out of inputs, using zero\n"); + memset(array, 0, nbytes); + } else { + BOutObject *o = &testData->objects[testPosition++]; + memcpy(array, o->bytes, nbytes<o->numBytes ? nbytes : o->numBytes); + if (nbytes != o->numBytes) { + fprintf(stderr, "ERROR: object sizes differ\n"); + if (o->numBytes < nbytes) + memset((char*) array + o->numBytes, 0, nbytes - o->numBytes); + } + } +} + +void klee_make_symbolic(void *array, unsigned nbytes) { + klee_make_symbolic_name(array, nbytes, "unnamed"); +} + +void *klee_malloc_n(unsigned nelems, unsigned size, unsigned alignment) { +#if 1 + return mmap((void*) 0x90000000, nelems*size, PROT_READ|PROT_WRITE, + MAP_PRIVATE +#ifdef MAP_ANONYMOUS + |MAP_ANONYMOUS +#endif + , 0, 0); +#else + char *buffer = malloc(nelems*size + alignment - 1); + buffer += (alignment - (long)buffer % alignment); + return buffer; +#endif +} + +void klee_silent_exit(int x) { + exit(x); +} + +unsigned klee_choose(unsigned n) { + unsigned x; + klee_make_symbolic(&x, sizeof x); + if(x >= n) + fprintf(stderr, "ERROR: max = %d, got = %d\n", n, x); + assert(x < n); + return x; +} + +void klee_assume(unsigned x) { + if (!x) { + fprintf(stderr, "ERROR: invalid klee_assume\n"); + } +} + +unsigned klee_get_value(unsigned x) { + return x; +} + +int klee_range_name(int begin, int end, const char* name) { + int x; + klee_make_symbolic_name(&x, sizeof x, name); + if (x<begin || x>=end) { + fprintf(stderr, + "KLEE: ERROR: invalid klee_range(%u,%u,%s) value, got: %u\n", + begin, end, name, x); + abort(); + } + return x; +} + +/* not sure we should even define. is for debugging. */ +void klee_print_expr(const char *msg, ...) { } + +void klee_set_forking(unsigned enable) { } diff --git a/runtime/klee-libc/Makefile b/runtime/klee-libc/Makefile new file mode 100755 index 00000000..bcd61f73 --- /dev/null +++ b/runtime/klee-libc/Makefile @@ -0,0 +1,19 @@ +#===-- runtime/klee-libc/Makefile --------------------------*- Makefile -*--===# +# +# The KLEE Symbolic Virtual Machine +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +LEVEL=../.. + +LIBRARYNAME=klee-libc +DONT_BUILD_RELINKED=1 +BYTECODE_LIBRARY=1 +# Don't strip debug info from the module. +DEBUG_RUNTIME=1 +NO_PEDANTIC=1 + +include $(LEVEL)/Makefile.common diff --git a/runtime/klee-libc/__cxa_atexit.c b/runtime/klee-libc/__cxa_atexit.c new file mode 100644 index 00000000..e7982848 --- /dev/null +++ b/runtime/klee-libc/__cxa_atexit.c @@ -0,0 +1,49 @@ +//===-- __cxa_atexit.c ----------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/klee.h" + +#define MAX_ATEXIT 128 + +static struct { + void (*fn)(void*); + void *arg; + void *dso_handle; +} AtExit[MAX_ATEXIT]; +static unsigned NumAtExit = 0; + +static void RunAtExit(void) __attribute__((destructor)); +static void RunAtExit(void) { + unsigned i; + + for (i=0; i<NumAtExit; ++i) + AtExit[i].fn(AtExit[i].arg); +} + +int __cxa_atexit(void (*fn)(void*), + void *arg, + void *dso_handle) { + klee_warning_once("FIXME: __cxa_atexit being ignored"); + + /* Better to just report an error here than return 1 (the defined + * semantics). + */ + if (NumAtExit == MAX_ATEXIT) + klee_report_error(__FILE__, + __LINE__, + "__cxa_atexit: no room in array!", + "exec"); + + AtExit[NumAtExit].fn = fn; + AtExit[NumAtExit].arg = arg; + ++NumAtExit; + + return 0; +} + diff --git a/runtime/klee-libc/abort.c b/runtime/klee-libc/abort.c new file mode 100644 index 00000000..0332d095 --- /dev/null +++ b/runtime/klee-libc/abort.c @@ -0,0 +1,16 @@ +//===-- abort.c -----------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <stdlib.h> + +#include "klee/klee.h" + +void abort(void) { + klee_abort(); +} diff --git a/runtime/klee-libc/atexit.c b/runtime/klee-libc/atexit.c new file mode 100644 index 00000000..c71b2cd6 --- /dev/null +++ b/runtime/klee-libc/atexit.c @@ -0,0 +1,16 @@ +//===-- atexit.c ----------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +int __cxa_atexit(void (*fn)(void*), + void *arg, + void *dso_handle); + +int atexit(void (*fn)(void)) { + return __cxa_atexit((void(*)(void*)) fn, 0, 0); +} diff --git a/runtime/klee-libc/atoi.c b/runtime/klee-libc/atoi.c new file mode 100644 index 00000000..ab97bfbb --- /dev/null +++ b/runtime/klee-libc/atoi.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <stdlib.h> + +int atoi(const char *str) { + return (int)strtol(str, (char **)NULL, 10); +} diff --git a/runtime/klee-libc/calloc.c b/runtime/klee-libc/calloc.c new file mode 100644 index 00000000..30b88b30 --- /dev/null +++ b/runtime/klee-libc/calloc.c @@ -0,0 +1,46 @@ +//===-- calloc.c ----------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <stdlib.h> +#include <string.h> + +// DWD - I prefer to be internal +#if 0 +void *calloc(size_t nmemb, size_t size) { + unsigned nbytes = nmemb * size; + void *addr = malloc(nbytes); + if(addr) + memset(addr, 0, nbytes); + return addr; +} +// Always reallocate. +void *realloc(void *ptr, size_t nbytes) { + if(!ptr) + return malloc(nbytes); + + if(!nbytes) { + free(ptr); + return 0; + } + + unsigned copy_nbytes = klee_get_obj_size(ptr); + //printf("REALLOC: current object = %d bytes!\n", copy_nbytes); + + void *addr = malloc(nbytes); + if(addr) { + // shrinking + if(copy_nbytes > nbytes) + copy_nbytes = nbytes; + //printf("REALLOC: copying = %d bytes!\n", copy_nbytes); + memcpy(addr, ptr, copy_nbytes); + free(ptr); + } + return addr; +} +#endif diff --git a/runtime/klee-libc/htonl.c b/runtime/klee-libc/htonl.c new file mode 100644 index 00000000..cec2d0f5 --- /dev/null +++ b/runtime/klee-libc/htonl.c @@ -0,0 +1,49 @@ +//===-- htonl.c -----------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <sys/types.h> +#include <sys/param.h> +#include <stdint.h> + +#undef htons +#undef htonl +#undef ntohs +#undef ntohl + +// Make sure we can recognize the endianness. +#if (!defined(BYTE_ORDER) || !defined(BIG_ENDIAN) || !defined(LITTLE_ENDIAN)) +#error "Unknown platform endianness!" +#endif + +#if BYTE_ORDER == LITTLE_ENDIAN + +uint16_t htons(uint16_t v) { + return (v >> 8) | (v << 8); +} +uint32_t htonl(uint32_t v) { + return htons(v >> 16) | (htons((uint16_t) v) << 16); +} + +#else + +uint16_t htons(uint16_t v) { + return v; +} +uint32_t htonl(uint32_t v) { + return v; +} + +#endif + +uint16_t ntohs(uint32_t v) { + return htons(v); +} +uint32_t ntohl(uint32_t v) { + return htonl(v); +} diff --git a/runtime/klee-libc/klee-choose.c b/runtime/klee-libc/klee-choose.c new file mode 100644 index 00000000..106b4f89 --- /dev/null +++ b/runtime/klee-libc/klee-choose.c @@ -0,0 +1,20 @@ +//===-- klee-choose.c -----------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/klee.h" + +unsigned klee_choose(unsigned n) { + unsigned x; + klee_make_symbolic(&x, sizeof x); + + // NB: this will *not* work if they don't compare to n values. + if(x >= n) + klee_silent_exit(0); + return x; +} diff --git a/runtime/klee-libc/memchr.c b/runtime/klee-libc/memchr.c new file mode 100644 index 00000000..fe0670a7 --- /dev/null +++ b/runtime/klee-libc/memchr.c @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <string.h> + +void * +memchr(s, c, n) + const void *s; + int c; + size_t n; +{ + if (n != 0) { + const unsigned char *p = s; + + do { + if (*p++ == c) + return ((void *)(p - 1)); + } while (--n != 0); + } + return (NULL); +} diff --git a/runtime/klee-libc/memcmp.c b/runtime/klee-libc/memcmp.c new file mode 100644 index 00000000..7562f4f7 --- /dev/null +++ b/runtime/klee-libc/memcmp.c @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <string.h> + +/* + * Compare memory regions. + */ +int memcmp(const void *s1, const void *s2, size_t n) { + if (n != 0) { + const unsigned char *p1 = s1, *p2 = s2; + + do { + if (*p1++ != *p2++) { + return (*--p1 - *--p2); + } + } while (--n != 0); + } + return (0); +} diff --git a/runtime/klee-libc/memcpy.c b/runtime/klee-libc/memcpy.c new file mode 100644 index 00000000..14b98ce6 --- /dev/null +++ b/runtime/klee-libc/memcpy.c @@ -0,0 +1,19 @@ +//===-- memcpy.c ----------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <stdlib.h> + +void *memcpy(void *destaddr, void const *srcaddr, unsigned int len) { + char *dest = destaddr; + char const *src = srcaddr; + + while (len-- > 0) + *dest++ = *src++; + return destaddr; +} diff --git a/runtime/klee-libc/memmove.c b/runtime/klee-libc/memmove.c new file mode 100644 index 00000000..c6e1ada9 --- /dev/null +++ b/runtime/klee-libc/memmove.c @@ -0,0 +1,28 @@ +//===-- memmove.c ---------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <stdlib.h> + +void *memmove(void *dst, const void *src, size_t count) { + char *a = dst; + const char *b = src; + + if (src == dst) + return dst; + + if (src>dst) { + while (count--) *a++ = *b++; + } else { + a+=count-1; + b+=count-1; + while (count--) *a-- = *b--; + } + + return dst; +} diff --git a/runtime/klee-libc/mempcpy.c b/runtime/klee-libc/mempcpy.c new file mode 100644 index 00000000..6327e748 --- /dev/null +++ b/runtime/klee-libc/mempcpy.c @@ -0,0 +1,19 @@ +//===-- mempcpy.c ---------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <stdlib.h> + +void *mempcpy(void *destaddr, void const *srcaddr, unsigned int len) { + char *dest = destaddr; + char const *src = srcaddr; + + while (len-- > 0) + *dest++ = *src++; + return dest; +} diff --git a/runtime/klee-libc/memset.c b/runtime/klee-libc/memset.c new file mode 100644 index 00000000..ee9ecb87 --- /dev/null +++ b/runtime/klee-libc/memset.c @@ -0,0 +1,17 @@ +//===-- memset.c ----------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <stdlib.h> + +void *memset(void * dst, int s, size_t count) { + char * a = dst; + while (count-- > 0) + *a++ = s; + return dst; +} diff --git a/runtime/klee-libc/putchar.c b/runtime/klee-libc/putchar.c new file mode 100644 index 00000000..f3fcf01d --- /dev/null +++ b/runtime/klee-libc/putchar.c @@ -0,0 +1,17 @@ +//===-- putchar.c ---------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> +#include <unistd.h> + +int putchar(int c) { + char x = c; + write(1, &x, 1); + return 1; +} diff --git a/runtime/klee-libc/stpcpy.c b/runtime/klee-libc/stpcpy.c new file mode 100644 index 00000000..1f774f3d --- /dev/null +++ b/runtime/klee-libc/stpcpy.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 1999 + * David E. O'Brien + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <string.h> + +/* Defeat object checking stuff. */ +#undef stpcpy + +char * +stpcpy(char * to, const char * from) +{ + + for (; (*to = *from); ++from, ++to); + return(to); +} diff --git a/runtime/klee-libc/strcat.c b/runtime/klee-libc/strcat.c new file mode 100644 index 00000000..195e9460 --- /dev/null +++ b/runtime/klee-libc/strcat.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <string.h> + +/* Defeat object checking stuff. */ +#undef strcat + +char * strcat(char * s, const char * append) { + char *save = s; + + for (; *s; ++s) + ; + while ((*s++ = *append++)) + ; + return(save); +} diff --git a/runtime/klee-libc/strchr.c b/runtime/klee-libc/strchr.c new file mode 100644 index 00000000..33f97bea --- /dev/null +++ b/runtime/klee-libc/strchr.c @@ -0,0 +1,23 @@ +//===-- strchr.c ----------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +char *strchr(const char *p, int ch) { + char c; + + c = ch; + for (;; ++p) { + if (*p == c) { + return ((char *)p); + } else if (*p == '\0') { + return 0; + } + } + /* NOTREACHED */ + return 0; +} diff --git a/runtime/klee-libc/strcmp.c b/runtime/klee-libc/strcmp.c new file mode 100644 index 00000000..6b8c4e85 --- /dev/null +++ b/runtime/klee-libc/strcmp.c @@ -0,0 +1,14 @@ +//===-- strcmp.c ----------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +int strcmp(const char *a, const char *b) { + while (*a && *a == *b) + ++a, ++b; + return *a - *b; +} diff --git a/runtime/klee-libc/strcoll.c b/runtime/klee-libc/strcoll.c new file mode 100644 index 00000000..73d59f89 --- /dev/null +++ b/runtime/klee-libc/strcoll.c @@ -0,0 +1,15 @@ +//===-- strcoll.c ---------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <string.h> + +// according to the manpage, this is equiv in the POSIX/C locale. +int strcoll(const char *s1, const char *s2) { + return strcmp(s1,s2); +} diff --git a/runtime/klee-libc/strcpy.c b/runtime/klee-libc/strcpy.c new file mode 100644 index 00000000..0fbaf9a7 --- /dev/null +++ b/runtime/klee-libc/strcpy.c @@ -0,0 +1,17 @@ +//===-- strcpy.c ----------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +char *strcpy(char *to, const char *from) { + char *start = to; + + while ((*to = *from)) + ++to, ++from; + + return start; +} diff --git a/runtime/klee-libc/strlen.c b/runtime/klee-libc/strlen.c new file mode 100644 index 00000000..e298410c --- /dev/null +++ b/runtime/klee-libc/strlen.c @@ -0,0 +1,17 @@ +//===-- strlen.c ----------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <string.h> + +size_t strlen(const char *str) { + const char *s = str; + while (*s) + ++s; + return s - str; +} diff --git a/runtime/klee-libc/strncmp.c b/runtime/klee-libc/strncmp.c new file mode 100644 index 00000000..a8dc3749 --- /dev/null +++ b/runtime/klee-libc/strncmp.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdlib.h> + +int strncmp(const char *s1, const char *s2, size_t n) +{ + + if (n == 0) + return (0); + do { + if (*s1 != *s2++) + return (*(unsigned char *)s1 - + *(unsigned char *)(s2 - 1)); + if (*s1++ == 0) + break; + } while (--n != 0); + return (0); +} diff --git a/runtime/klee-libc/strncpy.c b/runtime/klee-libc/strncpy.c new file mode 100644 index 00000000..62ab7993 --- /dev/null +++ b/runtime/klee-libc/strncpy.c @@ -0,0 +1,58 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdlib.h> +/* + * Copy src to dst, truncating or null-padding to always copy n bytes. + * Return dst. + */ +char * strncpy(char * dst, const char * src, size_t n) +{ + if (n != 0) { + char *d = dst; + const char *s = src; + + do { + if ((*d++ = *s++) == 0) { + /* NUL pad the remaining n-1 bytes */ + while (--n != 0) + *d++ = 0; + break; + } + } while (--n != 0); + } + return (dst); +} diff --git a/runtime/klee-libc/strrchr.c b/runtime/klee-libc/strrchr.c new file mode 100644 index 00000000..01128b48 --- /dev/null +++ b/runtime/klee-libc/strrchr.c @@ -0,0 +1,21 @@ +//===-- strrchr.c ---------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <string.h> + +char *strrchr(const char *t, int c) { + char ch; + const char *l=0; + + ch = c; + for (;;) { + if (*t == ch) l=t; if (!*t) return (char*)l; ++t; + } + return (char*)l; +} diff --git a/runtime/klee-libc/strtol.c b/runtime/klee-libc/strtol.c new file mode 100644 index 00000000..3be7fff3 --- /dev/null +++ b/runtime/klee-libc/strtol.c @@ -0,0 +1,135 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <limits.h> +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> + + +/* + * Convert a string to a long integer. + * + * Assumes that the upper and lower case + * alphabets and digits are each contiguous. + */ +long +strtol(const char * nptr, char ** endptr, int base) +{ + const char *s; + unsigned long acc; + char c; + unsigned long cutoff; + int neg, any, cutlim; + + /* + * Skip white space and pick up leading +/- sign if any. + * If base is 0, allow 0x for hex and 0 for octal, else + * assume decimal; if base is already 16, allow 0x. + */ + s = nptr; + do { + c = *s++; + } while (isspace((unsigned char)c)); + if (c == '-') { + neg = 1; + c = *s++; + } else { + neg = 0; + if (c == '+') + c = *s++; + } + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + acc = any = 0; + if (base < 2 || base > 36) + goto noconv; + + /* + * Compute the cutoff value between legal numbers and illegal + * numbers. That is the largest legal value, divided by the + * base. An input number that is greater than this value, if + * followed by a legal input character, is too big. One that + * is equal to this value may be valid or not; the limit + * between valid and invalid numbers is then based on the last + * digit. For instance, if the range for longs is + * [-2147483648..2147483647] and the input base is 10, + * cutoff will be set to 214748364 and cutlim to either + * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated + * a value > 214748364, or equal but the next digit is > 7 (or 8), + * the number is too big, and we will return a range error. + * + * Set 'any' if any `digits' consumed; make it negative to indicate + * overflow. + */ + cutoff = neg ? (unsigned long)-(LONG_MIN + LONG_MAX) + LONG_MAX + : LONG_MAX; + cutlim = cutoff % base; + cutoff /= base; + for ( ; ; c = *s++) { + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'A' && c <= 'Z') + c -= 'A' - 10; + else if (c >= 'a' && c <= 'z') + c -= 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) { + acc = neg ? LONG_MIN : LONG_MAX; + errno = ERANGE; + } else if (!any) { +noconv: + errno = EINVAL; + } else if (neg) + acc = -acc; + if (endptr != NULL) + *endptr = (char *)(any ? s - 1 : nptr); + return (acc); +} diff --git a/runtime/klee-libc/strtoul.c b/runtime/klee-libc/strtoul.c new file mode 100644 index 00000000..f23d5b54 --- /dev/null +++ b/runtime/klee-libc/strtoul.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <limits.h> +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> + +/* + * Convert a string to an unsigned long integer. + * + * Assumes that the upper and lower case + * alphabets and digits are each contiguous. + */ +unsigned long +strtoul(const char * nptr, char ** endptr, int base) +{ + const char *s; + unsigned long acc; + char c; + unsigned long cutoff; + int neg, any, cutlim; + + /* + * See strtol for comments as to the logic used. + */ + s = nptr; + do { + c = *s++; + } while (isspace((unsigned char)c)); + if (c == '-') { + neg = 1; + c = *s++; + } else { + neg = 0; + if (c == '+') + c = *s++; + } + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + acc = any = 0; + if (base < 2 || base > 36) + goto noconv; + + cutoff = ULONG_MAX / base; + cutlim = ULONG_MAX % base; + for ( ; ; c = *s++) { + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'A' && c <= 'Z') + c -= 'A' - 10; + else if (c >= 'a' && c <= 'z') + c -= 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) { + acc = ULONG_MAX; + errno = ERANGE; + } else if (!any) { +noconv: + errno = EINVAL; + } else if (neg) + acc = -acc; + if (endptr != NULL) + *endptr = (char *)(any ? s - 1 : nptr); + return (acc); +} diff --git a/runtime/klee-libc/tolower.c b/runtime/klee-libc/tolower.c new file mode 100644 index 00000000..265f5deb --- /dev/null +++ b/runtime/klee-libc/tolower.c @@ -0,0 +1,14 @@ +//===-- tolower.c ---------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +int tolower(int ch) { + if ( (unsigned int)(ch - 'A') < 26u ) + ch -= 'A' - 'a'; + return ch; +} diff --git a/runtime/klee-libc/toupper.c b/runtime/klee-libc/toupper.c new file mode 100644 index 00000000..37e5f9d6 --- /dev/null +++ b/runtime/klee-libc/toupper.c @@ -0,0 +1,14 @@ +//===-- toupper.c ---------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +int toupper(int ch) { + if ( (unsigned int)(ch - 'a') < 26u ) + ch += 'A' - 'a'; + return ch; +} diff --git a/scripts/IStatsMerge.py b/scripts/IStatsMerge.py new file mode 100755 index 00000000..1bd61c3e --- /dev/null +++ b/scripts/IStatsMerge.py @@ -0,0 +1,175 @@ +#!/usr/bin/python + +from __future__ import division + +import sys, os + +class MergeError(Exception): + pass + +def checkAssemblies(directories): + def read(d): + try: + return open(os.path.join(d,'assembly.ll')).read() + except: + raise MergeError("unable to open assembly for: %s"%(`d`,)) + + reference = read(directories[0]) + for d in directories[1:]: + if reference != read(d): + return False + return True + +def allEqual(l): + return not [i for i in l if i!=l[0]] + +def merge(inputs, output, outputDir): + inputs = [[None,iter(i)] for i in inputs] + + def getLine(elt): + la,i = elt + if la is None: + try: + ln = i.next() + except StopIteration: + ln = None + except: + raise MergeError("unexpected IO error") + return ln + else: + elt[0] = None + return la + def getLines(): + return map(getLine,inputs) + def putback(ln,elt): + assert elt[0] is None + elt[0] = ln + + events = None + + # read header (up to ob=) + while 1: + lns = getLines() + ln = lns[0] + if ln.startswith('ob='): + if [l for l in lns if not l.startswith('ob=')]: + raise MergeError("headers differ") + output.write('ob=%s\n'%(os.path.join(outputDir,'assembly.ll'),)) + break + else: + if ln.startswith('positions:'): + if ln!='positions: instr line\n': + raise MergeError("unexpected 'positions' directive") + elif ln.startswith('events:'): + events = ln[len('events: '):].strip().split(' ') + if not allEqual(lns): + raise MergeError("headers differ") + output.write(ln) + + if events is None: + raise MergeError('missing events directive') + boolTypes = set(['Icov','Iuncov']) + numEvents = len(events) + eventTypes = [e in boolTypes for e in events] + + def mergeStats(datas): + if not allEqual([d[:2] for d in datas]): + raise MergeError("instruction or line specifications differ") + elif [d for d in datas if len(d)!=numEvents+2]: + raise MergeError("statistics differ in event counts") + + result = [datas[0][0],datas[0][1]] + for i,ev in enumerate(events): + s = sum([d[i+2] for d in datas]) + if ev=='Icov': + result.append(max([d[i+2] for d in datas])) # true iff any are non-zero + elif ev=='Iuncov': + result.append(min([d[i+2] for d in datas])) # true iff any are non-zero + else: + result.append(s) + return result + + def readCalls(): + results = {} + for elt in inputs: + while 1: + ln = getLine(elt) + if ln is not None and (ln.startswith('cfl=') or ln.startswith('cfn=')): + if ln.startswith('cfl='): + cfl = ln + cfn = getLine(elt) + if not cfn.startswith('cfn='): + raise MergeError("unexpected cfl directive without function") + else: + cfl = None + cfn = ln + target = getLine(elt) + if not target.startswith('calls='): + raise MergeError("unexpected cfn directive with calls") + stat = map(int,getLine(elt).strip().split(' ')) + key = target + existing = results.get(target) + if existing is None: + results[key] = [cfl,cfn,target,stat] + else: + if existing[0]!=cfl or existing[1]!=cfn: + raise MergeError("multiple call descriptions for a single target") + existing[3] = mergeStats([existing[3],stat]) + else: + putback(ln, elt) + break + return results + + # read statistics + while 1: + lns = getLines() + ln = lns[0] + if ln is None: + if not allEqual(lns): + raise MergeError("unexpected end of input") + break + elif ln.startswith('fn') or ln.startswith('fl'): + if not allEqual(lns): + raise MergeError("files differ") + output.write(ln) + else: + # an actual statistic + data = [map(int,ln.strip().split(' ')) for ln in lns] + print >>output,' '.join(map(str,mergeStats(data))) + + # read any associated calls + for cfl,cfn,calls,stat in readCalls().values(): + if cfl is not None: + output.write(cfl) + output.write(cfn) + output.write(calls) + print >>output,' '.join(map(str,stat)) + +def main(args): + from optparse import OptionParser + op = OptionParser("usage: %prog [options] directories+ output") + opts,args = op.parse_args() + + output = args.pop() + directories = args + + if len(directories)<=1: + op.error("incorrect number of arguments") + + print 'Merging:',', '.join(directories) + print 'Into:',output + + if not checkAssemblies(directories): + raise MergeError("executables differ") + + if not os.path.exists(output): + os.mkdir(output) + + assembly = open(os.path.join(directories[0],'assembly.ll')).read() + open(os.path.join(output,'assembly.ll'),'w').write(assembly) + + inputs = [open(os.path.join(d,'run.istats')) for d in directories] + merge(inputs, open(os.path.join(output,'run.istats'),'w'), output) + +if __name__=='__main__': + main(sys.argv) diff --git a/scripts/IStatsSum.py b/scripts/IStatsSum.py new file mode 100755 index 00000000..ce680c7b --- /dev/null +++ b/scripts/IStatsSum.py @@ -0,0 +1,129 @@ +#!/usr/bin/python + +from __future__ import division + +import sys, os + +def getSummary(input): + inputs = [[None,iter(open(input))]] + def getLine(elt): + la,i = elt + if la is None: + try: + ln = i.next() + except StopIteration: + ln = None + except: + raise ValueError("unexpected IO error") + return ln + else: + elt[0] = None + return la + def getLines(): + return map(getLine,inputs) + def putback(ln,elt): + assert elt[0] is None + elt[0] = ln + + events = None + + # read header (up to ob=) + while 1: + lns = getLines() + ln = lns[0] + if ln.startswith('ob='): + break + else: + if ln.startswith('positions:'): + if ln!='positions: instr line\n': + raise ValueError("unexpected 'positions' directive") + elif ln.startswith('events:'): + events = ln[len('events: '):].strip().split(' ') + + if events is None: + raise ValueError('missing events directive') + boolTypes = set(['Icov','Iuncov']) + numEvents = len(events) + eventTypes = [e in boolTypes for e in events] + + def readCalls(): + results = {} + for elt in inputs: + while 1: + ln = getLine(elt) + if ln is not None and (ln.startswith('cfl=') or ln.startswith('cfn=')): + if ln.startswith('cfl='): + cfl = ln + cfn = getLine(elt) + if not cfn.startswith('cfn='): + raise ValueError("unexpected cfl directive without function") + else: + cfl = None + cfn = ln + target = getLine(elt) + if not target.startswith('calls='): + raise ValueError("unexpected cfn directive with calls") + stat = map(int,getLine(elt).strip().split(' ')) + key = target + existing = results.get(target) + if existing is None: + results[key] = [cfl,cfn,target,stat] + else: + if existing[0]!=cfl or existing[1]!=cfn: + raise ValueError("multiple call descriptions for a single target") + existing[3] = mergeStats([existing[3],stat]) + else: + putback(ln, elt) + break + return results + + summed = [0]*len(events) + + # read statistics + while 1: + lns = getLines() + ln = lns[0] + if ln is None: + break + elif ln.startswith('fn') or ln.startswith('fl'): + pass + elif ln.strip(): + # an actual statistic + data = [map(int,ln.strip().split(' ')) for ln in lns][0] + summed = map(lambda a,b: a+b, data[2:], summed) + + # read any associated calls + for cfl,cfn,calls,stat in readCalls().values(): + pass + + return events,summed + +def main(args): + from optparse import OptionParser + op = OptionParser("usage: %prog [options] file") + opts,args = op.parse_args() + + total = {} + for i in args: + events,summed = getSummary(i) + for e,s in zip(events,summed): + total[e] = total.get(e,[0,0]) + total[e][0] += s + total[e][1] += 1 + print '-- %s --'%(i,) + items = zip(events,summed) + items.sort() + for e,s in items: + print '%s: %s'%(e,s) + + print '-- totals --' + items = total.items() + table = [] + for e,(s,N) in items: + table.append((str(e),str(s),str(N),str(s//N))) + w = map(lambda l: max(map(len,l)), zip(*table)) + for (a,b,c,d) in table: + print '%-*s: %*s (in %*s files, avg: %*s)'%(w[0],a,w[1],b,w[2],c,w[3],d) + +if __name__=='__main__': + main(sys.argv) diff --git a/scripts/PrintStats.py b/scripts/PrintStats.py new file mode 100755 index 00000000..40994f87 --- /dev/null +++ b/scripts/PrintStats.py @@ -0,0 +1,231 @@ +#!/usr/bin/python + +from __future__ import division + +import sys +import os + +def getFile(dir): + return os.path.join(dir,'run.stats') + +def getLastRecord(dir): + f = open(getFile(dir)) + try: + f.seek(-1024, 2) + except IOError: + pass # at beginning? + for ln in f.read(1024).split('\n')[::-1]: + ln = ln.strip() + if ln.startswith('(') and ln.endswith(')'): + if '(' in ln[1:]: + print >>sys.stderr, 'WARNING: corrupted line in file, out of disk space?' + ln = ln[ln.index('(',1):] + return eval(ln) + raise IOError + + +class LazyEvalList: + def __init__(self, lines): + self.lines = lines + + def __getitem__(self, index): + item = self.lines[index] + if isinstance(item,str): + item = self.lines[index] = eval(item) + return item + + def __len__(self): + return len(self.lines) + + +def getMatchedRecord(data,reference,key): + refKey = key(reference) + lo = 1 # header + hi = len(data)-1 + while lo<hi: + mid = (lo+hi)//2 + if key(data[mid])<=refKey: + lo = mid + 1 + else: + hi = mid + return data[lo] + + +def stripCommonPathPrefix(table, col): + paths = map(os.path.normpath, [row[col] for row in table]) + pathElts = [p.split('/') for p in paths] + zipped = zip(*pathElts) + idx = 0 + for idx,elts in enumerate(zipped): + if len(set(elts))>1: + break + paths = ['/'.join(elts[idx:]) for elts in pathElts] + for i,row in enumerate(table): + table[i] = row[:col] + (paths[i],) + row[col+1:] + + +def getKeyIndex(keyName,labels): + def normalizedKey(key): + if key.endswith("(#)") or key.endswith("(%)") or key.endswith("(s)"): + key = key[:-3] + return key.lower() + + keyIndex = None + for i,title in enumerate(labels): + if normalizedKey(title)==normalizedKey(keyName): + keyIndex = i + break + else: + raise ValueError,'invalid keyName to sort by: %s'%`keyName` + return keyIndex + + +def sortTable(table, labels, keyName, ascending=False): + indices = range(len(table)) + keyIndex = getKeyIndex(keyName,labels) + indices.sort(key = lambda n: table[n][keyIndex]) + if not ascending: + indices.reverse() + table[:] = [table[n] for n in indices] + + +def printTable(table): + def strOrNone(ob): + if ob is None: + return '' + elif isinstance(ob,float): + return '%.2f'%ob + else: + return str(ob) + def printRow(row): + if row is None: + print header + else: + out.write('|') + for j,elt in enumerate(row): + if j: + out.write(' %*s |'%(widths[j],elt)) + else: + out.write(' %-*s |'%(widths[j],elt)) + out.write('\n') + maxLen = max([len(r) for r in table if r]) + for i,row in enumerate(table): + if row: + table[i] = row + (None,)*(maxLen-len(row)) + table = [row and map(strOrNone,row) or None for row in table] + tableLens = [map(len,row) for row in table if row] + from pprint import pprint + widths = map(max, zip(*tableLens)) + out = sys.stdout + header = '-'*(sum(widths) + maxLen*3 + 1) + map(printRow, table) + + +def main(args): + from optparse import OptionParser + op = OptionParser(usage="usage: %prog [options] directories*", + epilog= + "LEGEND " + "------ " + "Instrs: Number of executed instructions " + "Time: Total wall time (s) " + "ICov: Instruction coverage in the LLVM bitcode (%) " + "BCov: Branch coverage in the LLVM bitcode (%) " + "ICount: Total static instructions in the LLVM bitcode " + "Solver: Time spent in the constraint solver (%) " + "States: Number of currently active states " + "Mem: Megabytes of memory currently used " + "Queries: Number of queries issued to STP " + "AvgQC: Average number of query constructs per query " + "Tcex: Time spent in the counterexample caching code (%) " + "Tfork: Time spent forking (%) ") + + op.add_option('', '--print-more', dest='printMore', + action='store_true', default=False, + help='Print extra information (needed when monitoring an ongoing run).') + op.add_option('', '--print-all', dest='printAll', + action='store_true', default=False, + help='Print all available information.') + op.add_option('','--sort-by', dest='sortBy', + help='key value to sort by, e.g. --sort-by=Instrs') + op.add_option('','--ascending', dest='ascending', + action='store_true', default=False, + help='sort in ascending order (default is descending)') + op.add_option('','--compare-by', dest='compBy', + help="key value on which to compare runs to the reference one (which is the first one). E.g., --compare-by=Instrs shows how each run compares to the reference run after executing the same number of instructions as the reference run. If a run hasn't executed as many instructions as the reference one, we simply print the statistics at the end of that run.") + opts,dirs = op.parse_args() + + actualDirs = [] + for dir in dirs: + if os.path.exists(os.path.join(dir,'info')): + actualDirs.append(dir) + else: + for root,dirs,_ in os.walk(dir): + for d in dirs: + p = os.path.join(root,d) + if os.path.exists(os.path.join(p,'info')): + actualDirs.append(p) + dirs = actualDirs + + summary = [] + + if (opts.printAll): + labels = ('Path','Instrs','Time(s)','ICov(%)','BCov(%)','ICount','Solver(%)', 'States', 'Mem(MB)', 'Queries', 'AvgQC', 'Tcex(%)', 'Tfork(%)') + elif (opts.printMore): + labels = ('Path','Instrs','Time(s)','ICov(%)','BCov(%)','ICount','Solver(%)', 'States', 'Mem(MB)') + else: + labels = ('Path','Instrs','Time(s)','ICov(%)','BCov(%)','ICount','Solver(%)') + + + def addRecord(Path,rec): + (I,BFull,BPart,BTot,T,St,Mem,QTot,QCon,NObjs,Treal,SCov,SUnc,QT,Ts,Tcex,Tf) = rec + Mem=Mem/1024./1024. + AvgQC = int(QCon/max(1,QTot)) + if (opts.printAll): + table.append((Path, I, Treal, 100.*SCov/(SCov+SUnc), 100.*(2*BFull+BPart)/(2.*BTot), + SCov+SUnc, 100.*Ts/Treal, St, Mem, QTot, AvgQC, 100.*Tcex/Treal, 100.*Tf/Treal)) + elif (opts.printMore): + table.append((Path, I, Treal, 100.*SCov/(SCov+SUnc), 100.*(2*BFull+BPart)/(2.*BTot), + SCov+SUnc, 100.*Ts/Treal, St, Mem)) + else: + table.append((Path, I, Treal, 100.*SCov/(SCov+SUnc), 100.*(2*BFull+BPart)/(2.*BTot), + SCov+SUnc, 100.*Ts/Treal)) + + def addRow(Path,data): + data = tuple(data[:17]) + (None,)*(17-len(data)) + addRecord(Path,data) + if not summary: + summary[:] = list(data) + else: + summary[:] = [(a+b) for a,b in zip(summary,data)] + + datas = [(dir,LazyEvalList(list(open(getFile(dir))))) for dir in dirs] + reference = datas[0][1][-1] + + table = [] + + for dir,data in datas: + try: + if opts.compBy: + addRow(dir,getMatchedRecord(data,reference,lambda f: f[getKeyIndex(opts.compBy,labels)-1])) + else: + addRow(dir, data[len(data)-1]) #getLastRecord(dir)) + except IOError: + print 'Unable to open: ',dir + continue + + stripCommonPathPrefix(table, 0) + if opts.sortBy: + sortTable(table, labels, opts.sortBy, opts.ascending) + if not table: + sys.exit(1) + elif len(table)>1: + table.append(None) + addRecord('Total (%d)'%(len(table)-1,),summary) + table[0:0] = [None,labels,None] + table.append(None) + printTable(table) + + +if __name__=='__main__': + main(sys.argv) diff --git a/scripts/klee-control b/scripts/klee-control new file mode 100755 index 00000000..50c1f82d --- /dev/null +++ b/scripts/klee-control @@ -0,0 +1,89 @@ +#!/usr/bin/python + +import os, signal, popen2 + +def getPID(dir): + f = open(os.path.join(dir,'info')) + for ln in f.readlines(): + if ln.startswith('PID: '): + return int(ln[5:]) + return None + +def execCmd(pid, gdbCmd, opts): + cmd = ("gdb " + + "--batch " + + "--pid=%d " + + "--eval-command=\"%s\" " + + "--eval-command=detach")%(pid,gdbCmd) + cout,cin = popen2.popen2(cmd) + cin.close() + return cout.read() + +def main(): + from optparse import OptionParser + op = OptionParser("usage: %prog <PID | test directory>") + op.add_option('','--backtrace', dest='backtrace', + action='store_true', default=False) + op.add_option('-s','--stop-forking', dest='stopForking', + action='store_true', default=False) + op.add_option('-H','--halt-execution', dest='haltExecution', + action='store_true', default=False) + op.add_option('-d','--dump-states', dest='dumpStates', + action='store_true', default=False) + op.add_option('-t','--dump-tree', dest='dumpTree', + action='store_true', default=False) + op.add_option('-i','--int', dest='int', + action='store_true', default=False) + op.add_option('-k','--kill', dest='kill', + action='store_true', default=False) + op.add_option('','--print-pid', dest='printPid', + action='store_true', default=False) + op.add_option('','--print-ticks', dest='printTicks', + action='store_true', default=False) + opts,args = op.parse_args() + + if len(args) != 1: + op.error("invalid arguments") + + try: + pid = int(args[0]) + except: + pid = None + + if pid is None: + try: + pid = getPID(args[0]) + except: + pid = None + + if pid is None: + op.error("unable to determine PID (bad pid or test directory)") + + if opts.printPid: + print pid + return + print 'pid: %d'%pid + if opts.backtrace: + execCmd(pid, 'bt', opts) + if opts.dumpStates: + execCmd(pid, "p dumpStates = 1", opts) + if opts.dumpTree: + execCmd(pid, "p dumpPTree = 1", opts) + if opts.stopForking: + execCmd(pid, 'p stop_forking()', opts) + if opts.haltExecution: + execCmd(pid, 'p halt_execution()', opts) + if opts.printTicks: + res = execCmd(pid, 'p timerTicks', opts) + lns = res.split('\n') + for ln in lns: + if ln.startswith('$1') and '=' in ln: + print ln.split('=',1)[1].strip() + if opts.int: + os.kill(pid, signal.SIGINT) + if opts.kill: + os.kill(pid, signal.SIGKILL) + +if __name__=='__main__': + main() + diff --git a/scripts/objdump b/scripts/objdump new file mode 100755 index 00000000..45fe6384 --- /dev/null +++ b/scripts/objdump @@ -0,0 +1,41 @@ +#!/usr/bin/python + +""" +An objdump wrapper for use with klee & kcachegrind. + +This allows klee to output instruction level statistic information which +kcachegrind can then display by intercepting kcachegrind's calls to objdump +and returning the LLVM instructions in a format that it can parse. +""" + +import os, sys + +kActualObjdump = os.path.join(os.path.dirname(__file__),'objdump.actual') +if not os.path.exists(kActualObjdump): + for path in ['/usr/bin','/usr/local/bin']: + p = os.path.join(path,'objdump') + if os.path.exists(p): + kActualObjdump = p + +def fakeObjdumpOutput(file, start, end): + print 'Using fake objdump output:\n\n' + + lines = open(file).readlines() + for i in range(max(0,start-1),min(end-1,len(lines))): + print '%x: _ %s'%(i+1,lines[i]) + +def main(args): + # exact pattern match kcachegrind's calling sequence + if (len(args)>=6 and + args[1]=='-C' and + args[2]=='-d' and + args[3].startswith('--start-address=') and + args[4].startswith('--stop-address=') and + args[5].endswith('.ll')): + fakeObjdumpOutput(args[5], eval(args[3].split('=',1)[1]), eval(args[4].split('=',1)[1])) + else: + os.execv(kActualObjdump, args) + raise RuntimeError + +if __name__=='__main__': + main(sys.argv) diff --git a/stp/AST/AST.cpp b/stp/AST/AST.cpp new file mode 100644 index 00000000..ab290395 --- /dev/null +++ b/stp/AST/AST.cpp @@ -0,0 +1,1587 @@ +/******************************************************************** + * AUTHORS: Vijay Ganesh, David L. Dill + * + * BEGIN DATE: November, 2005 + * + * LICENSE: Please view LICENSE file in the home dir of this Program + ********************************************************************/ +// -*- c++ -*- + +#include "AST.h" +namespace BEEV { + //some global variables that are set through commandline options. it + //is best that these variables remain global. Default values set + //here + // + //collect statistics on certain functions + bool stats = false; + //print DAG nodes + bool print_nodes = false; + //tentative global var to allow for variable activity optimization + //in the SAT solver. deprecated. + bool variable_activity_optimize = false; + //run STP in optimized mode + bool optimize = true; + //do sat refinement, i.e. underconstraint the problem, and feed to + //SAT. if this works, great. else, add a set of suitable constraints + //to re-constraint the problem correctly, and call SAT again, until + //all constraints have been added. + bool arrayread_refinement = true; + //flag to control write refinement + bool arraywrite_refinement = true; + //check the counterexample against the original input to STP + bool check_counterexample = false; + //construct the counterexample in terms of original variable based + //on the counterexample returned by SAT solver + bool construct_counterexample = true; + bool print_counterexample = false; + //if this option is true then print the way dawson wants using a + //different printer. do not use this printer. + bool print_arrayval_declaredorder = false; + //flag to decide whether to print "valid/invalid" or not + bool print_output = false; + //do linear search in the array values of an input array. experimental + bool linear_search = false; + //print the variable order chosen by the sat solver while it is + //solving. + bool print_sat_varorder = false; + //turn on word level bitvector solver + bool wordlevel_solve = true; + //turn off XOR flattening + bool xor_flatten = false; + + //the smtlib parser has been turned on + bool smtlib_parser_enable = false; + //print the input back + bool print_STPinput_back = false; + + //global BEEVMGR for the parser + BeevMgr * globalBeevMgr_for_parser; + + void (*vc_error_hdlr)(const char* err_msg) = NULL; + /** This is reusable empty vector, for representing empty children arrays */ + ASTVec _empty_ASTVec; + //////////////////////////////////////////////////////////////// + // ASTInternal members + //////////////////////////////////////////////////////////////// + /** Trivial but virtual destructor */ + ASTInternal::~ASTInternal() { } + + //////////////////////////////////////////////////////////////// + // ASTInterior members + //////////////////////////////////////////////////////////////// + /** Copy constructor */ + // ASTInterior::ASTInterior(const ASTInterior &int_node) + // { + // _kind = int_node._kind; + // _children = int_node._children; + // } + + /** Trivial but virtual destructor */ + ASTInterior::~ASTInterior() { } + + // FIXME: Darn it! I think this ends up copying the children twice! + /** Either return an old node or create it if it doesn't exist. + Note that nodes are physically allocated in the hash table. */ + + // There is an inelegance here that I don't know how to solve. I'd + // like to heap allocate and do some other initialization on keys only + // if they aren't in the hash table. It would be great if the + // "insert" method took a "creator" class so that I could do that + // between when it notices that the key is not there and when it + // inserts it. Alternatively, it would be great if I could insert the + // temporary key and replace it if it actually got inserted. But STL + // hash_set doesn't have the creator feature and paternalistically + // declares that keys are immutable, even though (it seems to me) that + // they could be mutated if the hash value and eq values did not + // change. + + ASTInterior *BeevMgr::LookupOrCreateInterior(ASTInterior *n_ptr) { + ASTInteriorSet::iterator it; + + if ((it = _interior_unique_table.find(n_ptr)) == _interior_unique_table.end()) { + // Make a new ASTInterior node + // We want (NOT alpha) always to have alpha.nodenum + 1. + if (n_ptr->GetKind() == NOT) { + n_ptr->SetNodeNum(n_ptr->GetChildren()[0].GetNodeNum()+1); + } + else { + n_ptr->SetNodeNum(NewNodeNum()); + } + pair<ASTInteriorSet::const_iterator, bool> p = _interior_unique_table.insert(n_ptr); + return *(p.first); + } + else + // Delete the temporary node, and return the found node. + delete n_ptr; + return *it; + } + + size_t ASTInterior::ASTInteriorHasher::operator() (const ASTInterior *int_node_ptr) const { + //size_t hashval = 0; + size_t hashval = ((size_t) int_node_ptr->GetKind()); + const ASTVec &ch = int_node_ptr->GetChildren(); + ASTVec::const_iterator iend = ch.end(); + for (ASTVec::const_iterator i = ch.begin(); i != iend; i++) { + //Using "One at a time hash" by Bob Jenkins + hashval += i->Hash(); + hashval += (hashval << 10); + hashval ^= (hashval >> 6); + } + + hashval += (hashval << 3); + hashval ^= (hashval >> 11); + hashval += (hashval << 15); + return hashval; + //return hashval += ((size_t) int_node_ptr->GetKind()); + } + + + void ASTInterior::CleanUp() { + // cout << "Deleting node " << this->GetNodeNum() << endl; + _bm._interior_unique_table.erase(this); + delete this; + } + + //////////////////////////////////////////////////////////////// + // ASTNode members + //////////////////////////////////////////////////////////////// + //ASTNode constructors are inlined in AST.h + bool ASTNode::IsAlreadyPrinted() const { + BeevMgr &bm = GetBeevMgr(); + return (bm.AlreadyPrintedSet.find(*this) != bm.AlreadyPrintedSet.end()); + } + + void ASTNode::MarkAlreadyPrinted() const { + // FIXME: Fetching BeevMgr is annoying. Can we put this in lispprinter class? + BeevMgr &bm = GetBeevMgr(); + bm.AlreadyPrintedSet.insert(*this); + } + + // Get the name from a symbol (char *). It's an error if kind != SYMBOL + const char * const ASTNode::GetName() const { + if (GetKind() != SYMBOL) + FatalError("GetName: Called GetName on a non-symbol: ", *this); + return ((ASTSymbol *) _int_node_ptr)->GetName(); + } + + // Print in lisp format + ostream &ASTNode::LispPrint(ostream &os, int indentation) const { + // Clear the PrintMap + BeevMgr& bm = GetBeevMgr(); + bm.AlreadyPrintedSet.clear(); + return LispPrint_indent(os, indentation); + } + + // Print newline and indentation, then print the thing. + ostream &ASTNode::LispPrint_indent(ostream &os, + int indentation) const + { + os << endl << spaces(indentation); + LispPrint1(os, indentation); + return os; + } + + /** Internal function to print in lisp format. Assume newline + and indentation printed already before first line. Recursive + calls will have newline & indent, though */ + ostream &ASTNode::LispPrint1(ostream &os, int indentation) const { + if (!IsDefined()) { + os << "<undefined>"; + return os; + } + Kind kind = GetKind(); + // FIXME: figure out how to avoid symbols with same names as kinds. +// if (kind == READ) { +// const ASTVec &children = GetChildren(); +// children[0].LispPrint1(os, indentation); +// os << "[" << children[1] << "]"; +// } else + if(kind == BVGETBIT) { + const ASTVec &children = GetChildren(); + // child 0 is a symbol. Print without the NodeNum. + os << GetNodeNum() << ":"; + + + + children[0]._int_node_ptr->nodeprint(os); + //os << "{" << children[1].GetBVConst() << "}"; + os << "{"; + children[1]._int_node_ptr->nodeprint(os); + os << "}"; + } else if (kind == NOT) { + const ASTVec &children = GetChildren(); + os << GetNodeNum() << ":"; + os << "(NOT "; + children[0].LispPrint1(os, indentation); + os << ")"; + } + else if (Degree() == 0) { + // Symbol or a kind with no children print as index:NAME if shared, + // even if they have been printed before. + os << GetNodeNum() << ":"; + _int_node_ptr->nodeprint(os); + // os << "(" << _int_node_ptr->_ref_count << ")"; + // os << "{" << GetValueWidth() << "}"; + } + else if (IsAlreadyPrinted()) { + // print non-symbols as "[index]" if seen before. + os << "[" << GetNodeNum() << "]"; + // << "(" << _int_node_ptr->_ref_count << ")"; + } + else { + MarkAlreadyPrinted(); + const ASTVec &children = GetChildren(); + os << GetNodeNum() << ":" + //<< "(" << _int_node_ptr->_ref_count << ")" + << "(" << kind << " "; + // os << "{" << GetValueWidth() << "}"; + ASTVec::const_iterator iend = children.end(); + for (ASTVec::const_iterator i = children.begin(); i != iend; i++) { + i->LispPrint_indent(os, indentation+2); + } + os << ")"; + } + return os; + } + + //print in PRESENTATION LANGUAGE + // + //two pass algorithm: + // + //1. In the first pass, letize this Node, N: i.e. if a node + //1. appears more than once in N, then record this fact. + // + //2. In the second pass print a "global let" and then print N + //2. as follows: Every occurence of a node occuring more than + //2. once is replaced with the corresponding let variable. + ostream& ASTNode::PL_Print(ostream &os, + int indentation) const { + // Clear the PrintMap + BeevMgr& bm = GetBeevMgr(); + bm.PLPrintNodeSet.clear(); + bm.NodeLetVarMap.clear(); + bm.NodeLetVarVec.clear(); + bm.NodeLetVarMap1.clear(); + + //pass 1: letize the node + LetizeNode(); + + //pass 2: + // + //2. print all the let variables and their counterpart expressions + //2. as follows (LET var1 = expr1, var2 = expr2, ... + // + //3. Then print the Node itself, replacing every occurence of + //3. expr1 with var1, expr2 with var2, ... + //os << "("; + if(0 < bm.NodeLetVarMap.size()) { + //ASTNodeMap::iterator it=bm.NodeLetVarMap.begin(); + //ASTNodeMap::iterator itend=bm.NodeLetVarMap.end(); + std::vector<pair<ASTNode,ASTNode> >::iterator it = bm.NodeLetVarVec.begin(); + std::vector<pair<ASTNode,ASTNode> >::iterator itend = bm.NodeLetVarVec.end(); + + os << "(LET "; + //print the let var first + it->first.PL_Print1(os,indentation,false); + os << " = "; + //print the expr + it->second.PL_Print1(os,indentation,false); + + //update the second map for proper printing of LET + bm.NodeLetVarMap1[it->second] = it->first; + + for(it++;it!=itend;it++) { + os << "," << endl; + //print the let var first + it->first.PL_Print1(os,indentation,false); + os << " = "; + //print the expr + it->second.PL_Print1(os,indentation,false); + + //update the second map for proper printing of LET + bm.NodeLetVarMap1[it->second] = it->first; + } + + os << " IN " << endl; + PL_Print1(os,indentation, true); + os << ") "; + } + else + PL_Print1(os,indentation, false); + //os << " )"; + os << " "; + return os; + } //end of PL_Print() + + //traverse "*this", and construct "let variables" for terms that + //occur more than once in "*this". + void ASTNode::LetizeNode(void) const { + Kind kind = this->GetKind(); + + if(kind == SYMBOL || + kind == BVCONST || + kind == FALSE || + kind == TRUE) + return; + + //FIXME: this is ugly. + BeevMgr& bm = GetBeevMgr(); + const ASTVec &c = this->GetChildren(); + for(ASTVec::const_iterator it=c.begin(),itend=c.end();it!=itend;it++){ + ASTNode ccc = *it; + if(bm.PLPrintNodeSet.find(ccc) == bm.PLPrintNodeSet.end()){ + //If branch: if *it is not in NodeSet then, + // + //1. add it to NodeSet + // + //2. Letize its childNodes + + //FIXME: Fetching BeevMgr is annoying. Can we put this in + //some kind of a printer class + bm.PLPrintNodeSet.insert(ccc); + //debugging + //cerr << ccc; + ccc.LetizeNode(); + } + else{ + Kind k = ccc.GetKind(); + if(k == SYMBOL || + k == BVCONST || + k == FALSE || + k == TRUE) + continue; + + //0. Else branch: Node has been seen before + // + //1. Check if the node has a corresponding letvar in the + //1. NodeLetVarMap. + // + //2. if no, then create a new var and add it to the + //2. NodeLetVarMap + if(bm.NodeLetVarMap.find(ccc) == bm.NodeLetVarMap.end()) { + //Create a new symbol. Get some name. if it conflicts with a + //declared name, too bad. + int sz = bm.NodeLetVarMap.size(); + ostringstream oss; + oss << "let_k_" << sz; + + ASTNode CurrentSymbol = bm.CreateSymbol(oss.str().c_str()); + CurrentSymbol.SetValueWidth(this->GetValueWidth()); + CurrentSymbol.SetIndexWidth(this->GetIndexWidth()); + /* If for some reason the variable being created here is + * already declared by the user then the printed output will + * not be a legal input to the system. too bad. I refuse to + * check for this. [Vijay is the author of this comment.] + */ + + bm.NodeLetVarMap[ccc] = CurrentSymbol; + std::pair<ASTNode,ASTNode> node_letvar_pair(CurrentSymbol,ccc); + bm.NodeLetVarVec.push_back(node_letvar_pair); + } + } + } + } //end of LetizeNode() + + void ASTNode::PL_Print1(ostream& os, + int indentation, + bool letize) const { + //os << spaces(indentation); + //os << endl << spaces(indentation); + if (!IsDefined()) { + os << "<undefined>"; + return; + } + + //if this node is present in the letvar Map, then print the letvar + BeevMgr &bm = GetBeevMgr(); + + //this is to print letvars for shared subterms inside the printing + //of "(LET v0 = term1, v1=term1@term2,... + if((bm.NodeLetVarMap1.find(*this) != bm.NodeLetVarMap1.end()) && !letize) { + (bm.NodeLetVarMap1[*this]).PL_Print1(os,indentation,letize); + return; + } + + //this is to print letvars for shared subterms inside the actual + //term to be printed + if((bm.NodeLetVarMap.find(*this) != bm.NodeLetVarMap.end()) && letize) { + (bm.NodeLetVarMap[*this]).PL_Print1(os,indentation,letize); + return; + } + + //otherwise print it normally + Kind kind = GetKind(); + const ASTVec &c = GetChildren(); + switch(kind) { + case BVGETBIT: + c[0].PL_Print1(os,indentation,letize); + os << "{"; + c[1].PL_Print1(os,indentation,letize); + os << "}"; + break; + case BITVECTOR: + os << "BITVECTOR("; + unsigned char * str; + str = CONSTANTBV::BitVector_to_Hex(c[0].GetBVConst()); + os << str << ")"; + CONSTANTBV::BitVector_Dispose(str); + break; + case BOOLEAN: + os << "BOOLEAN"; + break; + case FALSE: + case TRUE: + os << kind; + break; + case BVCONST: + case SYMBOL: + _int_node_ptr->nodeprint(os); + break; + case READ: + c[0].PL_Print1(os, indentation,letize); + os << "["; + c[1].PL_Print1(os,indentation,letize); + os << "]"; + break; + case WRITE: + os << "("; + c[0].PL_Print1(os,indentation,letize); + os << " WITH ["; + c[1].PL_Print1(os,indentation,letize); + os << "] := "; + c[2].PL_Print1(os,indentation,letize); + os << ")"; + os << endl; + break; + case BVUMINUS: + os << kind << "( "; + c[0].PL_Print1(os,indentation,letize); + os << ")"; + break; + case NOT: + os << "NOT("; + c[0].PL_Print1(os,indentation,letize); + os << ") " << endl; + break; + case BVNEG: + os << " ~("; + c[0].PL_Print1(os,indentation,letize); + os << ")"; + break; + case BVCONCAT: + os << "("; + c[0].PL_Print1(os,indentation,letize); + os << " @ "; + c[1].PL_Print1(os,indentation,letize); + os << ")" << endl; + break; + case BVOR: + os << "("; + c[0].PL_Print1(os,indentation,letize); + os << " | "; + c[1].PL_Print1(os,indentation,letize); + os << ")"; + break; + case BVAND: + os << "("; + c[0].PL_Print1(os,indentation,letize); + os << " & "; + c[1].PL_Print1(os,indentation,letize); + os << ")"; + break; + case BVEXTRACT: + c[0].PL_Print1(os,indentation,letize); + os << "["; + os << GetUnsignedConst(c[1]); + os << ":"; + os << GetUnsignedConst(c[2]); + os << "]"; + break; + case BVLEFTSHIFT: + os << "("; + c[0].PL_Print1(os,indentation,letize); + os << " << "; + os << GetUnsignedConst(c[1]); + os << ")"; + break; + case BVRIGHTSHIFT: + os << "("; + c[0].PL_Print1(os,indentation,letize); + os << " >> "; + os << GetUnsignedConst(c[1]); + os << ")"; + break; + case BVMULT: + case BVSUB: + case BVPLUS: + case SBVDIV: + case SBVMOD: + case BVDIV: + case BVMOD: + os << kind << "("; + os << this->GetValueWidth(); + for(ASTVec::const_iterator it=c.begin(),itend=c.end();it!=itend;it++) { + os << ", " << endl; + it->PL_Print1(os,indentation,letize); + } + os << ")" << endl; + break; + case ITE: + os << "IF("; + c[0].PL_Print1(os,indentation,letize); + os << ")" << endl; + os << "THEN "; + c[1].PL_Print1(os,indentation,letize); + os << endl << "ELSE "; + c[2].PL_Print1(os,indentation,letize); + os << endl << "ENDIF"; + break; + case BVLT: + case BVLE: + case BVGT: + case BVGE: + case BVXOR: + case BVNAND: + case BVNOR: + case BVXNOR: + os << kind << "("; + c[0].PL_Print1(os,indentation,letize); + os << ","; + c[1].PL_Print1(os,indentation,letize); + os << ")" << endl; + break; + case BVSLT: + os << "SBVLT" << "("; + c[0].PL_Print1(os,indentation,letize); + os << ","; + c[1].PL_Print1(os,indentation,letize); + os << ")" << endl; + break; + case BVSLE: + os << "SBVLE" << "("; + c[0].PL_Print1(os,indentation,letize); + os << ","; + c[1].PL_Print1(os,indentation,letize); + os << ")" << endl; + break; + case BVSGT: + os << "SBVGT" << "("; + c[0].PL_Print1(os,indentation,letize); + os << ","; + c[1].PL_Print1(os,indentation,letize); + os << ")" << endl; + break; + case BVSGE: + os << "SBVGE" << "("; + c[0].PL_Print1(os,indentation,letize); + os << ","; + c[1].PL_Print1(os,indentation,letize); + os << ")" << endl; + break; + case EQ: + c[0].PL_Print1(os,indentation,letize); + os << " = "; + c[1].PL_Print1(os,indentation,letize); + os << endl; + break; + case NEQ: + c[0].PL_Print1(os,indentation,letize); + os << " /= "; + c[1].PL_Print1(os,indentation,letize); + os << endl; + break; + case AND: + case OR: + case NAND: + case NOR: + case XOR: { + os << "("; + c[0].PL_Print1(os,indentation,letize); + ASTVec::const_iterator it=c.begin(); + ASTVec::const_iterator itend=c.end(); + + it++; + for(;it!=itend;it++) { + os << " " << kind << " "; + it->PL_Print1(os,indentation,letize); + os << endl; + } + os << ")"; + break; + } + case IFF: + os << "("; + os << "("; + c[0].PL_Print1(os,indentation,letize); + os << ")"; + os << " <=> "; + os << "("; + c[1].PL_Print1(os,indentation,letize); + os << ")"; + os << ")"; + os << endl; + break; + case IMPLIES: + os << "("; + os << "("; + c[0].PL_Print1(os,indentation,letize); + os << ")"; + os << " => "; + os << "("; + c[1].PL_Print1(os,indentation,letize); + os << ")"; + os << ")"; + os << endl; + break; + case BVSX: + os << kind << "("; + c[0].PL_Print1(os,indentation,letize); + os << ","; + os << this->GetValueWidth(); + os << ")" << endl; + break; + default: + //remember to use LispPrinter here. Otherwise this function will + //go into an infinite loop. Recall that "<<" is overloaded to + //the lisp printer. FatalError uses lispprinter + FatalError("PL_Print1: printing not implemented for this kind: ",*this); + break; + } + } //end of PL_Print1() + + //////////////////////////////////////////////////////////////// + // BeevMgr members + //////////////////////////////////////////////////////////////// + ASTNode BeevMgr::CreateNode(Kind kind, const ASTVec & back_children) { + // create a new node. Children will be modified. + ASTInterior *n_ptr = new ASTInterior(kind, *this); + + // insert all of children at end of new_children. + ASTNode n(CreateInteriorNode(kind, n_ptr, back_children)); + return n; + } + + ASTNode BeevMgr::CreateNode(Kind kind, + const ASTNode& child0, + const ASTVec & back_children) { + + ASTInterior *n_ptr = new ASTInterior(kind, *this); + ASTVec &front_children = n_ptr->_children; + front_children.push_back(child0); + ASTNode n(CreateInteriorNode(kind, n_ptr, back_children)); + return n; + } + + ASTNode BeevMgr::CreateNode(Kind kind, + const ASTNode& child0, + const ASTNode& child1, + const ASTVec & back_children) { + + ASTInterior *n_ptr = new ASTInterior(kind, *this); + ASTVec &front_children = n_ptr->_children; + front_children.push_back(child0); + front_children.push_back(child1); + ASTNode n(CreateInteriorNode(kind, n_ptr, back_children)); + return n; + } + + + ASTNode BeevMgr::CreateNode(Kind kind, + const ASTNode& child0, + const ASTNode& child1, + const ASTNode& child2, + const ASTVec & back_children) { + ASTInterior *n_ptr = new ASTInterior(kind, *this); + ASTVec &front_children = n_ptr->_children; + front_children.push_back(child0); + front_children.push_back(child1); + front_children.push_back(child2); + ASTNode n(CreateInteriorNode(kind, n_ptr, back_children)); + return n; + } + + + ASTInterior *BeevMgr::CreateInteriorNode(Kind kind, + // children array of this node will be modified. + ASTInterior *n_ptr, + const ASTVec & back_children) { + + // insert back_children at end of front_children + ASTVec &front_children = n_ptr->_children; + + front_children.insert(front_children.end(), back_children.begin(), back_children.end()); + + // check for undefined nodes. + ASTVec::const_iterator it_end = front_children.end(); + for (ASTVec::const_iterator it = front_children.begin(); it != it_end; it++) { + if (it->IsNull()) + FatalError("CreateInteriorNode: Undefined childnode in CreateInteriorNode: ", ASTUndefined); + } + + return LookupOrCreateInterior(n_ptr); + } + + /** Trivial but virtual destructor */ + ASTSymbol::~ASTSymbol() {} + + ostream &operator<<(ostream &os, const ASTNodeMap &nmap) + { + ASTNodeMap::const_iterator iend = nmap.end(); + for (ASTNodeMap::const_iterator i = nmap.begin(); i!=iend; i++) { + os << "Key: " << i->first << endl; + os << "Value: " << i->second << endl; + } + return os; + } + + //////////////////////////////////////////////////////////////// + // BeevMgr member functions to create ASTSymbol and ASTBVConst + //////////////////////////////////////////////////////////////// + ASTNode BeevMgr::CreateSymbol(const char * const name) + { + ASTSymbol temp_sym(name, *this); + ASTNode n(LookupOrCreateSymbol(temp_sym)); + return n; + } + +#ifndef NATIVE_C_ARITH + //Create a ASTBVConst node + ASTNode BeevMgr::CreateBVConst(unsigned int width, + unsigned long long int bvconst){ + if(width > (sizeof(unsigned long long int)<<3) || width <= 0) + FatalError("CreateBVConst: trying to create a bvconst of width: ", ASTUndefined, width); + + + CBV bv = CONSTANTBV::BitVector_Create(width, true); + unsigned long c_val = (0x00000000ffffffffLL) & bvconst; + unsigned int copied = 0; + + // sizeof(unsigned long) returns the number of bytes in unsigned + // long. In order to convert it to bits, we need to shift left by + // 3. Hence, sizeof(unsigned long) << 3 + + //The algo below works as follows: It starts by copying the + //lower-order bits of the input "bvconst" in chunks of size = + //number of bits in unsigned long. The variable "copied" keeps + //track of the number of chunks copied so far + + while(copied + (sizeof(unsigned long)<<3) < width){ + CONSTANTBV::BitVector_Chunk_Store(bv, sizeof(unsigned long)<<3,copied,c_val); + bvconst = bvconst >> (sizeof(unsigned long) << 3); + c_val = (0x00000000ffffffffLL) & bvconst; + copied += sizeof(unsigned long) << 3; + } + CONSTANTBV::BitVector_Chunk_Store(bv,width - copied,copied,c_val); + return CreateBVConst(bv,width); + } + + //Create a ASTBVConst node from std::string + ASTNode BeevMgr::CreateBVConst(const char* const strval, int base) { + size_t width = strlen((const char *)strval); + if(!(2 == base || 10 == base || 16 == base)){ + FatalError("CreateBVConst: unsupported base: ",ASTUndefined,base); + } + //FIXME Tim: Earlier versions of the code assume that the length of + //binary strings is 32 bits. + if(10 == base) width = 32; + if(16 == base) width = width * 4; + + //checking if the input is in the correct format + CBV bv = CONSTANTBV::BitVector_Create(width,true); + CONSTANTBV::ErrCode e; + if(2 == base){ + e = CONSTANTBV::BitVector_from_Bin(bv, (unsigned char*)strval); + }else if(10 == base){ + e = CONSTANTBV::BitVector_from_Dec(bv, (unsigned char*)strval); + }else if(16 == base){ + e = CONSTANTBV::BitVector_from_Hex(bv, (unsigned char*)strval); + }else{ + e = CONSTANTBV::ErrCode_Pars; + } + + if(0 != e) { + cerr << "CreateBVConst: " << BitVector_Error(e); + FatalError("",ASTUndefined); + } + + //FIXME + return CreateBVConst(bv, width); + } + + + //FIXME Code currently assumes that it will destroy the bitvector passed to it + ASTNode BeevMgr::CreateBVConst(CBV bv, unsigned width){ + ASTBVConst temp_bvconst(bv, width, *this); + ASTNode n(LookupOrCreateBVConst(temp_bvconst)); + + CONSTANTBV::BitVector_Destroy(bv); + + return n; + } + + ASTNode BeevMgr::CreateZeroConst(unsigned width) { + CBV z = CONSTANTBV::BitVector_Create(width, true); + return CreateBVConst(z, width); + } + + ASTNode BeevMgr::CreateOneConst(unsigned width) { + CBV o = CONSTANTBV::BitVector_Create(width, true); + CONSTANTBV::BitVector_increment(o); + + return CreateBVConst(o,width); + } + + ASTNode BeevMgr::CreateTwoConst(unsigned width) { + CBV two = CONSTANTBV::BitVector_Create(width, true); + CONSTANTBV::BitVector_increment(two); + CONSTANTBV::BitVector_increment(two); + + return CreateBVConst(two,width); + } + + ASTNode BeevMgr::CreateMaxConst(unsigned width) { + CBV max = CONSTANTBV::BitVector_Create(width, false); + CONSTANTBV::BitVector_Fill(max); + + return CreateBVConst(max,width); + } + + //To ensure unique BVConst nodes, lookup the node in unique-table + //before creating a new one. + ASTBVConst *BeevMgr::LookupOrCreateBVConst(ASTBVConst &s) { + ASTBVConst *s_ptr = &s; // it's a temporary key. + + // Do an explicit lookup to see if we need to create a copy of the string. + ASTBVConstSet::const_iterator it; + if ((it = _bvconst_unique_table.find(s_ptr)) == _bvconst_unique_table.end()) { + // Make a new ASTBVConst with duplicated string (can't assign + // _name because it's const). Can cast the iterator to + // non-const -- carefully. + + ASTBVConst * s_copy = new ASTBVConst(s); + s_copy->SetNodeNum(NewNodeNum()); + + pair<ASTBVConstSet::const_iterator, bool> p = _bvconst_unique_table.insert(s_copy); + return *p.first; + } + else{ + // return symbol found in table. + return *it; + } + } + + // Inline because we need to wait until unique_table is defined + void ASTBVConst::CleanUp() { + // cout << "Deleting node " << this->GetNodeNum() << endl; + _bm._bvconst_unique_table.erase(this); + delete this; + } + + // Get the value of bvconst from a bvconst. It's an error if kind != BVCONST + CBV const ASTNode::GetBVConst() const { + if(GetKind() != BVCONST) + FatalError("GetBVConst: non bitvector-constant: ",*this); + return ((ASTBVConst *) _int_node_ptr)->GetBVConst(); + } +#else + //Create a ASTBVConst node + ASTNode BeevMgr::CreateBVConst(const unsigned int width, + const unsigned long long int bvconst) { + if(width > 64 || width <= 0) + FatalError("Fatal Error: CreateBVConst: trying to create a bvconst of width:", ASTUndefined, width); + + //64 bit mask + unsigned long long int mask = 0xffffffffffffffffLL; + mask = mask >> (64 - width); + + unsigned long long int bv = bvconst; + bv = bv & mask; + + ASTBVConst temp_bvconst(bv, *this); + temp_bvconst._value_width = width; + ASTNode n(LookupOrCreateBVConst(temp_bvconst)); + n.SetValueWidth(width); + n.SetIndexWidth(0); + return n; + } + //Create a ASTBVConst node from std::string + ASTNode BeevMgr::CreateBVConst(const char* strval, int base) { + if(!(base == 2 || base == 16 || base == 10)) + FatalError("CreateBVConst: This base is not supported: ", ASTUndefined, base); + + if(10 != base) { + unsigned int width = (base == 2) ? strlen(strval) : strlen(strval)*4; + unsigned long long int val = strtoull(strval, NULL, base); + ASTNode bvcon = CreateBVConst(width, val); + return bvcon; + } + else { + //this is an ugly hack to accomodate SMTLIB format + //restrictions. SMTLIB format represents bitvector constants in + //base 10 (what a terrible idea, but i have no choice but to + //support it), and make an implicit assumption that the length + //is 32 (another terrible idea). + unsigned width = 32; + unsigned long long int val = strtoull(strval, NULL, base); + ASTNode bvcon = CreateBVConst(width, val); + return bvcon; + } + } + + //To ensure unique BVConst nodes, lookup the node in unique-table + //before creating a new one. + ASTBVConst *BeevMgr::LookupOrCreateBVConst(ASTBVConst &s) { + ASTBVConst *s_ptr = &s; // it's a temporary key. + + // Do an explicit lookup to see if we need to create a copy of the + // string. + ASTBVConstSet::const_iterator it; + if ((it = _bvconst_unique_table.find(s_ptr)) == _bvconst_unique_table.end()) { + // Make a new ASTBVConst. Can cast the iterator to non-const -- + // carefully. + unsigned int width = s_ptr->_value_width; + ASTBVConst * s_ptr1 = new ASTBVConst(s_ptr->GetBVConst(), *this); + s_ptr1->SetNodeNum(NewNodeNum()); + s_ptr1->_value_width = width; + pair<ASTBVConstSet::const_iterator, bool> p = _bvconst_unique_table.insert(s_ptr1); + return *p.first; + } + else + // return BVConst found in table. + return *it; + } + + // Inline because we need to wait until unique_table is defined + void ASTBVConst::CleanUp() { + // cout << "Deleting node " << this->GetNodeNum() << endl; + _bm._bvconst_unique_table.erase(this); + delete this; + } + + // Get the value of bvconst from a bvconst. It's an error if kind + // != BVCONST + unsigned long long int ASTNode::GetBVConst() const { + if(GetKind() != BVCONST) + FatalError("GetBVConst: non bitvector-constant: ", *this); + return ((ASTBVConstTmp *) _int_node_ptr)->GetBVConst(); + } + + ASTNode BeevMgr::CreateZeroConst(unsigned width) { + return CreateBVConst(width,0); + } + + ASTNode BeevMgr::CreateOneConst(unsigned width) { + return CreateBVConst(width,1); + } + + ASTNode BeevMgr::CreateTwoConst(unsigned width) { + return CreateBVConst(width,2); + } + + ASTNode BeevMgr::CreateMaxConst(unsigned width) { + std::string s; + s.insert(s.end(),width,'1'); + return CreateBVConst(s.c_str(),2); + } + +#endif + + // FIXME: _name is now a constant field, and this assigns to it + // because it tries not to copy the string unless it needs to. How + // do I avoid copying children in ASTInterior? Perhaps I don't! + + // Note: There seems to be a limitation of hash_set, in that insert + // returns a const iterator to the value. That prevents us from + // modifying the name (in a hash-preserving way) after the symbol is + // inserted. FIXME: Is there a way to do this with insert? Need a + // function to make a new object in the middle of insert. Read STL + // documentation. + + ASTSymbol *BeevMgr::LookupOrCreateSymbol(ASTSymbol& s) { + ASTSymbol *s_ptr = &s; // it's a temporary key. + + // Do an explicit lookup to see if we need to create a copy of the string. + ASTSymbolSet::const_iterator it; + if ((it = _symbol_unique_table.find(s_ptr)) == _symbol_unique_table.end()) { + // Make a new ASTSymbol with duplicated string (can't assign + // _name because it's const). Can cast the iterator to + // non-const -- carefully. + //std::string strname(s_ptr->GetName()); + ASTSymbol * s_ptr1 = new ASTSymbol(strdup(s_ptr->GetName()), *this); + s_ptr1->SetNodeNum(NewNodeNum()); + s_ptr1->_value_width = s_ptr->_value_width; + pair<ASTSymbolSet::const_iterator, bool> p = _symbol_unique_table.insert(s_ptr1); + return *p.first; + } + else + // return symbol found in table. + return *it; + } + + bool BeevMgr::LookupSymbol(ASTSymbol& s) { + ASTSymbol* s_ptr = &s; // it's a temporary key. + + if(_symbol_unique_table.find(s_ptr) == _symbol_unique_table.end()) + return false; + else + return true; + } + + // Inline because we need to wait until unique_table is defined + void ASTSymbol::CleanUp() { + // cout << "Deleting node " << this->GetNodeNum() << endl; + _bm._symbol_unique_table.erase(this); + //FIXME This is a HUGE free to invoke. + //TEST IT! + free((char*) this->_name); + delete this; + } + + //////////////////////////////////////////////////////////////// + // + // IO manipulators for Lisp format printing of AST. + // + //////////////////////////////////////////////////////////////// + + // FIXME: Additional controls + // * Print node numbers (addresses/nums) + // * Printlength limit + // * Printdepth limit + + /** Print a vector of ASTNodes in lisp format */ + ostream &LispPrintVec(ostream &os, const ASTVec &v, int indentation) + { + // Print the children + ASTVec::const_iterator iend = v.end(); + for (ASTVec::const_iterator i = v.begin(); i != iend; i++) { + i->LispPrint_indent(os, indentation); + } + return os; + } + + // FIXME: Made non-ref in the hope that it would work better. + void lp(ASTNode node) + { + cout << lisp(node) << endl; + } + + void lpvec(const ASTVec &vec) + { + vec[0].GetBeevMgr().AlreadyPrintedSet.clear(); + LispPrintVec(cout, vec, 0); + cout << endl; + } + + // Copy constructor. Maintain _ref_count + ASTNode::ASTNode(const ASTNode &n) : _int_node_ptr(n._int_node_ptr) { +#ifndef SMTLIB + if (n._int_node_ptr) { + n._int_node_ptr->IncRef(); + } +#endif + } + + + /* FUNCTION: Typechecker for terms and formulas + * + * TypeChecker: Assumes that the immediate Children of the input + * ASTNode have been typechecked. This function is suitable in + * scenarios like where you are building the ASTNode Tree, and you + * typecheck as you go along. It is not suitable as a general + * typechecker + */ + void BeevMgr::BVTypeCheck(const ASTNode& n) { + Kind k = n.GetKind(); + //The children of bitvector terms are in turn bitvectors. + ASTVec v = n.GetChildren(); + if(is_Term_kind(k)) { + switch(k) { + case BVCONST: + if(BITVECTOR_TYPE != n.GetType()) + FatalError("BVTypeCheck: The term t does not typecheck, where t = \n",n); + break; + case SYMBOL: + return; + case ITE: + if(BOOLEAN_TYPE != n[0].GetType() && + BITVECTOR_TYPE != n[1].GetType() && + BITVECTOR_TYPE != n[2].GetType()) + FatalError("BVTypeCheck: The term t does not typecheck, where t = \n",n); + if(n[1].GetValueWidth() != n[2].GetValueWidth()) + FatalError("BVTypeCheck: length of THENbranch != length of ELSEbranch in the term t = \n",n); + if(n[1].GetIndexWidth() != n[2].GetIndexWidth()) + FatalError("BVTypeCheck: length of THENbranch != length of ELSEbranch in the term t = \n",n); + break; + case READ: + if(n[0].GetIndexWidth() != n[1].GetValueWidth()) { + cerr << "Length of indexwidth of array: " << n[0] << " is : " << n[0].GetIndexWidth() << endl; + cerr << "Length of the actual index is: " << n[1] << " is : " << n[1].GetValueWidth() << endl; + FatalError("BVTypeCheck: length of indexwidth of array != length of actual index in the term t = \n",n); + } + break; + case WRITE: + if(n[0].GetIndexWidth() != n[1].GetValueWidth()) + FatalError("BVTypeCheck: length of indexwidth of array != length of actual index in the term t = \n",n); + if(n[0].GetValueWidth() != n[2].GetValueWidth()) + FatalError("BVTypeCheck: valuewidth of array != length of actual value in the term t = \n",n); + break; + case BVOR: + case BVAND: + case BVXOR: + case BVNOR: + case BVNAND: + case BVXNOR: + case BVPLUS: + case BVMULT: + case BVDIV: + case BVMOD: + case BVSUB: { + if(!(v.size() >= 2)) + FatalError("BVTypeCheck:bitwise Booleans and BV arith operators must have atleast two arguments\n",n); + unsigned int width = n.GetValueWidth(); + for(ASTVec::iterator it=v.begin(),itend=v.end();it!=itend;it++){ + if(width != it->GetValueWidth()) { + cerr << "BVTypeCheck:Operands of bitwise-Booleans and BV arith operators must be of equal length\n"; + cerr << n << endl; + cerr << "width of term:" << width << endl; + cerr << "width of offending operand:" << it->GetValueWidth() << endl; + FatalError("BVTypeCheck:Offending operand:\n",*it); + } + if(BITVECTOR_TYPE != it->GetType()) + FatalError("BVTypeCheck: ChildNodes of bitvector-terms must be bitvectors\n",n); + } + break; + } + case BVSX: + //in BVSX(n[0],len), the length of the BVSX term must be + //greater than the length of n[0] + if(n[0].GetValueWidth() >= n.GetValueWidth()) { + FatalError("BVTypeCheck: BVSX(t,bvsx_len) : length of 't' must be <= bvsx_len\n",n); + } + break; + default: + for(ASTVec::iterator it=v.begin(),itend=v.end();it!=itend;it++) + if(BITVECTOR_TYPE != it->GetType()) { + cerr << "The type is: " << it->GetType() << endl; + FatalError("BVTypeCheck:ChildNodes of bitvector-terms must be bitvectors\n",n); + } + break; + } + + switch(k) { + case BVCONCAT: + if(n.Degree() != 2) + FatalError("BVTypeCheck: should have exactly 2 args\n",n); + if(n.GetValueWidth() != n[0].GetValueWidth() + n[1].GetValueWidth()) + FatalError("BVTypeCheck:BVCONCAT: lengths do not add up\n",n); + break; + case BVUMINUS: + case BVNEG: + if(n.Degree() != 1) + FatalError("BVTypeCheck: should have exactly 1 args\n",n); + break; + case BVEXTRACT: + if(n.Degree() != 3) + FatalError("BVTypeCheck: should have exactly 3 args\n",n); + if(!(BVCONST == n[1].GetKind() && BVCONST == n[2].GetKind())) + FatalError("BVTypeCheck: indices should be BVCONST\n",n); + if(n.GetValueWidth() != GetUnsignedConst(n[1])- GetUnsignedConst(n[2])+1) + FatalError("BVTypeCheck: length mismatch\n",n); + break; + case BVLEFTSHIFT: + case BVRIGHTSHIFT: + if(n.Degree() != 2) + FatalError("BVTypeCheck: should have exactly 2 args\n",n); + break; + //case BVVARSHIFT: + //case BVSRSHIFT: + break; + default: + break; + } + } + else { + if(!(is_Form_kind(k) && BOOLEAN_TYPE == n.GetType())) + FatalError("BVTypeCheck: not a formula:",n); + switch(k){ + case TRUE: + case FALSE: + case SYMBOL: + return; + case EQ: + case NEQ: + if(!(n[0].GetValueWidth() == n[1].GetValueWidth() && + n[0].GetIndexWidth() == n[1].GetIndexWidth())) { + cerr << "valuewidth of lhs of EQ: " << n[0].GetValueWidth() << endl; + cerr << "valuewidth of rhs of EQ: " << n[1].GetValueWidth() << endl; + cerr << "indexwidth of lhs of EQ: " << n[0].GetIndexWidth() << endl; + cerr << "indexwidth of rhs of EQ: " << n[1].GetIndexWidth() << endl; + FatalError("BVTypeCheck: terms in atomic formulas must be of equal length",n); + } + break; + case BVLT: + case BVLE: + case BVGT: + case BVGE: + case BVSLT: + case BVSLE: + case BVSGT: + case BVSGE: + if(BITVECTOR_TYPE != n[0].GetType() && BITVECTOR_TYPE != n[1].GetType()) + FatalError("BVTypeCheck: terms in atomic formulas must be bitvectors",n); + if(n[0].GetValueWidth() != n[1].GetValueWidth()) + FatalError("BVTypeCheck: terms in atomic formulas must be of equal length",n); + if(n[0].GetIndexWidth() != n[1].GetIndexWidth()) + FatalError("BVTypeCheck: terms in atomic formulas must be of equal length",n); + break; + case NOT: + if(1 != n.Degree()) + FatalError("BVTypeCheck: NOT formula can have exactly one childNode",n); + break; + case AND: + case OR: + case XOR: + case NAND: + case NOR: + if(2 > n.Degree()) + FatalError("BVTypeCheck: AND/OR/XOR/NAND/NOR: must have atleast 2 ChildNodes",n); + break; + case IFF: + case IMPLIES: + if(2 != n.Degree()) + FatalError("BVTypeCheck:IFF/IMPLIES must have exactly 2 ChildNodes",n); + break; + case ITE: + if(3 != n.Degree()) + FatalError("BVTypeCheck:ITE must have exactly 3 ChildNodes",n); + break; + default: + FatalError("BVTypeCheck: Unrecognized kind: ",ASTUndefined); + break; + } + } + } //End of TypeCheck function + + //add an assertion to the current logical context + void BeevMgr::AddAssert(const ASTNode& assert) { + if(!(is_Form_kind(assert.GetKind()) && BOOLEAN_TYPE == assert.GetType())) { + FatalError("AddAssert:Trying to assert a non-formula:",assert); + } + + ASTVec * v; + //if the stack of ASTVec is not empty, then take the top ASTVec + //and add the input assert to it + if(!_asserts.empty()) { + v = _asserts.back(); + //v->push_back(TransformFormula(assert)); + v->push_back(assert); + } + else { + //else create a logical context, and add it to the top of the + //stack + v = new ASTVec(); + //v->push_back(TransformFormula(assert)); + v->push_back(assert); + _asserts.push_back(v); + } + } + + void BeevMgr::Push(void) { + ASTVec * v; + v = new ASTVec(); + _asserts.push_back(v); + } + + void BeevMgr::Pop(void) { + if(!_asserts.empty()) { + ASTVec * c = _asserts.back(); + //by calling the clear function we ensure that the ref count is + //decremented for the ASTNodes stored in c + c->clear(); + delete c; + _asserts.pop_back(); + } + } + + void BeevMgr::AddQuery(const ASTNode& q) { + //_current_query = TransformFormula(q); + //cerr << "\nThe current query is: " << q << endl; + _current_query = q; + } + + const ASTNode BeevMgr::PopQuery() { + ASTNode q = _current_query; + _current_query = ASTTrue; + return q; + } + + const ASTNode BeevMgr::GetQuery() { + return _current_query; + } + + const ASTVec BeevMgr::GetAsserts(void) { + vector<ASTVec *>::iterator it = _asserts.begin(); + vector<ASTVec *>::iterator itend = _asserts.end(); + + ASTVec v; + for(;it!=itend;it++) { + if(!(*it)->empty()) + v.insert(v.end(),(*it)->begin(),(*it)->end()); + } + return v; + } + + //Create a new variable of ValueWidth 'n' + ASTNode BeevMgr::NewArrayVar(unsigned int index, unsigned int value) { + std:: string c("v"); + char d[32]; + sprintf(d,"%d",_symbol_count++); + std::string ccc(d); + c += "_writearray_" + ccc; + + ASTNode CurrentSymbol = CreateSymbol(c.c_str()); + CurrentSymbol.SetValueWidth(value); + CurrentSymbol.SetIndexWidth(index); + return CurrentSymbol; + } //end of NewArrayVar() + + + //Create a new variable of ValueWidth 'n' + ASTNode BeevMgr::NewVar(unsigned int value) { + std:: string c("v"); + char d[32]; + sprintf(d,"%d",_symbol_count++); + std::string ccc(d); + c += "_new_stp_var_" + ccc; + + ASTNode CurrentSymbol = CreateSymbol(c.c_str()); + CurrentSymbol.SetValueWidth(value); + CurrentSymbol.SetIndexWidth(0); + _introduced_symbols.insert(CurrentSymbol); + return CurrentSymbol; + } //end of NewVar() + + //prints statistics for the ASTNode + void BeevMgr::ASTNodeStats(const char * c, const ASTNode& a){ + if(!stats) + return; + + StatInfoSet.clear(); + //print node size: + cout << endl << "Printing: " << c; + if(print_nodes) { + //a.PL_Print(cout,0); + //cout << endl; + cout << a << endl; + } + cout << "Node size is: "; + cout << NodeSize(a) << endl << endl; + } + + unsigned int BeevMgr::NodeSize(const ASTNode& a, bool clearStatInfo) { + if(clearStatInfo) + StatInfoSet.clear(); + + ASTNodeSet::iterator it; + if((it = StatInfoSet.find(a)) != StatInfoSet.end()) + //has already been counted + return 0; + + //record that you have seen this node already + StatInfoSet.insert(a); + + //leaf node has a size of 1 + if(a.Degree() == 0) + return 1; + + unsigned newn = 1; + ASTVec c = a.GetChildren(); + for(ASTVec::iterator it=c.begin(),itend=c.end();it!=itend;it++) + newn += NodeSize(*it); + return newn; + } + + void BeevMgr::ClearAllTables(void) { + //clear all tables before calling toplevelsat + _ASTNode_to_SATVar.clear(); + _SATVar_to_AST.clear(); + + for(ASTtoBitvectorMap::iterator it=_ASTNode_to_Bitvector.begin(), + itend=_ASTNode_to_Bitvector.end();it!=itend;it++) { + delete it->second; + } + _ASTNode_to_Bitvector.clear(); + + /* OLD Destructor + * for(ASTNodeToVecMap::iterator ivec = BBTermMemo.begin(), + ivec_end=BBTermMemo.end();ivec!=ivec_end;ivec++) { + ivec->second.clear(); + }*/ + + /*What should I do here? For ASTNodes? + * for(ASTNodeMap::iterator ivec = BBTermMemo.begin(), + ivec_end=BBTermMemo.end();ivec!=ivec_end;ivec++) { + ivec->second.clear(); + }*/ + BBTermMemo.clear(); + BBFormMemo.clear(); + NodeLetVarMap.clear(); + NodeLetVarMap1.clear(); + PLPrintNodeSet.clear(); + AlreadyPrintedSet.clear(); + SimplifyMap.clear(); + SimplifyNegMap.clear(); + SolverMap.clear(); + AlwaysTrueFormMap.clear(); + _arrayread_ite.clear(); + _arrayread_symbol.clear(); + _introduced_symbols.clear(); + TransformMap.clear(); + _letid_expr_map.clear(); + CounterExampleMap.clear(); + ComputeFormulaMap.clear(); + StatInfoSet.clear(); + + // for(std::vector<ASTVec *>::iterator it=_asserts.begin(), + // itend=_asserts.end();it!=itend;it++) { + // (*it)->clear(); + // } + _asserts.clear(); + for(ASTNodeToVecMap::iterator iset = _arrayname_readindices.begin(), + iset_end = _arrayname_readindices.end(); + iset!=iset_end;iset++) { + iset->second.clear(); + } + + _arrayname_readindices.clear(); + _interior_unique_table.clear(); + _symbol_unique_table.clear(); + _bvconst_unique_table.clear(); + } + + void BeevMgr::ClearAllCaches(void) { + //clear all tables before calling toplevelsat + _ASTNode_to_SATVar.clear(); + _SATVar_to_AST.clear(); + + + for(ASTtoBitvectorMap::iterator it=_ASTNode_to_Bitvector.begin(), + itend=_ASTNode_to_Bitvector.end();it!=itend;it++) { + delete it->second; + } + _ASTNode_to_Bitvector.clear(); + + /*OLD destructor + * for(ASTNodeToVecMap::iterator ivec = BBTermMemo.begin(), + ivec_end=BBTermMemo.end();ivec!=ivec_end;ivec++) { + ivec->second.clear(); + }*/ + + /*What should I do here? + *for(ASTNodeMap::iterator ivec = BBTermMemo.begin(), + ivec_end=BBTermMemo.end();ivec!=ivec_end;ivec++) { + ivec->second.clear(); + }*/ + BBTermMemo.clear(); + BBFormMemo.clear(); + NodeLetVarMap.clear(); + NodeLetVarMap1.clear(); + PLPrintNodeSet.clear(); + AlreadyPrintedSet.clear(); + SimplifyMap.clear(); + SimplifyNegMap.clear(); + SolverMap.clear(); + AlwaysTrueFormMap.clear(); + _arrayread_ite.clear(); + _arrayread_symbol.clear(); + _introduced_symbols.clear(); + TransformMap.clear(); + _letid_expr_map.clear(); + CounterExampleMap.clear(); + ComputeFormulaMap.clear(); + StatInfoSet.clear(); + + for(ASTNodeToVecMap::iterator iset = _arrayname_readindices.begin(), + iset_end = _arrayname_readindices.end(); + iset!=iset_end;iset++) { + iset->second.clear(); + } + + _arrayname_readindices.clear(); + //_interior_unique_table.clear(); + //_symbol_unique_table.clear(); + //_bvconst_unique_table.clear(); + } + + void BeevMgr::CopySolverMap_To_CounterExample(void) { + if(!SolverMap.empty()) { + CounterExampleMap.insert(SolverMap.begin(),SolverMap.end()); + } + } + + void FatalError(const char * str, const ASTNode& a, int w) { + if(a.GetKind() != UNDEFINED) { + cerr << "Fatal Error: " << str << endl << a << endl; + cerr << w << endl; + } + else { + cerr << "Fatal Error: " << str << endl; + cerr << w << endl; + } + if (vc_error_hdlr) + vc_error_hdlr(str); + exit(-1); + //assert(0); + } + + void FatalError(const char * str) { + cerr << "Fatal Error: " << str << endl; + if (vc_error_hdlr) + vc_error_hdlr(str); + exit(-1); + //assert(0); + } + + //Variable Order Printer: A global function which converts a MINISAT + //var into a ASTNODE var. It then prints this var along with + //variable order dcisions taken by MINISAT. + void Convert_MINISATVar_To_ASTNode_Print(int minisat_var, + int decision_level, int polarity) { + BEEV::ASTNode vv = globalBeevMgr_for_parser->_SATVar_to_AST[minisat_var]; + cout << spaces(decision_level); + if(polarity) { + cout << "!"; + } + vv.PL_Print(cout,0); + cout << endl; + } + + void SortByExprNum(ASTVec& v) { + sort(v.begin(), v.end(), exprless); + } + + bool isAtomic(Kind kind) { + if(TRUE == kind || + FALSE == kind || + EQ == kind || + NEQ == kind || + BVLT == kind || + BVLE == kind || + BVGT == kind || + BVGE == kind || + BVSLT == kind || + BVSLE == kind || + BVSGT == kind || + BVSGE == kind || + SYMBOL == kind || + BVGETBIT == kind) + return true; + return false; + } + + BeevMgr::~BeevMgr() { + ClearAllTables(); + } +}; // end namespace + diff --git a/stp/AST/AST.h b/stp/AST/AST.h new file mode 100644 index 00000000..53ed7016 --- /dev/null +++ b/stp/AST/AST.h @@ -0,0 +1,1805 @@ +// -*- c++ -*- +/******************************************************************** + * AUTHORS: Vijay Ganesh, David L. Dill + * + * BEGIN DATE: November, 2005 + * + * LICENSE: Please view LICENSE file in the home dir of this Program + ********************************************************************/ + +#ifndef AST_H +#define AST_H +#include <vector> +#ifdef EXT_HASH_MAP +#include <ext/hash_set> +#include <ext/hash_map> +#else +#include <hash_set> +#include <hash_map> +#endif +#include <iostream> +#include <sstream> +#include <string> +#include <map> +#include <set> +#include "ASTUtil.h" +#include "ASTKind.h" +#include "../sat/Solver.h" +#include "../sat/SolverTypes.h" +#include <cstdlib> +#ifndef NATIVE_C_ARITH +#include "../constantbv/constantbv.h" +#endif +/***************************************************************************** + * LIST OF CLASSES DECLARED IN THIS FILE: + * + * class BeevMgr; + * class ASTNode; + * class ASTInternal; + * class ASTInterior; + * class ASTSymbol; + * class ASTBVConst; + *****************************************************************************/ +namespace BEEV { + using namespace std; + using namespace MINISAT; +#ifdef EXT_HASH_MAP + using namespace __gnu_cxx; +#endif + + //return types for the GetType() function in ASTNode class + enum types { + BOOLEAN_TYPE = 0, + BITVECTOR_TYPE, + ARRAY_TYPE, + UNKNOWN_TYPE + }; + + class BeevMgr; + class ASTNode; + class ASTInternal; + class ASTInterior; + class ASTSymbol; + class ASTBVConst; + class BVSolver; + + //Vector of ASTNodes, used for child nodes among other things. + typedef vector<ASTNode> ASTVec; + extern ASTVec _empty_ASTVec; + extern BeevMgr * globalBeevMgr_for_parser; + + typedef unsigned int * CBV; + + /***************************************************************************/ + /* Class ASTNode: Smart pointer to actual ASTNode internal datastructure. */ + /***************************************************************************/ + class ASTNode { + friend class BeevMgr; + friend class vector<ASTNode>; + //Print the arguments in lisp format. + friend ostream &LispPrintVec(ostream &os, + const ASTVec &v, int indentation = 0); + + private: + // FIXME: make this into a reference? + ASTInternal * _int_node_ptr; // The real data. + + // Usual constructor. + ASTNode(ASTInternal *in); + + //Check if it points to a null node + bool IsNull () const { return _int_node_ptr == NULL; } + + //Equal iff ASTIntNode pointers are the same. + friend bool operator==(const ASTNode node1, const ASTNode node2){ + return ((size_t) node1._int_node_ptr) == ((size_t) node2._int_node_ptr); + } + + /* FIXME: Nondeterministic code *** */ + /** questionable pointer comparison function */ + friend bool operator<(const ASTNode node1, const ASTNode node2){ + return ((size_t) node1._int_node_ptr) < ((size_t) node2._int_node_ptr); + } + + public: + // This is for sorting by expression number (used in Boolean + //optimization) + friend bool exprless(const ASTNode n1, const ASTNode n2) { + Kind k1 = n1.GetKind(); + Kind k2 = n2.GetKind(); + + if(BVCONST == k1 && BVCONST != k2){ + return true; + } + if(BVCONST != k1 && BVCONST == k2){ + return false; + } + + if(SYMBOL == k1 && SYMBOL != k2) { + return true; + } + + if(SYMBOL != k1 && SYMBOL == k2) { + return false; + } + + return (n1.GetNodeNum() < n2.GetNodeNum()); + }//end of exprless + + // Internal lisp-form printer that does not clear _node_print_table + ostream &LispPrint1(ostream &os, int indentation) const; + + ostream &LispPrint_indent(ostream &os, int indentation) const; + + // For lisp DAG printing. Has it been printed already, so we can + // just print the node number? + bool IsAlreadyPrinted() const; + void MarkAlreadyPrinted() const; + + public: + // Default constructor. This gets used when declaring an ASTVec + // of a given size, in the hash table, etc. For faster + // refcounting, create a symbol node for NULL. Give it a big + // initial refcount. Never free it. also check, for ref-count + // overflow? + ASTNode() : _int_node_ptr(NULL) { }; + + // Copy constructor + ASTNode(const ASTNode &n); + + // Destructor + ~ASTNode(); + + // Assignment (for ref counting) + ASTNode& operator=(const ASTNode& n); + + BeevMgr &GetBeevMgr() const; + + // Access node number + int GetNodeNum() const; + + // Access kind. Inlined later because of declaration ordering problems. + Kind GetKind() const; + + // access Children + const ASTVec &GetChildren() const; + + // Return the number of child nodes + size_t Degree() const{ + return GetChildren().size(); + }; + + // Get indexth childNode. + const ASTNode operator[](size_t index) const { + return GetChildren()[index]; + }; + + // Get begin() iterator for child nodes + ASTVec::const_iterator begin() const{ + return GetChildren().begin(); + }; + + // Get end() iterator for child nodes + ASTVec::const_iterator end() const{ + return GetChildren().end(); + }; + + //Get back() element for child nodes + const ASTNode back() const{ + return GetChildren().back(); + }; + + // Get the name from a symbol (char *). It's an error if kind != SYMBOL + const char * const GetName() const; + + //Get the BVCONST value +#ifndef NATIVE_C_ARITH + const CBV GetBVConst() const; +#else + unsigned long long int GetBVConst() const; +#endif + + /*ASTNode is of type BV <==> ((indexwidth=0)&&(valuewidth>0)) + * + *ASTNode is of type ARRAY <==> ((indexwidth>0)&&(valuewidth>0)) + * + *ASTNode is of type BOOLEAN <==> ((indexwidth=0)&&(valuewidth=0)) + * + *both indexwidth and valuewidth should never be less than 0 + */ + unsigned int GetIndexWidth () const; + + // FIXME: This function is dangerous. Try to eliminate it's use. + void SetIndexWidth (unsigned int iw) const; + + unsigned int GetValueWidth () const; + + // FIXME: This function is dangerous. Try to eliminate it's use. + void SetValueWidth (unsigned int vw) const; + + //return the type of the ASTNode + //0 iff BOOLEAN + //1 iff BITVECTOR + //2 iff ARRAY + + /*ASTNode is of type BV <==> ((indexwidth=0)&&(valuewidth>0)) + * + *ASTNode is of type ARRAY <==> ((indexwidth>0)&&(valuewidth>0)) + * + *ASTNode is of type BOOLEAN <==> ((indexwidth=0)&&(valuewidth=0)) + * + *both indexwidth and valuewidth should never be less than 0 + */ + types GetType(void) const; + + // Hash is pointer value of _int_node_ptr. + const size_t Hash() const{ + return (size_t) _int_node_ptr; + //return GetNodeNum(); + } + + // lisp-form printer + ostream& LispPrint(ostream &os, int indentation = 0) const; + + //Presentation Language Printer + ostream& PL_Print(ostream &os, int indentation = 0) const; + + void PL_Print1(ostream &os, int indentation = 0, bool b = false) const; + + //Construct let variables for shared subterms + void LetizeNode(void) const; + + // Attempt to define something that will work in the gdb + friend void lp(ASTNode &node); + friend void lpvec(const ASTVec &vec); + + friend ostream &operator<<(ostream &os, const ASTNode &node) { + node.LispPrint(os, 0); + return os; + }; + + // Check whether the ASTNode points to anything. Undefined nodes + // are created by the default constructor. In binding table (for + // lambda args, etc.), undefined nodes are used to represent + // deleted entries. + bool IsDefined() const { return _int_node_ptr != NULL; } + + /* Hasher class for STL hash_maps and hash_sets that use ASTNodes + * as keys. Needs to be public so people can define hash tables + * (and use ASTNodeMap class)*/ + class ASTNodeHasher { + public: + size_t operator() (const ASTNode& n) const{ + return (size_t) n._int_node_ptr; + //return (size_t)n.GetNodeNum(); + }; + }; //End of ASTNodeHasher + + /* Equality for ASTNode hash_set and hash_map. Returns true iff + * internal pointers are the same. Needs to be public so people + * can define hash tables (and use ASTNodeSet class)*/ + class ASTNodeEqual { + public: + bool operator()(const ASTNode& n1, const ASTNode& n2) const{ + return (n1._int_node_ptr == n2._int_node_ptr); + } + }; //End of ASTNodeEqual + }; //End of Class ASTNode + + void FatalError(const char * str, const ASTNode& a, int w = 0); + void FatalError(const char * str); + void SortByExprNum(ASTVec& c); + bool exprless(const ASTNode n1, const ASTNode n2); + bool isAtomic(Kind k); + + /***************************************************************************/ + /* Class ASTInternal:Abstract base class for internal node representation.*/ + /* Requires Kind and ChildNodes so same traversal works */ + /* on all nodes. */ + /***************************************************************************/ + class ASTInternal { + + friend class ASTNode; + + protected: + + // reference count. + int _ref_count; + + // Kind. It's a type tag and the operator. + Kind _kind; + + // The vector of children (*** should this be in ASTInterior? ***) + ASTVec _children; + + // Manager object. Having this backpointer means it's easy to + // find the manager when we need it. + BeevMgr &_bm; + + //Nodenum is a unique positive integer for the node. The nodenum + //of a node should always be greater than its descendents (which + //is easily achieved by incrementing the number each time a new + //node is created). + int _node_num; + + // Length of bitvector type for array index. The term is an + // array iff this is positive. Otherwise, the term is a bitvector + // or a bit. + unsigned int _index_width; + + // Length of bitvector type for scalar value or array element. + // If this is one, the term represents a single bit (same as a bitvector + // of length 1). It must be 1 or greater. + unsigned int _value_width; + + // Increment refcount. +#ifndef SMTLIB + void IncRef() { ++_ref_count; } +#else + void IncRef() { } +#endif + + // DecRef is a potentially expensive, because it has to delete + // the node from the unique table, in addition to freeing it. + // FIXME: Consider putting in a backpointer (iterator) to the hash + // table entry so it can be deleted without looking it up again. + void DecRef(); + + virtual const Kind GetKind() const { return _kind; } + + virtual ASTVec const &GetChildren() const { return _children; } + + int GetNodeNum() const { return _node_num; } + + void SetNodeNum(int nn) { _node_num = nn; }; + + // Constructor (bm only) + ASTInternal(BeevMgr &bm, int nodenum = 0) : + _ref_count(0), + _kind(UNDEFINED), + _bm(bm), + _node_num(nodenum), + _index_width(0), + _value_width(0) { } + + // Constructor (kind only, empty children, int nodenum) + ASTInternal(Kind kind, BeevMgr &bm, int nodenum = 0) : + _ref_count(0), + _kind(kind), + _bm(bm), + _node_num(nodenum), + _index_width(0), + _value_width(0) { } + + // Constructor (kind and children). This copies the contents of + // the child nodes. + // FIXME: is there a way to avoid repeating these? + ASTInternal(Kind kind, const ASTVec &children, BeevMgr &bm, int nodenum = 0) : + _ref_count(0), + _kind(kind), + _children(children), + _bm(bm), + _node_num(nodenum), + _index_width(0), + _value_width(0) { } + + // Copy constructor. This copies the contents of the child nodes + // array, along with everything else. Assigning the smart pointer, + // ASTNode, does NOT invoke this; This should only be used for + // temporary hash keys before uniquefication. + // FIXME: I don't think children need to be copied. + ASTInternal(const ASTInternal &int_node, int nodenum = 0) : + _ref_count(0), + _kind(int_node._kind), + _children(int_node._children), + _bm(int_node._bm), + _node_num(int_node._node_num), + _index_width(int_node._index_width), + _value_width(int_node._value_width) { } + + // Copying assign operator. Also copies contents of children. + ASTInternal& operator=(const ASTInternal &int_node); + + // Cleanup function for removing from hash table + virtual void CleanUp() = 0; + + // Destructor (does nothing, but is declared virtual here. + virtual ~ASTInternal(); + + // Abstract virtual print function for internal node. + virtual void nodeprint(ostream& os) { os << "*"; }; + }; //End of Class ASTInternal + + // FIXME: Should children be only in interior node type? + /*************************************************************************** + Class ASTInterior: Internal representation of an interior + ASTNode. Generally, these nodes should have at least one + child + ***************************************************************************/ + class ASTInterior : public ASTInternal { + + friend class BeevMgr; + friend class ASTNodeHasher; + friend class ASTNodeEqual; + + private: + + // Hasher for ASTInterior pointer nodes + class ASTInteriorHasher { + public: + size_t operator()(const ASTInterior *int_node_ptr) const; + }; + + // Equality for ASTInterior nodes + class ASTInteriorEqual { + public: + bool operator()(const ASTInterior *int_node_ptr1, + const ASTInterior *int_node_ptr2) const{ + return (*int_node_ptr1 == *int_node_ptr2); + } + }; + + // Used in Equality class for hash tables + friend bool operator==(const ASTInterior &int_node1, + const ASTInterior &int_node2){ + return (int_node1._kind == int_node2._kind) && + (int_node1._children == int_node2._children); + } + + // Call this when deleting a node that has been stored in the + // the unique table + virtual void CleanUp(); + + // Returns kinds. "lispprinter" handles printing of parenthesis + // and childnodes. + virtual void nodeprint(ostream& os) { + os << _kind_names[_kind]; + } + public: + + // FIXME: This should not be public, but has to be because the + // ASTInterior hash table insists on it. I can't seem to make the + // private destructor visible to hash_set. It does not even work + // to put "friend class hash_set<ASTInterior, ...>" in here. + + // Basic constructors + ASTInterior(Kind kind, BeevMgr &bm) : + ASTInternal(kind, bm) { } + + ASTInterior(Kind kind, ASTVec &children, BeevMgr &bm) : + ASTInternal(kind, children, bm) { } + + //Copy constructor. This copies the contents of the child nodes + //array, along with everything else. Assigning the smart pointer, + //ASTNode, does NOT invoke this. + ASTInterior(const ASTInterior &int_node) : ASTInternal(int_node) { } + + // Destructor (does nothing, but is declared virtual here. + virtual ~ASTInterior(); + + }; //End of ASTNodeInterior + + + /***************************************************************************/ + /* Class ASTSymbol: Class to represent internals of Symbol node. */ + /***************************************************************************/ + class ASTSymbol : public ASTInternal{ + friend class BeevMgr; + friend class ASTNode; + friend class ASTNodeHasher; + friend class ASTNodeEqual; + + private: + // The name of the symbol + const char * const _name; + + class ASTSymbolHasher{ + public: + size_t operator() (const ASTSymbol *sym_ptr) const{ + hash<char*> h; + return h(sym_ptr->_name); + }; + }; + + // Equality for ASTInternal nodes + class ASTSymbolEqual{ + public: + bool operator()(const ASTSymbol *sym_ptr1, const ASTSymbol *sym_ptr2) const{ + return (*sym_ptr1 == *sym_ptr2); + } + }; + + friend bool operator==(const ASTSymbol &sym1, const ASTSymbol &sym2){ + return (strcmp(sym1._name, sym2._name) == 0); + } + + const char * const GetName() const{return _name;} + + // Print function for symbol -- return name */ + virtual void nodeprint(ostream& os) { os << _name;} + + // Call this when deleting a node that has been stored in the + // the unique table + virtual void CleanUp(); + + public: + + // Default constructor + ASTSymbol(BeevMgr &bm) : ASTInternal(bm), _name(NULL) { } + + // Constructor. This does NOT copy its argument. + ASTSymbol(const char * const name, BeevMgr &bm) : ASTInternal(SYMBOL, bm), + _name(name) { } + + // Destructor (does nothing, but is declared virtual here. + virtual ~ASTSymbol(); + + // Copy constructor + // FIXME: seems to be calling default constructor for astinternal + ASTSymbol(const ASTSymbol &sym) : + ASTInternal(sym._kind, sym._children, sym._bm), + _name(sym._name) { } + }; //End of ASTSymbol + + + /***************************************************************************/ + /* Class ASTBVConst: Class to represent internals of a bitvectorconst */ + /***************************************************************************/ + +#ifndef NATIVE_C_ARITH + + class ASTBVConst : public ASTInternal { + friend class BeevMgr; + friend class ASTNode; + friend class ASTNodeHasher; + friend class ASTNodeEqual; + + private: + //This is the private copy of a bvconst currently + //This should not be changed at any point + CBV _bvconst; + + class ASTBVConstHasher{ + public: + size_t operator() (const ASTBVConst * bvc) const { + return CONSTANTBV::BitVector_Hash(bvc->_bvconst); + }; + }; + + class ASTBVConstEqual{ + public: + bool operator()(const ASTBVConst * bvc1, const ASTBVConst * bvc2) const { + if( bvc1->_value_width != bvc2->_value_width){ + return false; + } + return (0==CONSTANTBV::BitVector_Compare(bvc1->_bvconst,bvc2->_bvconst)); + } + }; + + //FIXME Keep an eye on this function + ASTBVConst(CBV bv, unsigned int width, BeevMgr &bm) : + ASTInternal(BVCONST, bm) + { + _bvconst = CONSTANTBV::BitVector_Clone(bv); + _value_width = width; + } + + friend bool operator==(const ASTBVConst &bvc1, const ASTBVConst &bvc2){ + if(bvc1._value_width != bvc2._value_width) + return false; + return (0==CONSTANTBV::BitVector_Compare(bvc1._bvconst,bvc2._bvconst)); + } + // Call this when deleting a node that has been stored in the + // the unique table + virtual void CleanUp(); + + // Print function for bvconst -- return _bvconst value in bin format + virtual void nodeprint(ostream& os) { + unsigned char *res; + const char *prefix; + + if (_value_width%4 == 0) { + res = CONSTANTBV::BitVector_to_Hex(_bvconst); + prefix = "0hex"; + } else { + res = CONSTANTBV::BitVector_to_Bin(_bvconst); + prefix = "0bin"; + } + if (NULL == res) { + os << "nodeprint: BVCONST : could not convert to string" << _bvconst; + FatalError(""); + } + os << prefix << res; + CONSTANTBV::BitVector_Dispose(res); + } + + // Copy constructor. + ASTBVConst(const ASTBVConst &sym) : + ASTInternal(sym._kind, sym._children, sym._bm) + { + _bvconst = CONSTANTBV::BitVector_Clone(sym._bvconst); + _value_width = sym._value_width; + } + + public: + virtual ~ASTBVConst(){ + CONSTANTBV::BitVector_Destroy(_bvconst); + } + + CBV GetBVConst() const {return _bvconst;} + }; //End of ASTBVConst + + //FIXME This function is DEPRICATED + //Do not use in the future + inline unsigned int GetUnsignedConst(const ASTNode n) { + if(32 < n.GetValueWidth()) + FatalError("GetUnsignedConst: cannot convert bvconst of length greater than 32 to unsigned int:"); + + return (unsigned int) *((unsigned int *)n.GetBVConst()); + } +#else + class ASTBVConst : public ASTInternal { + friend class BeevMgr; + friend class ASTNode; + friend class ASTNodeHasher; + friend class ASTNodeEqual; + + private: + // the bitvector contents. bitvector contents will be in two + // modes. one mode where all bitvectors are NATIVE and in this + // mode we use native unsigned long long int to represent the + // 32/64 bitvectors. The other for arbitrary length bitvector + // operations. + const unsigned long long int _bvconst; + + class ASTBVConstHasher{ + public: + size_t operator() (const ASTBVConst * bvc) const{ + //Thomas Wang's 64 bit Mix Function + unsigned long long int key(bvc->_bvconst); + key += ~(key << 32); + key ^= (key >> 22); + key += ~(key << 13); + key ^= (key >> 8); + key += (key << 3); + key ^= (key >> 15); + key += ~(key << 27); + key ^= (key >> 31); + + size_t return_key = key; + return return_key; + }; + }; + + class ASTBVConstEqual{ + public: + bool operator()(const ASTBVConst * bvc1, const ASTBVConst * bvc2) const { + return ((bvc1->_bvconst == bvc2->_bvconst) + && (bvc1->_value_width == bvc2->_value_width)); + } + }; + + // Call this when deleting a node that has been stored in the + // the unique table + virtual void CleanUp(); + public: + // Default constructor + ASTBVConst(const unsigned long long int bv, BeevMgr &bm) : + ASTInternal(BVCONST, bm), _bvconst(bv) { + } + + // Copy constructor. FIXME: figure out how this is supposed to + // work. + ASTBVConst(const ASTBVConst &sym) : + ASTInternal(sym._kind, sym._children, sym._bm), + _bvconst(sym._bvconst) { + _value_width = sym._value_width; + } + + // Destructor (does nothing, but is declared virtual here) + virtual ~ASTBVConst() { } + + friend bool operator==(const ASTBVConst &sym1, const ASTBVConst &sym2){ + return ((sym1._bvconst == sym2._bvconst) && + (sym1._value_width == sym2._value_width)); + } + + // Print function for bvconst -- return _bvconst value in binary format + virtual void nodeprint(ostream& os) { + string s = "0bin"; + unsigned long long int bitmask = 0x8000000000000000LL; + bitmask = bitmask >> (64-_value_width); + + for (; bitmask > 0; bitmask >>= 1) + s += (_bvconst & bitmask) ? '1' : '0'; + os << s; + } + + unsigned long long int GetBVConst() const {return _bvconst;} + }; //End of ASTBVConst + + //return value of bvconst + inline unsigned int GetUnsignedConst(const ASTNode n) { + if(32 < n.GetValueWidth()) + FatalError("GetUnsignedConst: cannot convert bvconst of length greater than 32 to unsigned int:"); + return (unsigned int)n.GetBVConst(); + } +#endif +/* +#else + // the bitvector contents. bitvector contents will be in two + // modes. one mode where all bitvectors are NATIVE and in this mode + // we use native unsigned long long int to represent the 32/64 + // bitvectors. The other for arbitrary length bitvector operations. + + //BVCONST defined for arbitrary length bitvectors + class ASTBVConst : public ASTInternal{ + friend class BeevMgr; + friend class ASTNode; + friend class ASTNodeHasher; + friend class ASTNodeEqual; + + private: + const char * const _bvconst; + + class ASTBVConstHasher{ + public: + size_t operator() (const ASTBVConst * bvc) const{ + hash<char*> h; + return h(bvc->_bvconst); + }; + }; + + class ASTBVConstEqual{ + public: + bool operator()(const ASTBVConst * bvc1, const ASTBVConst * bvc2) const { + if(bvc1->_value_width != bvc2->_value_width) + return false; + return (0 == strncmp(bvc1->_bvconst,bvc2->_bvconst,bvc1->_value_width)); + } + }; + + ASTBVConst(const char * bv, BeevMgr &bm) : + ASTInternal(BVCONST, bm), _bvconst(bv) { + //_value_width = strlen(bv); + } + + friend bool operator==(const ASTBVConst &bvc1, const ASTBVConst &bvc2){ + if(bvc1._value_width != bvc2._value_width) + return false; + return (0 == strncmp(bvc1._bvconst,bvc2._bvconst,bvc1._value_width)); + } + + // Call this when deleting a node that has been stored in the + // the unique table + virtual void CleanUp(); + + // Print function for bvconst -- return _bvconst value in binary format + virtual void nodeprint(ostream& os) { + if(_value_width%4 == 0) { + unsigned int * iii = CONSTANTBV::BitVector_Create(_value_width,true); + CONSTANTBV::ErrCode e = CONSTANTBV::BitVector_from_Bin(iii,(unsigned char*)_bvconst); + //error printing + if(0 != e) { + os << "nodeprint: BVCONST : wrong hex value: " << BitVector_Error(e); + FatalError(""); + } + unsigned char * ccc = CONSTANTBV::BitVector_to_Hex(iii); + os << "0hex" << ccc; + CONSTANTBV::BitVector_Destroy(iii); + } + else { + std::string s(_bvconst,_value_width); + s = "0bin" + s; + os << s; + } + } + + // Copy constructor. + ASTBVConst(const ASTBVConst &sym) : ASTInternal(sym._kind, sym._children, sym._bm),_bvconst(sym._bvconst) { + //checking if the input is in the correct format + for(unsigned int jj=0;jj<sym._value_width;jj++) + if(!(sym._bvconst[jj] == '0' || sym._bvconst[jj] == '1')) { + cerr << "Fatal Error: wrong input to ASTBVConst copy constructor:" << sym._bvconst << endl; + FatalError(""); + } + _value_width = sym._value_width; + } + public: + // Destructor (does nothing, but is declared virtual here) + virtual ~ASTBVConst(){} + + const char * const GetBVConst() const {return _bvconst;} + }; //End of ASTBVConst + + unsigned int * ConvertToCONSTANTBV(const char * s); + + //return value of bvconst + inline unsigned int GetUnsignedConst(const ASTNode n) { + if(32 < n.GetValueWidth()) + FatalError("GetUnsignedConst: cannot convert bvconst of length greater than 32 to unsigned int:"); + std::string s(n.GetBVConst(), n.GetValueWidth()); + unsigned int output = strtoul(s.c_str(),NULL,2); + return output; + } //end of ASTBVConst class +#endif +*/ + /*************************************************************************** + * Typedef ASTNodeMap: This is a hash table from ASTNodes to ASTNodes. + * It is very convenient for attributes that are not speed-critical + **************************************************************************/ + // These are generally useful for storing ASTNodes or attributes thereof + // Hash table from ASTNodes to ASTNodes + typedef hash_map<ASTNode, ASTNode, + ASTNode::ASTNodeHasher, + ASTNode::ASTNodeEqual> ASTNodeMap; + + // Function to dump contents of ASTNodeMap + ostream &operator<<(ostream &os, const ASTNodeMap &nmap); + + /*************************************************************************** + Typedef ASTNodeSet: This is a hash set of ASTNodes. Very useful + for representing things like "visited nodes" + ***************************************************************************/ + typedef hash_set<ASTNode, + ASTNode::ASTNodeHasher, + ASTNode::ASTNodeEqual> ASTNodeSet; + + typedef hash_multiset<ASTNode, + ASTNode::ASTNodeHasher, + ASTNode::ASTNodeEqual> ASTNodeMultiSet; + + //external parser table for declared symbols. + //FIXME: move to a more appropriate place + extern ASTNodeSet _parser_symbol_table; + + /*************************************************************************** + Class LispPrinter: iomanipulator for printing ASTNode or ASTVec + ***************************************************************************/ + class LispPrinter { + + public: + ASTNode _node; + + // number of spaces to print before first real character of + // object. + int _indentation; + + // FIXME: pass ASTNode by reference + // Constructor to build the LispPrinter object + LispPrinter(ASTNode node, int indentation): _node(node), _indentation(indentation) { } + + friend ostream &operator<<(ostream &os, const LispPrinter &lp){ + return lp._node.LispPrint(os, lp._indentation); + }; + + }; //End of ListPrinter + + //This is the IO manipulator. It builds an object of class + //"LispPrinter" that has a special overloaded "<<" operator. + inline LispPrinter lisp(const ASTNode &node, int indentation = 0){ + LispPrinter lp(node, indentation); + return lp; + } + + /***************************************************************************/ + /* Class LispVecPrinter:iomanipulator for printing vector of ASTNodes */ + /***************************************************************************/ + class LispVecPrinter { + + public: + const ASTVec * _vec; + // number of spaces to print before first real + // character of object. + int _indentation; + + // Constructor to build the LispPrinter object + LispVecPrinter(const ASTVec &vec, int indentation){ + _vec = &vec; _indentation = indentation; + } + + friend ostream &operator<<(ostream &os, const LispVecPrinter &lvp){ + LispPrintVec(os, *lvp._vec, lvp._indentation); + return os; + }; + }; //End of Class ListVecPrinter + + //iomanipulator. builds an object of class "LisPrinter" that has a + //special overloaded "<<" operator. + inline LispVecPrinter lisp(const ASTVec &vec, int indentation = 0){ + LispVecPrinter lvp(vec, indentation); + return lvp; + } + + + /***************************************************************** + * INLINE METHODS from various classed, declared here because of + * dependencies on classes that are declared later. + *****************************************************************/ + // ASTNode accessor function. + inline Kind ASTNode::GetKind() const { + //cout << "GetKind: " << _int_node_ptr; + return _int_node_ptr->GetKind(); + } + + // FIXME: should be const ASTVec const? + // Declared here because of same ordering problem as GetKind. + inline const ASTVec &ASTNode::GetChildren() const { + return _int_node_ptr->GetChildren(); + } + + // Access node number + inline int ASTNode::GetNodeNum() const { + return _int_node_ptr->_node_num; + } + + inline unsigned int ASTNode::GetIndexWidth () const { + return _int_node_ptr->_index_width; + } + + inline void ASTNode::SetIndexWidth (unsigned int iw) const { + _int_node_ptr->_index_width = iw; + } + + inline unsigned int ASTNode::GetValueWidth () const { + return _int_node_ptr->_value_width; + } + + inline void ASTNode::SetValueWidth (unsigned int vw) const { + _int_node_ptr->_value_width = vw; + } + + //return the type of the ASTNode: 0 iff BOOLEAN; 1 iff BITVECTOR; 2 + //iff ARRAY; 3 iff UNKNOWN; + inline types ASTNode::GetType() const { + if((GetIndexWidth() == 0) && (GetValueWidth() == 0)) //BOOLEAN + return BOOLEAN_TYPE; + if((GetIndexWidth() == 0) && (GetValueWidth() > 0)) //BITVECTOR + return BITVECTOR_TYPE; + if((GetIndexWidth() > 0) && (GetValueWidth() > 0)) //ARRAY + return ARRAY_TYPE; + return UNKNOWN_TYPE; + } + + // Constructor; creates a new pointer, increments refcount of + // pointed-to object. +#ifndef SMTLIB + inline ASTNode::ASTNode(ASTInternal *in) : _int_node_ptr(in) { + if (in) in->IncRef(); + } +#else + inline ASTNode::ASTNode(ASTInternal *in) : _int_node_ptr(in) { }; +#endif + + // Assignment. Increment refcount of new value, decrement refcount + // of old value and destroy if this was the last pointer. FIXME: + // accelerate this by creating an intnode with a ref counter instead + // of pointing to NULL. Need a special check in CleanUp to make + // sure the null node never gets freed. + +#ifndef SMTLIB + inline ASTNode& ASTNode::operator=(const ASTNode& n) { + if (n._int_node_ptr) { + n._int_node_ptr->IncRef(); + } + if (_int_node_ptr) { + _int_node_ptr->DecRef(); + } + _int_node_ptr = n._int_node_ptr; + return *this; + } +#else + inline ASTNode& ASTNode::operator=(const ASTNode& n) { + _int_node_ptr = n._int_node_ptr; + return *this; + } +#endif + +#ifndef SMTLIB + inline void ASTInternal::DecRef() + { + if (--_ref_count == 0) { + // Delete node from unique table and kill it. + CleanUp(); + } + } + + // Destructor + inline ASTNode::~ASTNode() + { + if (_int_node_ptr) { + _int_node_ptr->DecRef(); + } + }; +#else + // No refcounting + inline void ASTInternal::DecRef() + { + } + + // Destructor + inline ASTNode::~ASTNode() + { + }; +#endif + + inline BeevMgr& ASTNode::GetBeevMgr() const { return _int_node_ptr->_bm; } + + /*************************************************************************** + * Class BeevMgr. This holds all "global" variables for the system, such as + * unique tables for the various kinds of nodes. + ***************************************************************************/ + class BeevMgr { + friend class ASTNode; // ASTNode modifies AlreadyPrintedSet + // in BeevMgr + friend class ASTInterior; + friend class ASTBVConst; + friend class ASTSymbol; + + // FIXME: The values appear to be the same regardless of the value of SMTLIB + // initial hash table sizes, to save time on resizing. +#ifdef SMTLIB + static const int INITIAL_INTERIOR_UNIQUE_TABLE_SIZE = 100; + static const int INITIAL_SYMBOL_UNIQUE_TABLE_SIZE = 100; + static const int INITIAL_BVCONST_UNIQUE_TABLE_SIZE = 100; + static const int INITIAL_BBTERM_MEMO_TABLE_SIZE = 100; + static const int INITIAL_BBFORM_MEMO_TABLE_SIZE = 100; + + static const int INITIAL_SIMPLIFY_MAP_SIZE = 100; + static const int INITIAL_SOLVER_MAP_SIZE = 100; + static const int INITIAL_ARRAYREAD_SYMBOL_SIZE = 100; + static const int INITIAL_INTRODUCED_SYMBOLS_SIZE = 100; +#else + // these are the STL defaults + static const int INITIAL_INTERIOR_UNIQUE_TABLE_SIZE = 100; + static const int INITIAL_SYMBOL_UNIQUE_TABLE_SIZE = 100; + static const int INITIAL_BVCONST_UNIQUE_TABLE_SIZE = 100; + static const int INITIAL_BBTERM_MEMO_TABLE_SIZE = 100; + static const int INITIAL_BBFORM_MEMO_TABLE_SIZE = 100; + + static const int INITIAL_SIMPLIFY_MAP_SIZE = 100; + static const int INITIAL_SOLVER_MAP_SIZE = 100; + static const int INITIAL_ARRAYREAD_SYMBOL_SIZE = 100; + static const int INITIAL_INTRODUCED_SYMBOLS_SIZE = 100; +#endif + + private: + // Typedef for unique Interior node table. + typedef hash_set<ASTInterior *, + ASTInterior::ASTInteriorHasher, + ASTInterior::ASTInteriorEqual> ASTInteriorSet; + + // Typedef for unique Symbol node (leaf) table. + typedef hash_set<ASTSymbol *, + ASTSymbol::ASTSymbolHasher, + ASTSymbol::ASTSymbolEqual> ASTSymbolSet; + + // Unique tables to share nodes whenever possible. + ASTInteriorSet _interior_unique_table; + //The _symbol_unique_table is also the symbol table to be used + //during parsing/semantic analysis + ASTSymbolSet _symbol_unique_table; + + //Typedef for unique BVConst node (leaf) table. + typedef hash_set<ASTBVConst *, + ASTBVConst::ASTBVConstHasher, + ASTBVConst::ASTBVConstEqual> ASTBVConstSet; + + //table to uniquefy bvconst + ASTBVConstSet _bvconst_unique_table; + + // type of memo table. + typedef hash_map<ASTNode, ASTVec, + ASTNode::ASTNodeHasher, + ASTNode::ASTNodeEqual> ASTNodeToVecMap; + + typedef hash_map<ASTNode,ASTNodeSet, + ASTNode::ASTNodeHasher, + ASTNode::ASTNodeEqual> ASTNodeToSetMap; + + // Memo table for bit blasted terms. If a node has already been + // bitblasted, it is mapped to a vector of Boolean formulas for + // the bits. + + //OLD: ASTNodeToVecMap BBTermMemo; + ASTNodeMap BBTermMemo; + + // Memo table for bit blasted formulas. If a node has already + // been bitblasted, it is mapped to a node representing the + // bitblasted equivalent + ASTNodeMap BBFormMemo; + + //public: + // Get vector of Boolean formulas for sum of two + // vectors of Boolean formulas + void BBPlus2(ASTVec& sum, const ASTVec& y, ASTNode cin); + // Increment + ASTVec BBInc(ASTVec& x); + // Add one bit to a vector of bits. + ASTVec BBAddOneBit(ASTVec& x, ASTNode cin); + // Bitwise complement + ASTVec BBNeg(const ASTVec& x); + // Unary minus + ASTVec BBUminus(const ASTVec& x); + // Multiply. + ASTVec BBMult(const ASTVec& x, const ASTVec& y); + // AND each bit of vector y with single bit b and return the result. + // (used in BBMult) + ASTVec BBAndBit(const ASTVec& y, ASTNode b); + // Returns ASTVec for result - y. This destroys "result". + void BBSub(ASTVec& result, const ASTVec& y); + // build ITE's (ITE cond then[i] else[i]) for each i. + ASTVec BBITE(const ASTNode& cond, + const ASTVec& thn, const ASTVec& els); + // Build a vector of zeros. + ASTVec BBfill(unsigned int width, ASTNode fillval); + // build an EQ formula + ASTNode BBEQ(const ASTVec& left, const ASTVec& right); + + // This implements a variant of binary long division. + // q and r are "out" parameters. rwidth puts a bound on the + // recursion depth. Unsigned only, for now. + void BBDivMod(const ASTVec &y, + const ASTVec &x, + ASTVec &q, + ASTVec &r, + unsigned int rwidth); + + // Return formula for majority function of three formulas. + ASTNode Majority(const ASTNode& a, const ASTNode& b, const ASTNode& c); + + // Internal bit blasting routines. + ASTNode BBBVLE(const ASTVec& x, const ASTVec& y, bool is_signed); + + // Return bit-blasted form for BVLE, BVGE, BVGT, SBLE, etc. + ASTNode BBcompare(const ASTNode& form); + + // Left and right shift one. Writes into x. + void BBLShift(ASTVec& x); + void BBRShift(ASTVec& x); + + public: + // Simplifying create functions + ASTNode CreateSimpForm(Kind kind, ASTVec &children); + ASTNode CreateSimpForm(Kind kind, const ASTNode& child0); + ASTNode CreateSimpForm(Kind kind, + const ASTNode& child0, + const ASTNode& child1); + ASTNode CreateSimpForm(Kind kind, + const ASTNode& child0, + const ASTNode& child1, + const ASTNode& child2); + + ASTNode CreateSimpNot(const ASTNode& form); + + // These are for internal use only. + // FIXME: Find a way to make this local to SimpBool, so they're + // not in AST.h + ASTNode CreateSimpXor(const ASTNode& form1, + const ASTNode& form2); + ASTNode CreateSimpXor(ASTVec &children); + ASTNode CreateSimpAndOr(bool isAnd, + const ASTNode& form1, + const ASTNode& form2); + ASTNode CreateSimpAndOr(bool IsAnd, ASTVec &children); + ASTNode CreateSimpFormITE(const ASTNode& child0, + const ASTNode& child1, + const ASTNode& child2); + + + // Declarations of BitBlaster functions (BitBlast.cpp) + public: + // Adds or removes a NOT as necessary to negate a literal. + ASTNode Negate(const ASTNode& form); + + // Bit blast a bitvector term. The term must have a kind for a + // bitvector term. Result is a ref to a vector of formula nodes + // representing the boolean formula. + const ASTNode BBTerm(const ASTNode& term); + + const ASTNode BBForm(const ASTNode& formula); + + // Declarations of CNF conversion (ToCNF.cpp) + public: + // ToCNF converts a bit-blasted Boolean formula to Conjunctive + // Normal Form, suitable for many SAT solvers. Our CNF representation + // is an STL vector of STL vectors, for independence from any particular + // SAT solver's representation. There needs to be a separate driver to + // convert our clauselist to the representation used by the SAT solver. + // Currently, there is only one such solver and its driver is "ToSAT" + + // Datatype for clauses + typedef ASTVec * ClausePtr; + + // Datatype for Clauselists + typedef vector<ClausePtr> ClauseList; + + // Convert a Boolean formula to an equisatisfiable CNF formula. + ClauseList *ToCNF(const ASTNode& form); + + // Print function for debugging + void PrintClauseList(ostream& os, ClauseList& cll); + + // Free the clause list and all its clauses. + void DeleteClauseList(BeevMgr::ClauseList *cllp); + + // Map from formulas to representative literals, for debugging. + ASTNodeMap RepLitMap; + + private: + // Global for assigning new node numbers. + int _max_node_num; + + const ASTNode ASTFalse, ASTTrue, ASTUndefined; + + // I just did this so I could put it in as a fake return value in + // methods that return a ASTNode &, to make -Wall shut up. + ASTNode dummy_node; + + //BeevMgr Constructor, Destructor and other misc. functions + public: + + int NewNodeNum() { _max_node_num += 2; return _max_node_num; } + + // Table for DAG printing. + ASTNodeSet AlreadyPrintedSet; + + //Tables for Presentation language printing + + //Nodes seen so far + ASTNodeSet PLPrintNodeSet; + + //Map from ASTNodes to LetVars + ASTNodeMap NodeLetVarMap; + + //This is a vector which stores the Node to LetVars pairs. It + //allows for sorted printing, as opposed to NodeLetVarMap + std::vector<pair<ASTNode,ASTNode> > NodeLetVarVec; + + //a partial Map from ASTNodes to LetVars. Needed in order to + //correctly print shared subterms inside the LET itself + ASTNodeMap NodeLetVarMap1; + + //functions to lookup nodes from the memo tables. these should be + //private. + private: + //Destructively appends back_child nodes to front_child nodes. + //If back_child nodes is NULL, no appending is done. back_child + //nodes are not modified. Then it returns the hashed copy of the + //node, which is created if necessary. + ASTInterior *CreateInteriorNode(Kind kind, + ASTInterior *new_node, + // this is destructively modified. + const ASTVec & back_children = _empty_ASTVec); + + // Create unique ASTInterior node. + ASTInterior *LookupOrCreateInterior(ASTInterior *n); + + // Create unique ASTSymbol node. + ASTSymbol *LookupOrCreateSymbol(ASTSymbol& s); + + // Called whenever we want to make sure that the Symbol is + // declared during semantic analysis + bool LookupSymbol(ASTSymbol& s); + + // Called by ASTNode constructors to uniqueify ASTBVConst + ASTBVConst *LookupOrCreateBVConst(ASTBVConst& s); + + //Public functions for CreateNodes and Createterms + public: + // Create and return an ASTNode for a symbol + ASTNode CreateSymbol(const char * const name); + + // Create and return an ASTNode for a symbol + // Width is number of bits. + ASTNode CreateBVConst(unsigned int width, unsigned long long int bvconst); + ASTNode CreateZeroConst(unsigned int width); + ASTNode CreateOneConst(unsigned int width); + ASTNode CreateTwoConst(unsigned int width); + ASTNode CreateMaxConst(unsigned int width); + + // Create and return an ASTNode for a symbol + // Optional base was a problem because 0 could be an int or char *, + // so CreateBVConst was ambiguous. + ASTNode CreateBVConst(const char *strval, int base); + + //FIXME This is a dangerous function + ASTNode CreateBVConst(CBV bv, unsigned width); + + // Create and return an interior ASTNode + ASTNode CreateNode(Kind kind, const ASTVec &children = _empty_ASTVec); + + ASTNode CreateNode(Kind kind, + const ASTNode& child0, + const ASTVec &children = _empty_ASTVec); + + ASTNode CreateNode(Kind kind, + const ASTNode& child0, + const ASTNode& child1, + const ASTVec &children = _empty_ASTVec); + + ASTNode CreateNode(Kind kind, + const ASTNode& child0, + const ASTNode& child1, + const ASTNode& child2, + const ASTVec &children = _empty_ASTVec); + + // Create and return an ASTNode for a term + inline ASTNode CreateTerm(Kind kind, + unsigned int width, + const ASTVec &children = _empty_ASTVec) { + if(!is_Term_kind(kind)) + FatalError("CreateTerm: Illegal kind to CreateTerm:",ASTUndefined, kind); + ASTNode n = CreateNode(kind, children); + n.SetValueWidth(width); + + //by default we assume that the term is a Bitvector. If + //necessary the indexwidth can be changed later + n.SetIndexWidth(0); + return n; + } + + inline ASTNode CreateTerm(Kind kind, + unsigned int width, + const ASTNode& child0, + const ASTVec &children = _empty_ASTVec) { + if(!is_Term_kind(kind)) + FatalError("CreateTerm: Illegal kind to CreateTerm:",ASTUndefined, kind); + ASTNode n = CreateNode(kind, child0, children); + n.SetValueWidth(width); + return n; + } + + inline ASTNode CreateTerm(Kind kind, + unsigned int width, + const ASTNode& child0, + const ASTNode& child1, + const ASTVec &children = _empty_ASTVec) { + if(!is_Term_kind(kind)) + FatalError("CreateTerm: Illegal kind to CreateTerm:",ASTUndefined, kind); + ASTNode n = CreateNode(kind, child0, child1, children); + n.SetValueWidth(width); + return n; + } + + inline ASTNode CreateTerm(Kind kind, + unsigned int width, + const ASTNode& child0, + const ASTNode& child1, + const ASTNode& child2, + const ASTVec &children = _empty_ASTVec) { + if(!is_Term_kind(kind)) + FatalError("CreateTerm: Illegal kind to CreateTerm:",ASTUndefined, kind); + ASTNode n = CreateNode(kind, child0, child1, child2, children); + n.SetValueWidth(width); + return n; + } + + ASTNode SimplifyFormula_NoRemoveWrites(const ASTNode& a, bool pushNeg); + ASTNode SimplifyFormula_TopLevel(const ASTNode& a, bool pushNeg); + ASTNode SimplifyFormula(const ASTNode& a, bool pushNeg); + ASTNode SimplifyTerm_TopLevel(const ASTNode& b); + ASTNode SimplifyTerm(const ASTNode& a); + void CheckSimplifyInvariant(const ASTNode& a, const ASTNode& output); + private: + //memo table for simplifcation + ASTNodeMap SimplifyMap; + ASTNodeMap SimplifyNegMap; + ASTNodeMap SolverMap; + ASTNodeSet AlwaysTrueFormMap; + ASTNodeMap MultInverseMap; + + public: + ASTNode SimplifyAtomicFormula(const ASTNode& a, bool pushNeg); + ASTNode CreateSimplifiedEQ(const ASTNode& t1, const ASTNode& t2); + ASTNode ITEOpt_InEqs(const ASTNode& in1); + ASTNode CreateSimplifiedTermITE(const ASTNode& t1, const ASTNode& t2, const ASTNode& t3); + ASTNode CreateSimplifiedINEQ(Kind k, const ASTNode& a0, const ASTNode& a1, bool pushNeg); + ASTNode SimplifyNotFormula(const ASTNode& a, bool pushNeg); + ASTNode SimplifyAndOrFormula(const ASTNode& a, bool pushNeg); + ASTNode SimplifyXorFormula(const ASTNode& a, bool pushNeg); + ASTNode SimplifyNandFormula(const ASTNode& a, bool pushNeg); + ASTNode SimplifyNorFormula(const ASTNode& a, bool pushNeg); + ASTNode SimplifyImpliesFormula(const ASTNode& a, bool pushNeg); + ASTNode SimplifyIffFormula(const ASTNode& a, bool pushNeg); + ASTNode SimplifyIteFormula(const ASTNode& a, bool pushNeg); + ASTNode FlattenOneLevel(const ASTNode& a); + ASTNode FlattenAndOr(const ASTNode& a); + ASTNode CombineLikeTerms(const ASTNode& a); + ASTNode LhsMinusRhs(const ASTNode& eq); + ASTNode DistributeMultOverPlus(const ASTNode& a, + bool startdistribution=false); + ASTNode ConvertBVSXToITE(const ASTNode& a); + //checks if the input constant is odd or not + bool BVConstIsOdd(const ASTNode& c); + //computes the multiplicatve inverse of the input + ASTNode MultiplicativeInverse(const ASTNode& c); + + void ClearAllTables(void); + void ClearAllCaches(void); + int BeforeSAT_ResultCheck(const ASTNode& q); + int CallSAT_ResultCheck(MINISAT::Solver& newS, + const ASTNode& q, const ASTNode& orig_input); + int SATBased_ArrayReadRefinement(MINISAT::Solver& newS, + const ASTNode& q, const ASTNode& orig_input); + int SATBased_ArrayWriteRefinement(MINISAT::Solver& newS, const ASTNode& orig_input); + //creates array write axiom only for the input term or formula, if + //necessary. If there are no axioms to produce then it simply + //generates TRUE + ASTNode Create_ArrayWriteAxioms(const ASTNode& array_readoverwrite_term, const ASTNode& array_newname); + ASTVec ArrayWrite_RemainingAxioms; + //variable indicates that counterexample will now be checked by + //the counterexample checker, and hence simplifyterm must switch + //off certain optimizations. In particular, array write + //optimizations + bool start_abstracting; + bool Begin_RemoveWrites; + bool SimplifyWrites_InPlace_Flag; + + void CopySolverMap_To_CounterExample(void); + //int LinearSearch(const ASTNode& orig_input); + //Datastructures and functions needed for counterexample + //generation, and interface with MINISAT + private: + /* MAP: This is a map from ASTNodes to MINISAT::Vars. + * + * The map is populated while ASTclauses are read from the AST + * ClauseList returned by CNF converter. For every new boolean + * variable in ASTClause a new MINISAT::Var is created (these vars + * typedefs for ints) + */ + typedef hash_map<ASTNode, MINISAT::Var, + ASTNode::ASTNodeHasher, + ASTNode::ASTNodeEqual> ASTtoSATMap; + ASTtoSATMap _ASTNode_to_SATVar; + + public: + //converts the clause to SAT and calls SAT solver + bool toSATandSolve(MINISAT::Solver& S, ClauseList& cll); + + ///print SAT solver statistics + void PrintStats(MINISAT::SolverStats& stats); + + //accepts query and returns the answer. if query is valid, return + //true, else return false. Automatically constructs counterexample + //for invalid queries, and prints them upon request. + int TopLevelSAT(const ASTNode& query, const ASTNode& asserts); + + // Debugging function to find problems in BitBlast and ToCNF. + // See body in ToSAT.cpp for more explanation. + ASTNode CheckBBandCNF(MINISAT::Solver& newS, ASTNode form); + + // Internal recursive body of above. + ASTNode CheckBBandCNF_int(MINISAT::Solver& newS, ASTNode form); + + // Helper function for CheckBBandCNF + ASTNode SymbolTruthValue(MINISAT::Solver &newS, ASTNode form); + + //looksup a MINISAT var from the minisat-var memo-table. if none + //exists, then creates one. + const MINISAT::Var LookupOrCreateSATVar(MINISAT::Solver& S, const ASTNode& n); + + // Memo table for CheckBBandCNF debugging function + ASTNodeMap CheckBBandCNFMemo; + + + //Data structures for Array Read Transformations + private: + /* MAP: This is a map from Array Names to list of array-read + * indices in the input. This map is used by the TransformArray() + * function + * + * This map is useful in converting array reads into nested ITE + * constructs. Suppose there are two array reads in the input + * Read(A,i) and Read(A,j). Then Read(A,i) is replaced with a + * symbolic constant, say v1, and Read(A,j) is replaced with the + * following ITE: + * + * ITE(i=j,v1,v2) + */ + //CAUTION: I tried using a set instead of vector for + //readindicies. for some odd reason the performance went down + //considerably. this is totally inexplicable. + ASTNodeToVecMap _arrayname_readindices; + + /* MAP: This is a map from Array Names to nested ITE constructs, + * which are built as described below. This map is used by the + * TransformArray() function + * + * This map is useful in converting array reads into nested ITE + * constructs. Suppose there are two array reads in the input + * Read(A,i) and Read(A,j). Then Read(A,i) is replaced with a + * symbolic constant, say v1, and Read(A,j) is replaced with the + * following ITE: + * + * ITE(i=j,v1,v2) + */ + ASTNodeMap _arrayread_ite; + + /*MAP: This is a map from array-reads to symbolic constants. This + *map is used by the TransformArray() + */ + ASTNodeMap _arrayread_symbol; + + ASTNodeSet _introduced_symbols; + + /*Memoization map for TransformFormula/TransformTerm/TransformArray function + */ + ASTNodeMap TransformMap; + + //count to keep track of new symbolic constants introduced + //corresponding to Array Reads + unsigned int _symbol_count; + + //Formula/Term Transformers. Let Expr Manager, Type Checker + public: + //Functions that Transform ASTNodes + ASTNode TransformFormula(const ASTNode& query); + ASTNode TransformTerm(const ASTNode& term); + ASTNode TransformArray(const ASTNode& term); + ASTNode TranslateSignedDivMod(const ASTNode& term); + + //LET Management + private: + // MAP: This map is from bound IDs that occur in LETs to + // expression. The map is useful in checking replacing the IDs + // with the corresponding expressions. + ASTNodeMap _letid_expr_map; + public: + + ASTNode ResolveID(const ASTNode& var); + + //Functions that are used to manage LET expressions + void LetExprMgr(const ASTNode& var, const ASTNode& letExpr); + + //Delete Letid Map + void CleanupLetIDMap(void); + + //Allocate LetID map + void InitializeLetIDMap(void); + + //Substitute Let-vars with LetExprs + ASTNode SubstituteLetExpr(ASTNode inExpr); + + /* MAP: This is a map from MINISAT::Vars to ASTNodes + * + * This is a reverse map, useful in constructing + * counterexamples. MINISAT returns a model in terms of MINISAT + * Vars, and this map helps us convert it to a model over ASTNode + * variables. + */ + vector<ASTNode> _SATVar_to_AST; + + private: + /* MAP: This is a map from ASTNodes to vectors of bits + * + * This map is used in constructing and printing + * counterexamples. MINISAT returns values for each bit (a + * BVGETBIT Node), and this maps allows us to assemble the bits + * into bitvectors. + */ + typedef hash_map<ASTNode, hash_map<unsigned int, bool> *, + ASTNode::ASTNodeHasher, + ASTNode::ASTNodeEqual> ASTtoBitvectorMap; + ASTtoBitvectorMap _ASTNode_to_Bitvector; + + //Data structure that holds the counter-model + ASTNodeMap CounterExampleMap; + + //Checks if the counter_example is ok. In order for the + //counter_example to be ok, Every assert must evaluate to true + //w.r.t couner_example and the query must evaluate to + //false. Otherwise the counter_example is bogus. + void CheckCounterExample(bool t); + + //Converts a vector of bools to a BVConst + ASTNode BoolVectoBVConst(hash_map<unsigned,bool> * w, unsigned int l); + + //accepts a term and turns it into a constant-term w.r.t counter_example + ASTNode TermToConstTermUsingModel(const ASTNode& term, bool ArrayReadFlag = true); + ASTNode Expand_ReadOverWrite_UsingModel(const ASTNode& term, bool ArrayReadFlag = true); + //Computes the truth value of a formula w.r.t counter_example + ASTNode ComputeFormulaUsingModel(const ASTNode& form); + + //Replaces WRITE(Arr,i,val) with ITE(j=i, val, READ(Arr,j)) + ASTNode RemoveWrites_TopLevel(const ASTNode& term); + ASTNode RemoveWrites(const ASTNode& term); + ASTNode SimplifyWrites_InPlace(const ASTNode& term); + ASTNode ReadOverWrite_To_ITE(const ASTNode& term); + + ASTNode NewArrayVar(unsigned int index, unsigned int value); + ASTNode NewVar(unsigned int valuewidth); + //For ArrayWrite Abstraction: map from read-over-write term to + //newname. + ASTNodeMap ReadOverWrite_NewName_Map; + //For ArrayWrite Refinement: Map new arraynames to Read-Over-Write + //terms + ASTNodeMap NewName_ReadOverWrite_Map; + + public: + //print the STP solver output + void PrintOutput(bool true_iff_valid); + + //Converts MINISAT counterexample into an AST memotable (i.e. the + //function populates the datastructure CounterExampleMap) + void ConstructCounterExample(MINISAT::Solver& S); + + //Prints the counterexample to stdout + void PrintCounterExample(bool t,std::ostream& os=cout); + + //Prints the counterexample to stdout + void PrintCounterExample_InOrder(bool t); + + //queries the counterexample, and returns the value corresponding + //to e + ASTNode GetCounterExample(bool t, const ASTNode& e); + + int CounterExampleSize(void) const {return CounterExampleMap.size();} + + //FIXME: This is bloody dangerous function. Hack attack to take + //care of requests from users who want to store complete + //counter-examples in their own data structures. + ASTNodeMap GetCompleteCounterExample() {return CounterExampleMap;} + + // prints MINISAT assigment one bit at a time, for debugging. + void PrintSATModel(MINISAT::Solver& S); + + //accepts constant input and normalizes it. + ASTNode BVConstEvaluator(const ASTNode& t); + + //FUNCTION TypeChecker: Assumes that the immediate Children of the + //input ASTNode have been typechecked. This function is suitable + //in scenarios like where you are building the ASTNode Tree, and + //you typecheck as you go along. It is not suitable as a general + //typechecker + void BVTypeCheck(const ASTNode& n); + + private: + //stack of Logical Context. each entry in the stack is a logical + //context. A logical context is a vector of assertions. The + //logical context is represented by a ptr to a vector of + //assertions in that logical context. Logical contexts are created + //by PUSH/POP + std::vector<ASTVec *> _asserts; + //The query for the current logical context. + ASTNode _current_query; + + //this flag, when true, indicates that counterexample is being + //checked by the counterexample checker + bool counterexample_checking_during_refinement; + + //this flag indicates as to whether the input has been determined to + //be valid or not by this tool + bool ValidFlag; + + //this flag, when true, indicates that a BVDIV divide by zero + //exception occured. However, the program must not exit with a + //fatalerror. Instead, it should evaluate the whole formula (which + //contains the BVDIV term) to be FALSE. + bool bvdiv_exception_occured; + + public: + //set of functions that manipulate Logical Contexts. + // + //add an assertion to the current logical context + void AddAssert(const ASTNode& assert); + void Push(void); + void Pop(void); + void AddQuery(const ASTNode& q); + const ASTNode PopQuery(); + const ASTNode GetQuery(); + const ASTVec GetAsserts(void); + + //reports node size. Second arg is "clearstatinfo", whatever that is. + unsigned int NodeSize(const ASTNode& a, bool t = false); + + private: + //This memo map is used by the ComputeFormulaUsingModel() + ASTNodeMap ComputeFormulaMap; + //Map for statiscal purposes + ASTNodeSet StatInfoSet; + + + ASTNodeMap TermsAlreadySeenMap; + ASTNode CreateSubstitutionMap(const ASTNode& a); + public: + //prints statistics for the ASTNode. can add a prefix string c + void ASTNodeStats(const char * c, const ASTNode& a); + + //substitution + bool CheckSubstitutionMap(const ASTNode& a, ASTNode& output); + bool CheckSubstitutionMap(const ASTNode& a); + bool UpdateSubstitutionMap(const ASTNode& e0, const ASTNode& e1); + //if (a > b) in the termorder, then return 1 + //elseif (a < b) in the termorder, then return -1 + //else return 0 + int TermOrder(const ASTNode& a, const ASTNode& b); + //fill the arrayname_readindices vector if e0 is a READ(Arr,index) + //and index is a BVCONST + void FillUp_ArrReadIndex_Vec(const ASTNode& e0, const ASTNode& e1); + bool VarSeenInTerm(const ASTNode& var, const ASTNode& term); + + //functions for checking and updating simplifcation map + bool CheckSimplifyMap(const ASTNode& key, ASTNode& output, bool pushNeg); + void UpdateSimplifyMap(const ASTNode& key, const ASTNode& value, bool pushNeg); + bool CheckAlwaysTrueFormMap(const ASTNode& key); + void UpdateAlwaysTrueFormMap(const ASTNode& val); + bool CheckMultInverseMap(const ASTNode& key, ASTNode& output); + void UpdateMultInverseMap(const ASTNode& key, const ASTNode& value); + + //Map for solved variables + bool CheckSolverMap(const ASTNode& a, ASTNode& output); + bool CheckSolverMap(const ASTNode& a); + bool UpdateSolverMap(const ASTNode& e0, const ASTNode& e1); + public: + //FIXME: HACK_ATTACK. this vector was hacked into the code to + //support a special request by Dawson' group. They want the + //counterexample to be printed in the order of variables declared. + //TO BE COMMENTED LATER (say by 1st week of march,2006) + ASTVec _special_print_set; + + //prints the initial activity levels of variables + void PrintActivityLevels_Of_SATVars(char * init_msg, MINISAT::Solver& newS); + + //this function biases the activity levels of MINISAT variables. + void ChangeActivityLevels_Of_SATVars(MINISAT::Solver& n); + + // Constructor + BeevMgr() : _interior_unique_table(INITIAL_INTERIOR_UNIQUE_TABLE_SIZE), + _symbol_unique_table(INITIAL_SYMBOL_UNIQUE_TABLE_SIZE), + _bvconst_unique_table(INITIAL_BVCONST_UNIQUE_TABLE_SIZE), + BBTermMemo(INITIAL_BBTERM_MEMO_TABLE_SIZE), + BBFormMemo(INITIAL_BBFORM_MEMO_TABLE_SIZE), + _max_node_num(0), + ASTFalse(CreateNode(FALSE)), + ASTTrue(CreateNode(TRUE)), + ASTUndefined(CreateNode(UNDEFINED)), + SimplifyMap(INITIAL_SIMPLIFY_MAP_SIZE), + SimplifyNegMap(INITIAL_SIMPLIFY_MAP_SIZE), + SolverMap(INITIAL_SOLVER_MAP_SIZE), + _arrayread_symbol(INITIAL_ARRAYREAD_SYMBOL_SIZE), + _introduced_symbols(INITIAL_INTRODUCED_SYMBOLS_SIZE), + _symbol_count(0) { + _current_query = ASTUndefined; + ValidFlag = false; + bvdiv_exception_occured = false; + counterexample_checking_during_refinement = false; + start_abstracting = false; + Begin_RemoveWrites = false; + SimplifyWrites_InPlace_Flag = false; + }; + + //destructor + ~BeevMgr(); + }; //End of Class BeevMgr + + + class CompleteCounterExample { + ASTNodeMap counterexample; + BeevMgr * bv; + public: + CompleteCounterExample(ASTNodeMap a, BeevMgr* beev) : counterexample(a), bv(beev){} + ASTNode GetCounterExample(ASTNode e) { + if(BOOLEAN_TYPE == e.GetType() && SYMBOL != e.GetKind()) { + FatalError("You must input a term or propositional variables\n",e); + } + if(counterexample.find(e) != counterexample.end()) { + return counterexample[e]; + } + else { + if(SYMBOL == e.GetKind() && BOOLEAN_TYPE == e.GetType()) { + return bv->CreateNode(BEEV::FALSE); + } + + if(SYMBOL == e.GetKind()) { + ASTNode z = bv->CreateZeroConst(e.GetValueWidth()); + return z; + } + + return e; + } + } + }; + +}; // end namespace BEEV +#endif diff --git a/stp/AST/ASTKind.kinds b/stp/AST/ASTKind.kinds new file mode 100644 index 00000000..03112eb8 --- /dev/null +++ b/stp/AST/ASTKind.kinds @@ -0,0 +1,71 @@ +#Please refer LICENSE FILE in the home directory for licensing information +# name minkids maxkids cat1 cat2 ... +Categories: Term Form + +# Leaf nodes. +UNDEFINED 0 0 +SYMBOL 0 0 Term Form + +# These always produce terms +BVCONST 0 0 Term +BVNEG 1 1 Term +BVCONCAT 2 - Term +BVOR 1 - Term +BVAND 1 - Term +BVXOR 1 - Term +BVNAND 1 - Term +BVNOR 1 - Term +BVXNOR 1 - Term +BVEXTRACT 3 3 Term +BVLEFTSHIFT 3 3 Term +BVRIGHTSHIFT 3 3 Term +BVSRSHIFT 3 3 Term +BVVARSHIFT 3 3 Term +BVPLUS 1 - Term +BVSUB 2 2 Term +BVUMINUS 1 1 Term +BVMULTINVERSE 1 1 Term +BVMULT 1 - Term +BVDIV 2 2 Term +BVMOD 2 2 Term +SBVDIV 2 2 Term +SBVMOD 2 2 Term +BVSX 1 1 Term +BOOLVEC 0 - Term + +# Formula OR term, depending on context +ITE 3 3 Term Form + +# These produce formulas. +BVGETBIT 2 2 Form +BVLT 2 2 Form +BVLE 2 2 Form +BVGT 2 2 Form +BVGE 2 2 Form +BVSLT 2 2 Form +BVSLE 2 2 Form +BVSGT 2 2 Form +BVSGE 2 2 Form +EQ 2 2 Form +NEQ 2 2 Form +FALSE 0 0 Form +TRUE 0 0 Form +NOT 1 1 Form +AND 1 - Form +OR 1 - Form +NAND 1 - Form +NOR 1 - Form +XOR 1 - Form +IFF 1 - Form +IMPLIES 2 2 Form + +# array operations +READ 2 2 Term +WRITE 3 3 Term + +#Types: These kinds are used only in the API. Once processed inside +#the API, they are never used again in the system +ARRAY 0 0 +BITVECTOR 0 0 +BOOLEAN 0 0 + diff --git a/stp/AST/ASTUtil.cpp b/stp/AST/ASTUtil.cpp new file mode 100644 index 00000000..bc36812c --- /dev/null +++ b/stp/AST/ASTUtil.cpp @@ -0,0 +1,45 @@ +/******************************************************************** + * AUTHORS: Vijay Ganesh, David L. Dill + * + * BEGIN DATE: November, 2005 + * + * LICENSE: Please view LICENSE file in the home dir of this Program + ********************************************************************/ +// -*- c++ -*- + +#include "ASTUtil.h" +#include <ostream> + +namespace BEEV { + ostream &operator<<(ostream &os, const Spacer &sp) { + // Instead of wrapping lines with hundreds of spaces, prints + // a "+" at the beginning of the line for each wrap-around. + // so lines print like: +14+ (XOR ... + int blanks = sp._spaces % 60; + int wraps = sp._spaces / 60; + if (wraps > 0) { + os << "+" << wraps; + } + for (int i = 0; i < blanks; i++) + os << " "; + return os; + } + + //this function accepts the name of a function (as a char *), and + //records some stats about it. if the input is "print_func_stats", + //the function will then print the stats that it has collected. + void CountersAndStats(const char * functionname) { + if(!stats) + return; + static function_counters s; + + if(!strcmp(functionname,"print_func_stats")) { + cout << endl; + for(hash_map<const char*,int,hash<const char*>,eqstr>::iterator it=s.begin(),itend=s.end(); + it!=itend;it++) + cout << "Number of times the function: " << it->first << ": is called: " << it->second << endl; + return; + } + s[functionname] += 1; + } +};// end of namespace diff --git a/stp/AST/ASTUtil.h b/stp/AST/ASTUtil.h new file mode 100644 index 00000000..0ed6bfa2 --- /dev/null +++ b/stp/AST/ASTUtil.h @@ -0,0 +1,107 @@ +/******************************************************************** + * AUTHORS: Vijay Ganesh, David L. Dill + * + * BEGIN DATE: November, 2005 + * + * LICENSE: Please view LICENSE file in the home dir of this Program + ********************************************************************/ +// -*- c++ -*- + +#ifndef ASTUTIL_H +#define ASTUTIL_H + +#include <cstring> +#include <iostream> +#include <vector> +#ifdef EXT_HASH_MAP +#include <ext/hash_set> +#include <ext/hash_map> +#else +#include <hash_set> +#include <hash_map> +#endif + +using namespace std; +namespace BEEV { +#ifdef EXT_HASH_MAP + using namespace __gnu_cxx; +#endif + //some global variables that are set through commandline options. it + //is best that these variables remain global. Default values set + //here + // + //collect statistics on certain functions + extern bool stats; + //print DAG nodes + extern bool print_nodes; + //tentative global var to allow for variable activity optimization + //in the SAT solver. deprecated. + extern bool variable_activity_optimize; + //run STP in optimized mode + extern bool optimize; + //do sat refinement, i.e. underconstraint the problem, and feed to + //SAT. if this works, great. else, add a set of suitable constraints + //to re-constraint the problem correctly, and call SAT again, until + //all constraints have been added. + extern bool arrayread_refinement; + //switch to control write refinements + extern bool arraywrite_refinement; + //check the counterexample against the original input to STP + extern bool check_counterexample; + //construct the counterexample in terms of original variable based + //on the counterexample returned by SAT solver + extern bool construct_counterexample; + extern bool print_counterexample; + //if this option is true then print the way dawson wants using a + //different printer. do not use this printer. + extern bool print_arrayval_declaredorder; + //flag to decide whether to print "valid/invalid" or not + extern bool print_output; + //do linear search in the array values of an input array. experimental + extern bool linear_search; + //print the variable order chosen by the sat solver while it is + //solving. + extern bool print_sat_varorder; + //turn on word level bitvector solver + extern bool wordlevel_solve; + //XOR flattening optimizations. + extern bool xor_flatten; + //this flag indicates that the BVSolver() succeeded + extern bool toplevel_solved; + //the smtlib parser has been turned on + extern bool smtlib_parser_enable; + //print the input back + extern bool print_STPinput_back; + + extern void (*vc_error_hdlr)(const char* err_msg); + /*Spacer class is basically just an int, but the new class allows + overloading of << with a special definition that prints the int as + that many spaces. */ + class Spacer { + public: + int _spaces; + Spacer(int spaces) { _spaces = spaces; } + friend ostream& operator<<(ostream& os, const Spacer &ind); + }; + + inline Spacer spaces(int width) { + Spacer sp(width); + return sp; + } + + struct eqstr { + bool operator()(const char* s1, const char* s2) const { + return strcmp(s1, s2) == 0; + } + }; + + typedef hash_map<const char*,int, + hash<const char *>,eqstr> function_counters; + void CountersAndStats(const char * functionname); + + //global function which accepts an integer and looks up the + //corresponding ASTNode and prints a char* of that ASTNode + void Convert_MINISATVar_To_ASTNode_Print(int minisat_var, + int decision, int polarity=0); +}; // end namespace. +#endif diff --git a/stp/AST/BitBlast.cpp b/stp/AST/BitBlast.cpp new file mode 100644 index 00000000..de78ec74 --- /dev/null +++ b/stp/AST/BitBlast.cpp @@ -0,0 +1,812 @@ +/******************************************************************** + * AUTHORS: David L. Dill, Vijay Ganesh + * + * BEGIN DATE: November, 2005 + * + * LICENSE: Please view LICENSE file in the home dir of this Program + ********************************************************************/ +// -*- c++ -*- + +// BitBlast -- convert bitvector terms and formulas to boolean +// formulas. A term is something that can represent a multi-bit +// bitvector, such as BVPLUS or BVXOR (or a BV variable or constant). +// A formula (form) represents a boolean value, such as EQ or BVLE. +// Bit blasting a term representing an n-bit bitvector with BBTerm +// yields a vector of n boolean formulas (returning ASTVec). +// Bit blasting a formula returns a single boolean formula (type ASTNode). + +// A bitblasted term is a vector of ASTNodes for formulas. +// The 0th element of the vector corresponds to bit 0 -- the low-order bit. + +#include "AST.h" +namespace BEEV { + // extern void lpvec(ASTVec &vec); + +// FIXME: Assert no zero-length bit vectors!!! +// FIXME: Need top-level functions that create and destroy the memo tables. +// FIXME: Check resource limits and generate an exception when exceeded. +// FIXME: THis does a lot of unnecessary copying of vectors. +// Had to be careful not to modify memoized vectors! +// FIXME: Might be some redundant variables. + +// accepts a term, and returns a vector of bitblasted bits(ASTVec) + +ASTNode ASTJunk; +const ASTNode BeevMgr::BBTerm(const ASTNode& term) { + //CHANGED TermMemo is now an ASTNodeMap. Based on BBFormMemo + ASTNodeMap::iterator it = BBTermMemo.find(term); + if (it != BBTermMemo.end()) { + // already there. Just return it. + return it->second; + } + +// ASTNode& result = ASTJunk; + ASTNode result; + + Kind k = term.GetKind(); + if (!is_Term_kind(k)) + FatalError("BBTerm: Illegal kind to BBTerm",term); + + ASTVec::const_iterator kids_end = term.end(); + unsigned int num_bits = term.GetValueWidth(); + switch (k) { + case BVNEG: { + // bitwise complement + // bitblast the child. + //FIXME Uses a tempory const ASTNode + const ASTNode& bbkids = BBTerm(term[0]); + result = CreateNode(BOOLVEC, BBNeg(bbkids.GetChildren())); + break; + } + case BVSRSHIFT: + case BVVARSHIFT: + FatalError("BBTerm: These kinds have not been implemented in the BitBlaster: ", term); + break; + case ITE: { + // Term version of ITE. + + // Blast the args + // FIXME Uses temporary const ASTNodes and an ASTVec& + const ASTNode& cond = BBForm(term[0]); + const ASTNode& thn = BBTerm(term[1]); + const ASTNode& els = BBTerm(term[2]); + result = + CreateNode(BOOLVEC, BBITE(cond, thn.GetChildren(), els.GetChildren())); + break; + } + case BVSX: { + // Replicate high-order bit as many times as necessary. + // Arg 0 is expression to be sign extended. + const ASTNode& arg = term[0]; + unsigned long result_width = term.GetValueWidth(); + unsigned long arg_width = arg.GetValueWidth(); + //FIXME Uses a temporary const ASTNode reference + const ASTNode& bbarg = BBTerm(arg); + + if (result_width == arg_width) { + //nothing to sign extend + break; + } + else { + //we need to sign extend + const ASTNode& msbX = bbarg.back(); + //const ASTNode& msb1 = msbX; + + ASTVec ccc = msbX.GetChildren(); + const ASTNode& msb = CreateSimpForm(msbX.GetKind(),ccc); + + // Old version + // ASTNode msb = bbarg.back(); + // const ASTNode msb1 = msb; + + // ASTVec ccc = msb.GetChildren(); + // msb = CreateSimpForm(msb.GetKind(),ccc); + + // DD 1/14/07 Simplify silently drops all but first two args of XOR. + // I expanded XOR to N args with flattening optimization. + // This bug took 2 days to track down! + + // msb = SimplifyFormula(msb,false); + + // cout << "!!!!!!!!!!!!!!!!" << endl + // << "Simplify msb:" << msb2 << endl + // << "Simplify result:" << msb << endl; + + //FIXME Dynamically allocate the result vector? + //Is this doing multiple copies? + //ASTVec& tmp_res = *(new ASTVec(result_width)); + ASTVec tmp_res(result_width); + + //FIXME Should these be gotten from result? + ASTVec::const_iterator bb_it = bbarg.begin(); + ASTVec::iterator res_it = tmp_res.begin(); + ASTVec::iterator res_ext = res_it+arg_width; // first bit of extended part + ASTVec::iterator res_end = tmp_res.end(); + // copy LSBs directly from bbvec + for( ; res_it < res_ext; (res_it++, bb_it++)) { + *res_it = *bb_it; + } + // repeat MSB to fill up rest of result. + for( ; res_it < res_end; (res_it++, bb_it++)) { + *res_it = msb; + } + + //Temporary debugging code + // cout << "Sign extending:" << endl + // << " Vec "; + // lpvec( bbarg.GetChildren() ); + // cout << " Extended to "; + // lp(result); + // cout << endl; + + result = CreateNode(BOOLVEC, tmp_res); + + break; + } + } + case BVEXTRACT: { + // bitblast the child, then extract the relevant bits. + // Note: This could be optimized by not bitblasting the bits + // that aren't fetched. But that would be tricky, especially + // with memo-ization. + + //FIXME Using const ASTNode w/out reference + const ASTNode& bbkids = BBTerm(term[0]); + unsigned int high = GetUnsignedConst(term[1]); + unsigned int low = GetUnsignedConst(term[2]); + + ASTVec::const_iterator bbkfit = bbkids.begin(); + // I should have used pointers to ASTVec, to avoid this crock + + //FIXME Creates a new local ASTVec and does the CreateNode from that + result = CreateNode(BOOLVEC, ASTVec(bbkfit+low, bbkfit+high+1)); + break; + } + case BVCONCAT: { + //FIXME Using temporary const ASTNodes + const ASTNode& vec1 = BBTerm(term[0]); + const ASTNode& vec2 = BBTerm(term[1]); + + //FIXME This has to be an unnessecary copy and a memory leak + //Leaking ASTVec tmp_res = *(new ASTVec(vec2.GetChildren())); + ASTVec tmp_res(vec2.GetChildren()); + tmp_res.insert(tmp_res.end(), vec1.begin(), vec1.end()); + result = CreateNode(BOOLVEC, tmp_res); + break; + } + case BVPLUS: { + // ASSERT: at least one child. + // ASSERT: all children and result are the same size. + // Previous phase must make sure this is true. + // Add children pairwise and accumulate in BBsum + + // FIXME: Unnecessary array copies. + ASTVec::const_iterator it = term.begin(); + ASTVec tmp_res = BBTerm(*it).GetChildren(); + for (++it; it < kids_end; it++) { + const ASTVec& tmp = BBTerm(*it).GetChildren(); + BBPlus2(tmp_res, tmp, ASTFalse); + } + + result = CreateNode(BOOLVEC, tmp_res); + break; + } + case BVUMINUS: { + //FIXME Using const ASTNode reference + const ASTNode& bbkid = BBTerm(term[0]); + result = CreateNode(BOOLVEC, BBUminus(bbkid.GetChildren())); + break; + } + case BVSUB: { + // complement of subtrahend + // copy, since BBSub writes into it. + + //FIXME: Unnecessary array copies? + ASTVec tmp_res = BBTerm(term[0]).GetChildren(); + + const ASTVec& bbkid1 = BBTerm(term[1]).GetChildren(); + BBSub(tmp_res, bbkid1); + result = CreateNode(BOOLVEC, tmp_res); + break; + } + case BVMULT: { + // ASSERT 2 arguments, same length, result is same length. + + const ASTNode& t0 = term[0]; + const ASTNode& t1 = term[1]; + + const ASTNode& mpcd1 = BBTerm(t0); + const ASTNode& mpcd2 = BBTerm(t1); + //Reverese the order of the nodes w/out the need for temporaries + //This is needed because t0 an t1 must be const + if ((BVCONST != t0.GetKind()) && (BVCONST == t1.GetKind())) { + result = CreateNode(BOOLVEC, + BBMult(mpcd2.GetChildren(), mpcd1.GetChildren()) ); + }else{ + result = CreateNode(BOOLVEC, + BBMult(mpcd1.GetChildren(), mpcd2.GetChildren()) ); + } + break; + } + case BVDIV: + case BVMOD: { + const ASTNode& dvdd = BBTerm(term[0]); + const ASTNode& dvsr = BBTerm(term[1]); + unsigned int width = dvdd.Degree(); + ASTVec q(width); + ASTVec r(width); + BBDivMod(dvdd.GetChildren(), dvsr.GetChildren(), q, r, width); + if (k == BVDIV) + result = CreateNode(BOOLVEC, q); + else + result = CreateNode(BOOLVEC, r); + break; + } + // n-ary bitwise operators. + case BVXOR: + case BVXNOR: + case BVAND: + case BVOR: + case BVNOR: + case BVNAND: { + // Add children pairwise and accumulate in BBsum + ASTVec::const_iterator it = term.begin(); + Kind bk = UNDEFINED; // Kind of individual bit op. + switch (k) { + case BVXOR: bk = XOR; break; + case BVXNOR: bk = IFF; break; + case BVAND: bk = AND; break; + case BVOR: bk = OR; break; + case BVNOR: bk = NOR; break; + case BVNAND: bk = NAND; break; + default: + FatalError("BBTerm: Illegal kind to BBTerm",term); + break; + } + + // Sum is destructively modified in the loop, so make a copy of value + // returned by BBTerm. + ASTNode temp = BBTerm(*it); + ASTVec sum(temp.GetChildren()); // First operand. + + // Iterate over remaining bitvector term operands + for (++it; it < kids_end; it++) { + //FIXME FIXME FIXME: Why does using a temp. var change the behavior? + temp = BBTerm(*it); + const ASTVec& y = temp.GetChildren(); + + // Iterate over bits + // FIXME: Why is this not using an iterator??? + int n = y.size(); + for (int i = 0; i < n; i++) { + sum[i] = CreateSimpForm(bk, sum[i], y[i]); + } + } + result = CreateNode(BOOLVEC, sum); + break; + } + case SYMBOL: { + // ASSERT: IndexWidth = 0? Semantic analysis should check. + //Leaking ASTVec& bbvec = *(new ASTVec); + + //FIXME Why is isn't this ASTVEC bbvec(num_bits) ? + ASTVec bbvec; + for (unsigned int i = 0; i < num_bits; i++) { + ASTNode bit_node = + CreateNode(BVGETBIT, term, CreateBVConst(32,i)); + bbvec.push_back(bit_node); + } + result = CreateNode(BOOLVEC, bbvec); + break; + } + case BVCONST: { + ASTVec tmp_res(num_bits); +#ifndef NATIVE_C_ARITH + CBV bv = term.GetBVConst(); + for(unsigned int i = 0; i < num_bits; i++){ + tmp_res[i] = CONSTANTBV::BitVector_bit_test(bv,i) ? ASTTrue : ASTFalse; + } +#else + const unsigned long long int c = term.GetBVConst(); + unsigned long long int bitmask = 0x00000000000000001LL; + for (unsigned int i = 0; i < num_bits; i++, bitmask <<= 1) + tmp_res[i] = ((c & (bitmask)) ? ASTTrue : ASTFalse); +#endif + result = CreateNode(BOOLVEC, tmp_res); + break; + } + case BOOLVEC: { + cerr << "Hit a boolvec! what to do?" << endl; + break; + } + default: + FatalError("BBTerm: Illegal kind to BBTerm",term); + } + + //if(result == ASTJunk) + // cout<<"result does not change"<<endl; + // cout << "================" << endl << "BBTerm:" << term << endl; + // cout << "----------------" << endl << "BBTerm result:"; + // lpvec(result); + // cout << endl; + + return (BBTermMemo[term] = result); + +} + +// bit blast a formula (boolean term). Result is one bit wide, +// so it returns a single ASTNode. +// FIXME: Add IsNegated flag. +const ASTNode BeevMgr::BBForm(const ASTNode& form) +{ + + ASTNodeMap::iterator it = BBFormMemo.find(form); + if (it != BBFormMemo.end()) { + // already there. Just return it. + return it->second; + } + + ASTNode result = ASTUndefined; + + Kind k = form.GetKind(); + if (!is_Form_kind(k)) { + FatalError("BBForm: Illegal kind: ",form); + } + + // Not returning until end, and memoizing everything, makes it easier + // to trace coherently. + + // Various special cases + switch (k) { + case TRUE: + case FALSE: { + result = form; + break; + } + + case SYMBOL: + if (form.GetType() != BOOLEAN_TYPE) { + FatalError("BBForm: Symbol represents more than one bit", form); + } + + result = form; + break; + + case BVGETBIT: { + // exactly two children + const ASTNode bbchild = BBTerm(form[0]); + unsigned int index = GetUnsignedConst(form[1]); + result = bbchild[index]; + break; + } + + case NOT: + result = CreateSimpNot(BBForm(form[0])); + break; + + case ITE: + // FIXME: SHould this be CreateSimpITE? + result = CreateNode(ITE, BBForm(form[0]), BBForm(form[1]), BBForm(form[2])); + break; + + case AND: + case OR: + case NAND: + case NOR: + case IFF: + case XOR: + case IMPLIES: { + ASTVec bbkids; // bit-blasted children (formulas) + + // FIXME: Put in fast exits for AND/OR/NAND/NOR/IMPLIES + ASTVec::const_iterator kids_end = form.end(); + for (ASTVec::const_iterator it = form.begin(); it != kids_end; it++) { + bbkids.push_back(BBForm(*it)); + } + result = CreateSimpForm(k, bbkids); + break; + } + + case NEQ: { + ASTNode bbkid = BBForm(CreateNode(EQ, form.GetChildren())); + result = CreateSimpNot(bbkid); + break; + } + + case EQ: { + // Common code for binary operations + // FIXME: This ought to be in a semantic analysis phase. + const ASTNode left = BBTerm(form[0]); + const ASTNode right = BBTerm(form[1]); + if (left.Degree() != right.Degree()) { + cerr << "BBForm: Size mismatch" << endl << form[0] << endl << form[1] << endl; + FatalError("",ASTUndefined); + } + result = BBEQ(left.GetChildren(), right.GetChildren()); + break; + } + + case BVLE: + case BVGE: + case BVGT: + case BVLT: + case BVSLE: + case BVSGE: + case BVSGT: + case BVSLT: { + result = BBcompare(form); + break; + } + default: + FatalError("BBForm: Illegal kind: ", form); + break; + } + + // cout << "================" << endl + // << "BBForm: " << form << endl + // << "----------------" << endl + // << "BBForm Result: " << result << endl; + + return (BBFormMemo[form] = result); +} + +// Bit blast a sum of two equal length BVs. +// Update sum vector destructively with new sum. +void BeevMgr::BBPlus2(ASTVec& sum, const ASTVec& y, ASTNode cin) +{ +// cout << "Bitblasting plus. Operand 1: " << endl; +// lpvec(sum); +// cout << endl << " operand 2: " << endl; +// lpvec(y); +// cout << endl << "carry: " << endl << cin << endl; + + + int n = sum.size(); + // ASSERT: y.size() == x.size() + // FIXME: Don't bother computing i+1 carry, which is discarded. + for (int i = 0; i < n; i++) { + ASTNode nextcin = Majority(sum[i], y[i], cin); + sum[i] = CreateSimpForm(XOR, CreateSimpForm(XOR, sum[i], y[i]), cin); + cin = nextcin; + } + +// cout << "----------------" << endl << "Result: " << endl; +// lpvec(sum); +// cout << endl; + +} + +// Stores result - x in result, destructively +void BeevMgr::BBSub(ASTVec& result, const ASTVec& y) +{ + ASTVec compsubtrahend = BBNeg(y); + BBPlus2(result, compsubtrahend, ASTTrue); +} + +// Add one bit +ASTVec BeevMgr::BBAddOneBit(ASTVec& x, ASTNode cin) +{ + ASTVec result = ASTVec(0); + ASTVec::const_iterator itend = x.end(); + for (ASTVec::const_iterator it = x.begin(); it < itend; it++) { + ASTNode nextcin = CreateSimpForm(AND, *it, cin); + result.push_back(CreateSimpForm(XOR, *it, cin)); + cin = nextcin; + } + // FIXME: unnecessary array copy on return? + return result; +} + +// Increment bit-blasted vector and return result. +ASTVec BeevMgr::BBInc(ASTVec& x) +{ + return BBAddOneBit(x, ASTTrue); +} + +// Return formula for majority function of three bits. +// Pass arguments by reference to reduce refcounting. +ASTNode BeevMgr::Majority(const ASTNode& a, const ASTNode& b,const ASTNode& c) +{ + // Checking explicitly for constant a, b and c could + // be more efficient, because they are repeated in the logic. + if (ASTTrue == a) { + return CreateSimpForm(OR, b, c); + } + else if (ASTFalse == a) { + return CreateSimpForm(AND, b, c); + } + else if (ASTTrue == b) { + return CreateSimpForm(OR, a, c); + } + else if (ASTFalse == b) { + return CreateSimpForm(AND, a, c); + } + else if (ASTTrue == c) { + return CreateSimpForm(OR, a, b); + } + else if (ASTFalse == c) { + return CreateSimpForm(AND, a, b); + } + // there are lots more simplifications, but I'm not sure they're + // worth doing explicitly (e.g., a = b, a = ~b, etc.) + else { + return + CreateSimpForm(OR, + CreateSimpForm(AND, a, b), + CreateSimpForm(AND, b, c), + CreateSimpForm(AND, a, c)); + } +} + + +// Bitwise complement +ASTVec BeevMgr::BBNeg(const ASTVec& x) +{ + ASTVec result = ASTVec(0); // FIXME: faster to preallocate n entries? + // Negate each bit. + ASTVec::const_iterator xend = x.end(); + for (ASTVec::const_iterator it = x.begin(); it < xend; it++) { + result.push_back(CreateSimpNot(*it)); + } + // FIXME: unecessary array copy when it returns? + return result; +} + +// Compute unary minus +ASTVec BeevMgr::BBUminus(const ASTVec& x) +{ + ASTVec xneg = BBNeg(x); + return BBInc(xneg); +} + +// Multiply two bitblasted numbers +ASTVec BeevMgr::BBMult(const ASTVec& x, const ASTVec& y) +{ + ASTVec ycopy(y); + ASTVec::const_iterator xend = x.end(); + ASTVec::const_iterator xit = x.begin(); + // start prod with first partial product. + // FIXME: This is unnecessary. Clean it up. + ASTVec prod = ASTVec(BBAndBit(y, *xit)); + // start loop at next bit. + for(xit++; xit < xend; xit++) { + // shift first + BBLShift(ycopy); + + if (ASTFalse == *xit) { + // If this bit is zero, the partial product will + // be zero. No reason to add that in. + continue; + } + + ASTVec pprod = BBAndBit(ycopy, *xit); + // accumulate in the product. + BBPlus2(prod, pprod, ASTFalse); + } + return prod; +} + +// This implements a variant of binary long division. +// q and r are "out" parameters. rwidth puts a bound on the +// recursion depth. +void BeevMgr::BBDivMod(const ASTVec &y, const ASTVec &x, ASTVec &q, ASTVec &r, unsigned int rwidth) +{ + unsigned int width = y.size(); + if (rwidth == 0) { + // When we have shifted the entire width, y is guaranteed to be 0. + q = BBfill(width, ASTFalse); + r = BBfill(width, ASTFalse); + } + else { + ASTVec q1, r1; + ASTVec yrshift1(y); + BBRShift(yrshift1); + + // recursively divide y/2 by x. + BBDivMod(yrshift1, x, q1, r1, rwidth-1); + + ASTVec q1lshift1(q1); + BBLShift(q1lshift1); + + ASTVec r1lshift1(r1); + BBLShift(r1lshift1); + + ASTVec r1lshift1plusyodd = BBAddOneBit(r1lshift1, y[0]); + ASTVec rminusx(r1lshift1plusyodd); + BBSub(rminusx, x); + + // Adjusted q, r values when when r is too large. + ASTNode rtoolarge = BBBVLE(x, r1lshift1plusyodd, false); + ASTVec ygtrxqval = BBITE(rtoolarge, BBInc(q1lshift1), q1lshift1); + ASTVec ygtrxrval = BBITE(rtoolarge, rminusx, r1lshift1plusyodd); + + // q & r values when y >= x + ASTNode yeqx = BBEQ(y, x); + // *** Problem: the bbfill for qval is wrong. Should be 1, not -1. + ASTVec one = BBfill(width, ASTFalse); + one[0] = ASTTrue; + ASTVec notylessxqval = BBITE(yeqx, one, ygtrxqval); + ASTVec notylessxrval = BBITE(yeqx, BBfill(width, ASTFalse), ygtrxrval); + // y < x <=> not x >= y. + ASTNode ylessx = CreateSimpNot(BBBVLE(x, y, false)); + // final values of q and r + q = BBITE(ylessx, BBfill(width, ASTFalse), notylessxqval); + r = BBITE(ylessx, y, notylessxrval); + } +} + +// build ITE's (ITE cond then[i] else[i]) for each i. +ASTVec BeevMgr::BBITE(const ASTNode& cond, const ASTVec& thn, const ASTVec& els) +{ + // Fast exits. + if (ASTTrue == cond) { + return thn; + } + else if (ASTFalse == cond) { + return els; + } + + ASTVec result(0); + ASTVec::const_iterator th_it_end = thn.end(); + ASTVec::const_iterator el_it = els.begin(); + for (ASTVec::const_iterator th_it = thn.begin(); th_it < th_it_end; th_it++, el_it++) { + result.push_back(CreateSimpForm(ITE, cond, *th_it, *el_it)); + } + return result; +} +// AND each bit of vector y with single bit b and return the result. +ASTVec BeevMgr::BBAndBit(const ASTVec& y, ASTNode b) +{ + ASTVec result(0); + + if (ASTTrue == b) { + return y; + } + // FIXME: put in fast exits when b is constant 0. + + ASTVec::const_iterator yend = y.end(); + for(ASTVec::const_iterator yit = y.begin(); yit < yend; yit++) { + result.push_back(CreateSimpForm(AND, *yit, b)); + } + return result; +} + + +// Workhorse for comparison routines. This does a signed BVLE if is_signed +// is true, else it's unsigned. All other comparison operators can be reduced +// to this by swapping args or complementing the result bit. +// FIXME: If this were done MSB first, it would enable a fast exit sometimes +// when the MSB is constant, deciding the result without looking at the rest +// of the bits. +ASTNode BeevMgr::BBBVLE(const ASTVec& left, const ASTVec& right, bool is_signed) +{ + // "thisbit" represents BVLE of the suffixes of the BVs + // from that position . if R < L, return TRUE, else if L < R + // return FALSE, else return BVLE of lower-order bits. MSB is + // treated separately, because signed comparison is done by + // complementing the MSB of each BV, then doing an unsigned + // comparison. + ASTVec::const_iterator lit = left.begin(); + ASTVec::const_iterator litend = left.end(); + ASTVec::const_iterator rit = right.begin(); + ASTNode prevbit = ASTTrue; + for ( ; lit < litend-1; lit++, rit++) { + ASTNode neglit = CreateSimpNot(*lit); + ASTNode thisbit = + CreateSimpForm(OR, + CreateSimpForm(AND,neglit,*rit), // TRUE if l < r + CreateSimpForm(AND, + CreateSimpForm(OR, neglit, *rit), // false if not equal + prevbit)); // else prevbit + prevbit = thisbit; + } + + // Handle MSB -- negate MSBs if signed comparison + // FIXME: make into refs after it's debugged. + ASTNode lmsb = *lit; + ASTNode rmsb = *rit; + if (is_signed) { + lmsb = CreateSimpNot(*lit); + rmsb = CreateSimpNot(*rit); + } + + ASTNode neglmsb = CreateSimpNot(lmsb); + ASTNode msb = + CreateSimpForm(OR, + CreateSimpForm(AND,neglmsb, rmsb), // TRUE if l < r + CreateSimpForm(AND, + CreateSimpForm(OR, neglmsb, rmsb), // false if not equal + prevbit)); // else prevbit + return msb; +} + +// Left shift by 1 within fixed field inserting zeros at LSB. +// Writes result into first argument. +// Fixme: generalize to n bits +void BeevMgr::BBLShift(ASTVec& x) +{ + // left shift x (destructively) within width. + // loop backwards so that copy to self works correctly. (DON'T use STL insert!) + ASTVec::iterator xbeg = x.begin(); + for(ASTVec::iterator xit = x.end()-1; xit > xbeg; xit--) { + *xit = *(xit-1); + } + *xbeg = ASTFalse; // new LSB is zero. + // cout << "Shifted result" << endl; + // lpvec(x); +} + +// Right shift by 1 within fixed field, inserting new zeros at MSB. +// Writes result into first argument. +// Fixme: generalize to n bits. +void BeevMgr::BBRShift(ASTVec& x) +{ + ASTVec::iterator xend = x.end() - 1; + ASTVec::iterator xit = x.begin(); + for( ; xit < xend; xit++) { + *xit = *(xit+1); + } + *xit = ASTFalse; // new MSB is zero. +} + + +// Return bit-blasted form for BVLE, BVGE, BVGT, SBLE, etc. +ASTNode BeevMgr::BBcompare(const ASTNode& form) { + const ASTNode lnode = BBTerm(form[0]); + const ASTNode rnode = BBTerm(form[1]); + const ASTVec& left = lnode.GetChildren(); + const ASTVec& right = rnode.GetChildren(); + + //const ASTVec& left = BBTerm(form[0]).GetChildren(); + //const ASTVec& right = BBTerm(form[1]).GetChildren(); + + Kind k = form.GetKind(); + switch(k) { + case BVLE: { return BBBVLE(left, right, false); break; } + case BVGE: { return BBBVLE(right, left, false); break; } + case BVGT: { return CreateSimpNot(BBBVLE(left, right, false)); break; } + case BVLT: { return CreateSimpNot(BBBVLE(right, left, false)); break; } + case BVSLE: { return BBBVLE(left, right, true); break; } + case BVSGE: { return BBBVLE(right, left, true); break; } + case BVSGT: { return CreateSimpNot(BBBVLE(left, right, true)); break; } + case BVSLT: { return CreateSimpNot(BBBVLE(right, left, true)); break; } + default: + cerr << "BBCompare: Illegal kind" << form << endl; + FatalError("",ASTUndefined); + } + return ASTUndefined; +} + + +// return a vector with n copies of fillval +ASTVec BeevMgr::BBfill(unsigned int width, ASTNode fillval) +{ + ASTVec zvec(width, fillval); + return zvec; +} + +ASTNode BeevMgr::BBEQ(const ASTVec& left, const ASTVec& right) +{ + ASTVec andvec; + ASTVec::const_iterator lit = left.begin(); + ASTVec::const_iterator litend = left.end(); + ASTVec::const_iterator rit = right.begin(); + + if(left.size() > 1) { + for(; lit != litend; lit++, rit++) { + ASTNode biteq = CreateSimpForm(IFF, *lit, *rit); + // fast path exit + if (biteq == ASTFalse) { + return ASTFalse; + } + else { + andvec.push_back(biteq); + } + } + ASTNode n = CreateSimpForm(AND, andvec); + return n; + } + else + return CreateSimpForm(IFF,*lit,*rit); +} +} // BEEV namespace diff --git a/stp/AST/Makefile b/stp/AST/Makefile new file mode 100644 index 00000000..0218510b --- /dev/null +++ b/stp/AST/Makefile @@ -0,0 +1,54 @@ +include ../Makefile.common + +SRCS = AST.cpp ASTKind.cpp ASTUtil.cpp BitBlast.cpp SimpBool.cpp ToCNF.cpp ToSAT.cpp Transform.cpp +OBJS = $(SRCS:.cpp=.o) + +#Make the ast library for use by other modules +libast.a: $(OBJS) + -rm -rf $@ + $(AR) rc libast.a $(OBJS) + $(RANLIB) libast.a + +ASTKind.o: ASTKind.h ASTKind.cpp + $(CXX) $(CXXFLAGS) -c -o ASTKind.o ASTKind.cpp + +# ASTKind.h and ASTKind.cpp are automatically generated +ASTKind.h ASTKind.cpp: ASTKind.kinds genkinds.pl + ./genkinds.pl + +# cnftest: cnftest.o ToCNF.o AST.o ASTUtil.o ASTKind.o BitBlast.o AST.h +# $(CC) $(LDFLAGS) ToCNF.o BitBlast.o ASTKind.o ASTUtil.o AST.o cnftest.o -o cnftest + +# bbtest: $(OBJS) +# $(CC) $(LDFLAGS) BitBlast.o ASTKind.o ASTUtil.o AST.o bbtest.o -o bbtest + +# asttest: $(OBJS) +# $(CC) $(LDFLAGS) ASTKind.o ASTUtil.o AST.o asttest.o -lstdc++ -o asttest + +clean: + rm -rf *.o *~ bbtest asttest cnftest *.a ASTKind.h ASTKind.cpp .#* + +depend: + makedepend -Y -- $(CFLAGS) -- $(SRCS) +# DO NOT DELETE + +AST.o: AST.h ASTUtil.h ASTKind.h ../sat/Solver.h ../sat/SolverTypes.h +AST.o: ../sat/Global.h ../sat/VarOrder.h ../sat/Solver.h ../sat/Heap.h +AST.o: ../AST/ASTUtil.h ../sat/SolverTypes.h ../constantbv/constantbv.h +ASTUtil.o: ASTUtil.h +BitBlast.o: AST.h ASTUtil.h ASTKind.h ../sat/Solver.h ../sat/SolverTypes.h +BitBlast.o: ../sat/Global.h ../sat/VarOrder.h ../sat/Solver.h ../sat/Heap.h +BitBlast.o: ../AST/ASTUtil.h ../sat/SolverTypes.h ../constantbv/constantbv.h +SimpBool.o: AST.h ASTUtil.h ASTKind.h ../sat/Solver.h ../sat/SolverTypes.h +SimpBool.o: ../sat/Global.h ../sat/VarOrder.h ../sat/Solver.h ../sat/Heap.h +SimpBool.o: ../AST/ASTUtil.h ../sat/SolverTypes.h ../constantbv/constantbv.h +ToCNF.o: AST.h ASTUtil.h ASTKind.h ../sat/Solver.h ../sat/SolverTypes.h +ToCNF.o: ../sat/Global.h ../sat/VarOrder.h ../sat/Solver.h ../sat/Heap.h +ToCNF.o: ../AST/ASTUtil.h ../sat/SolverTypes.h ../constantbv/constantbv.h +ToSAT.o: AST.h ASTUtil.h ASTKind.h ../sat/Solver.h ../sat/SolverTypes.h +ToSAT.o: ../sat/Global.h ../sat/VarOrder.h ../sat/Solver.h ../sat/Heap.h +ToSAT.o: ../AST/ASTUtil.h ../sat/SolverTypes.h ../constantbv/constantbv.h +ToSAT.o: ../simplifier/bvsolver.h ../AST/AST.h +Transform.o: AST.h ASTUtil.h ASTKind.h ../sat/Solver.h ../sat/SolverTypes.h +Transform.o: ../sat/Global.h ../sat/VarOrder.h ../sat/Solver.h ../sat/Heap.h +Transform.o: ../AST/ASTUtil.h ../sat/SolverTypes.h ../constantbv/constantbv.h diff --git a/stp/AST/STLport_config.h b/stp/AST/STLport_config.h new file mode 100644 index 00000000..9b7bc14f --- /dev/null +++ b/stp/AST/STLport_config.h @@ -0,0 +1,20 @@ +/******************************************************************** + * AUTHORS: Vijay Ganesh, David L. Dill + * + * BEGIN DATE: November, 2005 + * + * LICENSE: Please view LICENSE file in the home dir of this Program + ********************************************************************/ +// -*- c++ -*- + +// STLport debug checking, if we use STLport threads flag is to get +// rid of link errors, since iostreams compiles with threads. alloc +// and uninitialized are extra checks Later on, if used with Purify or +// Valgrind, may want to set flags to prevent reporting of false +// leaks. For some reason, _STLP_THREADS works on the command line +// but not here (?) +#define _STLP_THREADS +#define _STLP_DEBUG 1 +#define _STLP_DEBUG_LEVEL _STLP_STANDARD_DBG_LEVEL +#define _STLP_DEBUG_ALLOC 1 +#define _STLP_DEBUG_UNINITIALIZED 1 diff --git a/stp/AST/SimpBool.cpp b/stp/AST/SimpBool.cpp new file mode 100644 index 00000000..67f9825d --- /dev/null +++ b/stp/AST/SimpBool.cpp @@ -0,0 +1,408 @@ +/******************************************************************** + * AUTHORS: Vijay Ganesh, David L. Dill + * + * BEGIN DATE: April, 2006 + * + * LICENSE: Please view LICENSE file in the home dir of this Program + ********************************************************************/ + +// -*- c++ -*- + +// Simplifying create methods for Boolean operations. +// These are only very simple local simplifications. + +// This is somewhat redundant with Vijay's simplifier code. They +// need to be merged. +// FIXME: control with optimize flag. + +static bool _trace_simpbool = 0; +static bool _disable_simpbool = 0; + +#include "AST.h" + +// SMTLIB experimental hack. Try allocating a single stack here for +// children to reduce growing of vectors. +//BEEV::ASTVec child_stack; + +namespace BEEV { + + ASTNode BeevMgr::CreateSimpForm(Kind kind, ASTVec &children = _empty_ASTVec) { + if (_disable_simpbool) { + return CreateNode(kind, children); + } + else { + switch (kind) { + case NOT: return CreateSimpNot(children[0]); break; + case AND: return CreateSimpAndOr(1, children); break; + case OR: return CreateSimpAndOr(0, children); break; + case NAND: return CreateSimpNot(CreateSimpAndOr(1, children)); break; + case NOR: return CreateSimpNot(CreateSimpAndOr(0, children)); break; + case IFF: { + // Not sure children can ever be empty, but what the heck. + // if (children.size() == 0) { + // return ASTTrue; + // } + // Convert IFF to XOR ASAP. IFF is not associative, so this makes + // flattening much easier. + children[0] = CreateSimpNot(children[0]); + return CreateSimpXor(children); break; + } + case XOR: + return CreateSimpXor(children); break; + // FIXME: Earlier, check that this only has two arguments + case IMPLIES: return CreateSimpAndOr(0, CreateSimpNot(children[0]), children[1]); break; + case ITE: return CreateSimpFormITE(children[0], children[1], children[2]); + default: return CreateNode(kind, children); + } + } + } + + // specialized versions + + ASTNode BeevMgr::CreateSimpForm(Kind kind, + const ASTNode& child0) { + ASTVec children; + //child_stack.clear(); // could just reset top pointer. + children.push_back(child0); + //child_stack.push_back(child0); + return CreateSimpForm(kind, children); + //return CreateSimpForm(kind, child_stack); + } + + ASTNode BeevMgr::CreateSimpForm(Kind kind, + const ASTNode& child0, + const ASTNode& child1) { + ASTVec children; + //child_stack.clear(); // could just reset top pointer. + children.push_back(child0); + //child_stack.push_back(child0); + children.push_back(child1); + //child_stack.push_back(child1); + return CreateSimpForm(kind, children); + //return CreateSimpForm(kind, child_stack); + } + + + ASTNode BeevMgr::CreateSimpForm(Kind kind, + const ASTNode& child0, + const ASTNode& child1, + const ASTNode& child2) { + ASTVec children; + //child_stack.clear(); // could just reset top pointer. + children.push_back(child0); + //child_stack.push_back(child0); + children.push_back(child1); + //child_stack.push_back(child1); + children.push_back(child2); + //child_stack.push_back(child2); + return CreateSimpForm(kind, children); + //return CreateSimpForm(kind, child_stack); + } + + ASTNode BeevMgr::CreateSimpNot(const ASTNode& form) { + Kind k = form.GetKind(); + switch (k) { + case FALSE: { return ASTTrue; } + case TRUE: { return ASTFalse; } + case NOT: { return form[0]; } // NOT NOT cancellation + case XOR: { + // Push negation down in this case. + // FIXME: Separate pre-pass to push negation down? + // CreateSimp should be local, and this isn't. + // It isn't memoized. Arg. + ASTVec children = form.GetChildren(); + children[0] = CreateSimpNot(children[0]); + return CreateSimpXor(children); + } + default: { return CreateNode(NOT, form); } + } + } + + // I don't think this is even called, since it called + // CreateSimpAndOr instead of CreateSimpXor until 1/9/07 with no + // ill effects. Calls seem to go to the version that takes a vector + // of children. + ASTNode BeevMgr::CreateSimpXor(const ASTNode& form1, const ASTNode& form2) { + ASTVec children; + children.push_back(form1); + children.push_back(form2); + return CreateSimpXor(children); + } + + + ASTNode BeevMgr::CreateSimpAndOr(bool IsAnd, const ASTNode& form1, const ASTNode& form2) { + ASTVec children; + children.push_back(form1); + children.push_back(form2); + return CreateSimpAndOr(IsAnd, children); + } + + ASTNode BeevMgr::CreateSimpAndOr(bool IsAnd, ASTVec &children) { + + if (_trace_simpbool) { + cout << "========" << endl << "CreateSimpAndOr " << (IsAnd ? "AND " : "OR ") ; + lpvec(children); + cout << endl; + } + + ASTVec new_children; + + // sort so that identical nodes occur in sequential runs, followed by + // their negations. + + SortByExprNum(children); + + ASTNode annihilator = (IsAnd ? ASTFalse : ASTTrue); + ASTNode identity = (IsAnd ? ASTTrue : ASTFalse); + + ASTNode retval; + + ASTVec::const_iterator it_end = children.end(); + ASTVec::const_iterator next_it; + for(ASTVec::const_iterator it = children.begin(); it != it_end; it = next_it) { + next_it = it + 1; + bool nextexists = (next_it < it_end); + + if (*it == annihilator) { + retval = annihilator; + if (_trace_simpbool) { + cout << "returns " << retval << endl; + } + return retval; + } + else if (*it == identity) { + // just drop it + } + else if (nextexists && (*next_it == *it)) { + // drop it + // cout << "Dropping [" << it->GetNodeNum() << "]" << endl; + } + else if (nextexists && (next_it->GetKind() == NOT) && ((*next_it)[0] == *it)) { + // form and negation -- return FALSE for AND, TRUE for OR. + retval = annihilator; + // cout << "X and/or NOT X" << endl; + if (_trace_simpbool) { + cout << "returns " << retval << endl; + } + return retval; + } + else { + // add to children + new_children.push_back(*it); + } + } + + // If we get here, we saw no annihilators, and children should + // be only the non-True nodes. + if (new_children.size() < 2) { + if (0 == new_children.size()) { + retval = identity; + } + else { + // there is just one child + retval = new_children[0]; + } + } + else { + // 2 or more children. Create a new node. + retval = CreateNode(IsAnd ? AND : OR, new_children); + } + if (_trace_simpbool) { + cout << "returns " << retval << endl; + } + return retval; + } + + + // Constant children are accumulated in "accumconst". + ASTNode BeevMgr::CreateSimpXor(ASTVec &children) { + + if (_trace_simpbool) { + cout << "========" << endl + << "CreateSimpXor "; + lpvec(children); + cout << endl; + } + + // Change this not to init to children if flattening code is present. + // ASTVec flat_children = children; // empty vector + + ASTVec flat_children; // empty vector + + ASTVec::const_iterator it_end = children.end(); + + if (xor_flatten) { + + bool fflag = 0; // ***Temp debugging + + // Experimental flattening code. + + for(ASTVec::iterator it = children.begin(); it != it_end; it++) { + Kind ck = it->GetKind(); + const ASTVec &gchildren = it->GetChildren(); + if (XOR == ck) { + fflag = 1; + // append grandchildren to children + flat_children.insert(flat_children.end(), gchildren.begin(), gchildren.end()); + } + else { + flat_children.push_back(*it); + } + } + + if (_trace_simpbool && fflag) { + cout << "========" << endl; + cout << "Flattening: " << endl; + lpvec(children); + + cout << "--------" << endl; + cout << "Flattening result: " << endl; + lpvec(flat_children); + } + } + else { + flat_children = children; + } + + + // sort so that identical nodes occur in sequential runs, followed by + // their negations. + SortByExprNum(flat_children); + + ASTNode retval; + + // This is the C Boolean value of all constant args seen. It is initially + // 0. TRUE children cause it to change value. + bool accumconst = 0; + + ASTVec new_children; + + it_end = flat_children.end(); + ASTVec::iterator next_it; + for(ASTVec::iterator it = flat_children.begin(); it != it_end; it++) { + next_it = it + 1; + bool nextexists = (next_it < it_end); + + if (ASTTrue == *it) { + accumconst = !accumconst; + } + else if (ASTFalse == *it) { + // Ignore it + } + else if (nextexists && (*next_it == *it)) { + // x XOR x = FALSE. Skip current, write "false" into next_it + // so that it gets tossed, too. + *next_it = ASTFalse; + } + else if (nextexists && (next_it->GetKind() == NOT) && ((*next_it)[0] == *it)) { + // x XOR NOT x = TRUE. Skip current, write "true" into next_it + // so that it gets tossed, too. + *next_it = ASTTrue; + } + else if (NOT == it->GetKind()) { + // If child is (NOT alpha), we can flip accumconst and use alpha. + // This is ok because (NOT alpha) == TRUE XOR alpha + accumconst = !accumconst; + // CreateSimpNot just takes child of not. + new_children.push_back(CreateSimpNot(*it)); + } + else { + new_children.push_back(*it); + } + } + + // Children should be non-constant. + if (new_children.size() < 2) { + if (0 == new_children.size()) { + // XOR(TRUE, FALSE) -- accumconst will be 1. + if (accumconst) { + retval = ASTTrue; + } + else { + retval = ASTFalse; + } + } + else { + // there is just one child + // XOR(x, TRUE) -- accumconst will be 1. + if (accumconst) { + retval = CreateSimpNot(new_children[0]); + } + else { + retval = new_children[0]; + } + } + } + else { + // negate first child if accumconst == 1 + if (accumconst) { + new_children[0] = CreateSimpNot(new_children[0]); + } + retval = CreateNode(XOR, new_children); + } + + if (_trace_simpbool) { + cout << "returns " << retval << endl; + } + return retval; + } + + // FIXME: How do I know whether ITE is a formula or not? + ASTNode BeevMgr::CreateSimpFormITE(const ASTNode& child0, + const ASTNode& child1, + const ASTNode& child2) { + + ASTNode retval; + + if (_trace_simpbool) { + cout << "========" << endl << "CreateSimpFormITE " + << child0 + << child1 + << child2 << endl; + } + + if (ASTTrue == child0) { + retval = child1; + } + else if (ASTFalse == child0) { + retval = child2; + } + else if (child1 == child2) { + retval = child1; + } + // ITE(x, TRUE, y ) == x OR y + else if (ASTTrue == child1) { + retval = CreateSimpAndOr(0, child0, child2); + } + // ITE(x, FALSE, y ) == (!x AND y) + else if (ASTFalse == child1) { + retval = CreateSimpAndOr(1, CreateSimpNot(child0), child2); + } + // ITE(x, y, TRUE ) == (!x OR y) + else if (ASTTrue == child2) { + retval = CreateSimpAndOr(0, CreateSimpNot(child0), child1); + } + // ITE(x, y, FALSE ) == (x AND y) + else if (ASTFalse == child2) { + retval = CreateSimpAndOr(1, child0, child1); + } + // ITE (x, !y, y) == x XOR y +// else if (NOT == child1.GetKind() && (child1[0] == child2)) { +// retval = CreateSimpXor(child0, child2); +// } +// // ITE (x, y, !y) == x IFF y. I think other cases are covered +// // by XOR/IFF optimizations +// else if (NOT == child2.GetKind() && (child2[0] == child1)) { +// retval = CreateSimpXor(CreateSimpNot(child0), child2); +// } + else { + retval = CreateNode(ITE, child0, child1, child2); + } + + if (_trace_simpbool) { + cout << "returns " << retval << endl; + } + + return retval; + } +} // BEEV namespace diff --git a/stp/AST/ToCNF.cpp b/stp/AST/ToCNF.cpp new file mode 100644 index 00000000..2a18b3f5 --- /dev/null +++ b/stp/AST/ToCNF.cpp @@ -0,0 +1,506 @@ +/******************************************************************** + * AUTHORS: David L. Dill, Vijay Ganesh + * + * BEGIN DATE: November, 2005 + * + * LICENSE: Please view LICENSE file in the home dir of this Program + ********************************************************************/ +// -*- c++ -*- + +// THEORY: This code translates an arbitrary Boolean DAG, generated by +// the BitBlast.cpp, to an equi-satisfiable CNF formula. There are +// four kinds of variables in the CNF formula: (1) propositional +// variables from the original formula; (2) BVGETBIT formulas from the +// original formula (a precondition is that the BVGETBIT can only be +// applied to bitvector-valued variables, array reads, or +// uninterpreted functions); (3) TRUE; or (4) representative variables +// (see below). Each literal in the CNF formula is one of these or +// its negation. + +// It is convenient (though not perfectly efficient) to be able to add +// TRUE and FALSE constants to clauses, which is not allowed in CNF. +// So, there is a "dummy variable" representing TRUE, which is used in +// its place (so FALSE is represented by the negation of the dummy +// var). The CNF formula has a unit clause containing this dummy +// variable, so that any satisfying assignment must make the dummy var +// TRUE. + +// Every sub-formula of the input formula has a "representative +// literal." A truth assignment satisfies the CNF formula iff the +// restriction of that assignment to the original variables satisfies +// the original formula, AND the rep lits have the same truth values +// as the subformulas they represent in the original formula. In the +// trivial cases, the representative literal is the input variable, or +// dummy true var, or its negation. Representative literals may be +// negated variables -- essentially, only AND formulas are translated, +// and everything else is handled by rewriting or negating formulas. +// The representative for (NOT alpha) is the negation of the +// representative for alpha. + +// The translation is performed by ToCNF_int, which traverses the original +// formula. ToCNF adds clauses that constrain the representative variables +// to be equal to the truth values of the formulas they represent. +// ToCNF always returns a literal whose value must be equivalent to the formula +// it translated. In trivial cases, this literal is a literal from the original +// formula, or the dummy true/false literals. If the formula is of the form +// (not alpha), ToCNF_int negates the literal representing alpha (which may +// itself be a negative literal) and returns it. Otherwise, ToCNF_int assigns +// a new rep var, adds the clauses, and returns the new var. ToCNF_int is +// memoized so that it doesn't assign more than one variable to a subformula, +// and to prevent exponential numbers of redundant visits to shared subformulas. + +// In reality, only AND/OR/NOT formulas are translated directly. Everything +// else (XOR, IFF, IMPLIES) is rewritten on-the-fly into these forms. I +// could have done that in bit-blasting, but I thought that, in the future, +// we might be able to translate these operations differently in different +// contexts to optimize the CNF formula. + +// FIXME: Inspection of the clauses is kind of horrifying. In +// addition to true/false, there are duplicate literals and duplicate +// clauses all over the place. +#include "AST.h" +static bool CNF_trace = false; +namespace BEEV { +/** Statistics class. Total number of variables is best tracked in + ToSAT. Number of clauses is just cll.size() */ + +class CNFstats { +public: + int _num_new_rep_vars; + int _num_clauses; + + // constructor + CNFstats() : _num_new_rep_vars(0), _num_clauses(0) {} + + void printStats() { + if(stats) { + cout << "ToCNF statistics:" << endl; + cout << "Number of new representative variables: " + << _num_new_rep_vars << endl; + cout << "Number of new clauses: " + << _num_clauses << endl; + } + } + +}; + + +/** This class contains private data and function members for the + CNF conversion */ +class CNFMgr { + + friend class BeevMgr; + +public: + + // Needed in the body of BeevMgr::ToCNF. It's not visible outside + // this file, though. + ASTNode dummy_true_var; + + // CNF Pre-pass + ASTNodeMap ToCNFPrePassMemo; + + // CNF Memo Table. + ASTNodeMap CNFMemo; + + +private: + + // Pointer back to BeevMgr with the node tables, etc. + BeevMgr *bm; + + // For ToCNF conversion. This holds a dummy variable representing + // "True". It is added as a unit clause, so that it will be assigned + // to true and propagated immediately by any CNF solver. + + ASTNode dummy_false_var; // not of dummy_true_var + + CNFstats stats; + + // constructor + CNFMgr(BeevMgr *bmgr) + { + bm = bmgr; + + // Dummy variable so TRUE can be a literal. + dummy_true_var = bm->CreateSymbol("*TrueDummy*"); + dummy_false_var = bm->CreateSimpNot(dummy_true_var); + } + + // Returns true iff result has been memoized. + // if it returns true, result is returned in by-ref parameter "result" + // Consider just putitng this in-line. + bool CNFIsMemoized(ASTNode &form, ASTNode &result) + { + ASTNodeMap::iterator it = CNFMemo.find(form); + if (it != CNFMemo.end()) { + result = it->second; //already there. Just return it. + return true; + } + else { + return false; + } + } + + + // Convert a big XOR to a bunch of AND/ORs. Assumes subformulas have + // already been converted. + ASTNode convertXORs(ASTVec children) + { + ASTNode accum = children[0]; + ASTVec::iterator itend = children.end(); + for (ASTVec::iterator it = children.begin()+1; it < itend; it++) { + // a XOR b -> (a | b) & (!a | !b) + + // For each XOR node with k children, creates approximately + // 5*(k-1) nodes. AND + 2 OR + 2 NOT. + + ASTNode or1 = bm->CreateNode(OR, accum, *it); + ASTNode or2 = bm->CreateNode(OR, bm->CreateSimpNot(accum), bm->CreateSimpNot(*it)); + accum = bm->CreateNode(AND, or1, or2); + + } + + return accum; + } + + + // Do preliminary transformations on bitblasted formula to make + // CNF conversion easier. + // Converts XORs to AND/OR form. + ASTNode ToCNFPrePass(const ASTNode &form) + { + + // Check memo table + ASTNodeMap::iterator mem_it = ToCNFPrePassMemo.find(form); + if (mem_it != ToCNFPrePassMemo.end()) { + return mem_it->second; + } + + ASTNode result; + + ASTVec new_children; + ASTVec::const_iterator endit = form.end(); + for (ASTVec::const_iterator it = form.begin(); it != endit; it++) { + ASTNode ch = ToCNFPrePass(*it); + new_children.push_back(ch); + } + + Kind k = form.GetKind(); + + switch (k) { + case FALSE: + case TRUE: + case SYMBOL: + case BVGETBIT: { + result = form; + break; + } + case XOR: { + // convertXORs can only be called once per XOR node. + result = convertXORs(new_children); + + // cout << "convertXORs num args: " << new_children.size() << endl; + // temporary node for node count. + // ASTNode tmp = bm->CreateNode(XOR, new_children ); + // cout << "convertXORs size of [" << form.GetNodeNum() << "] " << bm->NodeSize(form, true) << endl; + // cout << "convertXORs before size: " << bm->NodeSize(tmp, true) << endl; + // cout << "convertXORs after size: " << bm->NodeSize(result, true) << endl; + break; + } + default: { + // Be cautious about using CreateSimpForm -- It makes big xors! + result = bm->CreateNode(k, new_children); + } + } + +// cout << "================" << endl +// << "ToCNFPrePass:" << form << endl +// << "----------------" << endl +// << "ToCNFPrePass Result:" << result << endl; + + return (ToCNFPrePassMemo[form] = result); + + } + + // Memoize and return formula value + ASTNode CNFMemoize(ASTNode& form, ASTNode result) + { + CNFMemo[form] = result; + return result; + } + + + // Create a representative variable for an original formula. + // The convention is that the variable will have the same truth + // value as the expression numbered "num." + ASTNode RepLit(const char *name, int exprnum) + { + // FIXME: can this be done more efficiently with string type? + ostringstream oss; + oss << name << "{" << exprnum << "}"; + ASTNode t = bm->CreateSymbol(oss.str().c_str()); + + // Track how many we're generating. + stats._num_new_rep_vars++; + + //ASTNode is of type BOOLEAN <==> ((indexwidth=0)&&(valuewidth=0)) + t.SetIndexWidth(0); + t.SetValueWidth(0); + return t; + } + + // Handle the cases where it's necessary to do n children. + // This code translates ANDs, and converts NAND, NOR, OR by negating + // the inputs or outputs of the AND. + ASTNode ToCNF_AndLike(Kind k, BeevMgr::ClauseList& cll, ASTNode form) + { + // Build vectors of positive and negative rep lits for children + ASTVec kidlits(0); + ASTVec negkidlits(0); + ASTVec::const_iterator kids_end = form.end(); + for (ASTVec::const_iterator it = form.begin(); it != kids_end; it++) { + ASTNode kidreplit = ToCNF_int(cll, *it); + kidlits.push_back(kidreplit); + negkidlits.push_back(bm->CreateSimpNot(kidreplit)); + } + + ASTNode replit; + // translate the AND, negating inputs as appropriate. + if (k == OR || k == NOR) { + replit = ToCNF_AND(cll, form.GetNodeNum(), negkidlits, kidlits); + } + else { + replit = ToCNF_AND(cll, form.GetNodeNum(), kidlits, negkidlits); + } + + // Reduce NAND/OR to AND by negating result. + if (k == NAND || k == OR) { + return CNFMemoize(form, bm->CreateSimpNot(replit)); + } + else { + return CNFMemoize(form, replit); + } + } + + ASTNode ToCNF_AND(BeevMgr::ClauseList& cll, int nodenum, ASTVec& kidlits, ASTVec& negkidlits) + { + // Translate an AND, given rep lits for children + // Build clauses for (replit <-> a AND b AND c) + + ASTNode replit = RepLit("cnf", nodenum); + ASTNode notreplit = bm->CreateSimpNot(replit); + + if (CNF_trace) { + cout << "Translating AND" << endl << "-----------------------" << endl + << "Rep lit =" << replit << endl + << "-----------------------"; + } + + // (a AND b AND c -> replit) == (~a OR ~b OR ~c OR replit) + BeevMgr::ClausePtr clp = new ASTVec(negkidlits); + clp->push_back(replit); + + if (CNF_trace) { + LispPrintVec(cout, *clp, 0); + cout << endl << "-----------------------" << endl; + } + + cll.push_back(clp); + + // (replit -> (a AND b AND c)) == + // (~replit OR a) AND (~replit OR b) AND (~replit OR c) + ASTVec::const_iterator kidlits_end = kidlits.end(); + for (ASTVec::iterator it = kidlits.begin(); it != kidlits_end; it++) { + clp = new ASTVec(); + clp->push_back(notreplit); + clp->push_back(*it); + + if (CNF_trace) { + LispPrintVec(cout, *clp, 0); + cout << endl << "-----------------------" << endl; + } + + cll.push_back(clp); + } + + return replit; + } + +public: + + /** Builds clauses globally and returns a literal. + The literal can be a leaf from the expression, or a rep var + made up to represent the subexpression. */ + ASTNode ToCNF_int(BeevMgr::BeevMgr::ClauseList& cll, ASTNode form) { + // FIXME: assert indexwidth= 0, valuewidth = 1 + + // FIXME: rewriting is top-down, which is not efficient. + // It rewrites the top node of the tree, then does the children. + // Either rewrite in a separate pass, or translate children + // before rewriting somehow (might require handling rep lits + // as though they were real lits, which is probably ok). + + // Return memoized value if seen before. + ASTNode result; + Kind k = form.GetKind(); + + if (CNFIsMemoized(form, result)) { + return result; + } + + switch (k) { + // handle the trivial cases here. If none apply, call the + // heavy-duty function. If a constant or literal, just return + // without creating a clause. + case FALSE: { + result = dummy_false_var; + break; + } + case TRUE: { + result = dummy_true_var; + break; + } + case SYMBOL: + case BVGETBIT: { + result = form; + break; + } + + case NOT: { + ASTNode replit = ToCNF_int(cll, form[0]); + result = bm->CreateSimpNot(replit); + break; + } + + // For these, I can't think of anything better than expanding into ANDs/ORs + case ITE: { + // (ite a b c) == (~a OR b) AND (a OR c) + ASTNode l = bm->CreateNode(OR, bm->CreateSimpNot(form[0]), form[1]); + ASTNode r = bm->CreateNode(OR, form[0], form[2]); + ASTNode andor = bm->CreateNode(AND, l, r); + if (CNF_trace) { + cout << "Rewriting " << form << endl + << "to" << andor << endl + << "-------------------" << endl; + } + result = ToCNF_int(cll, andor); + break; + } + case IMPLIES: { + // Just rewrite into (~a OR b) + ASTNode l = bm->CreateSimpNot(form[0]); + ASTNode andor = bm->CreateNode(OR, l, form[1]); + if (CNF_trace) { + cout << "Rewriting " << form << endl + << "to" << andor << endl + << "-------------------" << endl; + } + result = ToCNF_int(cll, andor); + break; + } + case XOR: { + FatalError("ToCNF_int: XORs should have been converted to AND/OR by this point."); + break; + } + + case IFF: { + FatalError("BitBlaster failed to eliminate all IFFs."); + break; + } + + case AND: + case OR: + case NOR: + case NAND: { + result = ToCNF_AndLike(k, cll, form); + break; + } + default: + cerr << "ToCNF: can't handle this kind: " << k << endl; + FatalError(""); + } + + if (CNF_trace) { + cout << "ToCNF_int: Literal " << result << " represents formula " << + form << endl << "---------------" << endl; + } + + return CNFMemoize(form, result); + } //end of ToCNF_int() + + +}; // end of CNFMgr class + + // These are the bodies of functions in the BeevMgr that are part of + // the public interface. + + // Debug printing function. + void BeevMgr::PrintClauseList(ostream& os, BeevMgr::ClauseList& cll) + { + int num_clauses = cll.size(); + os << "Clauses: " << endl << "=========================================" << endl; + for(int i=0; i < num_clauses; i++) { + os << "Clause " << i << endl + << "-------------------------------------------" << endl; + LispPrintVec(os, *cll[i], 0); + os << endl + << "-------------------------------------------" << endl; + } + } + + void BeevMgr::DeleteClauseList(BeevMgr::ClauseList *cllp) + { + BeevMgr::ClauseList::const_iterator iend = cllp->end(); + for (BeevMgr::ClauseList::const_iterator i = cllp->begin(); i < iend; i++) { + delete *i; + } + delete cllp; + } + + // Top level conversion function + BeevMgr::ClauseList *BeevMgr::ToCNF(const ASTNode& form) + { + + // FIXME: This is leaked as well. + CNFMgr *cm = new CNFMgr(this); + + // Prepass + ASTNode form1 = cm->ToCNFPrePass(form); + + // cout << "Number of nodes after ToCNFPrePass" << NodeSize(form1, true) << endl; + + // cout << "ToCNF: After ToCNFPrePass" << form1 << endl; + + // FIXME: Assert CNFMemo is empty. + + // The clause list we will be building up. + // FIXME: This is never freed. + ClauseList *cllp = new ClauseList(); + + BeevMgr::ClausePtr dummy_true_unit_clause = new ASTVec(); + dummy_true_unit_clause->push_back(cm->dummy_true_var); + cllp->push_back(dummy_true_unit_clause); + + // This is where the translation happens. + ASTNode toplit = cm->ToCNF_int(*cllp, form1); + + // Add the top literal as a unit clause, since it must + // be true when original formula is satsfied. + BeevMgr::ClausePtr clp = new ASTVec(0); + clp->push_back(toplit); + cllp->push_back(clp); + + cm->stats._num_clauses = cllp->size(); + cm->stats.printStats(); + + RepLitMap = cm->CNFMemo; // Save memo table for debugging (DD 1/13/07). + + cm->CNFMemo.clear(); // Important to do this so nodes get freed. + + delete cm; + + return cllp; + } + +} // end namespace diff --git a/stp/AST/ToSAT.cpp b/stp/AST/ToSAT.cpp new file mode 100644 index 00000000..7a164c9c --- /dev/null +++ b/stp/AST/ToSAT.cpp @@ -0,0 +1,1385 @@ +/******************************************************************** + * AUTHORS: Vijay Ganesh, David L. Dill + * + * BEGIN DATE: November, 2005 + * + * LICENSE: Please view LICENSE file in the home dir of this Program + ********************************************************************/ +// -*- c++ -*- +#include "AST.h" +#include "ASTUtil.h" +#include "../simplifier/bvsolver.h" +#include <math.h> + + +namespace BEEV { + /* FUNCTION: lookup or create a new MINISAT literal + * lookup or create new MINISAT Vars from the global MAP + * _ASTNode_to_SATVar. + */ + const MINISAT::Var BeevMgr::LookupOrCreateSATVar(MINISAT::Solver& newS, const ASTNode& n) { + ASTtoSATMap::iterator it; + MINISAT::Var v; + + //look for the symbol in the global map from ASTNodes to ints. if + //not found, create a S.newVar(), else use the existing one. + if((it = _ASTNode_to_SATVar.find(n)) == _ASTNode_to_SATVar.end()) { + v = newS.newVar(); + _ASTNode_to_SATVar[n] = v; + + //ASSUMPTION: I am assuming that the newS.newVar() call increments v + //by 1 each time it is called, and the initial value of a + //MINISAT::Var is 0. + _SATVar_to_AST.push_back(n); + } + else + v = it->second; + return v; + } + + /* FUNCTION: convert ASTClauses to MINISAT clauses and solve. + * Accepts ASTClauses and converts them to MINISAT clauses. Then adds + * the newly minted MINISAT clauses to the local SAT instance, and + * calls solve(). If solve returns unsat, then stop and return + * unsat. else continue. + */ + // FIXME: Still need to deal with TRUE/FALSE in clauses! + bool BeevMgr::toSATandSolve(MINISAT::Solver& newS, BeevMgr::ClauseList& cll) + { + CountersAndStats("SAT Solver"); + + //iterate through the list (conjunction) of ASTclauses cll + BeevMgr::ClauseList::const_iterator i = cll.begin(), iend = cll.end(); + + if(i == iend) + FatalError("toSATandSolve: Nothing to Solve",ASTUndefined); + + //turnOffSubsumption + newS.turnOffSubsumption(); + + // (*i) is an ASTVec-ptr which denotes an ASTclause + for(; i!=iend; i++) { + //Clause for the SATSolver + MINISAT::vec<MINISAT::Lit> satSolverClause; + + //now iterate through the internals of the ASTclause itself + ASTVec::const_iterator j = (*i)->begin(), jend = (*i)->end(); + //j is a disjunct in the ASTclause (*i) + for(;j!=jend;j++) { + + bool negate = (NOT == j->GetKind()) ? true : false; + ASTNode n = negate ? (*j)[0] : *j; + + //Lookup or create the MINISAT::Var corresponding to the Booelan + //ASTNode Variable, and push into sat Solver clause + MINISAT::Var v = LookupOrCreateSATVar(newS,n); + MINISAT::Lit l(v, negate); + satSolverClause.push(l); + } + newS.addClause(satSolverClause); + // clause printing. + // (printClause<MINISAT::vec<MINISAT::Lit> >)(satSolverClause); + // cout << " 0 "; + // cout << endl; + + if(newS.okay()) { + continue; + } + else { + PrintStats(newS.stats); + return false; + } + + if(!newS.simplifyDB(false)) { + PrintStats(newS.stats); + return false; + } + } + + // if input is UNSAT return false, else return true + if(!newS.simplifyDB(false)) { + PrintStats(newS.stats); + return false; + } + + //PrintActivityLevels_Of_SATVars("Before SAT:",newS); + //ChangeActivityLevels_Of_SATVars(newS); + //PrintActivityLevels_Of_SATVars("Before SAT and after initial bias:",newS); + newS.solve(); + //PrintActivityLevels_Of_SATVars("After SAT",newS); + + PrintStats(newS.stats); + if (newS.okay()) + return true; + else + return false; + } + + // GLOBAL FUNCTION: Prints statistics from the MINISAT Solver + void BeevMgr::PrintStats(MINISAT::SolverStats& s) { + if(!stats) + return; + double cpu_time = MINISAT::cpuTime(); + MINISAT::int64 mem_used = MINISAT::memUsed(); + reportf("restarts : %"I64_fmt"\n", s.starts); + reportf("conflicts : %-12"I64_fmt" (%.0f /sec)\n", s.conflicts , s.conflicts /cpu_time); + reportf("decisions : %-12"I64_fmt" (%.0f /sec)\n", s.decisions , s.decisions /cpu_time); + reportf("propagations : %-12"I64_fmt" (%.0f /sec)\n", s.propagations, s.propagations/cpu_time); + reportf("conflict literals : %-12"I64_fmt" (%4.2f %% deleted)\n", + s.tot_literals, + (s.max_literals - s.tot_literals)*100 / (double)s.max_literals); + if (mem_used != 0) reportf("Memory used : %.2f MB\n", mem_used / 1048576.0); + reportf("CPU time : %g s\n", cpu_time); + } + + // Prints Satisfying assignment directly, for debugging. + void BeevMgr::PrintSATModel(MINISAT::Solver& newS) { + if(!newS.okay()) + FatalError("PrintSATModel: NO COUNTEREXAMPLE TO PRINT",ASTUndefined); + // FIXME: Don't put tests like this in the print functions. The print functions + // should print unconditionally. Put a conditional around the call if you don't + // want them to print + if(!(stats && print_nodes)) + return; + + int num_vars = newS.nVars(); + cout << "Satisfying assignment: " << endl; + for (int i = 0; i < num_vars; i++) { + if (newS.model[i] == MINISAT::l_True) { + ASTNode s = _SATVar_to_AST[i]; + cout << s << endl; + } + else if (newS.model[i] == MINISAT::l_False) { + ASTNode s = _SATVar_to_AST[i]; + cout << CreateNode(NOT, s) << endl; + } + } + } + + + // Looks up truth value of ASTNode SYMBOL in MINISAT satisfying assignment. + // Returns ASTTrue if true, ASTFalse if false or undefined. + ASTNode BeevMgr::SymbolTruthValue(MINISAT::Solver &newS, ASTNode form) + { + MINISAT::Var satvar = _ASTNode_to_SATVar[form]; + if (newS.model[satvar] == MINISAT::l_True) { + return ASTTrue; + } + else if (newS.model[satvar] == MINISAT::l_False){ + // False + return ASTFalse; + } + else { + return (rand() > 4096) ? ASTTrue : ASTFalse; + } + } + + + // This function is for debugging problems with BitBlast and especially + // ToCNF. It evaluates the bit-blasted formula in the satisfying + // assignment. While doing that, it checks that every subformula has + // the same truth value as its representative literal, if it has one. + // If this condition is violated, it halts immediately (on the leftmost + // lowest term). + // Use CreateSimpForm to evaluate, even though it's expensive, so that + // we can use the partial truth assignment. + ASTNode BeevMgr::CheckBBandCNF(MINISAT::Solver& newS, ASTNode form) + { + // Clear memo table (in case newS has changed). + CheckBBandCNFMemo.clear(); + // Call recursive version that does the work. + return CheckBBandCNF_int(newS, form); + } + + // Recursive body CheckBBandCNF + // FIXME: Modify this to just check if result is true, and print mismatch + // if not. Might have a trace flag for the other stuff. + ASTNode BeevMgr::CheckBBandCNF_int(MINISAT::Solver& newS, ASTNode form) + { + + // cout << "++++++++++++++++" << endl << "CheckBBandCNF_int form = " << + // form << endl; + + ASTNodeMap::iterator memoit = CheckBBandCNFMemo.find(form); + if (memoit != CheckBBandCNFMemo.end()) { + // found it. Return memoized value. + return memoit->second; + } + + ASTNode result; // return value, to memoize. + + Kind k = form.GetKind(); + switch (k) { + case TRUE: + case FALSE: { + return form; + break; + } + case SYMBOL: + case BVGETBIT: { + // Look up the truth value + // ASTNode -> Sat -> Truthvalue -> ASTTrue or ASTFalse; + // FIXME: Could make up a fresh var in undefined case. + + result = SymbolTruthValue(newS, form); + + cout << "================" << endl << "Checking BB formula:" << form << endl; + cout << "----------------" << endl << "Result:" << result << endl; + + break; + } + default: { + // Evaluate the children recursively. + ASTVec eval_children; + ASTVec ch = form.GetChildren(); + ASTVec::iterator itend = ch.end(); + for(ASTVec::iterator it = ch.begin(); it < itend; it++) { + eval_children.push_back(CheckBBandCNF_int(newS, *it)); + } + result = CreateSimpForm(k, eval_children); + + cout << "================" << endl << "Checking BB formula:" << form << endl; + cout << "----------------" << endl << "Result:" << result << endl; + + ASTNode replit_eval; + // Compare with replit, if there is one. + ASTNodeMap::iterator replit_it = RepLitMap.find(form); + if (replit_it != RepLitMap.end()) { + ASTNode replit = RepLitMap[form]; + // Replit is symbol or not symbol. + if (SYMBOL == replit.GetKind()) { + replit_eval = SymbolTruthValue(newS, replit); + } + else { + // It's (NOT sym). Get value of sym and complement. + replit_eval = CreateSimpNot(SymbolTruthValue(newS, replit[0])); + } + + cout << "----------------" << endl << "Rep lit: " << replit << endl; + cout << "----------------" << endl << "Rep lit value: " << replit_eval << endl; + + if (result != replit_eval) { + // Hit the panic button. + FatalError("Truth value of BitBlasted formula disagrees with representative literal in CNF."); + } + } + else { + cout << "----------------" << endl << "No rep lit" << endl; + } + + } + } + + return (CheckBBandCNFMemo[form] = result); + } + + /*FUNCTION: constructs counterexample from MINISAT counterexample + * step1 : iterate through MINISAT counterexample and assemble the + * bits for each AST term. Store it in a map from ASTNode to vector + * of bools (bits). + * + * step2: Iterate over the map from ASTNodes->Vector-of-Bools and + * populate the CounterExampleMap data structure (ASTNode -> BVConst) + */ + void BeevMgr::ConstructCounterExample(MINISAT::Solver& newS) { + //iterate over MINISAT counterexample and construct a map from AST + //terms to vector of bools. We need this iteration step because + //MINISAT might return the various bits of a term out of + //order. Therfore, we need to collect all the bits and assemble + //them properly + + if(!newS.okay()) + return; + if(!construct_counterexample) + return; + + CopySolverMap_To_CounterExample(); + for (int i = 0; i < newS.nVars(); i++) { + //Make sure that the MINISAT::Var is defined + if (newS.model[i] != MINISAT::l_Undef) { + + //mapping from MINISAT::Vars to ASTNodes. We do not need to + //print MINISAT vars or CNF vars. + ASTNode s = _SATVar_to_AST[i]; + + //assemble the counterexample here + if(s.GetKind() == BVGETBIT && s[0].GetKind() == SYMBOL) { + ASTNode symbol = s[0]; + unsigned int symbolWidth = symbol.GetValueWidth(); + + //'v' is the map from bit-index to bit-value + hash_map<unsigned,bool> * v; + if(_ASTNode_to_Bitvector.find(symbol) == _ASTNode_to_Bitvector.end()) + _ASTNode_to_Bitvector[symbol] = new hash_map<unsigned,bool>(symbolWidth); + + //v holds the map from bit-index to bit-value + v = _ASTNode_to_Bitvector[symbol]; + + //kk is the index of BVGETBIT + unsigned int kk = GetUnsignedConst(s[1]); + + //Collect the bits of 'symbol' and store in v. Store in reverse order. + if(newS.model[i]==MINISAT::l_True) + (*v)[(symbolWidth-1) - kk] = true; + else + (*v)[(symbolWidth-1) - kk] = false; + } + else { + if(s.GetKind() == SYMBOL && s.GetType() == BOOLEAN_TYPE) { + const char * zz = s.GetName(); + //if the variables are not cnf variables then add them to the counterexample + if(0 != strncmp("cnf",zz,3) && 0 != strcmp("*TrueDummy*",zz)) { + if(newS.model[i]==MINISAT::l_True) + CounterExampleMap[s] = ASTTrue; + else + CounterExampleMap[s] = ASTFalse; + } + } + } + } + } + + //iterate over the ASTNode_to_Bitvector data-struct and construct + //the the aggregate value of the bitvector, and populate the + //CounterExampleMap datastructure + for(ASTtoBitvectorMap::iterator it=_ASTNode_to_Bitvector.begin(),itend=_ASTNode_to_Bitvector.end(); + it!=itend;it++) { + ASTNode var = it->first; + //debugging + //cerr << var; + if(SYMBOL != var.GetKind()) + FatalError("ConstructCounterExample: error while constructing counterexample: not a variable: ",var); + + //construct the bitvector value + hash_map<unsigned,bool> * w = it->second; + ASTNode value = BoolVectoBVConst(w, var.GetValueWidth()); + //debugging + //cerr << value; + + //populate the counterexample datastructure. add only scalars + //variables which were declared in the input and newly + //introduced variables for array reads + CounterExampleMap[var] = value; + } + + //In this loop, we compute the value of each array read, the + //corresponding ITE against the counterexample generated above. + for(ASTNodeMap::iterator it=_arrayread_ite.begin(),itend=_arrayread_ite.end(); + it!=itend;it++){ + //the array read + ASTNode arrayread = it->first; + ASTNode value_ite = _arrayread_ite[arrayread]; + + //convert it to a constant array-read and store it in the + //counter-example. First convert the index into a constant. then + //construct the appropriate array-read and store it in the + //counterexample + ASTNode arrayread_index = TermToConstTermUsingModel(arrayread[1]); + ASTNode key = CreateTerm(READ,arrayread.GetValueWidth(),arrayread[0],arrayread_index); + + //Get the ITE corresponding to the array-read and convert it + //to a constant against the model + ASTNode value = TermToConstTermUsingModel(value_ite); + //save the result in the counter_example + if(!CheckSubstitutionMap(key)) + CounterExampleMap[key] = value; + } + } //End of ConstructCounterExample + + // FUNCTION: accepts a non-constant term, and returns the + // corresponding constant term with respect to a model. + // + // term READ(A,i) is treated as follows: + // + //1. If (the boolean variable 'ArrayReadFlag' is true && ArrayRead + //1. has value in counterexample), then return the value of the + //1. arrayread. + // + //2. If (the boolean variable 'ArrayReadFlag' is true && ArrayRead + //2. doesn't have value in counterexample), then return the + //2. arrayread itself (normalized such that arrayread has a constant + //2. index) + // + //3. If (the boolean variable 'ArrayReadFlag' is false) && ArrayRead + //3. has a value in the counterexample then return the value of the + //3. arrayread. + // + //4. If (the boolean variable 'ArrayReadFlag' is false) && ArrayRead + //4. doesn't have a value in the counterexample then return 0 as the + //4. value of the arrayread. + ASTNode BeevMgr::TermToConstTermUsingModel(const ASTNode& t, bool ArrayReadFlag) { + Begin_RemoveWrites = false; + SimplifyWrites_InPlace_Flag = false; + //ASTNode term = SimplifyTerm(t); + ASTNode term = t; + Kind k = term.GetKind(); + + + //cerr << "Input to TermToConstTermUsingModel: " << term << endl; + if(!is_Term_kind(k)) { + FatalError("TermToConstTermUsingModel: The input is not a term: ",term); + } + if(k == WRITE) { + FatalError("TermToConstTermUsingModel: The input has wrong kind: WRITE : ",term); + } + if(k == SYMBOL && BOOLEAN_TYPE == term.GetType()) { + FatalError("TermToConstTermUsingModel: The input has wrong kind: Propositional variable : ",term); + } + + ASTNodeMap::iterator it1; + if((it1 = CounterExampleMap.find(term)) != CounterExampleMap.end()) { + ASTNode val = it1->second; + if(BVCONST != val.GetKind()) { + //CounterExampleMap has two maps rolled into + //one. SubstitutionMap and SolverMap. + // + //recursion is fine here. There are two maps that are checked + //here. One is the substitutionmap. We garuntee that the value + //of a key in the substitutionmap is always a constant. + // + //in the SolverMap we garuntee that "term" does not occur in + //the value part of the map + if(term == val) { + FatalError("TermToConstTermUsingModel: The input term is stored as-is " + "in the CounterExample: Not ok: ",term); + } + return TermToConstTermUsingModel(val,ArrayReadFlag); + } + else { + return val; + } + } + + ASTNode output; + switch(k) { + case BVCONST: + output = term; + break; + case SYMBOL: { + if(term.GetType() == ARRAY_TYPE) { + return term; + } + + //when all else fails set symbol values to some constant by + //default. if the variable is queried the second time then add 1 + //to and return the new value. + ASTNode zero = CreateZeroConst(term.GetValueWidth()); + output = zero; + break; + } + case READ: { + ASTNode arrName = term[0]; + ASTNode index = term[1]; + if(0 == arrName.GetIndexWidth()) { + FatalError("TermToConstTermUsingModel: array has 0 index width: ",arrName); + } + + //READ over a WRITE + if(WRITE == arrName.GetKind()) { + ASTNode wrtterm = Expand_ReadOverWrite_UsingModel(term, ArrayReadFlag); + if(wrtterm == term) { + FatalError("TermToConstTermUsingModel: Read_Over_Write term must be expanded into an ITE", term); + } + ASTNode rtterm = TermToConstTermUsingModel(wrtterm,ArrayReadFlag); + return rtterm; + } + //READ over an ITE + if(ITE == arrName.GetKind()) { + arrName = TermToConstTermUsingModel(arrName,ArrayReadFlag); + } + + ASTNode modelentry; + if(CounterExampleMap.find(index) != CounterExampleMap.end()) { + //index has a const value in the CounterExampleMap + ASTNode indexVal = CounterExampleMap[index]; + modelentry = CreateTerm(READ, arrName.GetValueWidth(), arrName, indexVal); + } + else { + //index does not have a const value in the CounterExampleMap. compute it. + ASTNode indexconstval = TermToConstTermUsingModel(index,ArrayReadFlag); + //update model with value of the index + //CounterExampleMap[index] = indexconstval; + modelentry = CreateTerm(READ,arrName.GetValueWidth(), arrName,indexconstval); + } + //modelentry is now an arrayread over a constant index + BVTypeCheck(modelentry); + + //if a value exists in the CounterExampleMap then return it + if(CounterExampleMap.find(modelentry) != CounterExampleMap.end()) { + output = TermToConstTermUsingModel(CounterExampleMap[modelentry],ArrayReadFlag); + } + else if(ArrayReadFlag) { + //return the array read over a constantindex + output = modelentry; + } + else { + //when all else fails set symbol values to some constant by + //default. if the variable is queried the second time then add 1 + //to and return the new value. + ASTNode zero = CreateZeroConst(modelentry.GetValueWidth()); + output = zero; + } + break; + } + case ITE: { + ASTNode condcompute = ComputeFormulaUsingModel(term[0]); + if(ASTTrue == condcompute) { + output = TermToConstTermUsingModel(term[1],ArrayReadFlag); + } + else if(ASTFalse == condcompute) { + output = TermToConstTermUsingModel(term[2],ArrayReadFlag); + } + else { + cerr << "TermToConstTermUsingModel: termITE: value of conditional is wrong: " << condcompute << endl; + FatalError(" TermToConstTermUsingModel: termITE: cannot compute ITE conditional against model: ",term); + } + break; + } + default: { + ASTVec c = term.GetChildren(); + ASTVec o; + for(ASTVec::iterator it=c.begin(),itend=c.end();it!=itend;it++) { + ASTNode ff = TermToConstTermUsingModel(*it,ArrayReadFlag); + o.push_back(ff); + } + output = CreateTerm(k,term.GetValueWidth(),o); + //output is a CONST expression. compute its value and store it + //in the CounterExampleMap + ASTNode oo = BVConstEvaluator(output); + //the return value + output = oo; + break; + } + } + + //when this flag is false, we should compute the arrayread to a + //constant. this constant is stored in the counter_example + //datastructure + if(!ArrayReadFlag) { + CounterExampleMap[term] = output; + } + + //cerr << "Output to TermToConstTermUsingModel: " << output << endl; + return output; + } //End of TermToConstTermUsingModel + + //Expands read-over-write by evaluating (readIndex=writeIndex) for + //every writeindex until, either it evaluates to TRUE or all + //(readIndex=writeIndex) evaluate to FALSE + ASTNode BeevMgr::Expand_ReadOverWrite_UsingModel(const ASTNode& term, bool arrayread_flag) { + if(READ != term.GetKind() && + WRITE != term[0].GetKind()) { + FatalError("RemovesWrites: Input must be a READ over a WRITE",term); + } + + ASTNode output; + ASTNodeMap::iterator it1; + if((it1 = CounterExampleMap.find(term)) != CounterExampleMap.end()) { + ASTNode val = it1->second; + if(BVCONST != val.GetKind()) { + //recursion is fine here. There are two maps that are checked + //here. One is the substitutionmap. We garuntee that the value + //of a key in the substitutionmap is always a constant. + if(term == val) { + FatalError("TermToConstTermUsingModel: The input term is stored as-is " + "in the CounterExample: Not ok: ",term); + } + return TermToConstTermUsingModel(val,arrayread_flag); + } + else { + return val; + } + } + + unsigned int width = term.GetValueWidth(); + ASTNode writeA = ASTTrue; + ASTNode newRead = term; + ASTNode readIndex = TermToConstTermUsingModel(newRead[1],false); + //iteratively expand read-over-write, and evaluate against the + //model at every iteration + do { + ASTNode write = newRead[0]; + writeA = write[0]; + ASTNode writeIndex = TermToConstTermUsingModel(write[1],false); + ASTNode writeVal = TermToConstTermUsingModel(write[2],false); + + ASTNode cond = ComputeFormulaUsingModel(CreateSimplifiedEQ(writeIndex,readIndex)); + if(ASTTrue == cond) { + //found the write-value. return it + output = writeVal; + CounterExampleMap[term] = output; + return output; + } + + newRead = CreateTerm(READ,width,writeA,readIndex); + } while(READ == newRead.GetKind() && WRITE == newRead[0].GetKind()); + + output = TermToConstTermUsingModel(newRead,arrayread_flag); + + //memoize + CounterExampleMap[term] = output; + return output; + } //Exand_ReadOverWrite_To_ITE_UsingModel() + + /* FUNCTION: accepts a non-constant formula, and checks if the + * formula is ASTTrue or ASTFalse w.r.t to a model + */ + ASTNode BeevMgr::ComputeFormulaUsingModel(const ASTNode& form) { + ASTNode in = form; + Kind k = form.GetKind(); + if(!(is_Form_kind(k) && BOOLEAN_TYPE == form.GetType())) { + FatalError(" ComputeConstFormUsingModel: The input is a non-formula: ", form); + } + + //cerr << "Input to ComputeFormulaUsingModel:" << form << endl; + ASTNodeMap::iterator it1; + if((it1 = ComputeFormulaMap.find(form)) != ComputeFormulaMap.end()) { + ASTNode res = it1->second; + if(ASTTrue == res || ASTFalse == res) { + return res; + } + else { + FatalError("ComputeFormulaUsingModel: The value of a formula must be TRUE or FALSE:", form); + } + } + + ASTNode t0,t1; + ASTNode output = ASTFalse; + switch(k) { + case TRUE: + case FALSE: + output = form; + break; + case SYMBOL: + if(BOOLEAN_TYPE != form.GetType()) + FatalError(" ComputeFormulaUsingModel: Non-Boolean variables are not formulas",form); + if(CounterExampleMap.find(form) != CounterExampleMap.end()) { + ASTNode counterexample_val = CounterExampleMap[form]; + if(!VarSeenInTerm(form,counterexample_val)) { + output = ComputeFormulaUsingModel(counterexample_val); + } + else { + output = counterexample_val; + } + } + else + output = ASTFalse; + break; + case EQ: + case NEQ: + case BVLT: + case BVLE: + case BVGT: + case BVGE: + case BVSLT: + case BVSLE: + case BVSGT: + case BVSGE: + //convert form[0] into a constant term + t0 = TermToConstTermUsingModel(form[0],false); + //convert form[0] into a constant term + t1 = TermToConstTermUsingModel(form[1],false); + output = BVConstEvaluator(CreateNode(k,t0,t1)); + + //evaluate formula to false if bvdiv execption occurs while + //counterexample is being checked during refinement. + if(bvdiv_exception_occured && + counterexample_checking_during_refinement) { + output = ASTFalse; + } + break; + case NAND: { + ASTNode o = ASTTrue; + for(ASTVec::const_iterator it=form.begin(),itend=form.end();it!=itend;it++) + if(ASTFalse == ComputeFormulaUsingModel(*it)) { + o = ASTFalse; + break; + } + if(o == ASTTrue) + output = ASTFalse; + else + output = ASTTrue; + break; + } + case NOR: { + ASTNode o = ASTFalse; + for(ASTVec::const_iterator it=form.begin(),itend=form.end();it!=itend;it++) + if(ASTTrue == ComputeFormulaUsingModel(*it)) { + o = ASTTrue; + break; + } + if(o == ASTTrue) + output = ASTFalse; + else + output = ASTTrue; + break; + } + case NOT: + if(ASTTrue == ComputeFormulaUsingModel(form[0])) + output = ASTFalse; + else + output = ASTTrue; + break; + case OR: + for(ASTVec::const_iterator it=form.begin(),itend=form.end();it!=itend;it++) + if(ASTTrue == ComputeFormulaUsingModel(*it)) + output = ASTTrue; + break; + case AND: + output = ASTTrue; + for(ASTVec::const_iterator it=form.begin(),itend=form.end();it!=itend;it++) { + if(ASTFalse == ComputeFormulaUsingModel(*it)) { + output = ASTFalse; + break; + } + } + break; + case XOR: + t0 = ComputeFormulaUsingModel(form[0]); + t1 = ComputeFormulaUsingModel(form[1]); + if((ASTTrue == t0 && ASTTrue == t1) || (ASTFalse == t0 && ASTFalse == t1)) + output = ASTFalse; + else + output = ASTTrue; + break; + case IFF: + t0 = ComputeFormulaUsingModel(form[0]); + t1 = ComputeFormulaUsingModel(form[1]); + if((ASTTrue == t0 && ASTTrue == t1) || (ASTFalse == t0 && ASTFalse == t1)) + output = ASTTrue; + else + output = ASTFalse; + break; + case IMPLIES: + t0 = ComputeFormulaUsingModel(form[0]); + t1 = ComputeFormulaUsingModel(form[1]); + if((ASTFalse == t0) || (ASTTrue == t0 && ASTTrue == t1)) + output = ASTTrue; + else + output = ASTFalse; + break; + case ITE: + t0 = ComputeFormulaUsingModel(form[0]); + if(ASTTrue == t0) + output = ComputeFormulaUsingModel(form[1]); + else if(ASTFalse == t0) + output = ComputeFormulaUsingModel(form[2]); + else + FatalError("ComputeFormulaUsingModel: ITE: something is wrong with the formula: ",form); + break; + default: + FatalError(" ComputeFormulaUsingModel: the kind has not been implemented", ASTUndefined); + break; + } + + //cout << "ComputeFormulaUsingModel output is:" << output << endl; + ComputeFormulaMap[form] = output; + return output; + } + + void BeevMgr::CheckCounterExample(bool t) { + // FIXME: Code is more useful if enable flags are check OUTSIDE the method. + // If I want to check a counterexample somewhere, I don't want to have to set + // the flag in order to make it actualy happen! + + if(!check_counterexample) { + return; + } + + //input is valid, no counterexample to check + if(ValidFlag) + return; + + //t is true if SAT solver generated a counterexample, else it is false + if(!t) + FatalError("CheckCounterExample: No CounterExample to check", ASTUndefined); + const ASTVec c = GetAsserts(); + for(ASTVec::const_iterator it=c.begin(),itend=c.end();it!=itend;it++) + if(ASTFalse == ComputeFormulaUsingModel(*it)) + FatalError("CheckCounterExample:counterexample bogus:"\ + "assert evaluates to FALSE under counterexample: NOT OK",*it); + + if(ASTTrue == ComputeFormulaUsingModel(_current_query)) + FatalError("CheckCounterExample:counterexample bogus:"\ + "query evaluates to TRUE under counterexample: NOT OK",_current_query); + } + + /* FUNCTION: prints a counterexample for INVALID inputs. iterate + * through the CounterExampleMap data structure and print it to + * stdout + */ + void BeevMgr::PrintCounterExample(bool t, std::ostream& os) { + //global command-line option + // FIXME: This should always print the counterexample. If you want + // to turn it off, check the switch at the point of call. + if(!print_counterexample) + return; + + //input is valid, no counterexample to print + if(ValidFlag) + return; + + //if this option is true then print the way dawson wants using a + //different printer. do not use this printer. + if(print_arrayval_declaredorder) + return; + + //t is true if SAT solver generated a counterexample, else it is + //false + if(!t) { + cerr << "PrintCounterExample: No CounterExample to print: " << endl; + return; + } + + //os << "\nCOUNTEREXAMPLE: \n" << endl; + ASTNodeMap::iterator it = CounterExampleMap.begin(); + ASTNodeMap::iterator itend = CounterExampleMap.end(); + for(;it!=itend;it++) { + ASTNode f = it->first; + ASTNode se = it->second; + + if(ARRAY_TYPE == se.GetType()) { + FatalError("TermToConstTermUsingModel: entry in counterexample is an arraytype. bogus:",se); + } + + //skip over introduced variables + if(f.GetKind() == SYMBOL && (_introduced_symbols.find(f) != _introduced_symbols.end())) + continue; + if(f.GetKind() == SYMBOL || + (f.GetKind() == READ && f[0].GetKind() == SYMBOL && f[1].GetKind() == BVCONST)) { + os << "ASSERT( "; + f.PL_Print(os,0); + os << " = "; + if(BITVECTOR_TYPE == se.GetType()) { + TermToConstTermUsingModel(se,false).PL_Print(os,0); + } + else { + se.PL_Print(os,0); + } + os << " );" << endl; + } + } + //os << "\nEND OF COUNTEREXAMPLE" << endl; + } //End of PrintCounterExample + + /* iterate through the CounterExampleMap data structure and print it + * to stdout. this function prints only the declared array variables + * IN the ORDER in which they were declared. It also assumes that + * the variables are of the form 'varname_number'. otherwise it will + * not print anything. This function was specifically written for + * Dawson Engler's group (bug finding research group at Stanford) + */ + void BeevMgr::PrintCounterExample_InOrder(bool t) { + //global command-line option to print counterexample. we do not + //want both counterexample printers to print at the sametime. + // FIXME: This should always print the counterexample. If you want + // to turn it off, check the switch at the point of call. + if(print_counterexample) + return; + + //input is valid, no counterexample to print + if(ValidFlag) + return; + + //print if the commandline option is '-q'. allows printing the + //counterexample in order. + if(!print_arrayval_declaredorder) + return; + + //t is true if SAT solver generated a counterexample, else it is + //false + if(!t) { + cerr << "PrintCounterExample: No CounterExample to print: " << endl; + return; + } + + //vector to store the integer values + std::vector<int> out_int; + cout << "% "; + for(ASTVec::iterator it=_special_print_set.begin(),itend=_special_print_set.end(); + it!=itend;it++) { + if(ARRAY_TYPE == it->GetType()) { + //get the name of the variable + const char * c = it->GetName(); + std::string ss(c); + if(!(0 == strncmp(ss.c_str(),"ini_",4))) + continue; + reverse(ss.begin(),ss.end()); + + //cout << "debugging: " << ss; + size_t pos = ss.find('_',0); + if(!(0 < pos && pos < ss.size())) + continue; + + //get the associated length + std::string sss = ss.substr(0,pos); + reverse(sss.begin(),sss.end()); + int n = atoi(sss.c_str()); + + it->PL_Print(cout,2); + for(int j=0;j < n; j++) { + ASTNode index = CreateBVConst(it->GetIndexWidth(),j); + ASTNode readexpr = CreateTerm(READ,it->GetValueWidth(),*it,index); + ASTNode val = GetCounterExample(t, readexpr); + //cout << "ASSERT( "; + //cout << " = "; + out_int.push_back(GetUnsignedConst(val)); + //cout << "\n"; + } + } + } + cout << endl; + for(unsigned int jj=0; jj < out_int.size();jj++) + cout << out_int[jj] << endl; + cout << endl; + } //End of PrintCounterExample_InOrder + + /* FUNCTION: queries the CounterExampleMap object with 'expr' and + * returns the corresponding counterexample value. + */ + ASTNode BeevMgr::GetCounterExample(bool t, const ASTNode& expr) { + //input is valid, no counterexample to get + if(ValidFlag) + return ASTUndefined; + + if(BOOLEAN_TYPE == expr.GetType()) { + return ComputeFormulaUsingModel(expr); + } + + if(BVCONST == expr.GetKind()) { + return expr; + } + + ASTNodeMap::iterator it; + ASTNode output; + if((it = CounterExampleMap.find(expr)) != CounterExampleMap.end()) + output = TermToConstTermUsingModel(CounterExampleMap[expr],false); + else + output = CreateZeroConst(expr.GetValueWidth()); + return output; + } //End of GetCounterExample + + // FIXME: Don't use numeric codes. Use an enum type! + //Acceps a query, calls the SAT solver and generates Valid/InValid. + //if returned 0 then input is INVALID + //if returned 1 then input is VALID + //if returned 2 then ERROR + int BeevMgr::TopLevelSAT( const ASTNode& inputasserts, const ASTNode& query) { + /******start solving**********/ + ASTNode q = CreateNode(AND, inputasserts, CreateNode(NOT,query)); + ASTNode orig_input = q; + ASTNodeStats("input asserts and query: ", q); + + ASTNode newq = q; + //round of substitution, solving, and simplification. ensures that + //DAG is minimized as much as possibly, and ideally should + //garuntee that all liketerms in BVPLUSes have been combined. + BVSolver bvsolver(this); + SimplifyWrites_InPlace_Flag = false; + Begin_RemoveWrites = false; + start_abstracting = false; + TermsAlreadySeenMap.clear(); + do { + q = newq; + newq = CreateSubstitutionMap(newq); + //ASTNodeStats("after pure substitution: ", newq); + newq = SimplifyFormula_TopLevel(newq,false); + //ASTNodeStats("after simplification: ", newq); + //newq = bvsolver.TopLevelBVSolve(newq); + //ASTNodeStats("after solving: ", newq); + }while(q!=newq); + + ASTNodeStats("Before SimplifyWrites_Inplace begins: ", newq); + SimplifyWrites_InPlace_Flag = true; + Begin_RemoveWrites = false; + start_abstracting = false; + TermsAlreadySeenMap.clear(); + do { + q = newq; + //newq = CreateSubstitutionMap(newq); + //ASTNodeStats("after pure substitution: ", newq); + newq = SimplifyFormula_TopLevel(newq,false); + //ASTNodeStats("after simplification: ", newq); + newq = bvsolver.TopLevelBVSolve(newq); + //ASTNodeStats("after solving: ", newq); + }while(q!=newq); + ASTNodeStats("After SimplifyWrites_Inplace: ", newq); + + start_abstracting = (arraywrite_refinement) ? true : false; + SimplifyWrites_InPlace_Flag = false; + Begin_RemoveWrites = (start_abstracting) ? false : true; + if(start_abstracting) { + ASTNodeStats("before abstraction round begins: ", newq); + } + + TermsAlreadySeenMap.clear(); + do { + q = newq; + //newq = CreateSubstitutionMap(newq); + //Begin_RemoveWrites = true; + //ASTNodeStats("after pure substitution: ", newq); + newq = SimplifyFormula_TopLevel(newq,false); + //ASTNodeStats("after simplification: ", newq); + //newq = bvsolver.TopLevelBVSolve(newq); + //ASTNodeStats("after solving: ", newq); + }while(q!=newq); + + if(start_abstracting) { + ASTNodeStats("After abstraction: ", newq); + } + start_abstracting = false; + SimplifyWrites_InPlace_Flag = false; + Begin_RemoveWrites = false; + + newq = TransformFormula(newq); + ASTNodeStats("after transformation: ", newq); + TermsAlreadySeenMap.clear(); + + int res; + //solver instantiated here + MINISAT::Solver newS; + if(arrayread_refinement) { + counterexample_checking_during_refinement = true; + } + + //call SAT and check the result + res = CallSAT_ResultCheck(newS,newq,orig_input); + if(2 != res) { + CountersAndStats("print_func_stats"); + return res; + } + + res = SATBased_ArrayReadRefinement(newS,newq,orig_input); + if(2 != res) { + CountersAndStats("print_func_stats"); + return res; + } + + res = SATBased_ArrayWriteRefinement(newS,orig_input); + if(2 != res) { + CountersAndStats("print_func_stats"); + return res; + } + + res = SATBased_ArrayReadRefinement(newS,newq,orig_input); + if(2 != res) { + CountersAndStats("print_func_stats"); + return res; + } + + FatalError("TopLevelSAT: reached the end without proper conclusion:" + "either a divide by zero in the input or a bug in STP"); + //bogus return to make the compiler shut up + return 2; + } //End of TopLevelSAT + + //go over the list of indices for each array, and generate Leibnitz + //axioms. Then assert these axioms into the SAT solver. Check if the + //addition of the new constraints has made the bogus counterexample + //go away. if yes, return the correct answer. if no, continue adding + //Leibnitz axioms systematically. + // FIXME: What it really does is, for each array, loop over each index i. + // inside that loop, it finds all the true and false axioms with i as first + // index. When it's got them all, it adds the false axioms to the formula + // and re-solves, and returns if the result is correct. Otherwise, it + // goes on to the next index. + // If it gets through all the indices without a correct result (which I think + // is impossible, but this is pretty confusing), it then solves with all + // the true axioms, too. + // This is not the most obvious way to do it, and I don't know how it + // compares with other approaches (e.g., one false axiom at a time or + // all the false axioms each time). + int BeevMgr::SATBased_ArrayReadRefinement(MINISAT::Solver& newS, + const ASTNode& q, const ASTNode& orig_input) { + if(!arrayread_refinement) + FatalError("SATBased_ArrayReadRefinement: Control should not reach here"); + + ASTVec FalseAxiomsVec, RemainingAxiomsVec; + RemainingAxiomsVec.push_back(ASTTrue); + FalseAxiomsVec.push_back(ASTTrue); + + //in these loops we try to construct Leibnitz axioms and add it to + //the solve(). We add only those axioms that are false in the + //current counterexample. we keep adding the axioms until there + //are no more axioms to add + // + //for each array, fetch its list of indices seen so far + for(ASTNodeToVecMap::iterator iset = _arrayname_readindices.begin(), iset_end = _arrayname_readindices.end(); + iset!=iset_end;iset++) { + ASTVec listOfIndices = iset->second; + //loop over the list of indices for the array and create LA, and add to q + for(ASTVec::iterator it=listOfIndices.begin(),itend=listOfIndices.end();it!=itend;it++) { + if(BVCONST == it->GetKind()) { + continue; + } + + ASTNode the_index = *it; + //get the arrayname + ASTNode ArrName = iset->first; + // if(SYMBOL != ArrName.GetKind()) + // FatalError("SATBased_ArrayReadRefinement: arrname is not a SYMBOL",ArrName); + ASTNode arr_read1 = CreateTerm(READ, ArrName.GetValueWidth(), ArrName, the_index); + //get the variable corresponding to the array_read1 + ASTNode arrsym1 = _arrayread_symbol[arr_read1]; + if(!(SYMBOL == arrsym1.GetKind() || BVCONST == arrsym1.GetKind())) + FatalError("TopLevelSAT: refinementloop:term arrsym1 corresponding to READ must be a var", arrsym1); + + //we have nonconst index here. create Leibnitz axiom for it + //w.r.t every index in listOfIndices + for(ASTVec::iterator it1=listOfIndices.begin(),itend1=listOfIndices.end(); + it1!=itend1;it1++) { + ASTNode compare_index = *it1; + //do not compare with yourself + if(the_index == compare_index) + continue; + + //prepare for SAT LOOP + //first construct the antecedent for the LA axiom + ASTNode eqOfIndices = + (exprless(the_index,compare_index)) ? + CreateSimplifiedEQ(the_index,compare_index) : CreateSimplifiedEQ(compare_index,the_index); + + ASTNode arr_read2 = CreateTerm(READ, ArrName.GetValueWidth(), ArrName, compare_index); + //get the variable corresponding to the array_read2 + ASTNode arrsym2 = _arrayread_symbol[arr_read2]; + if(!(SYMBOL == arrsym2.GetKind() || BVCONST == arrsym2.GetKind())) + FatalError("TopLevelSAT: refinement loop:" + "term arrsym2 corresponding to READ must be a var", arrsym2); + + ASTNode eqOfReads = CreateSimplifiedEQ(arrsym1,arrsym2); + //construct appropriate Leibnitz axiom + ASTNode LeibnitzAxiom = CreateNode(IMPLIES, eqOfIndices, eqOfReads); + if(ASTFalse == ComputeFormulaUsingModel(LeibnitzAxiom)) + //FalseAxioms = CreateNode(AND,FalseAxioms,LeibnitzAxiom); + FalseAxiomsVec.push_back(LeibnitzAxiom); + else + //RemainingAxioms = CreateNode(AND,RemainingAxioms,LeibnitzAxiom); + RemainingAxiomsVec.push_back(LeibnitzAxiom); + } + ASTNode FalseAxioms = (FalseAxiomsVec.size()>1) ? CreateNode(AND,FalseAxiomsVec) : FalseAxiomsVec[0]; + ASTNodeStats("adding false readaxioms to SAT: ", FalseAxioms); + int res2 = CallSAT_ResultCheck(newS,FalseAxioms,orig_input); + if(2!=res2) { + return res2; + } + } + } + ASTNode RemainingAxioms = (RemainingAxiomsVec.size()>1) ? CreateNode(AND,RemainingAxiomsVec):RemainingAxiomsVec[0]; + ASTNodeStats("adding remaining readaxioms to SAT: ", RemainingAxioms); + return CallSAT_ResultCheck(newS,RemainingAxioms,orig_input); + } //end of SATBased_ArrayReadRefinement + + ASTNode BeevMgr::Create_ArrayWriteAxioms(const ASTNode& term, const ASTNode& newvar) { + if(READ != term.GetKind() && WRITE != term[0].GetKind()) { + FatalError("Create_ArrayWriteAxioms: Input must be a READ over a WRITE",term); + } + + ASTNode lhs = newvar; + ASTNode rhs = term; + ASTNode arraywrite_axiom = CreateSimplifiedEQ(lhs,rhs); + return arraywrite_axiom; + }//end of Create_ArrayWriteAxioms() + + int BeevMgr::SATBased_ArrayWriteRefinement(MINISAT::Solver& newS, const ASTNode& orig_input) { + ASTNode writeAxiom; + ASTNodeMap::iterator it = ReadOverWrite_NewName_Map.begin(); + ASTNodeMap::iterator itend = ReadOverWrite_NewName_Map.end(); + //int count = 0; + //int num_write_axioms = ReadOverWrite_NewName_Map.size(); + + ASTVec FalseAxioms, RemainingAxioms; + FalseAxioms.push_back(ASTTrue); + RemainingAxioms.push_back(ASTTrue); + for(;it!=itend;it++) { + //Guided refinement starts here + ComputeFormulaMap.clear(); + writeAxiom = Create_ArrayWriteAxioms(it->first,it->second); + if(ASTFalse == ComputeFormulaUsingModel(writeAxiom)) { + writeAxiom = TransformFormula(writeAxiom); + FalseAxioms.push_back(writeAxiom); + } + else { + writeAxiom = TransformFormula(writeAxiom); + RemainingAxioms.push_back(writeAxiom); + } + } + + writeAxiom = (FalseAxioms.size() != 1) ? CreateNode(AND,FalseAxioms) : FalseAxioms[0]; + ASTNodeStats("adding false writeaxiom to SAT: ", writeAxiom); + int res2 = CallSAT_ResultCheck(newS,writeAxiom,orig_input); + if(2!=res2) { + return res2; + } + + writeAxiom = (RemainingAxioms.size() != 1) ? CreateNode(AND,RemainingAxioms) : RemainingAxioms[0]; + ASTNodeStats("adding remaining writeaxiom to SAT: ", writeAxiom); + res2 = CallSAT_ResultCheck(newS,writeAxiom,orig_input); + if(2!=res2) { + return res2; + } + + return 2; + } //end of SATBased_ArrayWriteRefinement + + //Check result after calling SAT FIXME: Document arguments in + //comments, and give them meaningful names. How is anyone supposed + //to know what "q" is? + int BeevMgr::CallSAT_ResultCheck(MINISAT::Solver& newS, + const ASTNode& q, const ASTNode& orig_input) { + //Bitblast, CNF, call SAT now + ASTNode BBFormula = BBForm(q); + //ASTNodeStats("after bitblasting", BBFormula); + ClauseList *cllp = ToCNF(BBFormula); + // if(stats && print_nodes) { + // cout << "\nClause list" << endl; + // PrintClauseList(cout, *cllp); + // cerr << "\n finished printing clauselist\n"; + // } + + bool sat = toSATandSolve(newS,*cllp); + // Temporary debugging call. + // CheckBBandCNF(newS, BBFormula); + + DeleteClauseList(cllp); + if(!sat) { + PrintOutput(true); + return 1; + } + else if(newS.okay()) { + CounterExampleMap.clear(); + ConstructCounterExample(newS); + if (stats && print_nodes) { + PrintSATModel(newS); + } + //check if the counterexample is good or not + ComputeFormulaMap.clear(); + if(counterexample_checking_during_refinement) + bvdiv_exception_occured = false; + ASTNode orig_result = ComputeFormulaUsingModel(orig_input); + if(!(ASTTrue == orig_result || ASTFalse == orig_result)) + FatalError("TopLevelSat: Original input must compute to true or false against model"); + +// if(!arrayread_refinement && !(ASTTrue == orig_result)) { +// print_counterexample = true; +// PrintCounterExample(true); +// FatalError("counterexample bogus : arrayread_refinement is switched off: " +// "EITHER all LA axioms have not been added OR bitblaster() or ToCNF()" +// "or satsolver() or counterexamplechecker() have a bug"); +// } + + // if the counterexample is indeed a good one, then return + // invalid + if(ASTTrue == orig_result) { + CheckCounterExample(newS.okay()); + PrintOutput(false); + PrintCounterExample(newS.okay()); + PrintCounterExample_InOrder(newS.okay()); + return 0; + } + // counterexample is bogus: flag it + else { + if(stats && print_nodes) { + cout << "Supposedly bogus one: \n"; + bool tmp = print_counterexample; + print_counterexample = true; + PrintCounterExample(true); + print_counterexample = tmp; + } + + return 2; + } + } + else { + PrintOutput(true); + return -100; + } + } //end of CALLSAT_ResultCheck + + + //FUNCTION: this function accepts a boolvector and returns a BVConst + ASTNode BeevMgr::BoolVectoBVConst(hash_map<unsigned,bool> * w, unsigned int l) { + unsigned len = w->size(); + if(l < len) + FatalError("BoolVectorBVConst : length of bitvector does not match hash_map size:",ASTUndefined,l); + std::string cc; + for(unsigned int jj = 0; jj < l; jj++) { + if((*w)[jj] == true) + cc += '1'; + else if((*w)[jj] == false) + cc += '0'; + else + cc += '0'; + } + return CreateBVConst(cc.c_str(),2); + } + + void BeevMgr::PrintActivityLevels_Of_SATVars(char * init_msg, MINISAT::Solver& newS) { + if(!print_sat_varorder) + return; + + ASTtoSATMap::iterator itbegin = _ASTNode_to_SATVar.begin(); + ASTtoSATMap::iterator itend = _ASTNode_to_SATVar.end(); + + cout << init_msg; + cout << ": Printing activity levels of variables\n"; + for(ASTtoSATMap::iterator it=itbegin;it!=itend;it++){ + cout << (it->second) << " : "; + (it->first).PL_Print(cout,0); + cout << " : "; + cout << newS.returnActivity(it->second) << endl; + } + } + + //this function biases the activity levels of MINISAT variables. + void BeevMgr::ChangeActivityLevels_Of_SATVars(MINISAT::Solver& newS) { + if(!variable_activity_optimize) + return; + + ASTtoSATMap::iterator itbegin = _ASTNode_to_SATVar.begin(); + ASTtoSATMap::iterator itend = _ASTNode_to_SATVar.end(); + + unsigned int index=1; + double base = 2; + for(ASTtoSATMap::iterator it=itbegin;it!=itend;it++){ + ASTNode n = it->first; + + if(BVGETBIT == n.GetKind() || NOT == n.GetKind()) { + if(BVGETBIT == n.GetKind()) + index = GetUnsignedConst(n[1]); + else if (NOT == n.GetKind() && BVGETBIT == n[0].GetKind()) + index = GetUnsignedConst(n[0][1]); + else + index = 0; + double initial_activity = pow(base,(double)index); + newS.updateInitialActivity(it->second,initial_activity); + } + else { + double initial_activity = pow(base,pow(base,(double)index)); + newS.updateInitialActivity(it->second,initial_activity); + } + } + } + + //This function prints the output of the STP solver + void BeevMgr::PrintOutput(bool true_iff_valid) { + //self-explanatory + if(true_iff_valid) { + ValidFlag = true; + if(print_output) { + if(smtlib_parser_enable) + cout << "unsat\n"; + else + cout << "Valid.\n"; + } + } + else { + ValidFlag = false; + if(print_output) { + if(smtlib_parser_enable) + cout << "sat\n"; + else + cout << "Invalid.\n"; + } + } + } +}; //end of namespace BEEV diff --git a/stp/AST/Transform.cpp b/stp/AST/Transform.cpp new file mode 100644 index 00000000..598b831e --- /dev/null +++ b/stp/AST/Transform.cpp @@ -0,0 +1,492 @@ +/******************************************************************** + * AUTHORS: Vijay Ganesh, David L. Dill + * + * BEGIN DATE: November, 2005 + * + * LICENSE: Please view LICENSE file in the home dir of this Program + ********************************************************************/ +// -*- c++ -*- + +#include "AST.h" +#include <stdlib.h> +#include <stdio.h> +namespace BEEV { + + //Translates signed BVDIV/BVMOD into unsigned variety + ASTNode BeevMgr::TranslateSignedDivMod(const ASTNode& in) { + if(!(SBVMOD == in.GetKind() || SBVDIV == in.GetKind())) { + FatalError("TranslateSignedDivMod: input must be signed DIV/MOD\n",in); + } + + ASTNode dividend = in[0]; + ASTNode divisor = in[1]; + unsigned len = in.GetValueWidth(); + if(SBVMOD == in.GetKind()) { + //if(TopBit(dividend)==1) + // + //then -BVMOD(-dividend,abs(divisor)) + // + //else BVMOD(dividend,abs(divisor)) + + //create the condition for the dividend + ASTNode hi1 = CreateBVConst(32,len-1); + ASTNode one = CreateOneConst(1); + ASTNode cond = CreateNode(EQ,one,CreateTerm(BVEXTRACT,1,dividend,hi1,hi1)); + + //create the condition and conditional for the divisor + ASTNode cond_divisor = CreateNode(EQ,one,CreateTerm(BVEXTRACT,1,divisor,hi1,hi1)); + ASTNode pos_divisor = CreateTerm(ITE,len,cond_divisor,CreateTerm(BVUMINUS,len,divisor),divisor); + + //create the modulus term for each case + ASTNode modnode = CreateTerm(BVMOD,len,dividend,pos_divisor); + ASTNode minus_modnode = CreateTerm(BVMOD,len,CreateTerm(BVUMINUS,len,dividend),pos_divisor); + minus_modnode = CreateTerm(BVUMINUS,len,minus_modnode); + + //put everything together, simplify, and return + ASTNode n = CreateTerm(ITE,len,cond,minus_modnode,modnode); + return SimplifyTerm_TopLevel(n); + } + + //now handle the BVDIV case + //if topBit(dividend) is 1 and topBit(divisor) is 0 + // + //then output is -BVDIV(-dividend,divisor) + // + //elseif topBit(dividend) is 0 and topBit(divisor) is 1 + // + //then output is -BVDIV(dividend,-divisor) + // + //elseif topBit(dividend) is 1 and topBit(divisor) is 1 + // + // then output is BVDIV(-dividend,-divisor) + // + //else simply output BVDIV(dividend,divisor) + ASTNode hi1 = CreateBVConst(32,len-1); + ASTNode zero = CreateZeroConst(1); + ASTNode one = CreateOneConst(1); + ASTNode divnode = CreateTerm(BVDIV, len, dividend, divisor); + + ASTNode cond1 = CreateNode(AND, + CreateNode(EQ,zero,CreateTerm(BVEXTRACT,1,dividend,hi1,hi1)), + CreateNode(EQ,one, CreateTerm(BVEXTRACT,1,divisor,hi1,hi1))); + ASTNode minus_divnode1 = CreateTerm(BVDIV,len, + dividend, + CreateTerm(BVUMINUS,len,divisor)); + minus_divnode1 = CreateTerm(BVUMINUS,len,minus_divnode1); + + ASTNode cond2 = CreateNode(AND, + CreateNode(EQ,one,CreateTerm(BVEXTRACT,1,dividend,hi1,hi1)), + CreateNode(EQ,zero,CreateTerm(BVEXTRACT,1,divisor,hi1,hi1))); + ASTNode minus_divnode2 = CreateTerm(BVDIV,len, + CreateTerm(BVUMINUS,len,dividend), + divisor); + minus_divnode2 = CreateTerm(BVUMINUS,len,minus_divnode2); + + ASTNode cond3 = CreateNode(AND, + CreateNode(EQ,one,CreateTerm(BVEXTRACT,1,dividend,hi1,hi1)), + CreateNode(EQ,one,CreateTerm(BVEXTRACT,1,divisor,hi1,hi1))); + ASTNode minus_divnode3 = CreateTerm(BVDIV,len, + CreateTerm(BVUMINUS,len,dividend), + CreateTerm(BVUMINUS,len,divisor)); + ASTNode n = CreateTerm(ITE,len, + cond1, + minus_divnode1, + CreateTerm(ITE,len, + cond2, + minus_divnode2, + CreateTerm(ITE,len, + cond3, + minus_divnode3, + divnode))); + return SimplifyTerm_TopLevel(n); + }//end of TranslateSignedDivMod() + + ASTNode BeevMgr::TransformFormula(const ASTNode& form) { + ASTNode result; + + ASTNode simpleForm = form; + Kind k = simpleForm.GetKind(); + if(!(is_Form_kind(k) && BOOLEAN_TYPE == simpleForm.GetType())) { + //FIXME: "You have inputted a NON-formula"? + FatalError("TransformFormula: You have input a NON-formula",simpleForm); + } + + ASTNodeMap::iterator iter; + if((iter = TransformMap.find(simpleForm)) != TransformMap.end()) + return iter->second; + + switch(k) { + case TRUE: + case FALSE: { + result = simpleForm; + break; + } + case NOT: { + ASTVec c; + c.push_back(TransformFormula(simpleForm[0])); + result = CreateNode(NOT,c); + break; + } + case BVLT: + case BVLE: + case BVGT: + case BVGE: + case BVSLT: + case BVSLE: + case BVSGT: + case BVSGE: + case NEQ: { + ASTVec c; + c.push_back(TransformTerm(simpleForm[0])); + c.push_back(TransformTerm(simpleForm[1])); + result = CreateNode(k,c); + break; + } + case EQ: { + ASTNode term1 = TransformTerm(simpleForm[0]); + ASTNode term2 = TransformTerm(simpleForm[1]); + result = CreateSimplifiedEQ(term1,term2); + break; + } + case AND: + case OR: + case NAND: + case NOR: + case IFF: + case XOR: + case ITE: + case IMPLIES: { + ASTVec vec; + ASTNode o; + for (ASTVec::const_iterator it = simpleForm.begin(),itend=simpleForm.end(); it != itend; it++){ + o = TransformFormula(*it); + vec.push_back(o); + } + + result = CreateNode(k, vec); + break; + } + default: + if(k == SYMBOL && BOOLEAN_TYPE == simpleForm.GetType()) + result = simpleForm; + else { + cerr << "The input is: " << simpleForm << endl; + cerr << "The valuewidth of input is : " << simpleForm.GetValueWidth() << endl; + FatalError("TransformFormula: Illegal kind: ",ASTUndefined, k); + } + break; + } + //BVTypeCheck(result); + TransformMap[simpleForm] = result; + return result; + } //End of TransformFormula + + ASTNode BeevMgr::TransformTerm(const ASTNode& inputterm) { + ASTNode result; + ASTNode term = inputterm; + + Kind k = term.GetKind(); + if(!is_Term_kind(k)) + FatalError("TransformTerm: Illegal kind: You have input a nonterm:", inputterm, k); + ASTNodeMap::iterator iter; + if((iter = TransformMap.find(term)) != TransformMap.end()) + return iter->second; + switch(k) { + case SYMBOL: { + // ASTNodeMap::iterator itsym; +// if((itsym = CounterExampleMap.find(term)) != CounterExampleMap.end()) +// result = itsym->second; +// else + result = term; + break; + } + case BVCONST: + result = term; + break; + case WRITE: + FatalError("TransformTerm: this kind is not supported",term); + break; + case READ: + result = TransformArray(term); + break; + case ITE: { + ASTNode cond = term[0]; + ASTNode thn = term[1]; + ASTNode els = term[2]; + cond = TransformFormula(cond); + thn = TransformTerm(thn); + els = TransformTerm(els); + //result = CreateTerm(ITE,term.GetValueWidth(),cond,thn,els); + result = CreateSimplifiedTermITE(cond,thn,els); + result.SetIndexWidth(term.GetIndexWidth()); + break; + } + default: { + ASTVec c = term.GetChildren(); + ASTVec::iterator it = c.begin(); + ASTVec::iterator itend = c.end(); + unsigned width = term.GetValueWidth(); + unsigned indexwidth = term.GetIndexWidth(); + ASTVec o; + for(;it!=itend;it++) { + o.push_back(TransformTerm(*it)); + } + + result = CreateTerm(k,width,o); + result.SetIndexWidth(indexwidth); + + if(SBVDIV == result.GetKind() || SBVMOD == result.GetKind()) { + result = TranslateSignedDivMod(result); + } + break; + } + } + + TransformMap[term] = result; + if(term.GetValueWidth() != result.GetValueWidth()) + FatalError("TransformTerm: result and input terms are of different length", result); + if(term.GetIndexWidth() != result.GetIndexWidth()) { + cerr << "TransformTerm: input term is : " << term << endl; + FatalError("TransformTerm: result and input terms have different index length", result); + } + return result; + } //End of TransformTerm + + /* This function transforms Array Reads, Read over Writes, Read over + * ITEs into flattened form. + * + * Transform1: Suppose there are two array reads in the input + * Read(A,i) and Read(A,j) over the same array. Then Read(A,i) is + * replaced with a symbolic constant, say v1, and Read(A,j) is + * replaced with the following ITE: + * + * ITE(i=j,v1,v2) + * + * Transform2: + * + * Transform3: + */ + ASTNode BeevMgr::TransformArray(const ASTNode& term) { + ASTNode result = term; + + unsigned int width = term.GetValueWidth(); + Kind k = term.GetKind(); + if (!is_Term_kind(k)) + FatalError("TransformArray: Illegal kind: You have input a nonterm:", ASTUndefined, k); + ASTNodeMap::iterator iter; + if((iter = TransformMap.find(term)) != TransformMap.end()) + return iter->second; + + switch(k) { + //'term' is of the form READ(arrName, readIndex) + case READ: { + ASTNode arrName = term[0]; + switch (arrName.GetKind()) { + case SYMBOL: { + /* input is of the form: READ(A, readIndex) + * + * output is of the from: A1, if this is the first READ over A + * + * ITE(previous_readIndex=readIndex,A1,A2) + * + * ..... + */ + + // Recursively transform read index, which may also contain reads. + ASTNode readIndex = TransformTerm(term[1]); + ASTNode processedTerm = CreateTerm(READ,width,arrName,readIndex); + + //check if the 'processedTerm' has a corresponding ITE construct + //already. if so, return it. else continue processing. + ASTNodeMap::iterator it; + if((it = _arrayread_ite.find(processedTerm)) != _arrayread_ite.end()) { + result = it->second; + break; + } + //Constructing Symbolic variable corresponding to 'processedTerm' + ASTNode CurrentSymbol; + ASTNodeMap::iterator it1; + // First, check if read index is constant and it has a constant value in the substitution map. + if(CheckSubstitutionMap(processedTerm,CurrentSymbol)) { + _arrayread_symbol[processedTerm] = CurrentSymbol; + } + // Check if it already has an abstract variable. + else if((it1 = _arrayread_symbol.find(processedTerm)) != _arrayread_symbol.end()) { + CurrentSymbol = it1->second; + } + else { + // Make up a new abstract variable. + // FIXME: Make this into a method (there already may BE a method) and + // get rid of the fixed-length buffer! + //build symbolic name corresponding to array read. The symbolic + //name has 2 components: stringname, and a count + const char * b = arrName.GetName(); + std::string c(b); + char d[32]; + sprintf(d,"%d",_symbol_count++); + std::string ccc(d); + c += "array_" + ccc; + + CurrentSymbol = CreateSymbol(c.c_str()); + CurrentSymbol.SetValueWidth(processedTerm.GetValueWidth()); + CurrentSymbol.SetIndexWidth(processedTerm.GetIndexWidth()); + _arrayread_symbol[processedTerm] = CurrentSymbol; + } + + //list of array-read indices corresponding to arrName, seen while + //traversing the AST tree. we need this list to construct the ITEs + // Dill: we hope to make this irrelevant. Harmless for now. + ASTVec readIndices = _arrayname_readindices[arrName]; + + //construct the ITE structure for this array-read + ASTNode ite = CurrentSymbol; + _introduced_symbols.insert(CurrentSymbol); + BVTypeCheck(ite); + + if(arrayread_refinement) { + // ite is really a variable here; it is an ite in the + // else-branch + result = ite; + } + else { + // Full Seshia transform if we're not doing read refinement. + //do not loop if the current readIndex is a BVCONST + // if(BVCONST == term[1].GetKind() && !SeenNonConstReadIndex && optimize) { + // result = ite; + // } + // else { + //else part: SET the SeenNonConstReadIndex var, and do the hard work + //SeenNonConstReadIndex = true; + ASTVec::reverse_iterator it2=readIndices.rbegin(); + ASTVec::reverse_iterator it2end=readIndices.rend(); + for(;it2!=it2end;it2++) { + ASTNode cond = CreateSimplifiedEQ(readIndex,*it2); + if(ASTFalse == cond) + continue; + + ASTNode arrRead = CreateTerm(READ,width,arrName,*it2); + //Good idea to TypeCheck internally constructed nodes + BVTypeCheck(arrRead); + + ASTNode arrayreadSymbol = _arrayread_symbol[arrRead]; + if(arrayreadSymbol.IsNull()) + FatalError("TransformArray:symbolic variable for processedTerm, p," + "does not exist:p = ",arrRead); + ite = CreateSimplifiedTermITE(cond,arrayreadSymbol,ite); + } + result = ite; + //} + } + + _arrayname_readindices[arrName].push_back(readIndex); + //save the ite corresponding to 'processedTerm' + _arrayread_ite[processedTerm] = result; + break; + } //end of READ over a SYMBOL + case WRITE:{ + /* The input to this case is: READ((WRITE A i val) j) + * + * The output of this case is: ITE( (= i j) val (READ A i)) + */ + + /* 1. arrName or term[0] is infact a WRITE(A,i,val) expression + * + * 2. term[1] is the read-index j + * + * 3. arrName[0] is the new arrName i.e. A. A can be either a + SYMBOL or a nested WRITE. no other possibility + * + * 4. arrName[1] is the WRITE index i.e. i + * + * 5. arrName[2] is the WRITE value i.e. val (val can inturn + * be an array read) + */ + ASTNode readIndex = TransformTerm(term[1]); + ASTNode writeIndex = TransformTerm(arrName[1]); + ASTNode writeVal = TransformTerm(arrName[2]); + + if(!(SYMBOL == arrName[0].GetKind() || + WRITE == arrName[0].GetKind())) + FatalError("TransformArray: An array write is being attempted on a non-array:",term); + if(ARRAY_TYPE != arrName[0].GetType()) + FatalError("TransformArray: An array write is being attempted on a non-array:",term); + + ASTNode cond = CreateSimplifiedEQ(writeIndex,readIndex); + //TypeCheck internally created node + BVTypeCheck(cond); + ASTNode readTerm = CreateTerm(READ,width,arrName[0],readIndex); + //TypeCheck internally created node + BVTypeCheck(readTerm); + ASTNode readPushedIn = TransformArray(readTerm); + //TypeCheck internally created node + BVTypeCheck(readPushedIn); + //result = CreateTerm(ITE, arrName[0].GetValueWidth(),cond,writeVal,readPushedIn); + result = CreateSimplifiedTermITE(cond,writeVal,readPushedIn); + + //Good idea to typecheck terms created inside the system + BVTypeCheck(result); + break; + } //end of READ over a WRITE + case ITE: { + /* READ((ITE cond thn els) j) + * + * is transformed into + * + * (ITE cond (READ thn j) (READ els j)) + */ + + //(ITE cond thn els) + ASTNode term0 = term[0]; + //READINDEX j + ASTNode j = TransformTerm(term[1]); + + ASTNode cond = term0[0]; + //first array + ASTNode t01 = term0[1]; + //second array + ASTNode t02 = term0[2]; + + cond = TransformFormula(cond); + ASTNode thn = TransformTerm(t01); + ASTNode els = TransformTerm(t02); + + if(!(t01.GetValueWidth() == t02.GetValueWidth() && + t01.GetValueWidth() == thn.GetValueWidth() && + t01.GetValueWidth() == els.GetValueWidth())) + FatalError("TransformArray: length of THENbranch != length of ELSEbranch in the term t = \n",term); + + if(!(t01.GetIndexWidth() == t02.GetIndexWidth() && + t01.GetIndexWidth() == thn.GetIndexWidth() && + t01.GetIndexWidth() == els.GetIndexWidth())) + FatalError("TransformArray: length of THENbranch != length of ELSEbranch in the term t = \n",term); + + //(READ thn j) + ASTNode thnRead = CreateTerm(READ,width,thn,j); + BVTypeCheck(thnRead); + thnRead = TransformArray(thnRead); + + //(READ els j) + ASTNode elsRead = CreateTerm(READ,width,els,j); + BVTypeCheck(elsRead); + elsRead = TransformArray(elsRead); + + //(ITE cond (READ thn j) (READ els j)) + result = CreateSimplifiedTermITE(cond,thnRead,elsRead); + BVTypeCheck(result); + break; + } + default: + FatalError("TransformArray: The READ is NOT over SYMBOL/WRITE/ITE",term); + break; + } + break; + } //end of READ switch + default: + FatalError("TransformArray: input term is of wrong kind: ",ASTUndefined); + break; + } + + TransformMap[term] = result; + return result; + } //end of TransformArray() +} //end of namespace BEEV diff --git a/stp/AST/asttest.cpp b/stp/AST/asttest.cpp new file mode 100644 index 00000000..57f3d20c --- /dev/null +++ b/stp/AST/asttest.cpp @@ -0,0 +1,29 @@ +#include "AST.h" + +using namespace BEEV; + +int main() +{ + + BeevMgr * bm = new BeevMgr(); + ASTNode s1 = bm->CreateSymbol("foo"); + s1 = bm->CreateSymbol("foo1"); + s1 = bm->CreateSymbol("foo2"); + ASTNode s2 = bm->CreateSymbol("bar"); + cout << "s1" << s1 << endl; + cout << "s2" << s2 << endl; + + ASTNode b1 = bm->CreateBVConst(5,12); + ASTNode b2 = bm->CreateBVConst(6,36); + cout << "b1: " << b1 << endl; + cout << "b2: " << b2 << endl; + + ASTNode a1 = bm->CreateNode(EQ, s1, s2); + ASTNode a2 = bm->CreateNode(AND, s1, s2); + a1 = bm->CreateNode(OR, s1, s2); + ASTNode a3 = bm->CreateNode(IMPLIES, a1, a2); + ASTNode a4 = bm->CreateNode(IMPLIES, s1, a2); + cout << "a3" << a3 << endl; + cout << "a4" << a4 << endl; + return 0; +} diff --git a/stp/AST/bbtest.cpp b/stp/AST/bbtest.cpp new file mode 100644 index 00000000..83aa6a4e --- /dev/null +++ b/stp/AST/bbtest.cpp @@ -0,0 +1,96 @@ +#include "AST.h" + +using namespace BEEV; + +int main() +{ + const int size = 32; + + BeevMgr *bm = new BeevMgr(); + ASTNode s1 = bm->CreateSymbol("x"); + s1.SetValueWidth(size); + cout << "s1" << s1 << endl; + ASTNode s2 = bm->CreateSymbol("y"); + s2.SetValueWidth(size); + cout << "s2" << s2 << endl; + ASTNode s3 = bm->CreateSymbol("z"); + s3.SetValueWidth(size); + cout << "s3" << s3 << endl; + + ASTNode c1 = bm->CreateBVConst(size,0); + cout << "c1" << c1 << endl; + ASTVec bbc1 = bm->BBTerm(c1); + cout << "bitblasted c1 " << endl; + LispPrintVec(cout, bbc1, 0); + cout << endl; + bm->AlreadyPrintedSet.clear(); + + ASTNode c2 = bm->CreateBVConst(size,1); + c2.SetValueWidth(size); + cout << "c2" << c2 << endl; + ASTVec bbc2 = bm->BBTerm(c2); + cout << "bitblasted c2 " << endl; + LispPrintVec(cout, bbc2, 0); + cout << endl; + bm->AlreadyPrintedSet.clear(); + + ASTNode c3 = bm->CreateBVConst(size, 0xFFFFFFFF); + c3.SetValueWidth(size); + cout << "c3" << c3 << endl; + ASTVec bbc3 = bm->BBTerm(c3); + cout << "bitblasted c3 " << endl; + LispPrintVec(cout, bbc3, 0); + cout << endl; + bm->AlreadyPrintedSet.clear(); + + ASTNode c4 = bm->CreateBVConst(size, 0xAAAAAAAA); + c4.SetValueWidth(size); + cout << "c4" << c4 << endl; + ASTVec bbc4 = bm->BBTerm(c4); + cout << "bitblasted c4 " << endl; + LispPrintVec(cout, bbc4, 0); + cout << endl; + bm->AlreadyPrintedSet.clear(); + +// ASTNode b1 = bm->CreateBVConst(12); +// ASTNode b2 = bm->CreateBVConst(36); +// cout << "b1: " << b1 << endl; +// cout << "b2: " << b2 << endl; + + ASTNode a1 = bm->CreateNode(BVPLUS, s1, s2); + a1.SetValueWidth(size); + + ASTVec& bba1 = bm->BBTerm(a1); + cout << "bitblasted a1 " << endl; + LispPrintVec(cout, bba1, 0); + cout << endl; + bm->AlreadyPrintedSet.clear(); + + ASTNode a2 = bm->CreateNode(BVPLUS, s1, s2, s3); + a1.SetValueWidth(2); + + ASTVec& bba2 = bm->BBTerm(a2); + cout << "bitblasted a2 " << endl; + LispPrintVec(cout, bba2, 0); + cout << endl; + bm->AlreadyPrintedSet.clear(); + + ASTNode a3 = bm->CreateNode(BVXOR, s1, s2); + a3.SetValueWidth(2); + + ASTVec& bba3 = bm->BBTerm(a3); + cout << "bitblasted a3 " << endl; + LispPrintVec(cout, bba3, 0); + cout << endl; + bm->AlreadyPrintedSet.clear(); + + ASTNode a4 = bm->CreateNode(EQ, s1, s2); + ASTNode bba4 = bm->BBForm(a4); + cout << "bitblasted a4 " << endl << bba4 << endl; + + ASTNode a5 = bm->CreateNode(BVLE, s1, s2); + ASTNode bba5 = bm->BBForm(a5); + cout << "bitblasted a5 " << endl << bba5 << endl; + + return 0; +} diff --git a/stp/AST/cnftest.cpp b/stp/AST/cnftest.cpp new file mode 100644 index 00000000..7ce270c8 --- /dev/null +++ b/stp/AST/cnftest.cpp @@ -0,0 +1,47 @@ +// -*- c++ -*- + +// Test program for CNF conversion. + +#include "AST.h" + +using namespace BEEV; + +int main() +{ + const int size = 1; + + BeevMgr *bm = new BeevMgr(); + ASTNode s1 = bm->CreateSymbol("x"); + s1.SetValueWidth(size); + + cout << "s1" << s1 << endl; + ASTNode s2 = bm->CreateSymbol("y"); + s2.SetValueWidth(size); + + cout << "s2" << s2 << endl; + ASTNode s3 = bm->CreateSymbol("z"); + s3.SetValueWidth(size); + + cout << "s3" << s3 << endl; + + ASTNode bbs1 = bm->BBForm(s1); + cout << "bitblasted s1" << endl << bbs1 << endl; + bm->PrintClauseList(cout, bm->ToCNF(bbs1)); + + ASTNode a2 = bm->CreateNode(AND, s1, s2); + ASTNode bba2 = bm->BBForm(a2); + cout << "bitblasted a2" << endl << bba2 << endl; + bm->PrintClauseList(cout, bm->ToCNF(bba2)); + + ASTNode a3 = bm->CreateNode(OR, s1, s2); + ASTNode bba3 = bm->BBForm(a3); + cout << "bitblasted a3" << endl << bba3 << endl; + bm->PrintClauseList(cout, bm->ToCNF(bba3)); + + ASTNode a4 = bm->CreateNode(EQ, s1, s2); + ASTNode bba4 = bm->BBForm(a4); + cout << "bitblasted a4 " << endl << bba4 << endl; + + bm->PrintClauseList(cout, bm->ToCNF(bba4)); + +} diff --git a/stp/AST/genkinds.pl b/stp/AST/genkinds.pl new file mode 100755 index 00000000..7944832b --- /dev/null +++ b/stp/AST/genkinds.pl @@ -0,0 +1,123 @@ +#!/usr/bin/perl -w + +#AUTHORS: Vijay Ganesh, David L. Dill BEGIN DATE: November, 2005 +#LICENSE: Please view LICENSE file in the home dir of this Program +#given a file containing kind names, one per line produces .h and .cpp +#files for the kinds. + +#globals +@kindnames = (); +$minkids = 0; +$maxkids = 0; +@cat_bits = (); +@category_names = (); +%cat_index = (); + +$now = localtime time; + +sub read_kind_defs { + open(KFILE, "< ASTKind.kinds") || die "Cannot open .kinds file: $!\n"; + @kindlines = <KFILE>; + close(KFILE) +} + +# create lists of things indexed by kinds. +sub split_fields { + my $kind_cat_bits; + # matches anything with three whitespace-delimited alphanumeric fields, + # followed by rest of line. Automatically ignores lines beginning with '#' and blank lines. + for (@kindlines) { + if (/Categories:\s+(.*)/) { + @category_names = split(/\s+/, $1); + $i = 0; + for (@category_names) { + $cat_index{$_} = $i++; + # print "cat_index{$_} = $i\n"; + } + } + elsif (/^(\w+)\s+(\w+)\s+(\w+|-)\s+(.*)/) { + push(@kindnames, $1); + push(@minkids, $2); + push(@maxkids, $3); + @kind_cats = split(/\s+/, $4); + # build a bit vector of categories. + $kind_cat_bits = 0; + for (@kind_cats) { + $kind_cat_bits |= (1 << int($cat_index{$_})); + } + push(@cat_bits, $kind_cat_bits); + } + } +} + +sub gen_h_file { + open(HFILE, "> ASTKind.h") || die "Cannot open .h file: $!\n"; + + print HFILE + "// -*- c++ -*-\n", + "#ifndef TESTKINDS_H\n", + "#define TESTKINDS_H\n", + "// Generated automatically by genkinds.pl from ASTKind.kinds $now.\n", + "// Do not edit\n", + "namespace BEEV {\n typedef enum {\n"; + + for (@kindnames) { + print HFILE " $_,\n"; + } + + print HFILE + "} Kind;\n\n", + "extern unsigned char _kind_categories[];\n\n"; + + # For category named "cat", generate functions "bool is_cat_kind(k);" + + + for (@category_names) { + my $catname = $_; + my $kind_cat_bit = (1 << int($cat_index{$catname})); + print HFILE "inline bool is_", $catname, "_kind(Kind k) { return (_kind_categories[k] & $kind_cat_bit); }\n\n" + } + + print HFILE + "extern const char *_kind_names[];\n\n", + "/** Prints symbolic name of kind */\n", + "inline ostream& operator<<(ostream &os, const Kind &kind) { os << _kind_names[kind]; return os; }\n", + "\n\n", + "}; // end namespace\n", + "\n\n#endif\n"; + + close(HFILE); +} + +# generate the .cpp file + +sub gen_cpp_file { + open(CPPFILE, "> ASTKind.cpp") || die "Cannot open .h file: $!\n"; + + print CPPFILE + "// Generated automatically by genkinds.h from ASTKind.kinds $now.\n", + "// Do not edit\n", + "namespace BEEV {\n", + "const char * _kind_names[] = {\n"; + for (@kindnames) { + print CPPFILE " \"$_\",\n"; + } + print CPPFILE "};\n\n"; + + # category bits + print CPPFILE + "unsigned char _kind_categories[] = {\n"; + for (@cat_bits) { + print CPPFILE " $_,\n"; + } + print CPPFILE + "};\n", + "\n}; // end namespace\n"; + + close(CPPFILE); +} + +&read_kind_defs; +&split_fields; +&gen_h_file; +&gen_cpp_file; diff --git a/stp/INSTALL b/stp/INSTALL new file mode 100644 index 00000000..12cee121 --- /dev/null +++ b/stp/INSTALL @@ -0,0 +1,10 @@ +1. To install STP perform the following steps on your Unix/GNU-Linux/MacOS X commandline: + +./configure --with-prefix=$HOME (or another installation directory) +make clean +make +make install + +2. To test the system after installation: + +make regressall \ No newline at end of file diff --git a/stp/LICENSE b/stp/LICENSE new file mode 100644 index 00000000..41029509 --- /dev/null +++ b/stp/LICENSE @@ -0,0 +1,17 @@ +/*****************************************************************************/ +/* AUTHORS: Vijay Ganesh, David L. Dill DATE: Nov 2005 */ +/*****************************************************************************/ +/* Copyright (C) 2005 by the Board of Trustees of Leland Stanford */ +/* Junior University. */ +/* */ +/* License to use, copy, modify, sell and/or distribute this software */ +/* and its documentation for any purpose is hereby granted without */ +/* royalty, subject to the terms and conditions defined in the \ref */ +/* LICENSE file provided with this distribution. In particular: */ +/* */ +/* - The above copyright notice and this permission notice must appear */ +/* in all copies of the software and related documentation. */ +/* */ +/* - THE SOFTWARE IS PROVIDED "AS-IS", WITHOUT ANY WARRANTIES, */ +/* EXPRESSED OR IMPLIED. USE IT AT YOUR OWN RISK. */ +/*****************************************************************************/ diff --git a/stp/Makefile b/stp/Makefile new file mode 100644 index 00000000..f66644a5 --- /dev/null +++ b/stp/Makefile @@ -0,0 +1,64 @@ + # STP (Simple Theorem Prover) top level makefile + # + # To make in debug mode, type 'make "CLFAGS=-ggdb" + # To make in optimized mode, type 'make "CFLAGS=-O2" + +include Makefile.common + +BINARIES=bin/stp +LIBS=AST/libast.a sat/libsatsolver.a simplifier/libsimplifier.a bitvec/libconsteval.a constantbv/libconstantbv.a c_interface/libcinterface.a +DIRS=AST sat simplifier bitvec c_interface constantbv parser + +# NB: the TAGS target is a hack to get around this recursive make nonsense +# we want all the source and header files generated before we make tags +.PHONY: all +all: lib/libstp.a bin/stp include/stp/c_interface.h + +AST/libast.a: + @$(MAKE) -q -C `dirname $@` || $(MAKE) -C `dirname $@` +sat/libsatsolver.a: AST/libast.a + @$(MAKE) -q -C `dirname $@` || $(MAKE) -C `dirname $@` +simplifier/libsimplifier.a: AST/libast.a + @$(MAKE) -q -C `dirname $@` || $(MAKE) -C `dirname $@` +bitvec/libconsteval.a: AST/libast.a + @$(MAKE) -q -C `dirname $@` || $(MAKE) -C `dirname $@` +constantbv/libconstantbv.a: AST/libast.a + @$(MAKE) -q -C `dirname $@` || $(MAKE) -C `dirname $@` +c_interface/libcinterface.a: AST/libast.a + @$(MAKE) -q -C `dirname $@` || $(MAKE) -C `dirname $@` +parser/parser: $(LIBS) + @$(MAKE) -q -C `dirname $@` || $(MAKE) -C `dirname $@` + +lib/libstp.a: parser/parser $(LIBS) + @mkdir -p lib + rm -f $@ + @for dir in $(DIRS); do \ + $(AR) rc $@ $$dir/*.o; \ + done + $(RANLIB) $@ + +bin/stp: parser/parser $(LIBS) + @mkdir -p bin + @cp parser/parser $@ + +include/stp/c_interface.h: $(LIBS) + @mkdir -p include/stp + @cp c_interface/c_interface.h $@ + +.PHONY: clean +clean: + rm -rf *~ + rm -rf *.a + rm -rf lib/*.a + rm -rf bin/*~ + rm -rf bin/stp + rm -rf *.log + rm -f TAGS + $(MAKE) clean -C AST + $(MAKE) clean -C sat + $(MAKE) clean -C simplifier + $(MAKE) clean -C bitvec + $(MAKE) clean -C parser + $(MAKE) clean -C c_interface + $(MAKE) clean -C constantbv + diff --git a/stp/Makefile.common.in b/stp/Makefile.common.in new file mode 100644 index 00000000..a88e35ed --- /dev/null +++ b/stp/Makefile.common.in @@ -0,0 +1,16 @@ +# -*- Makefile -*- + +CFLAGS := @CFLAGS@ +CXXFLAGS := @CXXFLAGS@ -O2 +LDFLAGS := @LDFLAGS@ -lstdc++ + +# use the darmin test as a proxy for detecting Mac OS X +ifneq ($(shell uname -s), Darwin) + CFLAGS += -static +endif + +CXXFLAGS += -Wall -DEXT_HASH_MAP + +LEX := flex +YACC := bison -d -y --debug -v +RANLIB := ranlib diff --git a/stp/README b/stp/README new file mode 100644 index 00000000..da0f9b96 --- /dev/null +++ b/stp/README @@ -0,0 +1,26 @@ +/******************************************************************** + * PROGRAM NAME: STP (Simple Theorem Prover) + * + * AUTHORS: Vijay Ganesh, David L. Dill + * + * BEGIN DATE: November, 2005 + * + * LICENSE: Please view LICENSE file in the home dir of this Program + ********************************************************************/ + +Install +------- +See INSTALL file in the home dir of this program + +Authors +------- +Vijay Ganesh, Stanford University, Stanford, CA, USA +David L. Dill, Stanford University, Stanford, CA, USA +Tim King, Stanford University, Stanford, CA, USA + +Makefiles and configuration scripts +------------------------------------ +Cristian Cadar, Stanford University, Stanford, CA, USA +Paul Twohey, Stanford University, Stanford, CA, USA +Sergey Berezin, ATG Synopsys, Mountain View, CA, USA +Clark Barrett, New York University, New York, NY, USA diff --git a/stp/bitvec/Makefile b/stp/bitvec/Makefile new file mode 100644 index 00000000..eb6e1121 --- /dev/null +++ b/stp/bitvec/Makefile @@ -0,0 +1,11 @@ +include ../Makefile.common + +SRCS = consteval.cpp +OBJS = $(SRCS:.cpp=.o) + +libconsteval.a: $(OBJS) + $(AR) rc $@ $^ + $(RANLIB) $@ + +clean: + rm -rf *.o *~ *.a .#* diff --git a/stp/bitvec/consteval.cpp b/stp/bitvec/consteval.cpp new file mode 100644 index 00000000..7cb8dfcb --- /dev/null +++ b/stp/bitvec/consteval.cpp @@ -0,0 +1,1044 @@ +/******************************************************************** + * AUTHORS: Vijay Ganesh, David L. Dill + * + * BEGIN DATE: November, 2005 + * + * LICENSE: Please view LICENSE file in the home dir of this Program + ********************************************************************/ +// -*- c++ -*- + +#include "../AST/AST.h" +#include "../AST/ASTUtil.h" +namespace BEEV { + + //error printing + static void BVConstEvaluatorError(CONSTANTBV::ErrCode e, const ASTNode& t){ + std::string ss("BVConstEvaluator:"); + ss += (const char*)BitVector_Error(e); + FatalError(ss.c_str(), t); + } + +#ifndef NATIVE_C_ARITH + ASTNode BeevMgr::BVConstEvaluator(const ASTNode& t) { + ASTNode OutputNode; + Kind k = t.GetKind(); + + if(CheckSolverMap(t,OutputNode)) + return OutputNode; + OutputNode = t; + + unsigned int inputwidth = t.GetValueWidth(); + unsigned int outputwidth = inputwidth; + CBV output = NULL; + + CBV tmp0 = NULL; + CBV tmp1 = NULL; + + //saving some typing. BVPLUS does not use these variables. if the + //input BVPLUS has two nodes, then we want to avoid setting these + //variables. + if(1 == t.Degree() ){ + tmp0 = BVConstEvaluator(t[0]).GetBVConst(); + }else if(2 == t.Degree() && k != BVPLUS ) { + tmp0 = BVConstEvaluator(t[0]).GetBVConst(); + tmp1 = BVConstEvaluator(t[1]).GetBVConst(); + } + + switch(k) { + case UNDEFINED: + case READ: + case WRITE: + case SYMBOL: + FatalError("BVConstEvaluator: term is not a constant-term",t); + break; + case BVCONST: + //FIXME Handle this special case better + OutputNode = t; + break; + case BVNEG:{ + output = CONSTANTBV::BitVector_Create(inputwidth,true); + CONSTANTBV::Set_Complement(output,tmp0); + OutputNode = CreateBVConst(output,outputwidth); + break; + } + case BVSX: { + output = CONSTANTBV::BitVector_Create(inputwidth,true); + //unsigned * out0 = BVConstEvaluator(t[0]).GetBVConst(); + unsigned t0_width = t[0].GetValueWidth(); + if(inputwidth == t0_width) { + CONSTANTBV::BitVector_Copy(output, tmp0); + OutputNode = CreateBVConst(output, outputwidth); + } + else { + bool topbit_sign = (CONSTANTBV::BitVector_Sign(tmp0) < 0 ); + + if(topbit_sign){ + CONSTANTBV::BitVector_Fill(output); + } + CONSTANTBV::BitVector_Interval_Copy(output, tmp0, 0, 0, t0_width); + OutputNode = CreateBVConst(output, outputwidth); + } + break; + } + case BVAND: { + output = CONSTANTBV::BitVector_Create(inputwidth,true); + CONSTANTBV::Set_Intersection(output,tmp0,tmp1); + OutputNode = CreateBVConst(output, outputwidth); + break; + } + case BVOR: { + output = CONSTANTBV::BitVector_Create(inputwidth,true); + CONSTANTBV::Set_Union(output,tmp0,tmp1); + OutputNode = CreateBVConst(output, outputwidth); + break; + } + case BVXOR: { + output = CONSTANTBV::BitVector_Create(inputwidth,true); + CONSTANTBV::Set_ExclusiveOr(output,tmp0,tmp1); + OutputNode = CreateBVConst(output, outputwidth); + break; + } + case BVSUB: { + output = CONSTANTBV::BitVector_Create(inputwidth,true); + bool carry = false; + CONSTANTBV::BitVector_sub(output,tmp0,tmp1,&carry); + OutputNode = CreateBVConst(output, outputwidth); + break; + } + case BVUMINUS: { + output = CONSTANTBV::BitVector_Create(inputwidth,true); + CONSTANTBV::BitVector_Negate(output, tmp0); + OutputNode = CreateBVConst(output, outputwidth); + break; + } + case BVEXTRACT: { + output = CONSTANTBV::BitVector_Create(inputwidth,true); + tmp0 = BVConstEvaluator(t[0]).GetBVConst(); + unsigned int hi = GetUnsignedConst(BVConstEvaluator(t[1])); + unsigned int low = GetUnsignedConst(BVConstEvaluator(t[2])); + unsigned int len = hi-low+1; + + CONSTANTBV::BitVector_Destroy(output); + output = CONSTANTBV::BitVector_Create(len, false); + CONSTANTBV::BitVector_Interval_Copy(output, tmp0, 0, low, len); + outputwidth = len; + OutputNode = CreateBVConst(output, outputwidth); + break; + } + //FIXME Only 2 inputs? + case BVCONCAT: { + output = CONSTANTBV::BitVector_Create(inputwidth,true); + unsigned t0_width = t[0].GetValueWidth(); + unsigned t1_width = t[1].GetValueWidth(); + CONSTANTBV::BitVector_Destroy(output); + + output = CONSTANTBV::BitVector_Concat(tmp0, tmp1); + outputwidth = t0_width + t1_width; + OutputNode = CreateBVConst(output, outputwidth); + + break; + } + case BVMULT: { + output = CONSTANTBV::BitVector_Create(inputwidth,true); + CBV tmp = CONSTANTBV::BitVector_Create(2*inputwidth,true); + CONSTANTBV::ErrCode e = CONSTANTBV::BitVector_Multiply(tmp,tmp0,tmp1); + + if(0 != e) { + BVConstEvaluatorError(e,t); + } + //FIXME WHAT IS MY OUTPUT???? THE SECOND HALF of tmp? + //CONSTANTBV::BitVector_Interval_Copy(output, tmp, 0, inputwidth, inputwidth); + CONSTANTBV::BitVector_Interval_Copy(output, tmp, 0, 0, inputwidth); + OutputNode = CreateBVConst(output, outputwidth); + CONSTANTBV::BitVector_Destroy(tmp); + break; + } + case BVPLUS: { + output = CONSTANTBV::BitVector_Create(inputwidth,true); + bool carry = false; + ASTVec c = t.GetChildren(); + for(ASTVec::iterator it=c.begin(),itend=c.end();it!=itend;it++) { + CBV kk = BVConstEvaluator(*it).GetBVConst(); + CONSTANTBV::BitVector_add(output,output,kk,&carry); + carry = false; + //CONSTANTBV::BitVector_Destroy(kk); + } + OutputNode = CreateBVConst(output, outputwidth); + break; + } + //FIXME ANOTHER SPECIAL CASE + case SBVDIV: + case SBVMOD:{ + OutputNode = BVConstEvaluator(TranslateSignedDivMod(t)); + break; + } + case BVDIV: + case BVMOD: { + CBV quotient = CONSTANTBV::BitVector_Create(inputwidth,true); + CBV remainder = CONSTANTBV::BitVector_Create(inputwidth,true); + + // tmp0 is dividend, tmp1 is the divisor + //All parameters to BitVector_Div_Pos must be distinct unlike BitVector_Divide + //FIXME the contents of the second parameter to Div_Pos is destroyed + //As tmp0 is currently the same as the copy belonging to an ASTNode t[0] + //this must be copied. + tmp0 = CONSTANTBV::BitVector_Clone(tmp0); + CONSTANTBV::ErrCode e= CONSTANTBV::BitVector_Div_Pos(quotient,tmp0,tmp1,remainder); + CONSTANTBV::BitVector_Destroy(tmp0); + + if(0 != e) { + //error printing + if(counterexample_checking_during_refinement) { + output = CONSTANTBV::BitVector_Create(inputwidth,true); + OutputNode = CreateBVConst(output, outputwidth); + bvdiv_exception_occured = true; + + // CONSTANTBV::BitVector_Destroy(output); + break; + } + else { + BVConstEvaluatorError(e,t); + } + } //end of error printing + + //FIXME Not very standard in the current scheme + if(BVDIV == k){ + OutputNode = CreateBVConst(quotient, outputwidth); + CONSTANTBV::BitVector_Destroy(remainder); + }else{ + OutputNode = CreateBVConst(remainder, outputwidth); + CONSTANTBV::BitVector_Destroy(quotient); + } + + break; + } + case ITE: + if(ASTTrue == t[0]) + OutputNode = BVConstEvaluator(t[1]); + else if(ASTFalse == t[0]) + OutputNode = BVConstEvaluator(t[2]); + else + FatalError("BVConstEvaluator: ITE condiional must be either TRUE or FALSE:",t); + break; + case EQ: + if(CONSTANTBV::BitVector_equal(tmp0,tmp1)) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + case NEQ: + if(!CONSTANTBV::BitVector_equal(tmp0,tmp1)) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + case BVLT: + if(-1 == CONSTANTBV::BitVector_Lexicompare(tmp0,tmp1)) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + case BVLE: { + int comp = CONSTANTBV::BitVector_Lexicompare(tmp0,tmp1); + if(comp <= 0) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + } + case BVGT: + if(1 == CONSTANTBV::BitVector_Lexicompare(tmp0,tmp1)) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + case BVGE: { + int comp = CONSTANTBV::BitVector_Lexicompare(tmp0,tmp1); + if(comp >= 0) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + } + case BVSLT: + if(-1 == CONSTANTBV::BitVector_Compare(tmp0,tmp1)) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + case BVSLE: { + signed int comp = CONSTANTBV::BitVector_Compare(tmp0,tmp1); + if(comp <= 0) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + } + case BVSGT: + if(1 == CONSTANTBV::BitVector_Compare(tmp0,tmp1)) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + case BVSGE: { + int comp = CONSTANTBV::BitVector_Compare(tmp0,tmp1); + if(comp >= 0) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + } + default: + FatalError("BVConstEvaluator: The input kind is not supported yet:",t); + break; + } +/* + if(BVCONST != k){ + cerr<<inputwidth<<endl; + cerr<<"------------------------"<<endl; + t.LispPrint(cerr); + cerr<<endl; + OutputNode.LispPrint(cerr); + cerr<<endl<<"------------------------"<<endl; + } +*/ + UpdateSolverMap(t,OutputNode); + //UpdateSimplifyMap(t,OutputNode,false); + return OutputNode; + } +#else + //accepts 64 bit BVConst and sign extends it + static unsigned long long int SXBVConst64(const ASTNode& t) { + unsigned long long int c = t.GetBVConst(); + unsigned int len = t.GetValueWidth(); + + unsigned long long int mask = 1; + mask = mask << len-1; + + bool TopBit = (c & mask) ? true : false; + if(!TopBit) return c; + + unsigned long long int sign = 0xffffffffffffffffLL; + sign = sign << len-1; + + return (c | sign); + } + + //FIXME: Ideally I would like the ASTNodes to be able to operate on + //themselves (add, sub, concat, etc.) rather than doing a + //GetBVConst() and then do the operation externally. For now, + //this is the fastest path to completion. + ASTNode BeevMgr::BVConstEvaluator(const ASTNode& t) { + //cerr << "inside begin bcconstevaluator: " << t << endl; + + ASTNode OutputNode; + if(CheckSolverMap(t,OutputNode)) + return OutputNode; + OutputNode = ASTUndefined; + + Kind k = t.GetKind(); + unsigned long long int output = 0; + unsigned inputwidth = t.GetValueWidth(); + ASTNode t0 = ASTUndefined; + ASTNode t1 = ASTUndefined; + if(2 == t.Degree()) { + t0 = BVConstEvaluator(t[0]); + t1 = BVConstEvaluator(t[1]); + } + switch(k) { + case READ: + case UNDEFINED: + case WRITE: + case SYMBOL: + cerr << t; + FatalError("BVConstEvaluator: term is not a constant-term",t); + break; + case BVCONST: + return t; + break; + case BVNEG: + //compute bitwise negation in C + output = ~(BVConstEvaluator(t[0]).GetBVConst()); + break; + case BVSX: + output = SXBVConst64(BVConstEvaluator(t[0])); + break; + case BVAND: + output = t0.GetBVConst() & t1.GetBVConst(); + break; + case BVOR: + output = t0.GetBVConst() | t1.GetBVConst(); + break; + case BVXOR: + output = t0.GetBVConst() ^ t1.GetBVConst(); + break; + case BVSUB: + output = t0.GetBVConst() - t1.GetBVConst(); + break; + case BVUMINUS: + output = ~(BVConstEvaluator(t[0]).GetBVConst()) + 1; + break; + case BVEXTRACT: { + unsigned long long int val = BVConstEvaluator(t[0]).GetBVConst(); + unsigned int hi = GetUnsignedConst(BVConstEvaluator(t[1])); + unsigned int low = GetUnsignedConst(BVConstEvaluator(t[2])); + + if(!(0 <= hi <= 64)) + FatalError("ConstantEvaluator: hi bit in BVEXTRACT is > 32bits",t); + if(!(0 <= low <= hi <= 64)) + FatalError("ConstantEvaluator: low bit in BVEXTRACT is > 32bits or hi",t); + + //64 bit mask. + unsigned long long int mask1 = 0xffffffffffffffffLL; + mask1 >>= 64-(hi+1); + + //extract val[hi:0] + val &= mask1; + //extract val[hi:low] + val >>= low; + output = val; + break; + } + case BVCONCAT: { + unsigned long long int q = BVConstEvaluator(t0).GetBVConst(); + unsigned long long int r = BVConstEvaluator(t1).GetBVConst(); + + unsigned int qlen = t[0].GetValueWidth(); + unsigned int rlen = t[1].GetValueWidth(); + unsigned int slen = t.GetValueWidth(); + if(!(0 < qlen + rlen <= 64)) + FatalError("BVConstEvaluator:" + "lengths of childnodes of BVCONCAT are > 64:",t); + + //64 bit mask for q + unsigned long long int qmask = 0xffffffffffffffffLL; + qmask >>= 64-qlen; + //zero the useless bits of q + q &= qmask; + + //64 bit mask for r + unsigned long long int rmask = 0xffffffffffffffffLL; + rmask >>= 64-rlen; + //zero the useless bits of r + r &= rmask; + + //concatenate + q <<= rlen; + q |= r; + + //64 bit mask for output s + unsigned long long int smask = 0xffffffffffffffffLL; + smask >>= 64-slen; + + //currently q has the output + output = q; + output &= smask; + break; + } + case BVMULT: { + output = t0.GetBVConst() * t1.GetBVConst(); + + //64 bit mask + unsigned long long int mask = 0xffffffffffffffffLL; + mask = mask >> (64 - inputwidth); + output &= mask; + break; + } + case BVPLUS: { + ASTVec c = t.GetChildren(); + for(ASTVec::iterator it=c.begin(),itend=c.end();it!=itend;it++) + output += BVConstEvaluator(*it).GetBVConst(); + + //64 bit mask + unsigned long long int mask = 0xffffffffffffffffLL; + mask = mask >> (64 -inputwidth); + output &= mask; + break; + } + case SBVDIV: + case SBVMOD: { + output = BVConstEvaluator(TranslateSignedDivMod(t)).GetBVConst(); + break; + } + case BVDIV: { + if(0 == t1.GetBVConst()) { + //if denominator is 0 then + // (if refinement is ON then output is set to 0) + // (else produce a fatal error) + if(counterexample_checking_during_refinement) { + output = 0; + bvdiv_exception_occured = true; + break; + } + else { + FatalError("BVConstEvaluator: divide by zero not allowed:",t); + } + } + + output = t0.GetBVConst() / t1.GetBVConst(); + //64 bit mask + unsigned long long int mask = 0xffffffffffffffffLL; + mask = mask >> (64 - inputwidth); + output &= mask; + break; + } + case BVMOD: { + if(0 == t1.GetBVConst()) { + //if denominator is 0 then + // (if refinement is ON then output is set to 0) + // (else produce a fatal error) + if(counterexample_checking_during_refinement) { + output = 0; + bvdiv_exception_occured = true; + break; + } + else { + FatalError("BVConstEvaluator: divide by zero not allowed:",t); + } + } + + output = t0.GetBVConst() % t1.GetBVConst(); + //64 bit mask + unsigned long long int mask = 0xffffffffffffffffLL; + mask = mask >> (64 - inputwidth); + output &= mask; + break; + } + case ITE: + if(ASTTrue == t[0]) + OutputNode = BVConstEvaluator(t[1]); + else if(ASTFalse == t[0]) + OutputNode = BVConstEvaluator(t[2]); + else + FatalError("BVConstEvaluator:" + "ITE condiional must be either TRUE or FALSE:",t); + break; + case EQ: + if(t0.GetBVConst() == t1.GetBVConst()) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + case NEQ: + if(t0.GetBVConst() != t1.GetBVConst()) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + break; + case BVLT: { + unsigned long long n0 = t0.GetBVConst(); + unsigned long long n1 = t1.GetBVConst(); + if(n0 < n1) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + } + case BVLE: + if(t0.GetBVConst() <= t1.GetBVConst()) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + case BVGT: + if(t0.GetBVConst() > t1.GetBVConst()) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + case BVGE: + if(t0.GetBVConst() >= t1.GetBVConst()) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + case BVSLT: { + signed long long int n0 = SXBVConst64(t0); + signed long long int n1 = SXBVConst64(t1); + if(n0 < n1) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + } + case BVSLE: { + signed long long int n0 = SXBVConst64(t0); + signed long long int n1 = SXBVConst64(t1); + if(n0 <= n1) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + } + case BVSGT: { + signed long long int n0 = SXBVConst64(t0); + signed long long int n1 = SXBVConst64(t1); + if(n0 > n1) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + } + case BVSGE: { + signed long long int n0 = SXBVConst64(t0); + signed long long int n1 = SXBVConst64(t1); + if(n0 >= n1) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + } + default: + FatalError("BVConstEvaluator: The input kind is not supported yet:",t); + break; + } + + if(ASTTrue != OutputNode && ASTFalse != OutputNode) + OutputNode = CreateBVConst(inputwidth, output); + UpdateSolverMap(t,OutputNode); + //UpdateSimplifyMap(t,OutputNode,false); + return OutputNode; + } //End of BVConstEvaluator +#endif +//In the block below is the old string based version +//It is included here as an easy reference while the current code is being worked on. + +/* + ASTNode BeevMgr::BVConstEvaluator(const ASTNode& t) { + ASTNode OutputNode; + Kind k = t.GetKind(); + + if(CheckSolverMap(t,OutputNode)) + return OutputNode; + OutputNode = t; + + unsigned int inputwidth = t.GetValueWidth(); + unsigned * output = CONSTANTBV::BitVector_Create(inputwidth,true); + unsigned * One = CONSTANTBV::BitVector_Create(inputwidth,true); + CONSTANTBV::ErrCode e = CONSTANTBV::BitVector_from_Bin(One, (unsigned char*)"1"); + //error printing + if(0 != e) { + std::string ss("BVConstEvaluator:"); + ss += (const char*)BitVector_Error(e); + FatalError(ss.c_str(), t); + } + + unsigned * Zero = CONSTANTBV::BitVector_Create(inputwidth,true); + unsigned int * iii = One; + unsigned int * jjj = Zero; + + //saving some typing. BVPLUS does not use these variables. if the + //input BVPLUS has two nodes, then we want to avoid setting these + //variables. + if(2 == t.Degree() && k != BVPLUS && k != BVCONCAT) { + iii = ConvertToCONSTANTBV(BVConstEvaluator(t[0]).GetBVConst()); + jjj = ConvertToCONSTANTBV(BVConstEvaluator(t[1]).GetBVConst()); + } + + char * cccc; + switch(k) { + case UNDEFINED: + case READ: + case WRITE: + case SYMBOL: + FatalError("BVConstEvaluator: term is not a constant-term",t); + break; + case BVCONST: + OutputNode = t; + break; + case BVNEG:{ + //AARON + if (iii != One) free(iii); + //AARON + + iii = ConvertToCONSTANTBV(BVConstEvaluator(t[0]).GetBVConst()); + CONSTANTBV::Set_Complement(output,iii); + cccc = (char *)CONSTANTBV::BitVector_to_Bin(output); + OutputNode = CreateBVConst(cccc,2); + break; + } + case BVSX: { + unsigned * out0 = ConvertToCONSTANTBV(BVConstEvaluator(t[0]).GetBVConst()); + unsigned t0_width = t[0].GetValueWidth(); + if(inputwidth == t0_width) { + cccc = (char *)CONSTANTBV::BitVector_to_Bin(out0); + OutputNode = CreateBVConst(cccc,2); + + //AARON + free(cccc); + //AARON + + CONSTANTBV::BitVector_Destroy(out0); + } + else { + // FIXME: (Dill) I'm guessing that BitVector sign returns 1 if the + // number is positive, 0 if 0, and -1 if negative. But I'm only + // guessing. + signed int topbit_sign = (CONSTANTBV::BitVector_Sign(out0) < 0); + //out1 is the sign-extension bits + unsigned * out1 = CONSTANTBV::BitVector_Create(inputwidth-t0_width,true); + if(topbit_sign) + CONSTANTBV::BitVector_Fill(out1); + + //AARON + CONSTANTBV::BitVector_Destroy(output); + //AARON + + output = CONSTANTBV::BitVector_Concat(out1,out0); + cccc = (char *)CONSTANTBV::BitVector_to_Bin(output); + OutputNode = CreateBVConst(cccc,2); + + //AARON + free(cccc); + //AARON + + CONSTANTBV::BitVector_Destroy(out0); + CONSTANTBV::BitVector_Destroy(out1); + } + break; + } + case BVAND: { + CONSTANTBV::Set_Intersection(output,iii,jjj); + cccc = (char *)CONSTANTBV::BitVector_to_Bin(output); + OutputNode = CreateBVConst(cccc,2); + + //AARON + free(cccc); + //AARON + + break; + } + case BVOR: { + CONSTANTBV::Set_Union(output,iii,jjj); + cccc = (char *)CONSTANTBV::BitVector_to_Bin(output); + OutputNode = CreateBVConst(cccc,2); + + //AARON + free(cccc); + //AARON + + break; + } + case BVXOR: { + CONSTANTBV::Set_ExclusiveOr(output,iii,jjj); + cccc = (char *)CONSTANTBV::BitVector_to_Bin(output); + OutputNode = CreateBVConst(cccc,2); + + //AARON + free(cccc); + //AARON + + break; + } + case BVSUB: { + bool carry = false; + CONSTANTBV::BitVector_sub(output,iii,jjj,&carry); + cccc = (char *)CONSTANTBV::BitVector_to_Bin(output); + OutputNode = CreateBVConst(cccc,2); + + //AARON + free(cccc); + //AARON + + break; + } + case BVUMINUS: { + bool carry = false; + + //AARON + if (iii != One) free(iii); + //AARON + + iii = ConvertToCONSTANTBV(BVConstEvaluator(t[0]).GetBVConst()); + CONSTANTBV::Set_Complement(output,iii); + CONSTANTBV::BitVector_add(output,output,One,&carry); + cccc = (char *)CONSTANTBV::BitVector_to_Bin(output); + OutputNode = CreateBVConst(cccc,2); + + //AARON + free(cccc); + //AARON + + break; + } + case BVEXTRACT: { + string s(BVConstEvaluator(t[0]).GetBVConst()); + unsigned int hi = GetUnsignedConst(BVConstEvaluator(t[1])); + unsigned int low = GetUnsignedConst(BVConstEvaluator(t[2])); + + //length of substr to chop + unsigned int len = hi-low+1; + //distance from MSB + hi = s.size()-1 - hi; + string ss = s.substr(hi,len); + OutputNode = CreateBVConst(ss.c_str(),2); + break; + } + case BVCONCAT: { + string s(BVConstEvaluator(t[0]).GetBVConst()); + string r(BVConstEvaluator(t[1]).GetBVConst()); + + string q(s+r); + OutputNode = CreateBVConst(q.c_str(),2); + break; + } + case BVMULT: { + unsigned * output1 = CONSTANTBV::BitVector_Create(2*inputwidth,true); + CONSTANTBV::ErrCode e = CONSTANTBV::BitVector_Multiply(output1,iii,jjj); + //error printing + if(0 != e) { + std::string ss("BVConstEvaluator:"); + ss += (const char*)BitVector_Error(e); + //destroy all the CONSTANTBV bitvectors + CONSTANTBV::BitVector_Destroy(iii); + CONSTANTBV::BitVector_Destroy(jjj); + CONSTANTBV::BitVector_Destroy(One); + CONSTANTBV::BitVector_Destroy(Zero); + FatalError(ss.c_str(), t); + } + + cccc = (char *)CONSTANTBV::BitVector_to_Bin(output1); + std::string s(cccc); + + //AARON + free(cccc); + //AARON + + s = s.substr(inputwidth,inputwidth); + OutputNode = CreateBVConst(s.c_str(),2); + CONSTANTBV::BitVector_Destroy(output1); + break; + } + case BVPLUS: { + bool carry = false; + ASTVec c = t.GetChildren(); + for(ASTVec::iterator it=c.begin(),itend=c.end();it!=itend;it++) { + unsigned int * kk = ConvertToCONSTANTBV(BVConstEvaluator(*it).GetBVConst()); + CONSTANTBV::BitVector_add(output,output,kk,&carry); + carry = false; + CONSTANTBV::BitVector_Destroy(kk); + } + cccc = (char *)CONSTANTBV::BitVector_to_Bin(output); + OutputNode = CreateBVConst(cccc,2); + + //AARON + free(cccc); + //AARON + + break; + } + case SBVDIV: + case SBVMOD: { + OutputNode = BVConstEvaluator(TranslateSignedDivMod(t)); + break; + } + case BVDIV: { + unsigned * quotient = CONSTANTBV::BitVector_Create(inputwidth,true); + unsigned * remainder = CONSTANTBV::BitVector_Create(inputwidth,true); + // iii is dividend, jjj is the divisor + CONSTANTBV::ErrCode e = CONSTANTBV::BitVector_Div_Pos(quotient,iii,jjj,remainder); + + if(0 != e) { + //error printing + if(counterexample_checking_during_refinement) { + OutputNode = CreateZeroConst(inputwidth); + bvdiv_exception_occured = true; + break; + } + else { + std::string ss("BVConstEvaluator:"); + ss += (const char*)BitVector_Error(e); + //destroy all the CONSTANTBV bitvectors + CONSTANTBV::BitVector_Destroy(iii); + CONSTANTBV::BitVector_Destroy(jjj); + CONSTANTBV::BitVector_Destroy(One); + CONSTANTBV::BitVector_Destroy(Zero); + + //AARON + iii = jjj = One = Zero = NULL; + //AARON + + FatalError(ss.c_str(), t); + } + } //end of error printing + + cccc = (char *)CONSTANTBV::BitVector_to_Bin(quotient); + OutputNode = CreateBVConst(cccc,2); + + //AARON + free(cccc); + CONSTANTBV::BitVector_Destroy(quotient); + CONSTANTBV::BitVector_Destroy(remainder); + //AARON + + break; + } + case BVMOD: { + unsigned * quotient = CONSTANTBV::BitVector_Create(inputwidth,true); + unsigned * remainder = CONSTANTBV::BitVector_Create(inputwidth,true); + // iii is dividend, jjj is the divisor + CONSTANTBV::ErrCode e = CONSTANTBV::BitVector_Div_Pos(quotient,iii,jjj,remainder); + + if(0 != e) { + //error printing + if(counterexample_checking_during_refinement) { + OutputNode = CreateZeroConst(inputwidth); + bvdiv_exception_occured = true; + break; + } + else { + std::string ss("BVConstEvaluator:"); + ss += (const char*)BitVector_Error(e); + //destroy all the CONSTANTBV bitvectors + CONSTANTBV::BitVector_Destroy(iii); + CONSTANTBV::BitVector_Destroy(jjj); + CONSTANTBV::BitVector_Destroy(One); + CONSTANTBV::BitVector_Destroy(Zero); + + //AARON + iii = jjj = One = Zero = NULL; + //AARON + + FatalError(ss.c_str(), t); + } + } //end of errory printing + + cccc = (char *)CONSTANTBV::BitVector_to_Bin(remainder); + OutputNode = CreateBVConst(cccc,2); + + //AARON + free(cccc); + CONSTANTBV::BitVector_Destroy(quotient); + CONSTANTBV::BitVector_Destroy(remainder); + //AARON + + break; + } + case ITE: + if(ASTTrue == t[0]) + OutputNode = BVConstEvaluator(t[1]); + else if(ASTFalse == t[0]) + OutputNode = BVConstEvaluator(t[2]); + else + FatalError("BVConstEvaluator: ITE condiional must be either TRUE or FALSE:",t); + break; + case EQ: + if(CONSTANTBV::BitVector_equal(iii,jjj)) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + case NEQ: + if(!CONSTANTBV::BitVector_equal(iii,jjj)) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + case BVLT: + if(-1 == CONSTANTBV::BitVector_Lexicompare(iii,jjj)) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + case BVLE: { + int comp = CONSTANTBV::BitVector_Lexicompare(iii,jjj); + if(comp <= 0) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + } + case BVGT: + if(1 == CONSTANTBV::BitVector_Lexicompare(iii,jjj)) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + case BVGE: { + int comp = CONSTANTBV::BitVector_Lexicompare(iii,jjj); + if(comp >= 0) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + } + case BVSLT: + if(-1 == CONSTANTBV::BitVector_Compare(iii,jjj)) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + case BVSLE: { + signed int comp = CONSTANTBV::BitVector_Compare(iii,jjj); + if(comp <= 0) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + } + case BVSGT: + if(1 == CONSTANTBV::BitVector_Compare(iii,jjj)) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + case BVSGE: { + int comp = CONSTANTBV::BitVector_Compare(iii,jjj); + if(comp >= 0) + OutputNode = ASTTrue; + else + OutputNode = ASTFalse; + break; + } + default: + FatalError("BVConstEvaluator: The input kind is not supported yet:",t); + break; + } + + + + // //destroy all the CONSTANTBV bitvectors +// CONSTANTBV::BitVector_Destroy(iii); +// CONSTANTBV::BitVector_Destroy(jjj); +// CONSTANTBV::BitVector_Destroy(output); + +// if(k == BVNEG || k == BVUMINUS) +// CONSTANTBV::BitVector_Destroy(One); +// else if(k == BVAND || k == BVOR || k == BVXOR || k == BVSUB || +// k == BVMULT || k == EQ || k == NEQ || k == BVLT || +// k == BVLE || k == BVGT || k == BVGE || k == BVSLT || +// k == BVSLE || k == BVSGT || k == BVSGE) { +// CONSTANTBV::BitVector_Destroy(One); +// CONSTANTBV::BitVector_Destroy(Zero); +// } + + //AARON + if (output != NULL) CONSTANTBV::BitVector_Destroy(output); + if (One != NULL) CONSTANTBV::BitVector_Destroy(One); + if (Zero != NULL) CONSTANTBV::BitVector_Destroy(Zero); + if (iii != NULL && iii != One) CONSTANTBV::BitVector_Destroy(iii); + if (jjj != NULL && jjj != Zero) CONSTANTBV::BitVector_Destroy(jjj); + //AARON + + UpdateSolverMap(t,OutputNode); + //UpdateSimplifyMap(t,OutputNode,false); + return OutputNode; + } + + + unsigned int * ConvertToCONSTANTBV(const char * s) { + unsigned int length = strlen(s); + unsigned char * ccc = (unsigned char *)s; + unsigned * iii = CONSTANTBV::BitVector_Create(length,true); + CONSTANTBV::ErrCode e = CONSTANTBV::BitVector_from_Bin(iii,ccc); + //error printing + if(0 != e) { + cerr << "ConverToCONSTANTBV: wrong bin value: " << BitVector_Error(e); + FatalError(""); + } + + return iii; + } +*/ +}; //end of namespace BEEV diff --git a/stp/c_interface/Makefile b/stp/c_interface/Makefile new file mode 100644 index 00000000..cf6b09d1 --- /dev/null +++ b/stp/c_interface/Makefile @@ -0,0 +1,13 @@ +include ../Makefile.common + +SRCS = c_interface.cpp +OBJS = $(SRCS:.cpp=.o) + +libcinterface.a: $(OBJS) + $(AR) rc $@ $^ + $(RANLIB) $@ + +clean: + rm -rf *.o *~ *.a .#* + +c_interface.o: c_interface.h diff --git a/stp/c_interface/c_interface.cpp b/stp/c_interface/c_interface.cpp new file mode 100644 index 00000000..f4e53114 --- /dev/null +++ b/stp/c_interface/c_interface.cpp @@ -0,0 +1,1548 @@ +/******************************************************************** + * AUTHORS: Vijay Ganesh, David L. Dill + * + * BEGIN DATE: November, 2005 + * + * LICENSE: Please view LICENSE file in the home dir of this Program + ********************************************************************/ +// -*- c++ -*- +#include "c_interface.h" + +#include <cstdlib> +#include <cassert> +#include <ostream> +#include <iostream> +#include "fdstream.h" +#include "../AST/AST.h" + +//These typedefs lower the effort of using the keyboard to type (too +//many overloaded meanings of the word type) +typedef BEEV::ASTNode node; +typedef BEEV::ASTNode* nodestar; +typedef BEEV::BeevMgr* bmstar; +typedef BEEV::ASTVec nodelist; +typedef BEEV::CompleteCounterExample* CompleteCEStar; +BEEV::ASTVec *decls = NULL; +//vector<BEEV::ASTNode *> created_exprs; +bool cinterface_exprdelete_on = false; + +void vc_setFlags(char c) { + std::string helpstring = "Usage: stp [-option] [infile]\n\n"; + helpstring += "-r : switch refinement off (optimizations are ON by default)\n"; + helpstring += "-w : switch wordlevel solver off (optimizations are ON by default)\n"; + helpstring += "-a : switch optimizations off (optimizations are ON by default)\n"; + helpstring += "-s : print function statistics\n"; + helpstring += "-v : print nodes \n"; + helpstring += "-c : construct counterexample\n"; + helpstring += "-d : check counterexample\n"; + helpstring += "-p : print counterexample\n"; + helpstring += "-h : help\n"; + + switch(c) { + case 'a' : + BEEV::optimize = false; + BEEV::wordlevel_solve = false; + break; + case 'b': + BEEV::print_STPinput_back = true; + break; + case 'c': + BEEV::construct_counterexample = true; + break; + case 'd': + BEEV::construct_counterexample = true; + BEEV::check_counterexample = true; + break; + case 'e': + BEEV::variable_activity_optimize = true; + break; + case 'f': + BEEV::smtlib_parser_enable = true; + break; + case 'h': + cout << helpstring; + BEEV::FatalError(""); + break; + case 'l' : + BEEV::linear_search = true; + break; + case 'n': + BEEV::print_output = true; + break; + case 'p': + BEEV::print_counterexample = true; + break; + case 'q': + BEEV::print_arrayval_declaredorder = true; + break; + case 'r': + BEEV::arrayread_refinement = false; + break; + case 's' : + BEEV::stats = true; + break; + case 'u': + BEEV::arraywrite_refinement = true; + break; + case 'v' : + BEEV::print_nodes = true; + break; + case 'w': + BEEV::wordlevel_solve = false; + break; + case 'x': + cinterface_exprdelete_on = true; + break; + case 'z': + BEEV::print_sat_varorder = true; + break; + default: + std::string s = "C_interface: vc_setFlags: Unrecognized commandline flag:\n"; + s += helpstring; + BEEV::FatalError(s.c_str()); + break; + } +} + +//Create a validity Checker. This is the global BeevMgr +VC vc_createValidityChecker(void) { + vc_setFlags('d'); +#ifdef NATIVE_C_ARITH +#else + CONSTANTBV::ErrCode c = CONSTANTBV::BitVector_Boot(); + if(0 != c) { + cout << CONSTANTBV::BitVector_Error(c) << endl; + return 0; + } +#endif + bmstar bm = new BEEV::BeevMgr(); + decls = new BEEV::ASTVec(); + //created_exprs.clear(); + return (VC)bm; +} + +// Expr I/O +void vc_printExpr(VC vc, Expr e) { + //do not print in lisp mode + //bmstar b = (bmstar)vc; + BEEV::ASTNode q = (*(nodestar)e); + // b->Begin_RemoveWrites = true; + // BEEV::ASTNode q = b->SimplifyFormula_TopLevel(*((nodestar)e),false); + // b->Begin_RemoveWrites = false; + q.PL_Print(cout); +} + +void vc_printExprFile(VC vc, Expr e, int fd) { + fdostream os(fd); + ((nodestar)e)->PL_Print(os); + //os.flush(); +} + +static void vc_printVarDeclsToStream(VC vc, ostream &os) { + for(BEEV::ASTVec::iterator i = decls->begin(),iend=decls->end();i!=iend;i++) { + node a = *i; + switch(a.GetType()) { + case BEEV::BITVECTOR_TYPE: + a.PL_Print(os); + os << " : BITVECTOR(" << a.GetValueWidth() << ");" << endl; + break; + case BEEV::ARRAY_TYPE: + a.PL_Print(os); + os << " : ARRAY " << "BITVECTOR(" << a.GetIndexWidth() << ") OF "; + os << "BITVECTOR(" << a.GetValueWidth() << ");" << endl; + break; + case BEEV::BOOLEAN_TYPE: + a.PL_Print(os); + os << " : BOOLEAN;" << endl; + break; + default: + BEEV::FatalError("vc_printDeclsToStream: Unsupported type",a); + break; + } + } +} + +void vc_printVarDecls(VC vc) { + vc_printVarDeclsToStream(vc, cout); +} + +static void vc_printAssertsToStream(VC vc, ostream &os, int simplify_print) { + bmstar b = (bmstar)vc; + BEEV::ASTVec v = b->GetAsserts(); + for(BEEV::ASTVec::iterator i=v.begin(),iend=v.end();i!=iend;i++) { + b->Begin_RemoveWrites = true; + BEEV::ASTNode q = (simplify_print == 1) ? b->SimplifyFormula_TopLevel(*i,false) : *i; + q = (simplify_print == 1) ? b->SimplifyFormula_TopLevel(q,false) : q; + b->Begin_RemoveWrites = false; + os << "ASSERT( "; + q.PL_Print(os); + os << ");" << endl; + } +} + +void vc_printAsserts(VC vc, int simplify_print) { + vc_printAssertsToStream(vc, cout, simplify_print); +} + +void vc_printQueryStateToBuffer(VC vc, Expr e, char **buf, unsigned long *len, int simplify_print){ + assert(vc); + assert(e); + assert(buf); + assert(len); + bmstar b = (bmstar)vc; + + // formate the state of the query + stringstream os; + vc_printVarDeclsToStream(vc, os); + os << "%----------------------------------------------------" << endl; + vc_printAssertsToStream(vc, os, simplify_print); + os << "%----------------------------------------------------" << endl; + os << "QUERY( "; + b->Begin_RemoveWrites = true; + BEEV::ASTNode q = (simplify_print == 1) ? b->SimplifyFormula_TopLevel(*((nodestar)e),false) : *(nodestar)e; + b->Begin_RemoveWrites = false; + q.PL_Print(os); + os << " );" << endl; + + // convert to a c buffer + string s = os.str(); + const char *cstr = s.c_str(); + unsigned long size = s.size() + 1; // number of chars + terminating null + *buf = (char *)malloc(size); + if (!(*buf)) { + fprintf(stderr, "malloc(%lu) failed.", size); + assert(*buf); + } + *len = size; + memcpy(*buf, cstr, size); +} + +void vc_printCounterExampleToBuffer(VC vc, char **buf, unsigned long *len) { + assert(vc); + assert(buf); + assert(len); + bmstar b = (bmstar)vc; + + // formate the state of the query + std::ostringstream os; + BEEV::print_counterexample = true; + os << "COUNTEREXAMPLE BEGIN: \n"; + b->PrintCounterExample(true,os); + os << "COUNTEREXAMPLE END: \n"; + + // convert to a c buffer + string s = os.str(); + const char *cstr = s.c_str(); + unsigned long size = s.size() + 1; // number of chars + terminating null + *buf = (char *)malloc(size); + if (!(*buf)) { + fprintf(stderr, "malloc(%lu) failed.", size); + assert(*buf); + } + *len = size; + memcpy(*buf, cstr, size); +} + +void vc_printExprToBuffer(VC vc, Expr e, char **buf, unsigned long * len) { + stringstream os; + //bmstar b = (bmstar)vc; + BEEV::ASTNode q = *((nodestar)e); + // b->Begin_RemoveWrites = true; + // BEEV::ASTNode q = b->SimplifyFormula_TopLevel(*((nodestar)e),false); + // b->Begin_RemoveWrites = false; + q.PL_Print(os); + //((nodestar)e)->PL_Print(os); + string s = os.str(); + const char * cstr = s.c_str(); + unsigned long size = s.size() + 1; // number of chars + terminating null + *buf = (char *)malloc(size); + *len = size; + memcpy(*buf, cstr, size); +} + +void vc_printQuery(VC vc){ + ostream& os = std::cout; + bmstar b = (bmstar)vc; + os << "QUERY("; + //b->Begin_RemoveWrites = true; + //BEEV::ASTNode q = b->SimplifyFormula_TopLevel(b->GetQuery(),false); + BEEV::ASTNode q = b->GetQuery(); + //b->Begin_RemoveWrites = false; + q.PL_Print(os); + // b->GetQuery().PL_Print(os); + os << ");" << endl; +} + +///////////////////////////////////////////////////////////////////////////// +// Array-related methods // +///////////////////////////////////////////////////////////////////////////// +//! Create an array type +Type vc_arrayType(VC vc, Type typeIndex, Type typeData) { + bmstar b = (bmstar)vc; + nodestar ti = (nodestar)typeIndex; + nodestar td = (nodestar)typeData; + + if(!(ti->GetKind() == BEEV::BITVECTOR && (*ti)[0].GetKind() == BEEV::BVCONST)) + BEEV::FatalError("Tyring to build array whose indextype i is not a BITVECTOR, where i = ",*ti); + if(!(td->GetKind() == BEEV::BITVECTOR && (*td)[0].GetKind() == BEEV::BVCONST)) + BEEV::FatalError("Trying to build an array whose valuetype v is not a BITVECTOR. where a = ",*td); + nodestar output = new node(b->CreateNode(BEEV::ARRAY,(*ti)[0],(*td)[0])); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return (Type)output; +} + +//! Create an expression for the value of array at the given index +Expr vc_readExpr(VC vc, Expr array, Expr index) { + bmstar b = (bmstar)vc; + nodestar a = (nodestar)array; + nodestar i = (nodestar)index; + + b->BVTypeCheck(*a); + b->BVTypeCheck(*i); + node o = b->CreateTerm(BEEV::READ,a->GetValueWidth(),*a,*i); + b->BVTypeCheck(o); + + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +// //! Array update; equivalent to "array WITH [index] := newValue" +Expr vc_writeExpr(VC vc, Expr array, Expr index, Expr newValue) { + bmstar b = (bmstar)vc; + nodestar a = (nodestar)array; + nodestar i = (nodestar)index; + nodestar n = (nodestar)newValue; + + b->BVTypeCheck(*a); + b->BVTypeCheck(*i); + b->BVTypeCheck(*n); + node o = b->CreateTerm(BEEV::WRITE,a->GetValueWidth(),*a,*i,*n); + o.SetIndexWidth(a->GetIndexWidth()); + b->BVTypeCheck(o); + + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +///////////////////////////////////////////////////////////////////////////// +// Context-related methods // +///////////////////////////////////////////////////////////////////////////// +//! Assert a new formula in the current context. +/*! The formula must have Boolean type. */ +void vc_assertFormula(VC vc, Expr e) { + nodestar a = (nodestar)e; + bmstar b = (bmstar)vc; + + if(!BEEV::is_Form_kind(a->GetKind())) + BEEV::FatalError("Trying to assert a NON formula: ",*a); + + b->BVTypeCheck(*a); + b->AddAssert(*a); +} + +//! Check validity of e in the current context. +/*! If the result is true, then the resulting context is the same as + * the starting context. If the result is false, then the resulting + * context is a context in which e is false. e must have Boolean + * type. */ +int vc_query(VC vc, Expr e) { + nodestar a = (nodestar)e; + bmstar b = (bmstar)vc; + + if(!BEEV::is_Form_kind(a->GetKind())) + BEEV::FatalError("CInterface: Trying to QUERY a NON formula: ",*a); + + b->BVTypeCheck(*a); + b->AddQuery(*a); + + const BEEV::ASTVec v = b->GetAsserts(); + node o; + if(!v.empty()) { + if(v.size()==1) + return b->TopLevelSAT(v[0],*a); + else + return b->TopLevelSAT(b->CreateNode(BEEV::AND,v),*a); + } + else + return b->TopLevelSAT(b->CreateNode(BEEV::TRUE),*a); +} + +void vc_push(VC vc) { + bmstar b = (bmstar)vc; + b->ClearAllCaches(); + b->Push(); +} + +void vc_pop(VC vc) { + bmstar b = (bmstar)vc; + b->Pop(); +} + +void vc_printCounterExample(VC vc) { + bmstar b = (bmstar)vc; + BEEV::print_counterexample = true; + cout << "COUNTEREXAMPLE BEGIN: \n"; + b->PrintCounterExample(true); + cout << "COUNTEREXAMPLE END: \n"; +} + +// //! Return the counterexample after a failed query. +// /*! This method should only be called after a query which returns +// * false. It will try to return the simplest possible set of +// * assertions which are sufficient to make the queried expression +// * false. The caller is responsible for freeing the array when +// * finished with it. +// */ + +Expr vc_getCounterExample(VC vc, Expr e) { + nodestar a = (nodestar)e; + bmstar b = (bmstar)vc; + + bool t = false; + if(b->CounterExampleSize()) + t = true; + nodestar output = new node(b->GetCounterExample(t, *a)); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +int vc_counterexample_size(VC vc) { + bmstar b = (bmstar)vc; + return b->CounterExampleSize(); +} + +WholeCounterExample vc_getWholeCounterExample(VC vc) { + bmstar b = (bmstar)vc; + CompleteCEStar c = + new BEEV::CompleteCounterExample(b->GetCompleteCounterExample(), b); + return c; +} + +Expr vc_getTermFromCounterExample(VC vc, Expr e, CompleteCEStar cc) { + //bmstar b = (bmstar)vc; + nodestar n = (nodestar)e; + CompleteCEStar c = (CompleteCEStar)cc; + + nodestar output = new node(c->GetCounterExample(*n)); + return output; +} + +int vc_getBVLength(VC vc, Expr ex) { + nodestar e = (nodestar)ex; + + if(BEEV::BITVECTOR_TYPE != e->GetType()) { + BEEV::FatalError("c_interface: vc_GetBVLength: Input expression must be a bit-vector"); + } + + return e->GetValueWidth(); +} // end of vc_getBVLength + +///////////////////////////////////////////////////////////////////////////// +// Expr Creation methods // +///////////////////////////////////////////////////////////////////////////// +//! Create a variable with a given name and type +/*! The type cannot be a function type. */ +Expr vc_varExpr1(VC vc, char* name, + int indexwidth, int valuewidth) { + bmstar b = (bmstar)vc; + + node o = b->CreateSymbol(name); + o.SetIndexWidth(indexwidth); + o.SetValueWidth(valuewidth); + + nodestar output = new node(o); + ////if(cinterface_exprdelete_on) created_exprs.push_back(output); + b->BVTypeCheck(*output); + + //store the decls in a vector for printing purposes + decls->push_back(o); + return output; +} + +Expr vc_varExpr(VC vc, char * name, Type type) { + bmstar b = (bmstar)vc; + nodestar a = (nodestar)type; + + node o = b->CreateSymbol(name); + switch(a->GetKind()) { + case BEEV::BITVECTOR: + o.SetIndexWidth(0); + o.SetValueWidth(GetUnsignedConst((*a)[0])); + break; + case BEEV::ARRAY: + o.SetIndexWidth(GetUnsignedConst((*a)[0])); + o.SetValueWidth(GetUnsignedConst((*a)[1])); + break; + case BEEV::BOOLEAN: + o.SetIndexWidth(0); + o.SetValueWidth(0); + break; + default: + BEEV::FatalError("CInterface: vc_varExpr: Unsupported type",*a); + break; + } + nodestar output = new node(o); + ////if(cinterface_exprdelete_on) created_exprs.push_back(output); + b->BVTypeCheck(*output); + + //store the decls in a vector for printing purposes + decls->push_back(o); + return output; +} + +//! Create an equality expression. The two children must have the +//same type. +Expr vc_eqExpr(VC vc, Expr ccc0, Expr ccc1) { + bmstar b = (bmstar)vc; + + nodestar a = (nodestar)ccc0; + nodestar aa = (nodestar)ccc1; + b->BVTypeCheck(*a); + b->BVTypeCheck(*aa); + node o = b->CreateNode(BEEV::EQ,*a,*aa); + + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_boolType(VC vc) { + bmstar b = (bmstar)vc; + + node o = b->CreateNode(BEEV::BOOLEAN); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +///////////////////////////////////////////////////////////////////////////// +// BOOLEAN EXPR Creation methods // +///////////////////////////////////////////////////////////////////////////// +// The following functions create Boolean expressions. The children +// provided as arguments must be of type Boolean. +Expr vc_trueExpr(VC vc) { + bmstar b = (bmstar)vc; + node c = b->CreateNode(BEEV::TRUE); + + nodestar d = new node(c); + //if(cinterface_exprdelete_on) created_exprs.push_back(d); + return d; +} + +Expr vc_falseExpr(VC vc) { + bmstar b = (bmstar)vc; + node c = b->CreateNode(BEEV::FALSE); + + nodestar d = new node(c); + //if(cinterface_exprdelete_on) created_exprs.push_back(d); + return d; +} + +Expr vc_notExpr(VC vc, Expr ccc) { + bmstar b = (bmstar)vc; + nodestar a = (nodestar)ccc; + + node o = b->CreateNode(BEEV::NOT,*a); + b->BVTypeCheck(o); + + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_andExpr(VC vc, Expr left, Expr right) { + bmstar b = (bmstar)vc; + nodestar l = (nodestar)left; + nodestar r = (nodestar)right; + + node o = b->CreateNode(BEEV::AND,*l,*r); + b->BVTypeCheck(o); + + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_orExpr(VC vc, Expr left, Expr right) { + bmstar b = (bmstar)vc; + nodestar l = (nodestar)left; + nodestar r = (nodestar)right; + + node o = b->CreateNode(BEEV::OR,*l,*r); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_andExprN(VC vc, Expr* cc, int n) { + bmstar b = (bmstar)vc; + nodestar * c = (nodestar *)cc; + nodelist d; + + for(int i =0; i < n; i++) + d.push_back(*c[i]); + + node o = b->CreateNode(BEEV::AND,d); + b->BVTypeCheck(o); + + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + + +Expr vc_orExprN(VC vc, Expr* cc, int n) { + bmstar b = (bmstar)vc; + nodestar * c = (nodestar *)cc; + nodelist d; + + for(int i =0; i < n; i++) + d.push_back(*c[i]); + + node o = b->CreateNode(BEEV::OR,d); + b->BVTypeCheck(o); + + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_iteExpr(VC vc, Expr cond, Expr thenpart, Expr elsepart){ + bmstar b = (bmstar)vc; + nodestar c = (nodestar)cond; + nodestar t = (nodestar)thenpart; + nodestar e = (nodestar)elsepart; + + b->BVTypeCheck(*c); + b->BVTypeCheck(*t); + b->BVTypeCheck(*e); + node o; + //if the user asks for a formula then produce a formula, else + //prodcue a term + if(BEEV::BOOLEAN_TYPE == t->GetType()) + o = b->CreateNode(BEEV::ITE,*c,*t,*e); + else { + o = b->CreateTerm(BEEV::ITE,t->GetValueWidth(),*c,*t,*e); + o.SetIndexWidth(t->GetIndexWidth()); + } + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_impliesExpr(VC vc, Expr antecedent, Expr consequent){ + bmstar b = (bmstar)vc; + nodestar c = (nodestar)antecedent; + nodestar t = (nodestar)consequent; + + b->BVTypeCheck(*c); + b->BVTypeCheck(*t); + node o; + + o = b->CreateNode(BEEV::IMPLIES,*c,*t); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_iffExpr(VC vc, Expr e0, Expr e1){ + bmstar b = (bmstar)vc; + nodestar c = (nodestar)e0; + nodestar t = (nodestar)e1; + + b->BVTypeCheck(*c); + b->BVTypeCheck(*t); + node o; + + o = b->CreateNode(BEEV::IFF,*c,*t); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_boolToBVExpr(VC vc, Expr form) { + bmstar b = (bmstar)vc; + nodestar c = (nodestar)form; + + b->BVTypeCheck(*c); + if(!is_Form_kind(c->GetKind())) + BEEV::FatalError("CInterface: vc_BoolToBVExpr: You have input a NON formula:",*c); + + node o; + node one = b->CreateOneConst(1); + node zero = b->CreateZeroConst(1); + o = b->CreateTerm(BEEV::ITE,1,*c,one,zero); + + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +///////////////////////////////////////////////////////////////////////////// +// BITVECTOR EXPR Creation methods // +///////////////////////////////////////////////////////////////////////////// +Type vc_bvType(VC vc, int num_bits) { + bmstar b = (bmstar)vc; + + if(!(0 < num_bits)) + BEEV::FatalError("CInterface: number of bits in a bvtype must be a positive integer:", + b->CreateNode(BEEV::UNDEFINED)); + + node e = b->CreateBVConst(32, num_bits); + nodestar output = new node(b->CreateNode(BEEV::BITVECTOR,e)); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Type vc_bv32Type(VC vc) { + return vc_bvType(vc,32); +} + + +Expr vc_bvConstExprFromStr(VC vc, char* binary_repr) { + bmstar b = (bmstar)vc; + + node n = b->CreateBVConst(binary_repr,2); + b->BVTypeCheck(n); + nodestar output = new node(n); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_bvConstExprFromInt(VC vc, + int n_bits, + unsigned int value) { + bmstar b = (bmstar)vc; + + unsigned long long int v = (unsigned long long int)value; + node n = b->CreateBVConst(n_bits, v); + b->BVTypeCheck(n); + nodestar output = new node(n); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_bvConstExprFromLL(VC vc, + int n_bits, + unsigned long long value) { + bmstar b = (bmstar)vc; + + node n = b->CreateBVConst(n_bits, value); + b->BVTypeCheck(n); + nodestar output = new node(n); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_bvConcatExpr(VC vc, Expr left, Expr right) { + bmstar b = (bmstar)vc; + nodestar l = (nodestar)left; + nodestar r = (nodestar)right; + + b->BVTypeCheck(*l); + b->BVTypeCheck(*r); + node o = + b->CreateTerm(BEEV::BVCONCAT, + l->GetValueWidth()+ r->GetValueWidth(),*l,*r); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_bvPlusExpr(VC vc, int n_bits, Expr left, Expr right){ + bmstar b = (bmstar)vc; + nodestar l = (nodestar)left; + nodestar r = (nodestar)right; + + b->BVTypeCheck(*l); + b->BVTypeCheck(*r); + node o = b->CreateTerm(BEEV::BVPLUS,n_bits, *l, *r); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + + +Expr vc_bv32PlusExpr(VC vc, Expr left, Expr right) { + return vc_bvPlusExpr(vc, 32, left, right); +} + + +Expr vc_bvMinusExpr(VC vc, int n_bits, Expr left, Expr right) { + bmstar b = (bmstar)vc; + nodestar l = (nodestar)left; + nodestar r = (nodestar)right; + + b->BVTypeCheck(*l); + b->BVTypeCheck(*r); + node o = b->CreateTerm(BEEV::BVSUB,n_bits, *l, *r); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + + +Expr vc_bv32MinusExpr(VC vc, Expr left, Expr right) { + return vc_bvMinusExpr(vc, 32, left, right); +} + + +Expr vc_bvMultExpr(VC vc, int n_bits, Expr left, Expr right) { + bmstar b = (bmstar)vc; + nodestar l = (nodestar)left; + nodestar r = (nodestar)right; + + b->BVTypeCheck(*l); + b->BVTypeCheck(*r); + node o = b->CreateTerm(BEEV::BVMULT,n_bits, *l, *r); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_bvDivExpr(VC vc, int n_bits, Expr left, Expr right) { + bmstar b = (bmstar)vc; + nodestar l = (nodestar)left; + nodestar r = (nodestar)right; + + b->BVTypeCheck(*l); + b->BVTypeCheck(*r); + node o = b->CreateTerm(BEEV::BVDIV,n_bits, *l, *r); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_bvModExpr(VC vc, int n_bits, Expr left, Expr right) { + bmstar b = (bmstar)vc; + nodestar l = (nodestar)left; + nodestar r = (nodestar)right; + + b->BVTypeCheck(*l); + b->BVTypeCheck(*r); + node o = b->CreateTerm(BEEV::BVMOD,n_bits, *l, *r); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_sbvDivExpr(VC vc, int n_bits, Expr left, Expr right) { + bmstar b = (bmstar)vc; + nodestar l = (nodestar)left; + nodestar r = (nodestar)right; + + b->BVTypeCheck(*l); + b->BVTypeCheck(*r); + node o = b->CreateTerm(BEEV::SBVDIV,n_bits, *l, *r); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_sbvModExpr(VC vc, int n_bits, Expr left, Expr right) { + bmstar b = (bmstar)vc; + nodestar l = (nodestar)left; + nodestar r = (nodestar)right; + + b->BVTypeCheck(*l); + b->BVTypeCheck(*r); + node o = b->CreateTerm(BEEV::SBVMOD,n_bits, *l, *r); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_bv32MultExpr(VC vc, Expr left, Expr right) { + return vc_bvMultExpr(vc, 32, left, right); +} + + +// unsigned comparators +Expr vc_bvLtExpr(VC vc, Expr left, Expr right) { + bmstar b = (bmstar)vc; + nodestar l = (nodestar)left; + nodestar r = (nodestar)right; + + b->BVTypeCheck(*l); + b->BVTypeCheck(*r); + node o = b->CreateNode(BEEV::BVLT, *l, *r); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_bvLeExpr(VC vc, Expr left, Expr right) { + bmstar b = (bmstar)vc; + nodestar l = (nodestar)left; + nodestar r = (nodestar)right; + + b->BVTypeCheck(*l); + b->BVTypeCheck(*r); + node o = b->CreateNode(BEEV::BVLE, *l, *r); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_bvGtExpr(VC vc, Expr left, Expr right) { + bmstar b = (bmstar)vc; + nodestar l = (nodestar)left; + nodestar r = (nodestar)right; + + b->BVTypeCheck(*l); + b->BVTypeCheck(*r); + node o = b->CreateNode(BEEV::BVGT, *l, *r); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_bvGeExpr(VC vc, Expr left, Expr right) { + bmstar b = (bmstar)vc; + nodestar l = (nodestar)left; + nodestar r = (nodestar)right; + + b->BVTypeCheck(*l); + b->BVTypeCheck(*r); + node o = b->CreateNode(BEEV::BVGE, *l, *r); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +// signed comparators +Expr vc_sbvLtExpr(VC vc, Expr left, Expr right) { + bmstar b = (bmstar)vc; + nodestar l = (nodestar)left; + nodestar r = (nodestar)right; + + b->BVTypeCheck(*l); + b->BVTypeCheck(*r); + node o = b->CreateNode(BEEV::BVSLT, *l, *r); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_sbvLeExpr(VC vc, Expr left, Expr right) { + bmstar b = (bmstar)vc; + nodestar l = (nodestar)left; + nodestar r = (nodestar)right; + + b->BVTypeCheck(*l); + b->BVTypeCheck(*r); + node o = b->CreateNode(BEEV::BVSLE, *l, *r); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_sbvGtExpr(VC vc, Expr left, Expr right) { + bmstar b = (bmstar)vc; + nodestar l = (nodestar)left; + nodestar r = (nodestar)right; + + b->BVTypeCheck(*l); + b->BVTypeCheck(*r); + node o = b->CreateNode(BEEV::BVSGT, *l, *r); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_sbvGeExpr(VC vc, Expr left, Expr right) { + bmstar b = (bmstar)vc; + nodestar l = (nodestar)left; + nodestar r = (nodestar)right; + + b->BVTypeCheck(*l); + b->BVTypeCheck(*r); + node o = b->CreateNode(BEEV::BVSGE, *l, *r); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_bvUMinusExpr(VC vc, Expr ccc) { + bmstar b = (bmstar)vc; + nodestar a = (nodestar)ccc; + b->BVTypeCheck(*a); + + node o = b->CreateTerm(BEEV::BVUMINUS, a->GetValueWidth(), *a); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +// bitwise operations: these are terms not formulas +Expr vc_bvAndExpr(VC vc, Expr left, Expr right) { + bmstar b = (bmstar)vc; + nodestar l = (nodestar)left; + nodestar r = (nodestar)right; + + b->BVTypeCheck(*l); + b->BVTypeCheck(*r); + node o = b->CreateTerm(BEEV::BVAND, (*l).GetValueWidth(), *l, *r); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_bvOrExpr(VC vc, Expr left, Expr right) { + bmstar b = (bmstar)vc; + nodestar l = (nodestar)left; + nodestar r = (nodestar)right; + + b->BVTypeCheck(*l); + b->BVTypeCheck(*r); + node o = b->CreateTerm(BEEV::BVOR, (*l).GetValueWidth(), *l, *r); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_bvXorExpr(VC vc, Expr left, Expr right) { + bmstar b = (bmstar)vc; + nodestar l = (nodestar)left; + nodestar r = (nodestar)right; + + b->BVTypeCheck(*l); + b->BVTypeCheck(*r); + node o = b->CreateTerm(BEEV::BVXOR, (*l).GetValueWidth(), *l, *r); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_bvNotExpr(VC vc, Expr ccc) { + bmstar b = (bmstar)vc; + nodestar a = (nodestar)ccc; + + b->BVTypeCheck(*a); + node o = b->CreateTerm(BEEV::BVNEG, a->GetValueWidth(), *a); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_bvLeftShiftExpr(VC vc, int sh_amt, Expr ccc) { + bmstar b = (bmstar)vc; + nodestar a = (nodestar)ccc; + b->BVTypeCheck(*a); + + //convert leftshift to bvconcat + if(0 != sh_amt) { + node len = b->CreateBVConst(sh_amt, 0); + node o = b->CreateTerm(BEEV::BVCONCAT, a->GetValueWidth() + sh_amt, *a, len); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; + } + else + return a; +} + +Expr vc_bvRightShiftExpr(VC vc, int sh_amt, Expr ccc) { + bmstar b = (bmstar)vc; + nodestar a = (nodestar)ccc; + b->BVTypeCheck(*a); + + unsigned int w = a->GetValueWidth(); + //the amount by which you are rightshifting + //is less-than/equal-to the length of input + //bitvector + if(0 < (unsigned)sh_amt && (unsigned)sh_amt <= w) { + node len = b->CreateBVConst(sh_amt, 0); + node hi = b->CreateBVConst(32,w-1); + node low = b->CreateBVConst(32,sh_amt); + node extract = b->CreateTerm(BEEV::BVEXTRACT,w-sh_amt,*a,hi,low); + + node n = b->CreateTerm(BEEV::BVCONCAT, w,len, extract); + b->BVTypeCheck(n); + nodestar output = new node(n); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; + } + else if(sh_amt == 0) + return a; + else { + if(0== w) + BEEV::FatalError("CInterface: vc_bvRightShiftExpr: cannot have a bitvector of length 0:",*a); + nodestar output = new node(b->CreateBVConst(w,0)); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; + } +} + +/* Same as vc_bvLeftShift only that the answer in 32 bits long */ +Expr vc_bv32LeftShiftExpr(VC vc, int sh_amt, Expr child) { + return vc_bvExtract(vc, vc_bvLeftShiftExpr(vc, sh_amt, child), 31, 0); +} + +/* Same as vc_bvRightShift only that the answer in 32 bits long */ +Expr vc_bv32RightShiftExpr(VC vc, int sh_amt, Expr child) { + return vc_bvExtract(vc, vc_bvRightShiftExpr(vc, sh_amt, child), 31, 0); +} + + +Expr vc_bvVar32LeftShiftExpr(VC vc, Expr sh_amt, Expr child) { + Expr ifpart; + Expr thenpart; + Expr elsepart = vc_trueExpr(vc); + Expr ite = vc_trueExpr(vc); + + for(int count=32; count >= 0; count--){ + if(count != 32) { + ifpart = vc_eqExpr(vc, sh_amt, + vc_bvConstExprFromInt(vc, 32, count)); + thenpart = vc_bvExtract(vc, + vc_bvLeftShiftExpr(vc, count, child), + 31, 0); + + ite = vc_iteExpr(vc,ifpart,thenpart,elsepart); + elsepart = ite; + } + else + elsepart = vc_bvConstExprFromInt(vc,32, 0); + } + return ite; +} + +Expr vc_bvVar32DivByPowOfTwoExpr(VC vc, Expr child, Expr rhs) { + Expr ifpart; + Expr thenpart; + Expr elsepart = vc_trueExpr(vc); + Expr ite = vc_trueExpr(vc); + + for(int count=32; count >= 0; count--){ + if(count != 32) { + ifpart = vc_eqExpr(vc, rhs, + vc_bvConstExprFromInt(vc, 32, 1 << count)); + thenpart = vc_bvRightShiftExpr(vc, count, child); + ite = vc_iteExpr(vc,ifpart,thenpart,elsepart); + elsepart = ite; + } else { + elsepart = vc_bvConstExprFromInt(vc,32, 0); + } + } + return ite; +} + +Expr vc_bvVar32RightShiftExpr(VC vc, Expr sh_amt, Expr child) { + Expr ifpart; + Expr thenpart; + Expr elsepart = vc_trueExpr(vc); + Expr ite = vc_trueExpr(vc); + + for(int count=32; count >= 0; count--){ + if(count != 32) { + ifpart = vc_eqExpr(vc, sh_amt, + vc_bvConstExprFromInt(vc, 32, count)); + thenpart = vc_bvRightShiftExpr(vc, count, child); + ite = vc_iteExpr(vc,ifpart,thenpart,elsepart); + elsepart = ite; + } else { + elsepart = vc_bvConstExprFromInt(vc,32, 0); + } + } + return ite; +} + +Expr vc_bvExtract(VC vc, Expr ccc, int hi_num, int low_num) { + bmstar b = (bmstar)vc; + nodestar a = (nodestar)ccc; + b->BVTypeCheck(*a); + + node hi = b->CreateBVConst(32,hi_num); + node low = b->CreateBVConst(32,low_num); + node o = b->CreateTerm(BEEV::BVEXTRACT,hi_num-low_num+1,*a,hi,low); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_bvBoolExtract(VC vc, Expr ccc, int bit_num) { + bmstar b = (bmstar)vc; + nodestar a = (nodestar)ccc; + b->BVTypeCheck(*a); + + node bit = b->CreateBVConst(32,bit_num); + //node o = b->CreateNode(BEEV::BVGETBIT,*a,bit); + node zero = b->CreateBVConst(1,0); + node oo = b->CreateTerm(BEEV::BVEXTRACT,1,*a,bit,bit); + node o = b->CreateNode(BEEV::EQ,oo,zero); + b->BVTypeCheck(o); + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +Expr vc_bvSignExtend(VC vc, Expr ccc, int nbits) { + bmstar b = (bmstar)vc; + nodestar a = (nodestar)ccc; + + //width of the expr which is being sign extended. nbits is the + //resulting length of the signextended expr + b->BVTypeCheck(*a); + + unsigned exprlen = a->GetValueWidth(); + unsigned outputlen = nbits; + node n; + if(exprlen >= outputlen) { + //extract + node hi = b->CreateBVConst(32,outputlen-1); + node low = b->CreateBVConst(32,0); + n = b->CreateTerm(BEEV::BVEXTRACT,nbits,*a,hi,low); + b->BVTypeCheck(n); + } + else { + //sign extend + BEEV::ASTNode width = b->CreateBVConst(32,nbits); + n = b->CreateTerm(BEEV::BVSX,nbits,*a, width); + } + + b->BVTypeCheck(n); + nodestar output = new node(n); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; +} + +//! Return an int from a constant bitvector expression +int getBVInt(Expr e) { + //bmstar b = (bmstar)vc; + nodestar a = (nodestar)e; + + if(BEEV::BVCONST != a->GetKind()) + BEEV::FatalError("CInterface: getBVInt: Attempting to extract int value from a NON-constant BITVECTOR: ",*a); + return (int)GetUnsignedConst(*a); +} + +//! Return an unsigned int from a constant bitvector expression +unsigned int getBVUnsigned(Expr e) { + //bmstar b = (bmstar)vc; + nodestar a = (nodestar)e; + + if(BEEV::BVCONST != a->GetKind()) + BEEV::FatalError("getBVUnsigned: Attempting to extract int value from a NON-constant BITVECTOR: ",*a); + return (unsigned int)GetUnsignedConst(*a); +} + +//! Return an unsigned long long int from a constant bitvector expression +unsigned long long int getBVUnsignedLongLong(Expr e) { + //bmstar b = (bmstar)vc; + nodestar a = (nodestar)e; + + if(BEEV::BVCONST != a->GetKind()) + BEEV::FatalError("getBVUnsigned: Attempting to extract int value from a NON-constant BITVECTOR: ",*a); +#ifdef NATIVE_C_ARITH + return (unsigned long long int)a->GetBVConst(); +#else + unsigned* bv = a->GetBVConst(); + + char * str_bv = (char *)CONSTANTBV::BitVector_to_Bin(bv); + unsigned long long int tmp = strtoull(str_bv,NULL,2); + CONSTANTBV::BitVector_Dispose((unsigned char *)str_bv); + return tmp; +#endif +} + + +Expr vc_simplify(VC vc, Expr e) { + bmstar b = (bmstar)vc; + nodestar a = (nodestar)e; + + if(BEEV::BOOLEAN_TYPE == a->GetType()) { + nodestar output = new node(b->SimplifyFormula_TopLevel(*a,false)); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + b->Begin_RemoveWrites = true; + output = new node(b->SimplifyFormula_TopLevel(*output,false)); + b->Begin_RemoveWrites = false; + return output; + } + else { + nodestar output = new node(b->SimplifyTerm(*a)); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + b->Begin_RemoveWrites = true; + output = new node(b->SimplifyTerm(*output)); + b->Begin_RemoveWrites = false; + return output; + } +} + +/* C pointer support: C interface to support C memory arrays in CVCL */ +Expr vc_bvCreateMemoryArray(VC vc, char * arrayName) { + Type bv8 = vc_bvType(vc,8); + Type bv32 = vc_bvType(vc,32); + + Type malloced_mem0 = vc_arrayType(vc,bv32,bv8); + return vc_varExpr(vc, arrayName, malloced_mem0); +} + +Expr vc_bvReadMemoryArray(VC vc, + Expr array, + Expr byteIndex, int numOfBytes) { + if(!(numOfBytes > 0)) + BEEV::FatalError("numOfBytes must be greater than 0"); + + if(numOfBytes == 1) + return vc_readExpr(vc,array,byteIndex); + else { + int count = 1; + Expr a = vc_readExpr(vc,array,byteIndex); + while(--numOfBytes > 0) { + Expr b = vc_readExpr(vc,array, + /*vc_simplify(vc, */ + vc_bvPlusExpr(vc, 32, + byteIndex, + vc_bvConstExprFromInt(vc,32,count)))/*)*/; + a = vc_bvConcatExpr(vc,b,a); + count++; + } + return a; + } +} + +Expr vc_bvWriteToMemoryArray(VC vc, + Expr array, Expr byteIndex, + Expr element, int numOfBytes) { + if(!(numOfBytes > 0)) + BEEV::FatalError("numOfBytes must be greater than 0"); + + int newBitsPerElem = numOfBytes*8; + if(numOfBytes == 1) + return vc_writeExpr(vc, array, byteIndex, element); + else { + int count = 1; + int hi = newBitsPerElem - 1; + int low = newBitsPerElem - 8; + int low_elem = 0; + int hi_elem = low_elem + 7; + Expr c = vc_bvExtract(vc, element, hi_elem, low_elem); + Expr newarray = vc_writeExpr(vc, array, byteIndex, c); + while(--numOfBytes > 0) { + hi = low-1; + low = low-8; + + low_elem = low_elem + 8; + hi_elem = low_elem + 7; + + c = vc_bvExtract(vc, element, hi_elem, low_elem); + newarray = + vc_writeExpr(vc, newarray, + vc_bvPlusExpr(vc, 32, byteIndex, vc_bvConstExprFromInt(vc,32,count)), + c); + count++; + } + return newarray; + } +} + +Expr vc_bv32ConstExprFromInt(VC vc, unsigned int value){ + return vc_bvConstExprFromInt(vc, 32, value); +} + + +#if 0 +static char *val_to_binary_str(unsigned nbits, unsigned long long val) { + char s[65]; + + assert(nbits < sizeof s); + strcpy(s, ""); + while(nbits-- > 0) { + if((val >> nbits) & 1) + strcat(s, "1"); + else + strcat(s, "0"); + } + return strdup(s); +} +#endif + +char* exprString(Expr e){ + stringstream ss; + ((nodestar)e)->PL_Print(ss,0); + string s = ss.str(); + char *copy = strdup(s.c_str()); + return copy; +} + +char* typeString(Type t){ + stringstream ss; + ((nodestar)t)->PL_Print(ss,0); + + string s = ss.str(); + char *copy = strdup(s.c_str()); + return copy; +} + +Expr getChild(Expr e, int i){ + nodestar a = (nodestar)e; + + BEEV::ASTVec c = a->GetChildren(); + if(0 <= (unsigned)i && (unsigned)i < c.size()) { + BEEV::ASTNode o = c[i]; + nodestar output = new node(o); + //if(cinterface_exprdelete_on) created_exprs.push_back(output); + return output; + } + else + BEEV::FatalError("getChild: Error accessing childNode in expression: ",*a); + return a; +} + +void vc_registerErrorHandler(void (*error_hdlr)(const char* err_msg)) { + BEEV::vc_error_hdlr = error_hdlr; +} + + +int vc_getHashQueryStateToBuffer(VC vc, Expr query) { + assert(vc); + assert(query); + bmstar b = (bmstar)vc; + nodestar qry = (nodestar)query; + BEEV::ASTVec v = b->GetAsserts(); + BEEV::ASTNode out = b->CreateNode(BEEV::AND,b->CreateNode(BEEV::NOT,*qry),v); + return out.Hash(); +} + +Type vc_getType(VC vc, Expr ex) { + nodestar e = (nodestar)ex; + + switch(e->GetType()) { + case BEEV::BOOLEAN_TYPE: + return vc_boolType(vc); + break; + case BEEV::BITVECTOR_TYPE: + return vc_bvType(vc,e->GetValueWidth()); + break; + case BEEV::ARRAY_TYPE: { + Type typeindex = vc_bvType(vc,e->GetIndexWidth()); + Type typedata = vc_bvType(vc,e->GetValueWidth()); + return vc_arrayType(vc,typeindex,typedata); + break; + } + default: + BEEV::FatalError("c_interface: vc_GetType: expression with bad typing: please check your expression construction"); + return vc_boolType(vc); + break; + } +}// end of vc_gettype() + +//!if e is TRUE then return 1; if e is FALSE then return 0; otherwise +//return -1 +int vc_isBool(Expr e) { + nodestar input = (nodestar)e; + if(BEEV::TRUE == input->GetKind()) { + return 1; + } + + if(BEEV::FALSE == input->GetKind()) { + return 0; + } + + return -1; +} + +void vc_Destroy(VC vc) { + bmstar b = (bmstar)vc; + // for(std::vector<BEEV::ASTNode *>::iterator it=created_exprs.begin(), + // itend=created_exprs.end();it!=itend;it++) { + // BEEV::ASTNode * aaa = *it; + // delete aaa; + // } + delete decls; + delete b; +} + +void vc_DeleteExpr(Expr e) { + nodestar input = (nodestar)e; + //bmstar b = (bmstar)vc; + delete input; +} + +exprkind_t getExprKind(Expr e) { + nodestar input = (nodestar)e; + return (exprkind_t)(input->GetKind()); +} + +int getDegree (Expr e) { + nodestar input = (nodestar)e; + return input->Degree(); +} + +int getBVLength(Expr ex) { + nodestar e = (nodestar)ex; + + if(BEEV::BITVECTOR_TYPE != e->GetType()) { + BEEV::FatalError("c_interface: vc_GetBVLength: Input expression must be a bit-vector"); + } + + return e->GetValueWidth(); +} + +type_t getType (Expr ex) { + nodestar e = (nodestar)ex; + + return (type_t)(e->GetType()); +} + +int getVWidth (Expr ex) { + nodestar e = (nodestar)ex; + + return e->GetValueWidth(); +} + +int getIWidth (Expr ex) { + nodestar e = (nodestar)ex; + + return e->GetIndexWidth(); +} + +void vc_printCounterExampleFile(VC vc, int fd) { + fdostream os(fd); + bmstar b = (bmstar)vc; + BEEV::print_counterexample = true; + os << "COUNTEREXAMPLE BEGIN: \n"; + b->PrintCounterExample(true, os); + os << "COUNTEREXAMPLE END: \n"; +} + +const char* exprName(Expr e){ + return ((nodestar)e)->GetName(); +} + +int getExprID (Expr ex) { + BEEV::ASTNode q = (*(nodestar)ex); + + return q.GetNodeNum(); +} diff --git a/stp/c_interface/c_interface.h b/stp/c_interface/c_interface.h new file mode 100644 index 00000000..a2fa8cd7 --- /dev/null +++ b/stp/c_interface/c_interface.h @@ -0,0 +1,401 @@ +/******************************************************************** + * AUTHORS: Vijay Ganesh, David L. Dill + * + * BEGIN DATE: November, 2005 + * + * License to use, copy, modify, sell and/or distribute this software + * and its documentation for any purpose is hereby granted without + * royalty, subject to the terms and conditions defined in the \ref + * LICENSE file provided with this distribution. In particular: + * + * - The above copyright notice and this permission notice must appear + * in all copies of the software and related documentation. + * + * - THE SOFTWARE IS PROVIDED "AS-IS", WITHOUT ANY WARRANTIES, + * EXPRESSED OR IMPLIED. USE IT AT YOUR OWN RISK. + ********************************************************************/ +// -*- c++ -*- +#ifndef _cvcl__include__c_interface_h_ +#define _cvcl__include__c_interface_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef STP_STRONG_TYPING +#else + //This gives absolutely no pointer typing at compile-time. Most C + //users prefer this over stronger typing. User is the king. A + //stronger typed interface is in the works. + typedef void* VC; + typedef void* Expr; + typedef void* Type; + typedef void* WholeCounterExample; +#endif + + // o : optimizations + // c : check counterexample + // p : print counterexample + // h : help + // s : stats + // v : print nodes + void vc_setFlags(char c); + + //! Flags can be NULL + VC vc_createValidityChecker(void); + + // Basic types + Type vc_boolType(VC vc); + + //! Create an array type + Type vc_arrayType(VC vc, Type typeIndex, Type typeData); + + ///////////////////////////////////////////////////////////////////////////// + // Expr manipulation methods // + ///////////////////////////////////////////////////////////////////////////// + + //! Create a variable with a given name and type + /*! The type cannot be a function type. The var name can contain + only variables, numerals and underscore. If you use any other + symbol, you will get a segfault. */ + Expr vc_varExpr(VC vc, char * name, Type type); + + //The var name can contain only variables, numerals and + //underscore. If you use any other symbol, you will get a segfault. + Expr vc_varExpr1(VC vc, char* name, + int indexwidth, int valuewidth); + + //! Get the expression and type associated with a name. + /*! If there is no such Expr, a NULL Expr is returned. */ + //Expr vc_lookupVar(VC vc, char* name, Type* type); + + //! Get the type of the Expr. + Type vc_getType(VC vc, Expr e); + + int vc_getBVLength(VC vc, Expr e); + + //! Create an equality expression. The two children must have the same type. + Expr vc_eqExpr(VC vc, Expr child0, Expr child1); + + // Boolean expressions + + // The following functions create Boolean expressions. The children + // provided as arguments must be of type Boolean (except for the + // function vc_iteExpr(). In the case of vc_iteExpr() the + // conditional must always be Boolean, but the ifthenpart + // (resp. elsepart) can be bit-vector or Boolean type. But, the + // ifthenpart and elsepart must be both of the same type) + Expr vc_trueExpr(VC vc); + Expr vc_falseExpr(VC vc); + Expr vc_notExpr(VC vc, Expr child); + Expr vc_andExpr(VC vc, Expr left, Expr right); + Expr vc_andExprN(VC vc, Expr* children, int numOfChildNodes); + Expr vc_orExpr(VC vc, Expr left, Expr right); + Expr vc_orExprN(VC vc, Expr* children, int numOfChildNodes); + Expr vc_impliesExpr(VC vc, Expr hyp, Expr conc); + Expr vc_iffExpr(VC vc, Expr left, Expr right); + //The output type of vc_iteExpr can be Boolean (formula-level ite) + //or bit-vector (word-level ite) + Expr vc_iteExpr(VC vc, Expr conditional, Expr ifthenpart, Expr elsepart); + + //Boolean to single bit BV Expression + Expr vc_boolToBVExpr(VC vc, Expr form); + + // Arrays + + //! Create an expression for the value of array at the given index + Expr vc_readExpr(VC vc, Expr array, Expr index); + + //! Array update; equivalent to "array WITH [index] := newValue" + Expr vc_writeExpr(VC vc, Expr array, Expr index, Expr newValue); + + // Expr I/O + //! Expr vc_parseExpr(VC vc, char* s); + + //! Prints 'e' to stdout. + void vc_printExpr(VC vc, Expr e); + + //! Prints 'e' into an open file descriptor 'fd' + void vc_printExprFile(VC vc, Expr e, int fd); + + //! Prints state of 'vc' into malloc'd buffer '*buf' and stores the + // length into '*len'. It is the responsibility of the caller to + // free the buffer. + //void vc_printStateToBuffer(VC vc, char **buf, unsigned long *len); + + //! Prints 'e' to malloc'd buffer '*buf'. Sets '*len' to the length of + // the buffer. It is the responsibility of the caller to free the buffer. + void vc_printExprToBuffer(VC vc, Expr e, char **buf, unsigned long * len); + + //! Prints counterexample to stdout. + void vc_printCounterExample(VC vc); + + //! Prints variable declarations to stdout. + void vc_printVarDecls(VC vc); + + //! Prints asserts to stdout. The flag simplify_print must be set to + //"1" if you wish simplification to occur dring printing. It must be + //set to "0" otherwise + void vc_printAsserts(VC vc, int simplify_print); + + //! Prints the state of the query to malloc'd buffer '*buf' and + //stores ! the length of the buffer to '*len'. It is the + //responsibility of the caller to free the buffer. The flag + //simplify_print must be set to "1" if you wish simplification to + //occur dring printing. It must be set to "0" otherwise + void vc_printQueryStateToBuffer(VC vc, Expr e, + char **buf, unsigned long *len, int simplify_print); + + //! Similar to vc_printQueryStateToBuffer() + void vc_printCounterExampleToBuffer(VC vc, char **buf,unsigned long *len); + + //! Prints query to stdout. + void vc_printQuery(VC vc); + + ///////////////////////////////////////////////////////////////////////////// + // Context-related methods // + ///////////////////////////////////////////////////////////////////////////// + + //! Assert a new formula in the current context. + /*! The formula must have Boolean type. */ + void vc_assertFormula(VC vc, Expr e); + + //! Simplify e with respect to the current context + Expr vc_simplify(VC vc, Expr e); + + //! Check validity of e in the current context. e must be a FORMULA + // + //if returned 0 then input is INVALID. + // + //if returned 1 then input is VALID + // + //if returned 2 then ERROR + int vc_query(VC vc, Expr e); + + //! Return the counterexample after a failed query. + Expr vc_getCounterExample(VC vc, Expr e); + + //! get size of counterexample, i.e. the number of variables/array + //locations in the counterexample. + int vc_counterexample_size(VC vc); + + //! Checkpoint the current context and increase the scope level + void vc_push(VC vc); + + //! Restore the current context to its state at the last checkpoint + void vc_pop(VC vc); + + //! Return an int from a constant bitvector expression + int getBVInt(Expr e); + //! Return an unsigned int from a constant bitvector expression + unsigned int getBVUnsigned(Expr e); + //! Return an unsigned long long int from a constant bitvector expressions + unsigned long long int getBVUnsignedLongLong(Expr e); + + /**************************/ + /* BIT VECTOR OPERATIONS */ + /**************************/ + Type vc_bvType(VC vc, int no_bits); + Type vc_bv32Type(VC vc); + + Expr vc_bvConstExprFromStr(VC vc, char* binary_repr); + Expr vc_bvConstExprFromInt(VC vc, int n_bits, unsigned int value); + Expr vc_bvConstExprFromLL(VC vc, int n_bits, unsigned long long value); + Expr vc_bv32ConstExprFromInt(VC vc, unsigned int value); + + Expr vc_bvConcatExpr(VC vc, Expr left, Expr right); + Expr vc_bvPlusExpr(VC vc, int n_bits, Expr left, Expr right); + Expr vc_bv32PlusExpr(VC vc, Expr left, Expr right); + Expr vc_bvMinusExpr(VC vc, int n_bits, Expr left, Expr right); + Expr vc_bv32MinusExpr(VC vc, Expr left, Expr right); + Expr vc_bvMultExpr(VC vc, int n_bits, Expr left, Expr right); + Expr vc_bv32MultExpr(VC vc, Expr left, Expr right); + // left divided by right i.e. left/right + Expr vc_bvDivExpr(VC vc, int n_bits, Expr left, Expr right); + // left modulo right i.e. left%right + Expr vc_bvModExpr(VC vc, int n_bits, Expr left, Expr right); + // signed left divided by right i.e. left/right + Expr vc_sbvDivExpr(VC vc, int n_bits, Expr left, Expr right); + // signed left modulo right i.e. left%right + Expr vc_sbvModExpr(VC vc, int n_bits, Expr left, Expr right); + + Expr vc_bvLtExpr(VC vc, Expr left, Expr right); + Expr vc_bvLeExpr(VC vc, Expr left, Expr right); + Expr vc_bvGtExpr(VC vc, Expr left, Expr right); + Expr vc_bvGeExpr(VC vc, Expr left, Expr right); + + Expr vc_sbvLtExpr(VC vc, Expr left, Expr right); + Expr vc_sbvLeExpr(VC vc, Expr left, Expr right); + Expr vc_sbvGtExpr(VC vc, Expr left, Expr right); + Expr vc_sbvGeExpr(VC vc, Expr left, Expr right); + + Expr vc_bvUMinusExpr(VC vc, Expr child); + + // bitwise operations: these are terms not formulas + Expr vc_bvAndExpr(VC vc, Expr left, Expr right); + Expr vc_bvOrExpr(VC vc, Expr left, Expr right); + Expr vc_bvXorExpr(VC vc, Expr left, Expr right); + Expr vc_bvNotExpr(VC vc, Expr child); + + Expr vc_bvLeftShiftExpr(VC vc, int sh_amt, Expr child); + Expr vc_bvRightShiftExpr(VC vc, int sh_amt, Expr child); + /* Same as vc_bvLeftShift only that the answer in 32 bits long */ + Expr vc_bv32LeftShiftExpr(VC vc, int sh_amt, Expr child); + /* Same as vc_bvRightShift only that the answer in 32 bits long */ + Expr vc_bv32RightShiftExpr(VC vc, int sh_amt, Expr child); + Expr vc_bvVar32LeftShiftExpr(VC vc, Expr sh_amt, Expr child); + Expr vc_bvVar32RightShiftExpr(VC vc, Expr sh_amt, Expr child); + Expr vc_bvVar32DivByPowOfTwoExpr(VC vc, Expr child, Expr rhs); + + Expr vc_bvExtract(VC vc, Expr child, int high_bit_no, int low_bit_no); + + //accepts a bitvector and position, and returns a boolean + //corresponding to that position. More precisely, it return the + //equation (x[bit_no:bit_no] = 0) + //FIXME = 1 ? + Expr vc_bvBoolExtract(VC vc, Expr x, int bit_no); + Expr vc_bvSignExtend(VC vc, Expr child, int nbits); + + /*C pointer support: C interface to support C memory arrays in CVCL */ + Expr vc_bvCreateMemoryArray(VC vc, char * arrayName); + Expr vc_bvReadMemoryArray(VC vc, + Expr array, Expr byteIndex, int numOfBytes); + Expr vc_bvWriteToMemoryArray(VC vc, + Expr array, Expr byteIndex, + Expr element, int numOfBytes); + Expr vc_bv32ConstExprFromInt(VC vc, unsigned int value); + + // return a string representation of the Expr e. The caller is responsible + // for deallocating the string with free() + char* exprString(Expr e); + + // return a string representation of the Type t. The caller is responsible + // for deallocating the string with free() + char* typeString(Type t); + + Expr getChild(Expr e, int i); + + //1.if input expr is TRUE then the function returns 1; + // + //2.if input expr is FALSE then function returns 0; + // + //3.otherwise the function returns -1 + int vc_isBool(Expr e); + + /* Register the given error handler to be called for each fatal error.*/ + void vc_registerErrorHandler(void (*error_hdlr)(const char* err_msg)); + + int vc_getHashQueryStateToBuffer(VC vc, Expr query); + + //destroys the STP instance, and removes all the created expressions + void vc_Destroy(VC vc); + + //deletes the expression e + void vc_DeleteExpr(Expr e); + + //Get the whole counterexample from the current context + WholeCounterExample vc_getWholeCounterExample(VC vc); + + //Get the value of a term expression from the CounterExample + Expr vc_getTermFromCounterExample(VC vc, Expr e, WholeCounterExample c); + + //Kinds of Expr + enum exprkind_t{ + UNDEFINED, + SYMBOL, + BVCONST, + BVNEG, + BVCONCAT, + BVOR, + BVAND, + BVXOR, + BVNAND, + BVNOR, + BVXNOR, + BVEXTRACT, + BVLEFTSHIFT, + BVRIGHTSHIFT, + BVSRSHIFT, + BVVARSHIFT, + BVPLUS, + BVSUB, + BVUMINUS, + BVMULTINVERSE, + BVMULT, + BVDIV, + BVMOD, + SBVDIV, + SBVMOD, + BVSX, + BOOLVEC, + ITE, + BVGETBIT, + BVLT, + BVLE, + BVGT, + BVGE, + BVSLT, + BVSLE, + BVSGT, + BVSGE, + EQ, + NEQ, + FALSE, + TRUE, + NOT, + AND, + OR, + NAND, + NOR, + XOR, + IFF, + IMPLIES, + READ, + WRITE, + ARRAY, + BITVECTOR, + BOOLEAN + }; + + // type of expression + enum type_t { + BOOLEAN_TYPE = 0, + BITVECTOR_TYPE, + ARRAY_TYPE, + UNKNOWN_TYPE + }; + + // get the kind of the expression + exprkind_t getExprKind (Expr e); + + // get the number of children nodes + int getDegree (Expr e); + + // get the bv length + int getBVLength(Expr e); + + // get expression type + type_t getType (Expr e); + + // get value bit width + int getVWidth (Expr e); + + // get index bit width + int getIWidth (Expr e); + + // Prints counterexample to an open file descriptor 'fd' + void vc_printCounterExampleFile(VC vc, int fd); + + // get name of expression. must be a variable. + const char* exprName(Expr e); + + // get the node ID of an Expr. + int getExprID (Expr ex); + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/stp/c_interface/fdstream.h b/stp/c_interface/fdstream.h new file mode 100644 index 00000000..2cff613c --- /dev/null +++ b/stp/c_interface/fdstream.h @@ -0,0 +1,186 @@ +/*! @brief The following code declares classes to read from and write to + * file descriptore or file handles. + * + * See + * http://www.josuttis.com/cppcode + * for details and the latest version. + * + * - open: + * - integrating BUFSIZ on some systems? + * - optimized reading of multiple characters + * - stream for reading AND writing + * - i18n + * + * (C) Copyright Nicolai M. Josuttis 2001. + * Permission to copy, use, modify, sell and distribute this software + * is granted provided this copyright notice appears in all copies. + * This software is provided "as is" without express or implied + * warranty, and with no claim as to its suitability for any purpose. + * + * Version: Jul 28, 2002 + * History: + * Jul 28, 2002: bugfix memcpy() => memmove() + * fdinbuf::underflow(): cast for return statements + * Aug 05, 2001: first public version + */ +#ifndef BOOST_FDSTREAM_HPP +#define BOOST_FDSTREAM_HPP + +#include <istream> +#include <ostream> +#include <streambuf> + + +// for EOF: +#include <cstdio> +// for memmove(): +#include <cstring> + + +// low-level read and write functions +#ifdef _MSC_VER +# include <io.h> +#else +# include <unistd.h> +//extern "C" { +// int write (int fd, const char* buf, int num); +// int read (int fd, char* buf, int num); +//} +#endif + + +// BEGIN namespace BOOST +namespace std { + + +/************************************************************ + * fdostream + * - a stream that writes on a file descriptor + ************************************************************/ + + +class fdoutbuf : public std::streambuf { + protected: + int fd; // file descriptor + public: + // constructor + fdoutbuf (int _fd) : fd(_fd) { + } + protected: + // write one character + virtual int_type overflow (int_type c) { + if (c != EOF) { + char z = c; + if (write (fd, &z, 1) != 1) { + return EOF; + } + } + return c; + } + // write multiple characters + virtual + std::streamsize xsputn (const char* s, + std::streamsize num) { + return write(fd,s,num); + } +}; + +class fdostream : public std::ostream { + protected: + fdoutbuf buf; + public: + fdostream (int fd) : std::ostream(0), buf(fd) { + rdbuf(&buf); + } +}; + + +/************************************************************ + * fdistream + * - a stream that reads on a file descriptor + ************************************************************/ + +class fdinbuf : public std::streambuf { + protected: + int fd; // file descriptor + protected: + /* data buffer: + * - at most, pbSize characters in putback area plus + * - at most, bufSize characters in ordinary read buffer + */ + static const int pbSize = 4; // size of putback area + static const int bufSize = 1024; // size of the data buffer + char buffer[bufSize+pbSize]; // data buffer + + public: + /* constructor + * - initialize file descriptor + * - initialize empty data buffer + * - no putback area + * => force underflow() + */ + fdinbuf (int _fd) : fd(_fd) { + setg (buffer+pbSize, // beginning of putback area + buffer+pbSize, // read position + buffer+pbSize); // end position + } + + protected: + // insert new characters into the buffer + virtual int_type underflow () { +#ifndef _MSC_VER + using std::memmove; +#endif + + // is read position before end of buffer? + if (gptr() < egptr()) { + return traits_type::to_int_type(*gptr()); + } + + /* process size of putback area + * - use number of characters read + * - but at most size of putback area + */ + int numPutback; + numPutback = gptr() - eback(); + if (numPutback > pbSize) { + numPutback = pbSize; + } + + /* copy up to pbSize characters previously read into + * the putback area + */ + memmove (buffer+(pbSize-numPutback), gptr()-numPutback, + numPutback); + + // read at most bufSize new characters + int num; + num = read (fd, buffer+pbSize, bufSize); + if (num <= 0) { + // ERROR or EOF + return EOF; + } + + // reset buffer pointers + setg (buffer+(pbSize-numPutback), // beginning of putback area + buffer+pbSize, // read position + buffer+pbSize+num); // end of buffer + + // return next character + return traits_type::to_int_type(*gptr()); + } +}; + +class fdistream : public std::istream { + protected: + fdinbuf buf; + public: + fdistream (int fd) : std::istream(0), buf(fd) { + rdbuf(&buf); + } +}; + + +} // END namespace boost + +#endif /*BOOST_FDSTREAM_HPP*/ diff --git a/stp/constantbv/Makefile b/stp/constantbv/Makefile new file mode 100644 index 00000000..cd94ad94 --- /dev/null +++ b/stp/constantbv/Makefile @@ -0,0 +1,13 @@ +include ../Makefile.common + +SRCS = constantbv.cpp +OBJS = $(SRCS:.cpp=.o) + +libconstantbv.a: $(OBJS) + $(AR) rc $@ $^ + $(RANLIB) $@ + +clean: + rm -rf *.o *~ *.a .#* + +constantbv.o: constantbv.h diff --git a/stp/constantbv/constantbv.cpp b/stp/constantbv/constantbv.cpp new file mode 100644 index 00000000..65698981 --- /dev/null +++ b/stp/constantbv/constantbv.cpp @@ -0,0 +1,3571 @@ +/*****************************************************************************/ +/* AUTHOR: */ +/*****************************************************************************/ +/* */ +/* Steffen Beyer */ +/* mailto:sb@engelschall.com */ +/* http://www.engelschall.com/u/sb/download/ */ +/* */ +/*****************************************************************************/ +/* COPYRIGHT: */ +/*****************************************************************************/ +/* */ +/* Copyright (c) 1995 - 2004 by Steffen Beyer. */ +/* All rights reserved. */ +/* */ +/*****************************************************************************/ +/* LICENSE: */ +/*****************************************************************************/ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Library General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2 of the License, || (at your option) any later version. */ +/* */ +/* This library is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY || FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* Library General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Library General Public */ +/* License along with this library; if not, write to the */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/* || download a copy from ftp://ftp.gnu.org/pub/gnu/COPYING.LIB-2.0 */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* MODULE NAME: constantbv.cpp MODULE TYPE:constantbv*/ +/*****************************************************************************/ +/* MODULE IMPORTS: */ +/*****************************************************************************/ +#include <stdio.h> +#include <stdlib.h> /* MODULE TYPE: (sys) */ +#include <limits.h> /* MODULE TYPE: (sys) */ +#include <string.h> /* MODULE TYPE: (sys) */ +#include <ctype.h> /* MODULE TYPE: (sys) */ +#include "constantbv.h" + +namespace CONSTANTBV { +/*****************************************************************************/ +/* MODULE IMPLEMENTATION: */ +/*****************************************************************************/ + /**********************************************/ + /* global implementation-intrinsic constants: */ + /**********************************************/ + +#define BIT_VECTOR_HIDDEN_WORDS 3 + + /*****************************************************************/ + /* global machine-dependent constants (set by "BitVector_Boot"): */ + /*****************************************************************/ + +static unsigned int BITS; /* = # of bits in machine word (must be power of 2) */ +static unsigned int MODMASK; /* = BITS - 1 (mask for calculating modulo BITS) */ +static unsigned int LOGBITS; /* = ld(BITS) (logarithmus dualis) */ +static unsigned int FACTOR; /* = ld(BITS / 8) (ld of # of bytes) */ + +static unsigned int LSB = 1; /* = mask for least significant bit */ +static unsigned int MSB; /* = mask for most significant bit */ + +static unsigned int LONGBITS; /* = # of bits in unsigned long */ + +static unsigned int LOG10; /* = logarithm to base 10 of BITS - 1 */ +static unsigned int EXP10; /* = largest possible power of 10 in signed int */ + + /********************************************************************/ + /* global bit mask table for fast access (set by "BitVector_Boot"): */ + /********************************************************************/ + +static unsigned int * BITMASKTAB; + + /*****************************/ + /* global macro definitions: */ + /*****************************/ + +#define BIT_VECTOR_ZERO_WORDS(target,count) \ + while (count-- > 0) *target++ = 0; + +#define BIT_VECTOR_FILL_WORDS(target,fill,count) \ + while (count-- > 0) *target++ = fill; + +#define BIT_VECTOR_FLIP_WORDS(target,flip,count) \ + while (count-- > 0) *target++ ^= flip; + +#define BIT_VECTOR_COPY_WORDS(target,source,count) \ + while (count-- > 0) *target++ = *source++; + +#define BIT_VECTOR_BACK_WORDS(target,source,count) \ + { target += count; source += count; while (count-- > 0) *--target = *--source; } + +#define BIT_VECTOR_CLR_BIT(address,index) \ + *(address+(index>>LOGBITS)) &= ~ BITMASKTAB[index & MODMASK]; + +#define BIT_VECTOR_SET_BIT(address,index) \ + *(address+(index>>LOGBITS)) |= BITMASKTAB[index & MODMASK]; + +#define BIT_VECTOR_TST_BIT(address,index) \ + ((*(address+(index>>LOGBITS)) & BITMASKTAB[index & MODMASK]) != 0) + +#define BIT_VECTOR_FLP_BIT(address,index,mask) \ + (mask = BITMASKTAB[index & MODMASK]), \ + (((*(addr+(index>>LOGBITS)) ^= mask) & mask) != 0) + +#define BIT_VECTOR_DIGITIZE(type,value,digit) \ + value = (type) ((digit = value) / 10); \ + digit -= value * 10; \ + digit += (type) '0'; + + /*********************************************************/ + /* private low-level functions (potentially dangerous!): */ + /*********************************************************/ + +static unsigned int power10(unsigned int x) { + unsigned int y = 1; + + while (x-- > 0) y *= 10; + return(y); +} + +static void BIT_VECTOR_zro_words(unsigned int * addr, unsigned int count) { + BIT_VECTOR_ZERO_WORDS(addr,count) +} + +static void BIT_VECTOR_cpy_words(unsigned int * target, + unsigned int * source, unsigned int count) { + BIT_VECTOR_COPY_WORDS(target,source,count) +} + +static void BIT_VECTOR_mov_words(unsigned int * target, + unsigned int * source, unsigned int count) { + if (target != source) { + if (target < source) BIT_VECTOR_COPY_WORDS(target,source,count) + else BIT_VECTOR_BACK_WORDS(target,source,count) + } +} + +static void BIT_VECTOR_ins_words(unsigned int * addr, + unsigned int total, unsigned int count, boolean clear) { + unsigned int length; + + if ((total > 0) && (count > 0)) { + if (count > total) count = total; + length = total - count; + if (length > 0) BIT_VECTOR_mov_words(addr+count,addr,length); + if (clear) BIT_VECTOR_zro_words(addr,count); + } +} + +static void BIT_VECTOR_del_words(unsigned int * addr, + unsigned int total, unsigned int count, boolean clear) { + unsigned int length; + + if ((total > 0) && (count > 0)) { + if (count > total) count = total; + length = total - count; + if (length > 0) BIT_VECTOR_mov_words(addr,addr+count,length); + if (clear) BIT_VECTOR_zro_words(addr+length,count); + } +} + +static void BIT_VECTOR_reverse(unsigned char * string, unsigned int length) { + unsigned char * last; + unsigned char temp; + + if (length > 1) { + last = string + length - 1; + while (string < last) { + temp = *string; + *string = *last; + *last = temp; + string++; + last--; + } + } +} + +static unsigned int BIT_VECTOR_int2str(unsigned char * string, unsigned int value) { + unsigned int length; + unsigned int digit; + unsigned char * work; + + work = string; + if (value > 0) { + length = 0; + while (value > 0) { + BIT_VECTOR_DIGITIZE(unsigned int,value,digit) + *work++ = (unsigned char) digit; + length++; + } + BIT_VECTOR_reverse(string,length); + } + else { + length = 1; + *work++ = (unsigned char) '0'; + } + return(length); +} + +static unsigned int BIT_VECTOR_str2int(unsigned char * string, unsigned int *value) { + unsigned int length; + unsigned int digit; + + *value = 0; + length = 0; + digit = (unsigned int) *string++; + /* separate because isdigit() is likely a macro! */ + while (isdigit((int)digit) != 0) { + length++; + digit -= (unsigned int) '0'; + if (*value) *value *= 10; + *value += digit; + digit = (unsigned int) *string++; + } + return(length); +} + + /********************************************/ + /* routine to convert error code to string: */ + /********************************************/ + +unsigned char * BitVector_Error(ErrCode error) { + switch (error) { + case ErrCode_Ok: return( (unsigned char *) NULL ); break; + case ErrCode_Type: return( (unsigned char *) ERRCODE_TYPE ); break; + case ErrCode_Bits: return( (unsigned char *) ERRCODE_BITS ); break; + case ErrCode_Word: return( (unsigned char *) ERRCODE_WORD ); break; + case ErrCode_Long: return( (unsigned char *) ERRCODE_LONG ); break; + case ErrCode_Powr: return( (unsigned char *) ERRCODE_POWR ); break; + case ErrCode_Loga: return( (unsigned char *) ERRCODE_LOGA ); break; + case ErrCode_Null: return( (unsigned char *) ERRCODE_NULL ); break; + case ErrCode_Indx: return( (unsigned char *) ERRCODE_INDX ); break; + case ErrCode_Ordr: return( (unsigned char *) ERRCODE_ORDR ); break; + case ErrCode_Size: return( (unsigned char *) ERRCODE_SIZE ); break; + case ErrCode_Pars: return( (unsigned char *) ERRCODE_PARS ); break; + case ErrCode_Ovfl: return( (unsigned char *) ERRCODE_OVFL ); break; + case ErrCode_Same: return( (unsigned char *) ERRCODE_SAME ); break; + case ErrCode_Expo: return( (unsigned char *) ERRCODE_EXPO ); break; + case ErrCode_Zero: return( (unsigned char *) ERRCODE_ZERO ); break; + default: return( (unsigned char *) ERRCODE_OOPS ); break; + } +} + + /*****************************************/ + /* automatic self-configuration routine: */ + /*****************************************/ + + /*******************************************************/ + /* */ + /* MUST be called once prior to any other function */ + /* to initialize the machine dependent constants */ + /* of this package! (But call only ONCE, or you */ + /* will suffer memory leaks!) */ + /* */ + /*******************************************************/ + +ErrCode BitVector_Boot(void) { + unsigned long longsample = 1L; + unsigned int sample = LSB; + unsigned int lsb; + + if (sizeof(unsigned int) > sizeof(size_t)) return(ErrCode_Type); + + BITS = 1; + while (sample <<= 1) BITS++; /* determine # of bits in a machine word */ + + if (BITS != (sizeof(unsigned int) << 3)) return(ErrCode_Bits); + + if (BITS < 16) return(ErrCode_Word); + + LONGBITS = 1; + while (longsample <<= 1) LONGBITS++; /* = # of bits in an unsigned long */ + + if (BITS > LONGBITS) return(ErrCode_Long); + + LOGBITS = 0; + sample = BITS; + lsb = (sample & LSB); + while ((sample >>= 1) && (! lsb)) { + LOGBITS++; + lsb = (sample & LSB); + } + + if (sample) return(ErrCode_Powr); /* # of bits is not a power of 2! */ + + if (BITS != (LSB << LOGBITS)) return(ErrCode_Loga); + + MODMASK = BITS - 1; + FACTOR = LOGBITS - 3; /* ld(BITS / 8) = ld(BITS) - ld(8) = ld(BITS) - 3 */ + MSB = (LSB << MODMASK); + + BITMASKTAB = (unsigned int * ) malloc((size_t) (BITS << FACTOR)); + + if (BITMASKTAB == NULL) return(ErrCode_Null); + + for ( sample = 0; sample < BITS; sample++ ) { + BITMASKTAB[sample] = (LSB << sample); + } + + LOG10 = (unsigned int) (MODMASK * 0.30103); /* = (BITS - 1) * ( ln 2 / ln 10 ) */ + EXP10 = power10(LOG10); + + return(ErrCode_Ok); +} + +unsigned int BitVector_Size(unsigned int bits) { /* bit vector size (# of words) */ + unsigned int size; + + size = bits >> LOGBITS; + if (bits & MODMASK) size++; + return(size); +} + +unsigned int BitVector_Mask(unsigned int bits) /* bit vector mask (unused bits) */ +{ + unsigned int mask; + + mask = bits & MODMASK; + if (mask) mask = (unsigned int) ~(~0L << mask); else mask = (unsigned int) ~0L; + return(mask); +} + +unsigned char * BitVector_Version(void) +{ + return((unsigned char *)"6.4"); +} + +unsigned int BitVector_Word_Bits(void) +{ + return(BITS); +} + +unsigned int BitVector_Long_Bits(void) +{ + return(LONGBITS); +} + +/********************************************************************/ +/* */ +/* WARNING: Do not "free()" constant character strings, i.e., */ +/* don't call "BitVector_Dispose()" for strings returned */ +/* by "BitVector_Error()" or "BitVector_Version()"! */ +/* */ +/* ONLY call this function for strings allocated with "malloc()", */ +/* i.e., the strings returned by the functions "BitVector_to_*()" */ +/* and "BitVector_Block_Read()"! */ +/* */ +/********************************************************************/ + +void BitVector_Dispose(unsigned char * string) /* free string */ +{ + if (string != NULL) free((void *) string); +} + +void BitVector_Destroy(unsigned int * addr) /* free bitvec */ +{ + if (addr != NULL) + { + addr -= BIT_VECTOR_HIDDEN_WORDS; + free((void *) addr); + } +} + +void BitVector_Destroy_List(unsigned int * * list, unsigned int count) /* free list */ +{ + unsigned int * * slot; + + if (list != NULL) + { + slot = list; + while (count-- > 0) + { + BitVector_Destroy(*slot++); + } + free((void *) list); + } +} + +unsigned int * BitVector_Create(unsigned int bits, boolean clear) /* malloc */ +{ + unsigned int size; + unsigned int mask; + unsigned int bytes; + unsigned int * addr; + unsigned int * zero; + + size = BitVector_Size(bits); + mask = BitVector_Mask(bits); + bytes = (size + BIT_VECTOR_HIDDEN_WORDS) << FACTOR; + addr = (unsigned int * ) malloc((size_t) bytes); + if (addr != NULL) + { + *addr++ = bits; + *addr++ = size; + *addr++ = mask; + if (clear) + { + zero = addr; + BIT_VECTOR_ZERO_WORDS(zero,size) + } + } + return(addr); +} + +unsigned int * * BitVector_Create_List(unsigned int bits, boolean clear, unsigned int count) +{ + unsigned int * * list = NULL; + unsigned int * * slot; + unsigned int * addr; + unsigned int i; + + if (count > 0) + { + list = (unsigned int * * ) malloc(sizeof(unsigned int * ) * count); + if (list != NULL) + { + slot = list; + for ( i = 0; i < count; i++ ) + { + addr = BitVector_Create(bits,clear); + if (addr == NULL) + { + BitVector_Destroy_List(list,i); + return(NULL); + } + *slot++ = addr; + } + } + } + return(list); +} + +unsigned int * BitVector_Resize(unsigned int * oldaddr, unsigned int bits) /* realloc */ +{ + unsigned int bytes; + unsigned int oldsize; + unsigned int oldmask; + unsigned int newsize; + unsigned int newmask; + unsigned int * newaddr; + unsigned int * source; + unsigned int * target; + + oldsize = size_(oldaddr); + oldmask = mask_(oldaddr); + newsize = BitVector_Size(bits); + newmask = BitVector_Mask(bits); + if (oldsize > 0) *(oldaddr+oldsize-1) &= oldmask; + if (newsize <= oldsize) + { + newaddr = oldaddr; + bits_(newaddr) = bits; + size_(newaddr) = newsize; + mask_(newaddr) = newmask; + if (newsize > 0) *(newaddr+newsize-1) &= newmask; + } + else + { + bytes = (newsize + BIT_VECTOR_HIDDEN_WORDS) << FACTOR; + newaddr = (unsigned int * ) malloc((size_t) bytes); + if (newaddr != NULL) + { + *newaddr++ = bits; + *newaddr++ = newsize; + *newaddr++ = newmask; + target = newaddr; + source = oldaddr; + newsize -= oldsize; + BIT_VECTOR_COPY_WORDS(target,source,oldsize) + BIT_VECTOR_ZERO_WORDS(target,newsize) + } + BitVector_Destroy(oldaddr); + } + return(newaddr); +} + +unsigned int * BitVector_Shadow(unsigned int * addr) /* makes new, same size but empty */ +{ + return( BitVector_Create(bits_(addr),true) ); +} + +unsigned int * BitVector_Clone(unsigned int * addr) /* makes exact duplicate */ +{ + unsigned int bits; + unsigned int * twin; + + bits = bits_(addr); + twin = BitVector_Create(bits,false); + if ((twin != NULL) && (bits > 0)) + BIT_VECTOR_cpy_words(twin,addr,size_(addr)); + return(twin); +} + +unsigned int * BitVector_Concat(unsigned int * X, unsigned int * Y) /* returns concatenation */ +{ + /* BEWARE that X = most significant part, Y = least significant part! */ + + unsigned int bitsX; + unsigned int bitsY; + unsigned int bitsZ; + unsigned int * Z; + + bitsX = bits_(X); + bitsY = bits_(Y); + bitsZ = bitsX + bitsY; + Z = BitVector_Create(bitsZ,false); + if ((Z != NULL) && (bitsZ > 0)) + { + BIT_VECTOR_cpy_words(Z,Y,size_(Y)); + BitVector_Interval_Copy(Z,X,bitsY,0,bitsX); + *(Z+size_(Z)-1) &= mask_(Z); + } + return(Z); +} + +void BitVector_Copy(unsigned int * X, unsigned int * Y) /* X = Y */ +{ + unsigned int sizeX = size_(X); + unsigned int sizeY = size_(Y); + unsigned int maskX = mask_(X); + unsigned int maskY = mask_(Y); + unsigned int fill = 0; + unsigned int * lastX; + unsigned int * lastY; + + if ((X != Y) && (sizeX > 0)) + { + lastX = X + sizeX - 1; + if (sizeY > 0) + { + lastY = Y + sizeY - 1; + if ( (*lastY & (maskY & ~ (maskY >> 1))) == 0 ) *lastY &= maskY; + else + { + fill = (unsigned int) ~0L; + *lastY |= ~ maskY; + } + while ((sizeX > 0) && (sizeY > 0)) + { + *X++ = *Y++; + sizeX--; + sizeY--; + } + *lastY &= maskY; + } + while (sizeX-- > 0) *X++ = fill; + *lastX &= maskX; + } +} + +void BitVector_Empty(unsigned int * addr) /* X = {} clr all */ +{ + unsigned int size = size_(addr); + + BIT_VECTOR_ZERO_WORDS(addr,size) +} + +void BitVector_Fill(unsigned int * addr) /* X = ~{} set all */ +{ + unsigned int size = size_(addr); + unsigned int mask = mask_(addr); + unsigned int fill = (unsigned int) ~0L; + + if (size > 0) + { + BIT_VECTOR_FILL_WORDS(addr,fill,size) + *(--addr) &= mask; + } +} + +void BitVector_Flip(unsigned int * addr) /* X = ~X flip all */ +{ + unsigned int size = size_(addr); + unsigned int mask = mask_(addr); + unsigned int flip = (unsigned int) ~0L; + + if (size > 0) + { + BIT_VECTOR_FLIP_WORDS(addr,flip,size) + *(--addr) &= mask; + } +} + +void BitVector_Primes(unsigned int * addr) +{ + unsigned int bits = bits_(addr); + unsigned int size = size_(addr); + unsigned int * work; + unsigned int temp; + unsigned int i,j; + + if (size > 0) + { + temp = 0xAAAA; + i = BITS >> 4; + while (--i > 0) + { + temp <<= 16; + temp |= 0xAAAA; + } + i = size; + work = addr; + *work++ = temp ^ 0x0006; + while (--i > 0) *work++ = temp; + for ( i = 3; (j = i * i) < bits; i += 2 ) + { + for ( ; j < bits; j += i ) BIT_VECTOR_CLR_BIT(addr,j) + } + *(addr+size-1) &= mask_(addr); + } +} + +void BitVector_Reverse(unsigned int * X, unsigned int * Y) +{ + unsigned int bits = bits_(X); + unsigned int mask; + unsigned int bit; + unsigned int value; + + if (bits > 0) + { + if (X == Y) BitVector_Interval_Reverse(X,0,bits-1); + else if (bits == bits_(Y)) + { +/* mask = mask_(Y); */ +/* mask &= ~ (mask >> 1); */ + mask = BITMASKTAB[(bits-1) & MODMASK]; + Y += size_(Y) - 1; + value = 0; + bit = LSB; + while (bits-- > 0) + { + if ((*Y & mask) != 0) + { + value |= bit; + } + if (! (mask >>= 1)) + { + Y--; + mask = MSB; + } + if (! (bit <<= 1)) + { + *X++ = value; + value = 0; + bit = LSB; + } + } + if (bit > LSB) *X = value; + } + } +} + +void BitVector_Interval_Empty(unsigned int * addr, unsigned int lower, unsigned int upper) +{ /* X = X \ [lower..upper] */ + unsigned int bits = bits_(addr); + unsigned int size = size_(addr); + unsigned int * loaddr; + unsigned int * hiaddr; + unsigned int lobase; + unsigned int hibase; + unsigned int lomask; + unsigned int himask; + unsigned int diff; + + if ((size > 0) && (lower < bits) && (upper < bits) && (lower <= upper)) + { + lobase = lower >> LOGBITS; + hibase = upper >> LOGBITS; + diff = hibase - lobase; + loaddr = addr + lobase; + hiaddr = addr + hibase; + + lomask = (unsigned int) (~0L << (lower & MODMASK)); + himask = (unsigned int) ~((~0L << (upper & MODMASK)) << 1); + + if (diff == 0) + { + *loaddr &= ~ (lomask & himask); + } + else + { + *loaddr++ &= ~ lomask; + while (--diff > 0) + { + *loaddr++ = 0; + } + *hiaddr &= ~ himask; + } + } +} + +void BitVector_Interval_Fill(unsigned int * addr, unsigned int lower, unsigned int upper) +{ /* X = X + [lower..upper] */ + unsigned int bits = bits_(addr); + unsigned int size = size_(addr); + unsigned int fill = (unsigned int) ~0L; + unsigned int * loaddr; + unsigned int * hiaddr; + unsigned int lobase; + unsigned int hibase; + unsigned int lomask; + unsigned int himask; + unsigned int diff; + + if ((size > 0) && (lower < bits) && (upper < bits) && (lower <= upper)) + { + lobase = lower >> LOGBITS; + hibase = upper >> LOGBITS; + diff = hibase - lobase; + loaddr = addr + lobase; + hiaddr = addr + hibase; + + lomask = (unsigned int) (~0L << (lower & MODMASK)); + himask = (unsigned int) ~((~0L << (upper & MODMASK)) << 1); + + if (diff == 0) + { + *loaddr |= (lomask & himask); + } + else + { + *loaddr++ |= lomask; + while (--diff > 0) + { + *loaddr++ = fill; + } + *hiaddr |= himask; + } + *(addr+size-1) &= mask_(addr); + } +} + +void BitVector_Interval_Flip(unsigned int * addr, unsigned int lower, unsigned int upper) +{ /* X = X ^ [lower..upper] */ + unsigned int bits = bits_(addr); + unsigned int size = size_(addr); + unsigned int flip = (unsigned int) ~0L; + unsigned int * loaddr; + unsigned int * hiaddr; + unsigned int lobase; + unsigned int hibase; + unsigned int lomask; + unsigned int himask; + unsigned int diff; + + if ((size > 0) && (lower < bits) && (upper < bits) && (lower <= upper)) + { + lobase = lower >> LOGBITS; + hibase = upper >> LOGBITS; + diff = hibase - lobase; + loaddr = addr + lobase; + hiaddr = addr + hibase; + + lomask = (unsigned int) (~0L << (lower & MODMASK)); + himask = (unsigned int) ~((~0L << (upper & MODMASK)) << 1); + + if (diff == 0) + { + *loaddr ^= (lomask & himask); + } + else + { + *loaddr++ ^= lomask; + while (--diff > 0) + { + *loaddr++ ^= flip; + } + *hiaddr ^= himask; + } + *(addr+size-1) &= mask_(addr); + } +} + +void BitVector_Interval_Reverse(unsigned int * addr, unsigned int lower, unsigned int upper) +{ + unsigned int bits = bits_(addr); + unsigned int * loaddr; + unsigned int * hiaddr; + unsigned int lomask; + unsigned int himask; + + if ((bits > 0) && (lower < bits) && (upper < bits) && (lower < upper)) + { + loaddr = addr + (lower >> LOGBITS); + hiaddr = addr + (upper >> LOGBITS); + lomask = BITMASKTAB[lower & MODMASK]; + himask = BITMASKTAB[upper & MODMASK]; + for ( bits = upper - lower + 1; bits > 1; bits -= 2 ) + { + if (((*loaddr & lomask) != 0) ^ ((*hiaddr & himask) != 0)) + { + *loaddr ^= lomask; /* swap bits only if they differ! */ + *hiaddr ^= himask; + } + if (! (lomask <<= 1)) + { + lomask = LSB; + loaddr++; + } + if (! (himask >>= 1)) + { + himask = MSB; + hiaddr--; + } + } + } +} + +boolean BitVector_interval_scan_inc(unsigned int * addr, unsigned int start, + unsigned int * min, unsigned int * max) +{ + unsigned int size = size_(addr); + unsigned int mask = mask_(addr); + unsigned int offset; + unsigned int bitmask; + unsigned int value; + boolean empty; + + if ((size == 0) || (start >= bits_(addr))) return(false); + + *min = start; + *max = start; + + offset = start >> LOGBITS; + + *(addr+size-1) &= mask; + + addr += offset; + size -= offset; + + bitmask = BITMASKTAB[start & MODMASK]; + mask = ~ (bitmask | (bitmask - 1)); + + value = *addr++; + if ((value & bitmask) == 0) + { + value &= mask; + if (value == 0) + { + offset++; + empty = true; + while (empty && (--size > 0)) + { + if ((value = *addr++)) empty = false; else offset++; + } + if (empty) return(false); + } + start = offset << LOGBITS; + bitmask = LSB; + mask = value; + while (! (mask & LSB)) + { + bitmask <<= 1; + mask >>= 1; + start++; + } + mask = ~ (bitmask | (bitmask - 1)); + *min = start; + *max = start; + } + value = ~ value; + value &= mask; + if (value == 0) + { + offset++; + empty = true; + while (empty && (--size > 0)) + { + if ((value = ~ *addr++)) empty = false; else offset++; + } + if (empty) value = LSB; + } + start = offset << LOGBITS; + while (! (value & LSB)) + { + value >>= 1; + start++; + } + *max = --start; + return(true); +} + +boolean BitVector_interval_scan_dec(unsigned int * addr, unsigned int start, + unsigned int * min, unsigned int * max) +{ + unsigned int size = size_(addr); + unsigned int mask = mask_(addr); + unsigned int offset; + unsigned int bitmask; + unsigned int value; + boolean empty; + + if ((size == 0) || (start >= bits_(addr))) return(false); + + *min = start; + *max = start; + + offset = start >> LOGBITS; + + if (offset >= size) return(false); + + *(addr+size-1) &= mask; + + addr += offset; + size = ++offset; + + bitmask = BITMASKTAB[start & MODMASK]; + mask = (bitmask - 1); + + value = *addr--; + if ((value & bitmask) == 0) + { + value &= mask; + if (value == 0) + { + offset--; + empty = true; + while (empty && (--size > 0)) + { + if ((value = *addr--)) empty = false; else offset--; + } + if (empty) return(false); + } + start = offset << LOGBITS; + bitmask = MSB; + mask = value; + while (! (mask & MSB)) + { + bitmask >>= 1; + mask <<= 1; + start--; + } + mask = (bitmask - 1); + *max = --start; + *min = start; + } + value = ~ value; + value &= mask; + if (value == 0) + { + offset--; + empty = true; + while (empty && (--size > 0)) + { + if ((value = ~ *addr--)) empty = false; else offset--; + } + if (empty) value = MSB; + } + start = offset << LOGBITS; + while (! (value & MSB)) + { + value <<= 1; + start--; + } + *min = start; + return(true); +} + +void BitVector_Interval_Copy(unsigned int * X, unsigned int * Y, unsigned int Xoffset, + unsigned int Yoffset, unsigned int length) +{ + unsigned int bitsX = bits_(X); + unsigned int bitsY = bits_(Y); + unsigned int source = 0; /* silence compiler warning */ + unsigned int target = 0; /* silence compiler warning */ + unsigned int s_lo_base; + unsigned int s_hi_base; + unsigned int s_lo_bit; + unsigned int s_hi_bit; + unsigned int s_base; + unsigned int s_lower = 0; /* silence compiler warning */ + unsigned int s_upper = 0; /* silence compiler warning */ + unsigned int s_bits; + unsigned int s_min; + unsigned int s_max; + unsigned int t_lo_base; + unsigned int t_hi_base; + unsigned int t_lo_bit; + unsigned int t_hi_bit; + unsigned int t_base; + unsigned int t_lower = 0; /* silence compiler warning */ + unsigned int t_upper = 0; /* silence compiler warning */ + unsigned int t_bits; + unsigned int t_min; + unsigned int mask; + unsigned int bits; + unsigned int select; + boolean ascending; + boolean notfirst; + unsigned int * Z = X; + + if ((length > 0) && (Xoffset < bitsX) && (Yoffset < bitsY)) + { + if ((Xoffset + length) > bitsX) length = bitsX - Xoffset; + if ((Yoffset + length) > bitsY) length = bitsY - Yoffset; + + ascending = (Xoffset <= Yoffset); + + s_lo_base = Yoffset >> LOGBITS; + s_lo_bit = Yoffset & MODMASK; + Yoffset += --length; + s_hi_base = Yoffset >> LOGBITS; + s_hi_bit = Yoffset & MODMASK; + + t_lo_base = Xoffset >> LOGBITS; + t_lo_bit = Xoffset & MODMASK; + Xoffset += length; + t_hi_base = Xoffset >> LOGBITS; + t_hi_bit = Xoffset & MODMASK; + + if (ascending) + { + s_base = s_lo_base; + t_base = t_lo_base; + } + else + { + s_base = s_hi_base; + t_base = t_hi_base; + } + s_bits = 0; + t_bits = 0; + Y += s_base; + X += t_base; + notfirst = false; + while (true) + { + if (t_bits == 0) + { + if (notfirst) + { + *X = target; + if (ascending) + { + if (t_base == t_hi_base) break; + t_base++; + X++; + } + else + { + if (t_base == t_lo_base) break; + t_base--; + X--; + } + } + select = ((t_base == t_hi_base) << 1) | (t_base == t_lo_base); + switch (select) + { + case 0: + t_lower = 0; + t_upper = BITS - 1; + t_bits = BITS; + target = 0; + break; + case 1: + t_lower = t_lo_bit; + t_upper = BITS - 1; + t_bits = BITS - t_lo_bit; + mask = (unsigned int) (~0L << t_lower); + target = *X & ~ mask; + break; + case 2: + t_lower = 0; + t_upper = t_hi_bit; + t_bits = t_hi_bit + 1; + mask = (unsigned int) ((~0L << t_upper) << 1); + target = *X & mask; + break; + case 3: + t_lower = t_lo_bit; + t_upper = t_hi_bit; + t_bits = t_hi_bit - t_lo_bit + 1; + mask = (unsigned int) (~0L << t_lower); + mask &= (unsigned int) ~((~0L << t_upper) << 1); + target = *X & ~ mask; + break; + } + } + if (s_bits == 0) + { + if (notfirst) + { + if (ascending) + { + if (s_base == s_hi_base) break; + s_base++; + Y++; + } + else + { + if (s_base == s_lo_base) break; + s_base--; + Y--; + } + } + source = *Y; + select = ((s_base == s_hi_base) << 1) | (s_base == s_lo_base); + switch (select) + { + case 0: + s_lower = 0; + s_upper = BITS - 1; + s_bits = BITS; + break; + case 1: + s_lower = s_lo_bit; + s_upper = BITS - 1; + s_bits = BITS - s_lo_bit; + break; + case 2: + s_lower = 0; + s_upper = s_hi_bit; + s_bits = s_hi_bit + 1; + break; + case 3: + s_lower = s_lo_bit; + s_upper = s_hi_bit; + s_bits = s_hi_bit - s_lo_bit + 1; + break; + } + } + notfirst = true; + if (s_bits > t_bits) + { + bits = t_bits - 1; + if (ascending) + { + s_min = s_lower; + s_max = s_lower + bits; + } + else + { + s_max = s_upper; + s_min = s_upper - bits; + } + t_min = t_lower; + } + else + { + bits = s_bits - 1; + if (ascending) t_min = t_lower; + else t_min = t_upper - bits; + s_min = s_lower; + s_max = s_upper; + } + bits++; + mask = (unsigned int) (~0L << s_min); + mask &= (unsigned int) ~((~0L << s_max) << 1); + if (s_min == t_min) target |= (source & mask); + else + { + if (s_min < t_min) target |= (source & mask) << (t_min-s_min); + else target |= (source & mask) >> (s_min-t_min); + } + if (ascending) + { + s_lower += bits; + t_lower += bits; + } + else + { + s_upper -= bits; + t_upper -= bits; + } + s_bits -= bits; + t_bits -= bits; + } + *(Z+size_(Z)-1) &= mask_(Z); + } +} + + +unsigned int * BitVector_Interval_Substitute(unsigned int * X, unsigned int * Y, + unsigned int Xoffset, unsigned int Xlength, + unsigned int Yoffset, unsigned int Ylength) +{ + unsigned int Xbits = bits_(X); + unsigned int Ybits = bits_(Y); + unsigned int limit; + unsigned int diff; + + if ((Xoffset <= Xbits) && (Yoffset <= Ybits)) + { + limit = Xoffset + Xlength; + if (limit > Xbits) + { + limit = Xbits; + Xlength = Xbits - Xoffset; + } + if ((Yoffset + Ylength) > Ybits) + { + Ylength = Ybits - Yoffset; + } + if (Xlength == Ylength) + { + if ((Ylength > 0) && ((X != Y) || (Xoffset != Yoffset))) + { + BitVector_Interval_Copy(X,Y,Xoffset,Yoffset,Ylength); + } + } + else /* Xlength != Ylength */ + { + if (Xlength > Ylength) + { + diff = Xlength - Ylength; + if (Ylength > 0) BitVector_Interval_Copy(X,Y,Xoffset,Yoffset,Ylength); + if (limit < Xbits) BitVector_Delete(X,Xoffset+Ylength,diff,false); + if ((X = BitVector_Resize(X,Xbits-diff)) == NULL) return(NULL); + } + else /* Ylength > Xlength ==> Ylength > 0 */ + { + diff = Ylength - Xlength; + if (X != Y) + { + if ((X = BitVector_Resize(X,Xbits+diff)) == NULL) return(NULL); + if (limit < Xbits) BitVector_Insert(X,limit,diff,false); + BitVector_Interval_Copy(X,Y,Xoffset,Yoffset,Ylength); + } + else /* in-place */ + { + if ((Y = X = BitVector_Resize(X,Xbits+diff)) == NULL) return(NULL); + if (limit >= Xbits) + { + BitVector_Interval_Copy(X,Y,Xoffset,Yoffset,Ylength); + } + else /* limit < Xbits */ + { + BitVector_Insert(X,limit,diff,false); + if ((Yoffset+Ylength) <= limit) + { + BitVector_Interval_Copy(X,Y,Xoffset,Yoffset,Ylength); + } + else /* overlaps or lies above critical area */ + { + if (limit <= Yoffset) + { + Yoffset += diff; + BitVector_Interval_Copy(X,Y,Xoffset,Yoffset,Ylength); + } + else /* Yoffset < limit */ + { + Xlength = limit - Yoffset; + BitVector_Interval_Copy(X,Y,Xoffset,Yoffset,Xlength); + Yoffset = Xoffset + Ylength; /* = limit + diff */ + Xoffset += Xlength; + Ylength -= Xlength; + BitVector_Interval_Copy(X,Y,Xoffset,Yoffset,Ylength); + } + } + } + } + } + } + } + return(X); +} + +boolean BitVector_is_empty(unsigned int * addr) /* X == {} ? */ +{ + unsigned int size = size_(addr); + boolean r = true; + + if (size > 0) + { + *(addr+size-1) &= mask_(addr); + while (r && (size-- > 0)) r = ( *addr++ == 0 ); + } + return(r); +} + +boolean BitVector_is_full(unsigned int * addr) /* X == ~{} ? */ +{ + unsigned int size = size_(addr); + unsigned int mask = mask_(addr); + boolean r = false; + unsigned int * last; + + if (size > 0) + { + r = true; + last = addr + size - 1; + *last |= ~ mask; + while (r && (size-- > 0)) r = ( ~ *addr++ == 0 ); + *last &= mask; + } + return(r); +} + +boolean BitVector_equal(unsigned int * X, unsigned int * Y) /* X == Y ? */ +{ + unsigned int size = size_(X); + unsigned int mask = mask_(X); + boolean r = false; + + if (bits_(X) == bits_(Y)) + { + r = true; + if (size > 0) + { + *(X+size-1) &= mask; + *(Y+size-1) &= mask; + while (r && (size-- > 0)) r = (*X++ == *Y++); + } + } + return(r); +} + +/* X <,=,> Y ? : unsigned */ +signed int BitVector_Lexicompare(unsigned int * X, unsigned int * Y) { + unsigned int bitsX = bits_(X); + unsigned int bitsY = bits_(Y); + unsigned int size = size_(X); + boolean r = true; + + if (bitsX == bitsY) { + if (size > 0) { + X += size; + Y += size; + while (r && (size-- > 0)) r = (*(--X) == *(--Y)); + } + if (r) return((signed int) 0); + else { + if (*X < *Y) return((signed int) -1); else return((signed int) 1); + } + } + else { + if (bitsX < bitsY) return((signed int) -1); else return((signed int) 1); + } +} + +signed int BitVector_Compare(unsigned int * X, unsigned int * Y) /* X <,=,> Y ? */ +{ /* signed */ + unsigned int bitsX = bits_(X); + unsigned int bitsY = bits_(Y); + unsigned int size = size_(X); + unsigned int mask = mask_(X); + unsigned int sign; + boolean r = true; + + if (bitsX == bitsY) + { + if (size > 0) + { + X += size; + Y += size; + mask &= ~ (mask >> 1); + if ((sign = (*(X-1) & mask)) != (*(Y-1) & mask)) + { + if (sign) return((signed int) -1); else return((signed int) 1); + } + while (r && (size-- > 0)) r = (*(--X) == *(--Y)); + } + if (r) return((signed int) 0); + else + { + if (*X < *Y) return((signed int) -1); else return((signed int) 1); + } + } + else + { + if (bitsX < bitsY) return((signed int) -1); else return((signed int) 1); + } +} + +size_t BitVector_Hash(unsigned int * addr) +{ + unsigned int bits = bits_(addr); + unsigned int size = size_(addr); + unsigned int value; + unsigned int count; + unsigned int digit; + unsigned int length; + + size_t result = 0; + + length = bits >> 2; + if (bits & 0x0003) length++; + if (size > 0) + { + *(addr+size-1) &= mask_(addr); + while ((size-- > 0) && (length > 0)) + { + value = *addr++; + count = BITS >> 2; + while ((count-- > 0) && (length > 0)) + { + digit = value & 0x000F; + if (digit > 9) digit += (unsigned int) 'A' - 10; + else digit += (unsigned int) '0'; + result = 5*result + digit; length--; + if ((count > 0) && (length > 0)) value >>= 4; + } + } + } + return result; +} + + +unsigned char * BitVector_to_Hex(unsigned int * addr) +{ + unsigned int bits = bits_(addr); + unsigned int size = size_(addr); + unsigned int value; + unsigned int count; + unsigned int digit; + unsigned int length; + unsigned char * string; + + length = bits >> 2; + if (bits & 0x0003) length++; + string = (unsigned char *) malloc((size_t) (length+1)); + if (string == NULL) return(NULL); + string += length; + *string = (unsigned char) '\0'; + if (size > 0) + { + *(addr+size-1) &= mask_(addr); + while ((size-- > 0) && (length > 0)) + { + value = *addr++; + count = BITS >> 2; + while ((count-- > 0) && (length > 0)) + { + digit = value & 0x000F; + if (digit > 9) digit += (unsigned int) 'A' - 10; + else digit += (unsigned int) '0'; + *(--string) = (unsigned char) digit; length--; + if ((count > 0) && (length > 0)) value >>= 4; + } + } + } + return(string); +} + +ErrCode BitVector_from_Hex(unsigned int * addr, unsigned char * string) +{ + unsigned int size = size_(addr); + unsigned int mask = mask_(addr); + boolean ok = true; + unsigned int length; + unsigned int value; + unsigned int count; + int digit; + + if (size > 0) + { + length = strlen((char *) string); + string += length; + while (size-- > 0) + { + value = 0; + for ( count = 0; (ok && (length > 0) && (count < BITS)); count += 4 ) + { + digit = (int) *(--string); length--; + /* separate because toupper() is likely a macro! */ + digit = toupper(digit); + if ((ok = (isxdigit(digit) != 0))) + { + if (digit >= (int) 'A') digit -= (int) 'A' - 10; + else digit -= (int) '0'; + value |= (((unsigned int) digit) << count); + } + } + *addr++ = value; + } + *(--addr) &= mask; + } + if (ok) return(ErrCode_Ok); + else return(ErrCode_Pars); +} + +unsigned char * BitVector_to_Bin(unsigned int * addr) +{ + unsigned int size = size_(addr); + unsigned int value; + unsigned int count; + unsigned int digit; + unsigned int length; + unsigned char * string; + + length = bits_(addr); + string = (unsigned char *) malloc((size_t) (length+1)); + if (string == NULL) return(NULL); + string += length; + *string = (unsigned char) '\0'; + if (size > 0) + { + *(addr+size-1) &= mask_(addr); + while (size-- > 0) + { + value = *addr++; + count = BITS; + if (count > length) count = length; + while (count-- > 0) + { + digit = value & 0x0001; + digit += (unsigned int) '0'; + *(--string) = (unsigned char) digit; length--; + if (count > 0) value >>= 1; + } + } + } + return(string); +} + +ErrCode BitVector_from_Bin(unsigned int * addr, unsigned char * string) +{ + unsigned int size = size_(addr); + unsigned int mask = mask_(addr); + boolean ok = true; + unsigned int length; + unsigned int value; + unsigned int count; + int digit; + + if (size > 0) + { + length = strlen((char *) string); + string += length; + while (size-- > 0) + { + value = 0; + for ( count = 0; (ok && (length > 0) && (count < BITS)); count++ ) + { + digit = (int) *(--string); length--; + switch (digit) + { + case (int) '0': + break; + case (int) '1': + value |= BITMASKTAB[count]; + break; + default: + ok = false; + break; + } + } + *addr++ = value; + } + *(--addr) &= mask; + } + if (ok) return(ErrCode_Ok); + else return(ErrCode_Pars); +} + +unsigned char * BitVector_to_Dec(unsigned int * addr) +{ + unsigned int bits = bits_(addr); + unsigned int length; + unsigned int digits; + unsigned int count; + unsigned int q; + unsigned int r; + boolean loop; + unsigned char * result; + unsigned char * string; + unsigned int * quot; + unsigned int * rest; + unsigned int * temp; + unsigned int * base; + signed int sign; + + length = (unsigned int) (bits / 3.3); /* digits = bits * ln(2) / ln(10) */ + length += 2; /* compensate for truncating & provide space for minus sign */ + result = (unsigned char *) malloc((size_t) (length+1)); /* remember the '\0'! */ + if (result == NULL) return(NULL); + string = result; + sign = BitVector_Sign(addr); + if ((bits < 4) || (sign == 0)) + { + if (bits > 0) digits = *addr; else digits = (unsigned int) 0; + if (sign < 0) digits = ((unsigned int)(-((signed int)digits))) & mask_(addr); + *string++ = (unsigned char) digits + (unsigned char) '0'; + digits = 1; + } + else + { + quot = BitVector_Create(bits,false); + if (quot == NULL) + { + BitVector_Dispose(result); + return(NULL); + } + rest = BitVector_Create(bits,false); + if (rest == NULL) + { + BitVector_Dispose(result); + BitVector_Destroy(quot); + return(NULL); + } + temp = BitVector_Create(bits,false); + if (temp == NULL) + { + BitVector_Dispose(result); + BitVector_Destroy(quot); + BitVector_Destroy(rest); + return(NULL); + } + base = BitVector_Create(bits,true); + if (base == NULL) + { + BitVector_Dispose(result); + BitVector_Destroy(quot); + BitVector_Destroy(rest); + BitVector_Destroy(temp); + return(NULL); + } + if (sign < 0) BitVector_Negate(quot,addr); + else BitVector_Copy(quot,addr); + digits = 0; + *base = EXP10; + loop = (bits >= BITS); + do + { + if (loop) + { + BitVector_Copy(temp,quot); + if (BitVector_Div_Pos(quot,temp,base,rest)) + { + BitVector_Dispose(result); /* emergency exit */ + BitVector_Destroy(quot); + BitVector_Destroy(rest); /* should never occur */ + BitVector_Destroy(temp); /* under normal operation */ + BitVector_Destroy(base); + return(NULL); + } + loop = ! BitVector_is_empty(quot); + q = *rest; + } + else q = *quot; + count = LOG10; + while (((loop && (count-- > 0)) || ((! loop) && (q != 0))) && + (digits < length)) + { + if (q != 0) + { + BIT_VECTOR_DIGITIZE(unsigned int,q,r) + } + else r = (unsigned int) '0'; + *string++ = (unsigned char) r; + digits++; + } + } + while (loop && (digits < length)); + BitVector_Destroy(quot); + BitVector_Destroy(rest); + BitVector_Destroy(temp); + BitVector_Destroy(base); + } + if ((sign < 0) && (digits < length)) + { + *string++ = (unsigned char) '-'; + digits++; + } + *string = (unsigned char) '\0'; + BIT_VECTOR_reverse(result,digits); + return(result); +} + +ErrCode BitVector_from_Dec(unsigned int * addr, unsigned char * string) +{ + ErrCode error = ErrCode_Ok; + unsigned int bits = bits_(addr); + unsigned int mask = mask_(addr); + boolean init = (bits > BITS); + boolean minus; + boolean shift; + boolean carry; + unsigned int * term; + unsigned int * base; + unsigned int * prod; + unsigned int * rank; + unsigned int * temp; + unsigned int accu; + unsigned int powr; + unsigned int count; + unsigned int length; + int digit; + + if (bits > 0) + { + length = strlen((char *) string); + if (length == 0) return(ErrCode_Pars); + digit = (int) *string; + if ((minus = (digit == (int) '-')) || + (digit == (int) '+')) + { + string++; + if (--length == 0) return(ErrCode_Pars); + } + string += length; + term = BitVector_Create(BITS,false); + if (term == NULL) + { + return(ErrCode_Null); + } + base = BitVector_Create(BITS,false); + if (base == NULL) + { + BitVector_Destroy(term); + return(ErrCode_Null); + } + prod = BitVector_Create(bits,init); + if (prod == NULL) + { + BitVector_Destroy(term); + BitVector_Destroy(base); + return(ErrCode_Null); + } + rank = BitVector_Create(bits,init); + if (rank == NULL) + { + BitVector_Destroy(term); + BitVector_Destroy(base); + BitVector_Destroy(prod); + return(ErrCode_Null); + } + temp = BitVector_Create(bits,false); + if (temp == NULL) + { + BitVector_Destroy(term); + BitVector_Destroy(base); + BitVector_Destroy(prod); + BitVector_Destroy(rank); + return(ErrCode_Null); + } + BitVector_Empty(addr); + *base = EXP10; + shift = false; + while ((! error) && (length > 0)) + { + accu = 0; + powr = 1; + count = LOG10; + while ((! error) && (length > 0) && (count-- > 0)) + { + digit = (int) *(--string); length--; + /* separate because isdigit() is likely a macro! */ + if (isdigit(digit) != 0) + { + accu += ((unsigned int) digit - (unsigned int) '0') * powr; + powr *= 10; + } + else error = ErrCode_Pars; + } + if (! error) + { + if (shift) + { + *term = accu; + BitVector_Copy(temp,rank); + error = BitVector_Mul_Pos(prod,temp,term,false); + } + else + { + *prod = accu; + if ((! init) && ((accu & ~ mask) != 0)) error = ErrCode_Ovfl; + } + if (! error) + { + carry = false; + BitVector_compute(addr,addr,prod,false,&carry); + /* ignores sign change (= overflow) but ! */ + /* numbers too large (= carry) for resulting bit vector */ + if (carry) error = ErrCode_Ovfl; + else + { + if (length > 0) + { + if (shift) + { + BitVector_Copy(temp,rank); + error = BitVector_Mul_Pos(rank,temp,base,false); + } + else + { + *rank = *base; + shift = true; + } + } + } + } + } + } + BitVector_Destroy(term); + BitVector_Destroy(base); + BitVector_Destroy(prod); + BitVector_Destroy(rank); + BitVector_Destroy(temp); + if (! error && minus) + { + BitVector_Negate(addr,addr); + if ((*(addr + size_(addr) - 1) & mask & ~ (mask >> 1)) == 0) + error = ErrCode_Ovfl; + } + } + return(error); +} + +unsigned char * BitVector_to_Enum(unsigned int * addr) +{ + unsigned int bits = bits_(addr); + unsigned int sample; + unsigned int length; + unsigned int digits; + unsigned int factor; + unsigned int power; + unsigned int start; + unsigned int min; + unsigned int max; + unsigned char * string; + unsigned char * target; + boolean comma; + + if (bits > 0) + { + sample = bits - 1; /* greatest possible index */ + length = 2; /* account for index 0 && terminating '\0' */ + digits = 1; /* account for intervening dashes && commas */ + factor = 1; + power = 10; + while (sample >= (power-1)) + { + length += ++digits * factor * 6; /* 9,90,900,9000,... (9*2/3 = 6) */ + factor = power; + power *= 10; + } + if (sample > --factor) + { + sample -= factor; + factor = (unsigned int) ( sample / 3 ); + factor = (factor << 1) + (sample - (factor * 3)); + length += ++digits * factor; + } + } + else length = 1; + string = (unsigned char *) malloc((size_t) length); + if (string == NULL) return(NULL); + start = 0; + comma = false; + target = string; + while ((start < bits) && BitVector_interval_scan_inc(addr,start,&min,&max)) + { + start = max + 2; + if (comma) *target++ = (unsigned char) ','; + if (min == max) + { + target += BIT_VECTOR_int2str(target,min); + } + else + { + if (min+1 == max) + { + target += BIT_VECTOR_int2str(target,min); + *target++ = (unsigned char) ','; + target += BIT_VECTOR_int2str(target,max); + } + else + { + target += BIT_VECTOR_int2str(target,min); + *target++ = (unsigned char) '-'; + target += BIT_VECTOR_int2str(target,max); + } + } + comma = true; + } + *target = (unsigned char) '\0'; + return(string); +} + +ErrCode BitVector_from_Enum(unsigned int * addr, unsigned char * string) +{ + ErrCode error = ErrCode_Ok; + unsigned int bits = bits_(addr); + unsigned int state = 1; + unsigned int token; + unsigned int index = 0; + unsigned int start = 0; /* silence compiler warning */ + + if (bits > 0) + { + BitVector_Empty(addr); + while ((! error) && (state != 0)) + { + token = (unsigned int) *string; + /* separate because isdigit() is likely a macro! */ + if (isdigit((int)token) != 0) + { + string += BIT_VECTOR_str2int(string,&index); + if (index < bits) token = (unsigned int) '0'; + else error = ErrCode_Indx; + } + else string++; + if (! error) + switch (state) + { + case 1: + switch (token) + { + case (unsigned int) '0': + state = 2; + break; + case (unsigned int) '\0': + state = 0; + break; + default: + error = ErrCode_Pars; + break; + } + break; + case 2: + switch (token) + { + case (unsigned int) '-': + start = index; + state = 3; + break; + case (unsigned int) ',': + BIT_VECTOR_SET_BIT(addr,index) + state = 5; + break; + case (unsigned int) '\0': + BIT_VECTOR_SET_BIT(addr,index) + state = 0; + break; + default: + error = ErrCode_Pars; + break; + } + break; + case 3: + switch (token) + { + case (unsigned int) '0': + if (start < index) + BitVector_Interval_Fill(addr,start,index); + else if (start == index) + BIT_VECTOR_SET_BIT(addr,index) + else error = ErrCode_Ordr; + state = 4; + break; + default: + error = ErrCode_Pars; + break; + } + break; + case 4: + switch (token) + { + case (unsigned int) ',': + state = 5; + break; + case (unsigned int) '\0': + state = 0; + break; + default: + error = ErrCode_Pars; + break; + } + break; + case 5: + switch (token) + { + case (unsigned int) '0': + state = 2; + break; + default: + error = ErrCode_Pars; + break; + } + break; + } + } + } + return(error); +} + +void BitVector_Bit_Off(unsigned int * addr, unsigned int index) /* X = X \ {x} */ +{ + if (index < bits_(addr)) BIT_VECTOR_CLR_BIT(addr,index) +} + +void BitVector_Bit_On(unsigned int * addr, unsigned int index) /* X = X + {x} */ +{ + if (index < bits_(addr)) BIT_VECTOR_SET_BIT(addr,index) +} + +boolean BitVector_bit_flip(unsigned int * addr, unsigned int index) /* X=(X+{x})\(X*{x}) */ +{ + unsigned int mask; + + if (index < bits_(addr)) return( BIT_VECTOR_FLP_BIT(addr,index,mask) ); + else return( false ); +} + +boolean BitVector_bit_test(unsigned int * addr, unsigned int index) /* {x} in X ? */ +{ + if (index < bits_(addr)) return( BIT_VECTOR_TST_BIT(addr,index) ); + else return( false ); +} + +void BitVector_Bit_Copy(unsigned int * addr, unsigned int index, boolean bit) +{ + if (index < bits_(addr)) + { + if (bit) BIT_VECTOR_SET_BIT(addr,index) + else BIT_VECTOR_CLR_BIT(addr,index) + } +} + +void BitVector_LSB(unsigned int * addr, boolean bit) +{ + if (bits_(addr) > 0) + { + if (bit) *addr |= LSB; + else *addr &= ~ LSB; + } +} + +void BitVector_MSB(unsigned int * addr, boolean bit) +{ + unsigned int size = size_(addr); + unsigned int mask = mask_(addr); + + if (size-- > 0) + { + if (bit) *(addr+size) |= mask & ~ (mask >> 1); + else *(addr+size) &= ~ mask | (mask >> 1); + } +} + +boolean BitVector_lsb_(unsigned int * addr) +{ + if (size_(addr) > 0) return( (*addr & LSB) != 0 ); + else return( false ); +} + +boolean BitVector_msb_(unsigned int * addr) +{ + unsigned int size = size_(addr); + unsigned int mask = mask_(addr); + + if (size-- > 0) + return( (*(addr+size) & (mask & ~ (mask >> 1))) != 0 ); + else + return( false ); +} + +boolean BitVector_rotate_left(unsigned int * addr) +{ + unsigned int size = size_(addr); + unsigned int mask = mask_(addr); + unsigned int msb; + boolean carry_in; + boolean carry_out = false; + + if (size > 0) + { + msb = mask & ~ (mask >> 1); + carry_in = ((*(addr+size-1) & msb) != 0); + while (size-- > 1) + { + carry_out = ((*addr & MSB) != 0); + *addr <<= 1; + if (carry_in) *addr |= LSB; + carry_in = carry_out; + addr++; + } + carry_out = ((*addr & msb) != 0); + *addr <<= 1; + if (carry_in) *addr |= LSB; + *addr &= mask; + } + return(carry_out); +} + +boolean BitVector_rotate_right(unsigned int * addr) +{ + unsigned int size = size_(addr); + unsigned int mask = mask_(addr); + unsigned int msb; + boolean carry_in; + boolean carry_out = false; + + if (size > 0) + { + msb = mask & ~ (mask >> 1); + carry_in = ((*addr & LSB) != 0); + addr += size-1; + *addr &= mask; + carry_out = ((*addr & LSB) != 0); + *addr >>= 1; + if (carry_in) *addr |= msb; + carry_in = carry_out; + addr--; + size--; + while (size-- > 0) + { + carry_out = ((*addr & LSB) != 0); + *addr >>= 1; + if (carry_in) *addr |= MSB; + carry_in = carry_out; + addr--; + } + } + return(carry_out); +} + +boolean BitVector_shift_left(unsigned int * addr, boolean carry_in) +{ + unsigned int size = size_(addr); + unsigned int mask = mask_(addr); + unsigned int msb; + boolean carry_out = carry_in; + + if (size > 0) + { + msb = mask & ~ (mask >> 1); + while (size-- > 1) + { + carry_out = ((*addr & MSB) != 0); + *addr <<= 1; + if (carry_in) *addr |= LSB; + carry_in = carry_out; + addr++; + } + carry_out = ((*addr & msb) != 0); + *addr <<= 1; + if (carry_in) *addr |= LSB; + *addr &= mask; + } + return(carry_out); +} + +boolean BitVector_shift_right(unsigned int * addr, boolean carry_in) +{ + unsigned int size = size_(addr); + unsigned int mask = mask_(addr); + unsigned int msb; + boolean carry_out = carry_in; + + if (size > 0) + { + msb = mask & ~ (mask >> 1); + addr += size-1; + *addr &= mask; + carry_out = ((*addr & LSB) != 0); + *addr >>= 1; + if (carry_in) *addr |= msb; + carry_in = carry_out; + addr--; + size--; + while (size-- > 0) + { + carry_out = ((*addr & LSB) != 0); + *addr >>= 1; + if (carry_in) *addr |= MSB; + carry_in = carry_out; + addr--; + } + } + return(carry_out); +} + +void BitVector_Move_Left(unsigned int * addr, unsigned int bits) +{ + unsigned int count; + unsigned int words; + + if (bits > 0) + { + count = bits & MODMASK; + words = bits >> LOGBITS; + if (bits >= bits_(addr)) BitVector_Empty(addr); + else + { + while (count-- > 0) BitVector_shift_left(addr,0); + BitVector_Word_Insert(addr,0,words,true); + } + } +} + +void BitVector_Move_Right(unsigned int * addr, unsigned int bits) +{ + unsigned int count; + unsigned int words; + + if (bits > 0) + { + count = bits & MODMASK; + words = bits >> LOGBITS; + if (bits >= bits_(addr)) BitVector_Empty(addr); + else + { + while (count-- > 0) BitVector_shift_right(addr,0); + BitVector_Word_Delete(addr,0,words,true); + } + } +} + +void BitVector_Insert(unsigned int * addr, unsigned int offset, unsigned int count, boolean clear) +{ + unsigned int bits = bits_(addr); + unsigned int last; + + if ((count > 0) && (offset < bits)) + { + last = offset + count; + if (last < bits) + { + BitVector_Interval_Copy(addr,addr,last,offset,(bits-last)); + } + else last = bits; + if (clear) BitVector_Interval_Empty(addr,offset,(last-1)); + } +} + +void BitVector_Delete(unsigned int * addr, unsigned int offset, unsigned int count, boolean clear) +{ + unsigned int bits = bits_(addr); + unsigned int last; + + if ((count > 0) && (offset < bits)) + { + last = offset + count; + if (last < bits) + { + BitVector_Interval_Copy(addr,addr,offset,last,(bits-last)); + } + else count = bits - offset; + if (clear) BitVector_Interval_Empty(addr,(bits-count),(bits-1)); + } +} + +boolean BitVector_increment(unsigned int * addr) /* X++ */ +{ + unsigned int size = size_(addr); + unsigned int mask = mask_(addr); + unsigned int * last = addr + size - 1; + boolean carry = true; + + if (size > 0) + { + *last |= ~ mask; + while (carry && (size-- > 0)) + { + carry = (++(*addr++) == 0); + } + *last &= mask; + } + return(carry); +} + +boolean BitVector_decrement(unsigned int * addr) /* X-- */ +{ + unsigned int size = size_(addr); + unsigned int mask = mask_(addr); + unsigned int * last = addr + size - 1; + boolean carry = true; + + if (size > 0) + { + *last &= mask; + while (carry && (size-- > 0)) + { + carry = (*addr == 0); + --(*addr++); + } + *last &= mask; + } + return(carry); +} + +boolean BitVector_compute(unsigned int * X, unsigned int * Y, unsigned int * Z, boolean minus, boolean *carry) +{ + unsigned int size = size_(X); + unsigned int mask = mask_(X); + unsigned int vv = 0; + unsigned int cc; + unsigned int mm; + unsigned int yy; + unsigned int zz; + unsigned int lo; + unsigned int hi; + + if (size > 0) + { + if (minus) cc = (*carry == 0); + else cc = (*carry != 0); + /* deal with (size-1) least significant full words first: */ + while (--size > 0) + { + yy = *Y++; + if (minus) zz = (unsigned int) ~ ( Z ? *Z++ : 0 ); + else zz = (unsigned int) ( Z ? *Z++ : 0 ); + lo = (yy & LSB) + (zz & LSB) + cc; + hi = (yy >> 1) + (zz >> 1) + (lo >> 1); + cc = ((hi & MSB) != 0); + *X++ = (hi << 1) | (lo & LSB); + } + /* deal with most significant word (may be used only partially): */ + yy = *Y & mask; + if (minus) zz = (unsigned int) ~ ( Z ? *Z : 0 ); + else zz = (unsigned int) ( Z ? *Z : 0 ); + zz &= mask; + if (mask == LSB) /* special case, only one bit used */ + { + vv = cc; + lo = yy + zz + cc; + cc = (lo >> 1); + vv ^= cc; + *X = lo & LSB; + } + else + { + if (~ mask) /* not all bits are used, but more than one */ + { + mm = (mask >> 1); + vv = (yy & mm) + (zz & mm) + cc; + mm = mask & ~ mm; + lo = yy + zz + cc; + cc = (lo >> 1); + vv ^= cc; + vv &= mm; + cc &= mm; + *X = lo & mask; + } + else /* other special case, all bits are used */ + { + mm = ~ MSB; + lo = (yy & mm) + (zz & mm) + cc; + vv = lo & MSB; + hi = ((yy & MSB) >> 1) + ((zz & MSB) >> 1) + (vv >> 1); + cc = hi & MSB; + vv ^= cc; + *X = (hi << 1) | (lo & mm); + } + } + if (minus) *carry = (cc == 0); + else *carry = (cc != 0); + } + return(vv != 0); +} + +boolean BitVector_add(unsigned int * X, unsigned int * Y, unsigned int * Z, boolean *carry) +{ + return(BitVector_compute(X,Y,Z,false,carry)); +} + +boolean BitVector_sub(unsigned int * X, unsigned int * Y, unsigned int * Z, boolean *carry) +{ + return(BitVector_compute(X,Y,Z,true,carry)); +} + +boolean BitVector_inc(unsigned int * X, unsigned int * Y) +{ + boolean carry = true; + + return(BitVector_compute(X,Y,NULL,false,&carry)); +} + +boolean BitVector_dec(unsigned int * X, unsigned int * Y) +{ + boolean carry = true; + + return(BitVector_compute(X,Y,NULL,true,&carry)); +} + +void BitVector_Negate(unsigned int * X, unsigned int * Y) +{ + unsigned int size = size_(X); + unsigned int mask = mask_(X); + boolean carry = true; + + if (size > 0) + { + while (size-- > 0) + { + *X = ~ *Y++; + if (carry) + { + carry = (++(*X) == 0); + } + X++; + } + *(--X) &= mask; + } +} + +void BitVector_Absolute(unsigned int * X, unsigned int * Y) +{ + unsigned int size = size_(Y); + unsigned int mask = mask_(Y); + + if (size > 0) + { + if (*(Y+size-1) & (mask & ~ (mask >> 1))) BitVector_Negate(X,Y); + else BitVector_Copy(X,Y); + } +} + +// FIXME: What the hell does the return value of this mean? +// It returns 0, 1, or -1 under mysterious circumstances. +signed int BitVector_Sign(unsigned int * addr) +{ + unsigned int size = size_(addr); + unsigned int mask = mask_(addr); + unsigned int * last = addr + size - 1; + boolean r = true; + + if (size > 0) + { + *last &= mask; + while (r && (size-- > 0)) r = ( *addr++ == 0 ); + } + if (r) return((signed int) 0); + else + { + if (*last & (mask & ~ (mask >> 1))) return((signed int) -1); + else return((signed int) 1); + } +} + +ErrCode BitVector_Mul_Pos(unsigned int * X, unsigned int * Y, unsigned int * Z, boolean strict) +{ + unsigned int mask; + unsigned int limit; + unsigned int count; + signed long last; + unsigned int * sign; + boolean carry; + boolean overflow; + boolean ok = true; + + /* + Requirements: + - X, Y && Z must be distinct + - X && Y must have equal sizes (whereas Z may be any size!) + - Z should always contain the SMALLER of the two factors Y && Z + Constraints: + - The contents of Y (&& of X, of course) are destroyed + (only Z is preserved!) + */ + + if ((X == Y) || (X == Z) || (Y == Z)) return(ErrCode_Same); + if (bits_(X) != bits_(Y)) return(ErrCode_Size); + BitVector_Empty(X); + if (BitVector_is_empty(Y)) return(ErrCode_Ok); /* exit also taken if bits_(Y)==0 */ + if ((last = Set_Max(Z)) < 0L) return(ErrCode_Ok); + limit = (unsigned int) last; + sign = Y + size_(Y) - 1; + mask = mask_(Y); + *sign &= mask; + mask &= ~ (mask >> 1); + for ( count = 0; (ok && (count <= limit)); count++ ) + { + if ( BIT_VECTOR_TST_BIT(Z,count) ) + { + carry = false; + overflow = BitVector_compute(X,X,Y,false,&carry); + if (strict) ok = ! (carry || overflow); + else ok = ! carry; + } + if (ok && (count < limit)) + { + carry = BitVector_shift_left(Y,0); + if (strict) + { + overflow = ((*sign & mask) != 0); + ok = ! (carry || overflow); + } + else ok = ! carry; + } + } + if (ok) return(ErrCode_Ok); else return(ErrCode_Ovfl); +} + +ErrCode BitVector_Multiply(unsigned int * X, unsigned int * Y, unsigned int * Z) +{ + ErrCode error = ErrCode_Ok; + unsigned int bit_x = bits_(X); + unsigned int bit_y = bits_(Y); + unsigned int bit_z = bits_(Z); + unsigned int size; + unsigned int mask; + unsigned int msb; + unsigned int * ptr_y; + unsigned int * ptr_z; + boolean sgn_x; + boolean sgn_y; + boolean sgn_z; + boolean zero; + unsigned int * A; + unsigned int * B; + + /* + Requirements: + - Y && Z must have equal sizes + - X must have at least the same size as Y && Z but may be larger (!) + Features: + - The contents of Y && Z are preserved + - X may be identical with Y or Z (or both!) + (in-place multiplication is possible!) + */ + + if ((bit_y != bit_z) || (bit_x < bit_y)) return(ErrCode_Size); + if (BitVector_is_empty(Y) || BitVector_is_empty(Z)) + { + BitVector_Empty(X); + } + else + { + A = BitVector_Create(bit_y,false); + if (A == NULL) return(ErrCode_Null); + B = BitVector_Create(bit_z,false); + if (B == NULL) { BitVector_Destroy(A); return(ErrCode_Null); } + size = size_(Y); + mask = mask_(Y); + msb = (mask & ~ (mask >> 1)); + sgn_y = (((*(Y+size-1) &= mask) & msb) != 0); + sgn_z = (((*(Z+size-1) &= mask) & msb) != 0); + sgn_x = sgn_y ^ sgn_z; + if (sgn_y) BitVector_Negate(A,Y); else BitVector_Copy(A,Y); + if (sgn_z) BitVector_Negate(B,Z); else BitVector_Copy(B,Z); + ptr_y = A + size; + ptr_z = B + size; + zero = true; + while (zero && (size-- > 0)) + { + zero &= (*(--ptr_y) == 0); + zero &= (*(--ptr_z) == 0); + } + if (*ptr_y > *ptr_z) + { + if (bit_x > bit_y) + { + A = BitVector_Resize(A,bit_x); + if (A == NULL) { BitVector_Destroy(B); return(ErrCode_Null); } + } + error = BitVector_Mul_Pos(X,A,B,true); + } + else + { + if (bit_x > bit_z) + { + B = BitVector_Resize(B,bit_x); + if (B == NULL) { BitVector_Destroy(A); return(ErrCode_Null); } + } + error = BitVector_Mul_Pos(X,B,A,true); + } + if ((! error) && sgn_x) BitVector_Negate(X,X); + BitVector_Destroy(A); + BitVector_Destroy(B); + } + return(error); +} + +ErrCode BitVector_Div_Pos(unsigned int * Q, unsigned int * X, unsigned int * Y, unsigned int * R) +{ + unsigned int bits = bits_(Q); + unsigned int mask; + unsigned int * addr; + signed long last; + boolean flag; + boolean copy = false; /* flags whether valid rest is in R (0) || X (1) */ + + /* + Requirements: + - All bit vectors must have equal sizes + - Q, X, Y && R must all be distinct bit vectors + - Y must be non-zero (of course!) + Constraints: + - The contents of X (&& Q && R, of course) are destroyed + (only Y is preserved!) + */ + + if ((bits != bits_(X)) || (bits != bits_(Y)) || (bits != bits_(R))) + return(ErrCode_Size); + if ((Q == X) || (Q == Y) || (Q == R) || (X == Y) || (X == R) || (Y == R)) + return(ErrCode_Same); + if (BitVector_is_empty(Y)) + return(ErrCode_Zero); + + BitVector_Empty(R); + BitVector_Copy(Q,X); + if ((last = Set_Max(Q)) < 0L) return(ErrCode_Ok); + bits = (unsigned int) ++last; + while (bits-- > 0) + { + addr = Q + (bits >> LOGBITS); + mask = BITMASKTAB[bits & MODMASK]; + flag = ((*addr & mask) != 0); + if (copy) + { + BitVector_shift_left(X,flag); + flag = false; + BitVector_compute(R,X,Y,true,&flag); + } + else + { + BitVector_shift_left(R,flag); + flag = false; + BitVector_compute(X,R,Y,true,&flag); + } + if (flag) *addr &= ~ mask; + else + { + *addr |= mask; + copy = ! copy; + } + } + if (copy) BitVector_Copy(R,X); + return(ErrCode_Ok); +} + +ErrCode BitVector_Divide(unsigned int * Q, unsigned int * X, unsigned int * Y, unsigned int * R) +{ + ErrCode error = ErrCode_Ok; + unsigned int bits = bits_(Q); + unsigned int size = size_(Q); + unsigned int mask = mask_(Q); + unsigned int msb = (mask & ~ (mask >> 1)); + boolean sgn_q; + boolean sgn_x; + boolean sgn_y; + unsigned int * A; + unsigned int * B; + + /* + Requirements: + - All bit vectors must have equal sizes + - Q && R must be two distinct bit vectors + - Y must be non-zero (of course!) + Features: + - The contents of X && Y are preserved + - Q may be identical with X || Y (or both) + (in-place division is possible!) + - R may be identical with X || Y (or both) + (but not identical with Q!) + */ + + if ((bits != bits_(X)) || (bits != bits_(Y)) || (bits != bits_(R))) + return(ErrCode_Size); + if (Q == R) + return(ErrCode_Same); + if (BitVector_is_empty(Y)) + return(ErrCode_Zero); + + if (BitVector_is_empty(X)) + { + BitVector_Empty(Q); + BitVector_Empty(R); + } + else + { + A = BitVector_Create(bits,false); + if (A == NULL) return(ErrCode_Null); + B = BitVector_Create(bits,false); + if (B == NULL) { BitVector_Destroy(A); return(ErrCode_Null); } + size--; + sgn_x = (((*(X+size) &= mask) & msb) != 0); + sgn_y = (((*(Y+size) &= mask) & msb) != 0); + sgn_q = sgn_x ^ sgn_y; + if (sgn_x) BitVector_Negate(A,X); else BitVector_Copy(A,X); + if (sgn_y) BitVector_Negate(B,Y); else BitVector_Copy(B,Y); + if (! (error = BitVector_Div_Pos(Q,A,B,R))) + { + if (sgn_q) BitVector_Negate(Q,Q); + if (sgn_x) BitVector_Negate(R,R); + } + BitVector_Destroy(A); + BitVector_Destroy(B); + } + return(error); +} + +ErrCode BitVector_GCD(unsigned int * X, unsigned int * Y, unsigned int * Z) +{ + ErrCode error = ErrCode_Ok; + unsigned int bits = bits_(X); + unsigned int size = size_(X); + unsigned int mask = mask_(X); + unsigned int msb = (mask & ~ (mask >> 1)); + boolean sgn_a; + boolean sgn_b; + boolean sgn_r; + unsigned int * Q; + unsigned int * R; + unsigned int * A; + unsigned int * B; + unsigned int * T; + + /* + Requirements: + - All bit vectors must have equal sizes + Features: + - The contents of Y && Z are preserved + - X may be identical with Y || Z (or both) + (in-place is possible!) + - GCD(0,z) == GCD(z,0) == z + - negative values are h&&led correctly + */ + + if ((bits != bits_(Y)) || (bits != bits_(Z))) return(ErrCode_Size); + if (BitVector_is_empty(Y)) + { + if (X != Z) BitVector_Copy(X,Z); + return(ErrCode_Ok); + } + if (BitVector_is_empty(Z)) + { + if (X != Y) BitVector_Copy(X,Y); + return(ErrCode_Ok); + } + Q = BitVector_Create(bits,false); + if (Q == NULL) + { + return(ErrCode_Null); + } + R = BitVector_Create(bits,false); + if (R == NULL) + { + BitVector_Destroy(Q); + return(ErrCode_Null); + } + A = BitVector_Create(bits,false); + if (A == NULL) + { + BitVector_Destroy(Q); + BitVector_Destroy(R); + return(ErrCode_Null); + } + B = BitVector_Create(bits,false); + if (B == NULL) + { + BitVector_Destroy(Q); + BitVector_Destroy(R); + BitVector_Destroy(A); + return(ErrCode_Null); + } + size--; + sgn_a = (((*(Y+size) &= mask) & msb) != 0); + sgn_b = (((*(Z+size) &= mask) & msb) != 0); + if (sgn_a) BitVector_Negate(A,Y); else BitVector_Copy(A,Y); + if (sgn_b) BitVector_Negate(B,Z); else BitVector_Copy(B,Z); + while (! error) + { + if (! (error = BitVector_Div_Pos(Q,A,B,R))) + { + if (BitVector_is_empty(R)) break; + T = A; sgn_r = sgn_a; + A = B; sgn_a = sgn_b; + B = R; sgn_b = sgn_r; + R = T; + } + } + if (! error) + { + if (sgn_b) BitVector_Negate(X,B); else BitVector_Copy(X,B); + } + BitVector_Destroy(Q); + BitVector_Destroy(R); + BitVector_Destroy(A); + BitVector_Destroy(B); + return(error); +} + +ErrCode BitVector_GCD2(unsigned int * U, unsigned int * V, unsigned int * W, unsigned int * X, unsigned int * Y) +{ + ErrCode error = ErrCode_Ok; + unsigned int bits = bits_(U); + unsigned int size = size_(U); + unsigned int mask = mask_(U); + unsigned int msb = (mask & ~ (mask >> 1)); + boolean minus; + boolean carry; + boolean sgn_q; + boolean sgn_r; + boolean sgn_a; + boolean sgn_b; + boolean sgn_x; + boolean sgn_y; + unsigned int * * L; + unsigned int * Q; + unsigned int * R; + unsigned int * A; + unsigned int * B; + unsigned int * T; + unsigned int * X1; + unsigned int * X2; + unsigned int * X3; + unsigned int * Y1; + unsigned int * Y2; + unsigned int * Y3; + unsigned int * Z; + + /* + Requirements: + - All bit vectors must have equal sizes + - U, V, && W must all be distinct bit vectors + Features: + - The contents of X && Y are preserved + - U, V && W may be identical with X || Y (or both, + provided that U, V && W are mutually distinct) + (i.e., in-place is possible!) + - GCD(0,z) == GCD(z,0) == z + - negative values are h&&led correctly + */ + + if ((bits != bits_(V)) || + (bits != bits_(W)) || + (bits != bits_(X)) || + (bits != bits_(Y))) + { + return(ErrCode_Size); + } + if ((U == V) || (U == W) || (V == W)) + { + return(ErrCode_Same); + } + if (BitVector_is_empty(X)) + { + if (U != Y) BitVector_Copy(U,Y); + BitVector_Empty(V); + BitVector_Empty(W); + *W = 1; + return(ErrCode_Ok); + } + if (BitVector_is_empty(Y)) + { + if (U != X) BitVector_Copy(U,X); + BitVector_Empty(V); + BitVector_Empty(W); + *V = 1; + return(ErrCode_Ok); + } + if ((L = BitVector_Create_List(bits,false,11)) == NULL) + { + return(ErrCode_Null); + } + Q = L[0]; + R = L[1]; + A = L[2]; + B = L[3]; + X1 = L[4]; + X2 = L[5]; + X3 = L[6]; + Y1 = L[7]; + Y2 = L[8]; + Y3 = L[9]; + Z = L[10]; + size--; + sgn_a = (((*(X+size) &= mask) & msb) != 0); + sgn_b = (((*(Y+size) &= mask) & msb) != 0); + if (sgn_a) BitVector_Negate(A,X); else BitVector_Copy(A,X); + if (sgn_b) BitVector_Negate(B,Y); else BitVector_Copy(B,Y); + BitVector_Empty(X1); + BitVector_Empty(X2); + *X1 = 1; + BitVector_Empty(Y1); + BitVector_Empty(Y2); + *Y2 = 1; + sgn_x = false; + sgn_y = false; + while (! error) + { + if ((error = BitVector_Div_Pos(Q,A,B,R))) + { + break; + } + if (BitVector_is_empty(R)) + { + break; + } + sgn_q = sgn_a ^ sgn_b; + + if (sgn_x) BitVector_Negate(Z,X2); else BitVector_Copy(Z,X2); + if ((error = BitVector_Mul_Pos(X3,Z,Q,true))) + { + break; + } + minus = ! (sgn_x ^ sgn_q); + carry = 0; + if (BitVector_compute(X3,X1,X3,minus,&carry)) + { + error = ErrCode_Ovfl; + break; + } + sgn_x = (((*(X3+size) &= mask) & msb) != 0); + + if (sgn_y) BitVector_Negate(Z,Y2); else BitVector_Copy(Z,Y2); + if ((error = BitVector_Mul_Pos(Y3,Z,Q,true))) + { + break; + } + minus = ! (sgn_y ^ sgn_q); + carry = 0; + if (BitVector_compute(Y3,Y1,Y3,minus,&carry)) + { + error = ErrCode_Ovfl; + break; + } + sgn_y = (((*(Y3+size) &= mask) & msb) != 0); + + T = A; sgn_r = sgn_a; + A = B; sgn_a = sgn_b; + B = R; sgn_b = sgn_r; + R = T; + + T = X1; + X1 = X2; + X2 = X3; + X3 = T; + + T = Y1; + Y1 = Y2; + Y2 = Y3; + Y3 = T; + } + if (! error) + { + if (sgn_b) BitVector_Negate(U,B); else BitVector_Copy(U,B); + BitVector_Copy(V,X2); + BitVector_Copy(W,Y2); + } + BitVector_Destroy_List(L,11); + return(error); +} + +ErrCode BitVector_Power(unsigned int * X, unsigned int * Y, unsigned int * Z) +{ + ErrCode error = ErrCode_Ok; + unsigned int bits = bits_(X); + boolean first = true; + signed long last; + unsigned int limit; + unsigned int count; + unsigned int * T; + + /* + Requirements: + - X must have at least the same size as Y but may be larger (!) + - X may not be identical with Z + - Z must be positive + Features: + - The contents of Y && Z are preserved + */ + + if (X == Z) return(ErrCode_Same); + if (bits < bits_(Y)) return(ErrCode_Size); + if (BitVector_msb_(Z)) return(ErrCode_Expo); + if ((last = Set_Max(Z)) < 0L) + { + if (bits < 2) return(ErrCode_Ovfl); + BitVector_Empty(X); + *X |= LSB; + return(ErrCode_Ok); /* anything ^ 0 == 1 */ + } + if (BitVector_is_empty(Y)) + { + if (X != Y) BitVector_Empty(X); + return(ErrCode_Ok); /* 0 ^ anything ! zero == 0 */ + } + T = BitVector_Create(bits,false); + if (T == NULL) return(ErrCode_Null); + limit = (unsigned int) last; + for ( count = 0; ((!error) && (count <= limit)); count++ ) + { + if ( BIT_VECTOR_TST_BIT(Z,count) ) + { + if (first) + { + first = false; + if (count) { BitVector_Copy(X,T); } + else { if (X != Y) BitVector_Copy(X,Y); } + } + else error = BitVector_Multiply(X,T,X); /* order important because T > X */ + } + if ((!error) && (count < limit)) + { + if (count) error = BitVector_Multiply(T,T,T); + else error = BitVector_Multiply(T,Y,Y); + } + } + BitVector_Destroy(T); + return(error); +} + +void BitVector_Block_Store(unsigned int * addr, unsigned char * buffer, unsigned int length) +{ + unsigned int size = size_(addr); + unsigned int mask = mask_(addr); + unsigned int value; + unsigned int count; + + /* provide translation for independence of endian-ness: */ + if (size > 0) + { + while (size-- > 0) + { + value = 0; + for ( count = 0; (length > 0) && (count < BITS); count += 8 ) + { + value |= (((unsigned int) *buffer++) << count); length--; + } + *addr++ = value; + } + *(--addr) &= mask; + } +} + +unsigned char * BitVector_Block_Read(unsigned int * addr, unsigned int * length) +{ + unsigned int size = size_(addr); + unsigned int value; + unsigned int count; + unsigned char * buffer; + unsigned char * target; + + /* provide translation for independence of endian-ness: */ + *length = size << FACTOR; + buffer = (unsigned char *) malloc((size_t) ((*length)+1)); + if (buffer == NULL) return(NULL); + target = buffer; + if (size > 0) + { + *(addr+size-1) &= mask_(addr); + while (size-- > 0) + { + value = *addr++; + count = BITS >> 3; + while (count-- > 0) + { + *target++ = (unsigned char) (value & 0x00FF); + if (count > 0) value >>= 8; + } + } + } + *target = (unsigned char) '\0'; + return(buffer); +} + +void BitVector_Word_Store(unsigned int * addr, unsigned int offset, unsigned int value) +{ + unsigned int size = size_(addr); + + if (size > 0) + { + if (offset < size) *(addr+offset) = value; + *(addr+size-1) &= mask_(addr); + } +} + +unsigned int BitVector_Word_Read(unsigned int * addr, unsigned int offset) +{ + unsigned int size = size_(addr); + + if (size > 0) + { + *(addr+size-1) &= mask_(addr); + if (offset < size) return( *(addr+offset) ); + } + return( (unsigned int) 0 ); +} + +void BitVector_Word_Insert(unsigned int * addr, unsigned int offset, unsigned int count, + boolean clear) +{ + unsigned int size = size_(addr); + unsigned int mask = mask_(addr); + unsigned int * last = addr+size-1; + + if (size > 0) + { + *last &= mask; + if (offset > size) offset = size; + BIT_VECTOR_ins_words(addr+offset,size-offset,count,clear); + *last &= mask; + } +} + +void BitVector_Word_Delete(unsigned int * addr, unsigned int offset, unsigned int count, + boolean clear) +{ + unsigned int size = size_(addr); + unsigned int mask = mask_(addr); + unsigned int * last = addr+size-1; + + if (size > 0) + { + *last &= mask; + if (offset > size) offset = size; + BIT_VECTOR_del_words(addr+offset,size-offset,count,clear); + *last &= mask; + } +} + +void BitVector_Chunk_Store(unsigned int * addr, unsigned int chunksize, unsigned int offset, + unsigned long value) +{ + unsigned int bits = bits_(addr); + unsigned int mask; + unsigned int temp; + + if ((chunksize > 0) && (offset < bits)) + { + if (chunksize > LONGBITS) chunksize = LONGBITS; + if ((offset + chunksize) > bits) chunksize = bits - offset; + addr += offset >> LOGBITS; + offset &= MODMASK; + while (chunksize > 0) + { + mask = (unsigned int) (~0L << offset); + bits = offset + chunksize; + if (bits < BITS) + { + mask &= (unsigned int) ~(~0L << bits); + bits = chunksize; + } + else bits = BITS - offset; + temp = (unsigned int) (value << offset); + temp &= mask; + *addr &= ~ mask; + *addr++ |= temp; + value >>= bits; + chunksize -= bits; + offset = 0; + } + } +} + +unsigned long BitVector_Chunk_Read(unsigned int * addr, unsigned int chunksize, unsigned int offset) +{ + unsigned int bits = bits_(addr); + unsigned int chunkbits = 0; + unsigned long value = 0L; + unsigned long temp; + unsigned int mask; + + if ((chunksize > 0) && (offset < bits)) + { + if (chunksize > LONGBITS) chunksize = LONGBITS; + if ((offset + chunksize) > bits) chunksize = bits - offset; + addr += offset >> LOGBITS; + offset &= MODMASK; + while (chunksize > 0) + { + bits = offset + chunksize; + if (bits < BITS) + { + mask = (unsigned int) ~(~0L << bits); + bits = chunksize; + } + else + { + mask = (unsigned int) ~0L; + bits = BITS - offset; + } + temp = (unsigned long) ((*addr++ & mask) >> offset); + value |= temp << chunkbits; + chunkbits += bits; + chunksize -= bits; + offset = 0; + } + } + return(value); +} + + /*******************/ + /* set operations: */ + /*******************/ + +void Set_Union(unsigned int * X, unsigned int * Y, unsigned int * Z) /* X = Y + Z */ +{ + unsigned int bits = bits_(X); + unsigned int size = size_(X); + unsigned int mask = mask_(X); + + if ((size > 0) && (bits == bits_(Y)) && (bits == bits_(Z))) + { + while (size-- > 0) *X++ = *Y++ | *Z++; + *(--X) &= mask; + } +} + +void Set_Intersection(unsigned int * X, unsigned int * Y, unsigned int * Z) /* X = Y * Z */ +{ + unsigned int bits = bits_(X); + unsigned int size = size_(X); + unsigned int mask = mask_(X); + + if ((size > 0) && (bits == bits_(Y)) && (bits == bits_(Z))) + { + while (size-- > 0) *X++ = *Y++ & *Z++; + *(--X) &= mask; + } +} + +void Set_Difference(unsigned int * X, unsigned int * Y, unsigned int * Z) /* X = Y \ Z */ +{ + unsigned int bits = bits_(X); + unsigned int size = size_(X); + unsigned int mask = mask_(X); + + if ((size > 0) && (bits == bits_(Y)) && (bits == bits_(Z))) + { + while (size-- > 0) *X++ = *Y++ & ~ *Z++; + *(--X) &= mask; + } +} + +void Set_ExclusiveOr(unsigned int * X, unsigned int * Y, unsigned int * Z) /* X=(Y+Z)\(Y*Z) */ +{ + unsigned int bits = bits_(X); + unsigned int size = size_(X); + unsigned int mask = mask_(X); + + if ((size > 0) && (bits == bits_(Y)) && (bits == bits_(Z))) + { + while (size-- > 0) *X++ = *Y++ ^ *Z++; + *(--X) &= mask; + } +} + +void Set_Complement(unsigned int * X, unsigned int * Y) /* X = ~Y */ +{ + unsigned int size = size_(X); + unsigned int mask = mask_(X); + + if ((size > 0) && (bits_(X) == bits_(Y))) + { + while (size-- > 0) *X++ = ~ *Y++; + *(--X) &= mask; + } +} + + /******************/ + /* set functions: */ + /******************/ + +boolean Set_subset(unsigned int * X, unsigned int * Y) /* X subset Y ? */ +{ + unsigned int size = size_(X); + boolean r = false; + + if ((size > 0) && (bits_(X) == bits_(Y))) + { + r = true; + while (r && (size-- > 0)) r = ((*X++ & ~ *Y++) == 0); + } + return(r); +} + +unsigned int Set_Norm(unsigned int * addr) /* = | X | */ +{ + unsigned char * byte; + unsigned int bytes; + unsigned int n; + + byte = (unsigned char *) addr; + bytes = size_(addr) << FACTOR; + n = 0; + while (bytes-- > 0) + { + n += BitVector_BYTENORM[*byte++]; + } + return(n); +} + +unsigned int Set_Norm2(unsigned int * addr) /* = | X | */ +{ + unsigned int size = size_(addr); + unsigned int w0,w1; + unsigned int n,k; + + n = 0; + while (size-- > 0) + { + k = 0; + w1 = ~ (w0 = *addr++); + while (w0 && w1) + { + w0 &= w0 - 1; + w1 &= w1 - 1; + k++; + } + if (w0 == 0) n += k; + else n += BITS - k; + } + return(n); +} + +unsigned int Set_Norm3(unsigned int * addr) /* = | X | */ +{ + unsigned int size = size_(addr); + unsigned int count = 0; + unsigned int c; + + while (size-- > 0) + { + c = *addr++; + while (c) + { + c &= c - 1; + count++; + } + } + return(count); +} + +signed long Set_Min(unsigned int * addr) /* = min(X) */ +{ + boolean empty = true; + unsigned int size = size_(addr); + unsigned int i = 0; + unsigned int c = 0; /* silence compiler warning */ + + while (empty && (size-- > 0)) + { + if ((c = *addr++)) empty = false; else i++; + } + if (empty) return((signed long) LONG_MAX); /* plus infinity */ + i <<= LOGBITS; + while (! (c & LSB)) + { + c >>= 1; + i++; + } + return((signed long) i); +} + +signed long Set_Max(unsigned int * addr) /* = max(X) */ +{ + boolean empty = true; + unsigned int size = size_(addr); + unsigned int i = size; + unsigned int c = 0; /* silence compiler warning */ + + addr += size-1; + while (empty && (size-- > 0)) + { + if ((c = *addr--)) empty = false; else i--; + } + if (empty) return((signed long) LONG_MIN); /* minus infinity */ + i <<= LOGBITS; + while (! (c & MSB)) + { + c <<= 1; + i--; + } + return((signed long) --i); +} + + /**********************************/ + /* matrix-of-booleans operations: */ + /**********************************/ + +void Matrix_Multiplication(unsigned int * X, unsigned int rowsX, unsigned int colsX, + unsigned int * Y, unsigned int rowsY, unsigned int colsY, + unsigned int * Z, unsigned int rowsZ, unsigned int colsZ) +{ + unsigned int i; + unsigned int j; + unsigned int k; + unsigned int indxX; + unsigned int indxY; + unsigned int indxZ; + unsigned int termX; + unsigned int termY; + unsigned int sum; + + if ((colsY == rowsZ) && (rowsX == rowsY) && (colsX == colsZ) && + (bits_(X) == rowsX*colsX) && + (bits_(Y) == rowsY*colsY) && + (bits_(Z) == rowsZ*colsZ)) + { + for ( i = 0; i < rowsY; i++ ) + { + termX = i * colsX; + termY = i * colsY; + for ( j = 0; j < colsZ; j++ ) + { + indxX = termX + j; + sum = 0; + for ( k = 0; k < colsY; k++ ) + { + indxY = termY + k; + indxZ = k * colsZ + j; + if ( BIT_VECTOR_TST_BIT(Y,indxY) & + BIT_VECTOR_TST_BIT(Z,indxZ) ) sum ^= 1; + } + if (sum) BIT_VECTOR_SET_BIT(X,indxX) + else BIT_VECTOR_CLR_BIT(X,indxX) + } + } + } +} + +void Matrix_Product(unsigned int * X, unsigned int rowsX, unsigned int colsX, + unsigned int * Y, unsigned int rowsY, unsigned int colsY, + unsigned int * Z, unsigned int rowsZ, unsigned int colsZ) +{ + unsigned int i; + unsigned int j; + unsigned int k; + unsigned int indxX; + unsigned int indxY; + unsigned int indxZ; + unsigned int termX; + unsigned int termY; + unsigned int sum; + + if ((colsY == rowsZ) && (rowsX == rowsY) && (colsX == colsZ) && + (bits_(X) == rowsX*colsX) && + (bits_(Y) == rowsY*colsY) && + (bits_(Z) == rowsZ*colsZ)) + { + for ( i = 0; i < rowsY; i++ ) + { + termX = i * colsX; + termY = i * colsY; + for ( j = 0; j < colsZ; j++ ) + { + indxX = termX + j; + sum = 0; + for ( k = 0; k < colsY; k++ ) + { + indxY = termY + k; + indxZ = k * colsZ + j; + if ( BIT_VECTOR_TST_BIT(Y,indxY) & + BIT_VECTOR_TST_BIT(Z,indxZ) ) sum |= 1; + } + if (sum) BIT_VECTOR_SET_BIT(X,indxX) + else BIT_VECTOR_CLR_BIT(X,indxX) + } + } + } +} + +void Matrix_Closure(unsigned int * addr, unsigned int rows, unsigned int cols) +{ + unsigned int i; + unsigned int j; + unsigned int k; + unsigned int ii; + unsigned int ij; + unsigned int ik; + unsigned int kj; + unsigned int termi; + unsigned int termk; + + if ((rows == cols) && (bits_(addr) == rows*cols)) + { + for ( i = 0; i < rows; i++ ) + { + ii = i * cols + i; + BIT_VECTOR_SET_BIT(addr,ii) + } + for ( k = 0; k < rows; k++ ) + { + termk = k * cols; + for ( i = 0; i < rows; i++ ) + { + termi = i * cols; + ik = termi + k; + for ( j = 0; j < rows; j++ ) + { + ij = termi + j; + kj = termk + j; + if ( BIT_VECTOR_TST_BIT(addr,ik) & + BIT_VECTOR_TST_BIT(addr,kj) ) + BIT_VECTOR_SET_BIT(addr,ij) + } + } + } + } +} + +void Matrix_Transpose(unsigned int * X, unsigned int rowsX, unsigned int colsX, + unsigned int * Y, unsigned int rowsY, unsigned int colsY) +{ + unsigned int i; + unsigned int j; + unsigned int ii; + unsigned int ij; + unsigned int ji; + unsigned int addii; + unsigned int addij; + unsigned int addji; + unsigned int bitii; + unsigned int bitij; + unsigned int bitji; + unsigned int termi; + unsigned int termj; + boolean swap; + + /* BEWARE that "in-place" is ONLY possible if the matrix is quadratic!! */ + + if ((rowsX == colsY) && (colsX == rowsY) && + (bits_(X) == rowsX*colsX) && + (bits_(Y) == rowsY*colsY)) + { + if (rowsY == colsY) /* in-place is possible! */ + { + for ( i = 0; i < rowsY; i++ ) + { + termi = i * colsY; + for ( j = 0; j < i; j++ ) + { + termj = j * colsX; + ij = termi + j; + ji = termj + i; + addij = ij >> LOGBITS; + addji = ji >> LOGBITS; + bitij = BITMASKTAB[ij & MODMASK]; + bitji = BITMASKTAB[ji & MODMASK]; + swap = ((*(Y+addij) & bitij) != 0); + if ((*(Y+addji) & bitji) != 0) + *(X+addij) |= bitij; + else + *(X+addij) &= ~ bitij; + if (swap) + *(X+addji) |= bitji; + else + *(X+addji) &= ~ bitji; + } + ii = termi + i; + addii = ii >> LOGBITS; + bitii = BITMASKTAB[ii & MODMASK]; + if ((*(Y+addii) & bitii) != 0) + *(X+addii) |= bitii; + else + *(X+addii) &= ~ bitii; + } + } + else /* rowsX != colsX, in-place is ~ possible! */ + { + for ( i = 0; i < rowsY; i++ ) + { + termi = i * colsY; + for ( j = 0; j < colsY; j++ ) + { + termj = j * colsX; + ij = termi + j; + ji = termj + i; + addij = ij >> LOGBITS; + addji = ji >> LOGBITS; + bitij = BITMASKTAB[ij & MODMASK]; + bitji = BITMASKTAB[ji & MODMASK]; + if ((*(Y+addij) & bitij) != 0) + *(X+addji) |= bitji; + else + *(X+addji) &= ~ bitji; + } + } + } + } +} +}; //end of namespace CONSTANTBV diff --git a/stp/constantbv/constantbv.h b/stp/constantbv/constantbv.h new file mode 100644 index 00000000..47e0c56f --- /dev/null +++ b/stp/constantbv/constantbv.h @@ -0,0 +1,316 @@ +#ifndef MODULE_BIT_VECTOR +#define MODULE_BIT_VECTOR +/*****************************************************************************/ +/* AUTHOR: */ +/*****************************************************************************/ +/* */ +/* Steffen Beyer */ +/* mailto:sb@engelschall.com */ +/* http://www.engelschall.com/u/sb/download/ */ +/* */ +/*****************************************************************************/ +/* COPYRIGHT: */ +/*****************************************************************************/ +/* */ +/* Copyright (c) 1995 - 2004 by Steffen Beyer. */ +/* All rights reserved. */ +/* */ +/*****************************************************************************/ +/* LICENSE: */ +/*****************************************************************************/ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Library General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2 of the License, or (at your option) any later version. */ +/* */ +/* This library is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* Library General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Library General Public */ +/* License along with this library; if not, write to the */ +/* Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/* or download a copy from ftp://ftp.gnu.org/pub/gnu/COPYING.LIB-2.0 */ +/* */ +/*****************************************************************************/ + + +/*****************************************************************************/ +/* MODULE NAME: BitVector.h MODULE TYPE: (adt) */ +/*****************************************************************************/ +/* MODULE IMPORTS: */ +/*****************************************************************************/ +#include <stdlib.h> /* MODULE TYPE: (sys) */ +#include <limits.h> /* MODULE TYPE: (sys) */ +#include <string.h> /* MODULE TYPE: (sys) */ +#include <ctype.h> /* MODULE TYPE: (sys) */ +/*****************************************************************************/ +/* MODULE INTERFACE: */ +/*****************************************************************************/ + +namespace CONSTANTBV { + +#ifdef __cplusplus + extern "C" { + typedef bool boolean; +#else + typedef enum { false = (0!=0), true = (0==0) } boolean; +#endif + + typedef enum { + ErrCode_Ok = 0, /* everything went allright */ + ErrCode_Type, /* types word and size_t have incompatible sizes */ + ErrCode_Bits, /* bits of word and sizeof(word) are inconsistent */ + ErrCode_Word, /* size of word is less than 16 bits */ + ErrCode_Long, /* size of word is greater than size of long */ + ErrCode_Powr, /* number of bits of word is not a power of two */ + ErrCode_Loga, /* error in calculation of logarithm */ + ErrCode_Null, /* unable to allocate memory */ + ErrCode_Indx, /* index out of range */ + ErrCode_Ordr, /* minimum > maximum index */ + ErrCode_Size, /* bit vector size mismatch */ + ErrCode_Pars, /* input string syntax error */ + ErrCode_Ovfl, /* numeric overflow error */ + ErrCode_Same, /* operands must be distinct */ + ErrCode_Expo, /* exponent must be positive */ + ErrCode_Zero /* division by zero error */ + } ErrCode; + + + /* ===> MISCELLANEOUS BASIC FUNCTIONS: <=== */ + unsigned char * BitVector_Error(ErrCode error); /* return string for err code */ + ErrCode BitVector_Boot (void); /* 0 = ok, 1..7 = error */ + unsigned int BitVector_Size(unsigned int bits); /* bit vector size (# of words) */ + unsigned int BitVector_Mask(unsigned int bits); /* bit vector mask (unused bits) */ + + /* ===> CLASS METHODS: <=== */ + unsigned char * BitVector_Version(void); /* return version string */ + unsigned int BitVector_Word_Bits(void); /* return # of bits in machine word */ + unsigned int BitVector_Long_Bits(void); /* return # of bits in unsigned long */ + + /* ===> CONSTRUCTOR METHODS: <=== */ + unsigned int * BitVector_Create (unsigned int bits, boolean clear); /* malloc */ + unsigned int ** BitVector_Create_List (unsigned int bits, boolean clear, unsigned int count); + unsigned int * BitVector_Resize (unsigned int * oldaddr, unsigned int bits); /* realloc */ + unsigned int * BitVector_Shadow (unsigned int * addr); /* make new same size but empty */ + unsigned int * BitVector_Clone (unsigned int * addr); /* make exact duplicate */ + unsigned int * BitVector_Concat (unsigned int * X, unsigned int * Y); /* return concatenation */ + + /* ===> DESTRUCTOR METHODS: <=== */ + void BitVector_Dispose (unsigned char * string); /* string */ + void BitVector_Destroy (unsigned int * addr); /* bitvec */ + void BitVector_Destroy_List (unsigned int * * list, unsigned int count); /* list */ + + /* ===> OBJECT METHODS: <=== */ + + /* ===> bit vector hash: */ + size_t BitVector_Hash (unsigned int * X); + + /* ===> bit vector copy function: */ + void BitVector_Copy (unsigned int * X, unsigned int * Y); /* X := Y */ + + /* ===> bit vector initialization: */ + void BitVector_Empty (unsigned int * addr); /* X = {} */ + void BitVector_Fill (unsigned int * addr); /* X = ~{} */ + void BitVector_Flip (unsigned int * addr); /* X = ~X */ + void BitVector_Primes (unsigned int * addr); + + /* ===> miscellaneous functions: */ + void BitVector_Reverse (unsigned int * X, unsigned int * Y); + + /* ===> bit vector interval operations and functions: */ + void BitVector_Interval_Empty (unsigned int * addr, unsigned int lower, unsigned int upper); + void BitVector_Interval_Fill (unsigned int * addr, unsigned int lower, unsigned int upper); + void BitVector_Interval_Flip (unsigned int * addr, unsigned int lower, unsigned int upper); + void BitVector_Interval_Reverse (unsigned int * addr, unsigned int lower, unsigned int upper); + + boolean BitVector_interval_scan_inc (unsigned int * addr, unsigned int start, + unsigned int * min, unsigned int * max); + boolean BitVector_interval_scan_dec (unsigned int * addr, unsigned int start, + unsigned int * min, unsigned int * max); + void BitVector_Interval_Copy (unsigned int * X, unsigned int * Y, + unsigned int Xoffset, unsigned int Yoffset, unsigned int length); + unsigned int * BitVector_Interval_Substitute(unsigned int * X, unsigned int * Y, + unsigned int Xoffset, unsigned int Xlength, + unsigned int Yoffset, unsigned int Ylength); + + /* ===> bit vector test functions: */ + boolean BitVector_is_empty (unsigned int * addr); /* X == {} ? */ + boolean BitVector_is_full (unsigned int * addr); /* X == ~{} ? */ + boolean BitVector_equal (unsigned int * X, unsigned int * Y); /* X == Y ? */ + signed int BitVector_Lexicompare (unsigned int * X, unsigned int * Y); /* X <,=,> Y ? */ + signed int BitVector_Compare (unsigned int * X, unsigned int * Y); /* X <,=,> Y ? */ + + /* ===> bit vector string conversion functions: */ + unsigned char * BitVector_to_Hex (unsigned int * addr); + ErrCode BitVector_from_Hex (unsigned int * addr, unsigned char * string); + unsigned char * BitVector_to_Bin (unsigned int * addr); + ErrCode BitVector_from_Bin (unsigned int * addr, unsigned char * string); + unsigned char * BitVector_to_Dec (unsigned int * addr); + ErrCode BitVector_from_Dec (unsigned int * addr, unsigned char * string); + unsigned char * BitVector_to_Enum (unsigned int * addr); + ErrCode BitVector_from_Enum (unsigned int * addr, unsigned char * string); + + /* ===> bit vector bit operations, functions & tests: */ + void BitVector_Bit_Off (unsigned int * addr, unsigned int index); /* X = X \ {x} */ + void BitVector_Bit_On (unsigned int * addr, unsigned int index); /* X = X + {x} */ + boolean BitVector_bit_flip (unsigned int * addr, unsigned int index); /* (X+{x})\(X*{x}) */ + boolean BitVector_bit_test (unsigned int * addr, unsigned int index); /* {x} in X ? */ + void BitVector_Bit_Copy (unsigned int * addr, unsigned int index, boolean bit); + + /* ===> bit vector bit shift & rotate functions: */ + void BitVector_LSB (unsigned int * addr, boolean bit); + void BitVector_MSB (unsigned int * addr, boolean bit); + boolean BitVector_lsb_ (unsigned int * addr); + boolean BitVector_msb_ (unsigned int * addr); + boolean BitVector_rotate_left (unsigned int * addr); + boolean BitVector_rotate_right (unsigned int * addr); + boolean BitVector_shift_left (unsigned int * addr, boolean carry_in); + boolean BitVector_shift_right (unsigned int * addr, boolean carry_in); + void BitVector_Move_Left (unsigned int * addr, unsigned int bits); + void BitVector_Move_Right (unsigned int * addr, unsigned int bits); + + /* ===> bit vector insert/delete bits: */ + void BitVector_Insert (unsigned int * addr, + unsigned int offset, unsigned int count, boolean clear); + void BitVector_Delete (unsigned int * addr, + unsigned int offset, unsigned int count, boolean clear); + + /* ===> bit vector arithmetic: */ + boolean BitVector_increment (unsigned int * addr); /* X++ */ + boolean BitVector_decrement (unsigned int * addr); /* X-- */ + boolean BitVector_compute (unsigned int * X, unsigned int * Y, + unsigned int * Z, boolean minus, boolean *carry); + boolean BitVector_add (unsigned int * X, + unsigned int * Y, unsigned int * Z, boolean *carry); + boolean BitVector_sub (unsigned int * X, + unsigned int * Y, unsigned int * Z, boolean *carry); /* X = Y-Z*/ + boolean BitVector_inc (unsigned int * X, unsigned int * Y); + boolean BitVector_dec (unsigned int * X, unsigned int * Y); + + void BitVector_Negate (unsigned int * X, unsigned int * Y); + void BitVector_Absolute (unsigned int * X, unsigned int * Y); + signed int BitVector_Sign (unsigned int * addr); + ErrCode BitVector_Mul_Pos (unsigned int * X, + unsigned int * Y, unsigned int * Z, boolean strict); + ErrCode BitVector_Multiply (unsigned int * X, unsigned int * Y, unsigned int * Z); + ErrCode BitVector_Div_Pos (unsigned int * Q, unsigned int * X, unsigned int * Y, unsigned int * R); + ErrCode BitVector_Divide (unsigned int * Q, unsigned int * X, unsigned int * Y, unsigned int * R); + ErrCode BitVector_GCD (unsigned int * X, unsigned int * Y, unsigned int * Z); + ErrCode BitVector_GCD2 (unsigned int * U, unsigned int * V, unsigned int * W, /* O */ + unsigned int * X, unsigned int * Y); /* I */ + ErrCode BitVector_Power (unsigned int * X, unsigned int * Y, unsigned int * Z); + + /* ===> direct memory access functions: */ + void BitVector_Block_Store (unsigned int * addr, + unsigned char * buffer, unsigned int length); + unsigned char * BitVector_Block_Read (unsigned int * addr, unsigned int * length); + + /* ===> word array functions: */ + void BitVector_Word_Store (unsigned int * addr, unsigned int offset, unsigned int value); + unsigned int BitVector_Word_Read (unsigned int * addr, unsigned int offset); + void BitVector_Word_Insert (unsigned int * addr, + unsigned int offset, unsigned int count, boolean clear); + void BitVector_Word_Delete (unsigned int * addr, + unsigned int offset, unsigned int count, boolean clear); + + /* ===> arbitrary size chunk functions: */ + void BitVector_Chunk_Store (unsigned int * addr, unsigned int chunksize, + unsigned int offset, unsigned long value); + unsigned long BitVector_Chunk_Read (unsigned int * addr, + unsigned int chunksize,unsigned int offset); + + /* ===> set operations: */ + void Set_Union (unsigned int * X, unsigned int * Y, unsigned int * Z); /* X = Y + Z */ + void Set_Intersection (unsigned int * X, unsigned int * Y, unsigned int * Z); /* X = Y * Z */ + void Set_Difference (unsigned int * X, unsigned int * Y, unsigned int * Z); /* X = Y \ Z */ + void Set_ExclusiveOr (unsigned int * X, unsigned int * Y, unsigned int * Z); /*(Y+Z)\(Y*Z)*/ + void Set_Complement (unsigned int * X, unsigned int * Y); /* X = ~Y */ + + /* ===> set functions: */ + boolean Set_subset (unsigned int * X, unsigned int * Y); /* X in Y ? */ + unsigned int Set_Norm (unsigned int * addr); /* = | X | */ + unsigned int Set_Norm2 (unsigned int * addr); /* = | X | */ + unsigned int Set_Norm3 (unsigned int * addr); /* = | X | */ + signed long Set_Min (unsigned int * addr); /* = min(X) */ + signed long Set_Max (unsigned int * addr); /* = max(X) */ + + /* ===> matrix-of-booleans operations: */ + void Matrix_Multiplication (unsigned int * X, unsigned int rowsX, unsigned int colsX, + unsigned int * Y, unsigned int rowsY, unsigned int colsY, + unsigned int * Z, unsigned int rowsZ, unsigned int colsZ); + void Matrix_Product (unsigned int * X, unsigned int rowsX, unsigned int colsX, + unsigned int * Y, unsigned int rowsY, unsigned int colsY, + unsigned int * Z, unsigned int rowsZ, unsigned int colsZ); + void Matrix_Closure (unsigned int * addr, unsigned int rows, unsigned int cols); + void Matrix_Transpose (unsigned int * X, unsigned int rowsX, unsigned int colsX, + unsigned int * Y, unsigned int rowsY, unsigned int colsY); + + /*****************************************************************************/ + /* MODULE RESOURCES: */ + /*****************************************************************************/ +#define bits_(BitVector) *(BitVector-3) +#define size_(BitVector) *(BitVector-2) +#define mask_(BitVector) *(BitVector-1) + +#define ERRCODE_TYPE "sizeof(word) > sizeof(size_t)" +#define ERRCODE_BITS "bits(word) != sizeof(word)*8" +#define ERRCODE_WORD "bits(word) < 16" +#define ERRCODE_LONG "bits(word) > bits(long)" +#define ERRCODE_POWR "bits(word) != 2^x" +#define ERRCODE_LOGA "bits(word) != 2^ld(bits(word))" +#define ERRCODE_NULL "unable to allocate memory" +#define ERRCODE_INDX "index out of range" +#define ERRCODE_ORDR "minimum > maximum index" +#define ERRCODE_SIZE "bit vector size mismatch" +#define ERRCODE_PARS "input string syntax error" +#define ERRCODE_OVFL "numeric overflow error" +#define ERRCODE_SAME "result vector(s) must be distinct" +#define ERRCODE_EXPO "exponent must be positive" +#define ERRCODE_ZERO "division by zero error" +#define ERRCODE_OOPS "unexpected internal error - please contact author" + + const unsigned int BitVector_BYTENORM[256] = { + 0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, + 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, /* 0x00 */ + 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, + 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, /* 0x10 */ + 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, + 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, /* 0x20 */ + 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, + 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, /* 0x30 */ + 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, + 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, /* 0x40 */ + 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, + 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, /* 0x50 */ + 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, + 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, /* 0x60 */ + 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, + 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, /* 0x70 */ + 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, + 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, /* 0x80 */ + 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, + 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, /* 0x90 */ + 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, + 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, /* 0xA0 */ + 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, + 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, /* 0xB0 */ + 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, + 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, /* 0xC0 */ + 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, + 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, /* 0xD0 */ + 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, + 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, /* 0xE0 */ + 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, + 0x05, 0x06, 0x06, 0x07, 0x06, 0x07, 0x07, 0x08 /* 0xF0 */ + }; +#ifdef __cplusplus + }; +#endif +}; //end of namespace CONSTANTBV +#endif + diff --git a/stp/parser/Makefile b/stp/parser/Makefile new file mode 100644 index 00000000..8211c825 --- /dev/null +++ b/stp/parser/Makefile @@ -0,0 +1,27 @@ +include ../Makefile.common + +SRCS = lexPL.cpp parsePL.cpp let-funcs.cpp main.cpp +OBJS = $(SRCS:.cpp=.o) +LIBS = -L../AST -last -L../sat -lsatsolver -L../simplifier -lsimplifier -L../bitvec -lconsteval -L../constantbv -lconstantbv + +all: parser + +parser: lexPL.o parsePL.o let-funcs.o main.o + $(CXX) $(CFLAGS) $(LDFLAGS) lexPL.o parsePL.o main.o let-funcs.o $(LIBS) -o parser + +main.o: parsePL_defs.h + +lexPL.cpp: PL.lex parsePL_defs.h ../AST/AST.h + $(LEX) -o lexPL.cpp PL.lex + +parsePL_defs.h: y.tab.h + @cp y.tab.h parsePL_defs.h +parsePL.cpp: y.tab.c + @cp y.tab.c parsePL.cpp + +y.tab.c y.tab.h: PL.y + $(YACC) PL.y + + +clean: + rm -rf *.o parsePL_defs.h *~ lexPL.cpp parsePL.cpp *.output parser y.tab.* lex.yy.c .#* diff --git a/stp/parser/PL.lex b/stp/parser/PL.lex new file mode 100644 index 00000000..e9358a0e --- /dev/null +++ b/stp/parser/PL.lex @@ -0,0 +1,128 @@ +%{ +/******************************************************************** + * AUTHORS: Vijay Ganesh, David L. Dill + * + * BEGIN DATE: November, 2005 + * + * LICENSE: Please view LICENSE file in the home dir of this Program + ********************************************************************/ +#include <iostream> +#include "../AST/AST.h" +#include "parsePL_defs.h" + +extern char *yytext; +extern int yyerror (const char *msg); +%} + +%option noyywrap +%option nounput +%option noreject +%option noyymore +%option yylineno +%x COMMENT +%x STRING_LITERAL +LETTER ([a-zA-Z]) +HEX ([0-9a-fA-F]) +BITS ([0-1]) +DIGIT ([0-9]) +OPCHAR (['?\_$]) +ANYTHING ({LETTER}|{DIGIT}|{OPCHAR}) +%% + +[()[\]{},.;:'!#?_=] { return yytext[0];} + +[\n] { /*Skip new line */ } +[ \t\r\f] { /* skip whitespace */ } +0b{BITS}+ { yylval.node = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateBVConst(yytext+2, 2)); return BVCONST_TOK;} +0bin{BITS}+ { yylval.node = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateBVConst(yytext+4, 2)); return BVCONST_TOK;} +0h{HEX}+ { yylval.node = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateBVConst(yytext+2, 16)); return BVCONST_TOK;} +0hex{HEX}+ { yylval.node = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateBVConst(yytext+4, 16)); return BVCONST_TOK;} +{DIGIT}+ { yylval.uintval = strtoul(yytext, NULL, 10); return NUMERAL_TOK;} + +"%" { BEGIN COMMENT;} +<COMMENT>"\n" { BEGIN INITIAL; /* return to normal mode */} +<COMMENT>. { /* stay in comment mode */} + +"ARRAY" { return ARRAY_TOK; } +"OF" { return OF_TOK; } +"WITH" { return WITH_TOK; } +"AND" { return AND_TOK;} +"NAND" { return NAND_TOK;} +"NOR" { return NOR_TOK;} +"NOT" { return NOT_TOK; } +"OR" { return OR_TOK; } +"/=" { return NEQ_TOK; } + ":=" { return ASSIGN_TOK;} +"=>" { return IMPLIES_TOK; } +"<=>" { return IFF_TOK; } +"XOR" { return XOR_TOK; } +"IF" { return IF_TOK; } +"THEN" { return THEN_TOK; } +"ELSE" { return ELSE_TOK; } +"ELSIF" { return ELSIF_TOK; } +"END" { return END_TOK; } +"ENDIF" { return ENDIF_TOK; } +"BV" { return BV_TOK;} +"BITVECTOR" { return BV_TOK;} +"BOOLEAN" { return BOOLEAN_TOK;} +"<<" { return BVLEFTSHIFT_TOK;} +">>" { return BVRIGHTSHIFT_TOK;} +"BVPLUS" { return BVPLUS_TOK;} +"BVSUB" { return BVSUB_TOK;} +"BVUMINUS" { return BVUMINUS_TOK;} +"BVMULT" { return BVMULT_TOK;} +"BVDIV" { return BVDIV_TOK;} +"BVMOD" { return BVMOD_TOK;} +"SBVDIV" { return SBVDIV_TOK;} +"SBVMOD" { return SBVMOD_TOK;} +"~" { return BVNEG_TOK;} +"&" { return BVAND_TOK;} +"|" { return BVOR_TOK;} +"BVXOR" { return BVXOR_TOK;} +"BVNAND" { return BVNAND_TOK;} +"BVNOR" { return BVNOR_TOK;} +"BVXNOR" { return BVXNOR_TOK;} +"@" { return BVCONCAT_TOK;} +"BVLT" { return BVLT_TOK;} +"BVGT" { return BVGT_TOK;} +"BVLE" { return BVLE_TOK;} +"BVGE" { return BVGE_TOK;} +"BVSLT" { return BVSLT_TOK;} +"BVSGT" { return BVSGT_TOK;} +"BVSLE" { return BVSLE_TOK;} +"BVSGE" { return BVSGE_TOK;} +"BVSX" { return BVSX_TOK;} +"SBVLT" { return BVSLT_TOK;} +"SBVGT" { return BVSGT_TOK;} +"SBVLE" { return BVSLE_TOK;} +"SBVGE" { return BVSGE_TOK;} +"SX" { return BVSX_TOK;} +"BOOLEXTRACT" { return BOOLEXTRACT_TOK;} +"BOOLBV" { return BOOL_TO_BV_TOK;} +"ASSERT" { return ASSERT_TOK; } +"QUERY" { return QUERY_TOK; } +"FALSE" { return FALSELIT_TOK;} +"TRUE" { return TRUELIT_TOK;} +"IN" { return IN_TOK;} +"LET" { return LET_TOK;} +"COUNTEREXAMPLE" { return COUNTEREXAMPLE_TOK;} +"COUNTERMODEL" { return COUNTEREXAMPLE_TOK;} + "PUSH" { return PUSH_TOK;} + "POP" { return POP_TOK;} + +(({LETTER})|(_)({ANYTHING}))({ANYTHING})* { + BEEV::ASTNode nptr = BEEV::globalBeevMgr_for_parser->CreateSymbol(yytext); + + // Check valuesize to see if it's a prop var. I don't like doing + // type determination in the lexer, but it's easier than rewriting + // the whole grammar to eliminate the term/formula distinction. + yylval.node = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->ResolveID(nptr)); + //yylval.node = new BEEV::ASTNode(nptr); + if ((yylval.node)->GetType() == BEEV::BOOLEAN_TYPE) + return FORMID_TOK; + else + return TERMID_TOK; +} + +. { yyerror("Illegal input character."); } +%% diff --git a/stp/parser/PL.y b/stp/parser/PL.y new file mode 100644 index 00000000..aa58aa4c --- /dev/null +++ b/stp/parser/PL.y @@ -0,0 +1,1006 @@ +%{ +/******************************************************************** + * AUTHORS: Vijay Ganesh, David L. Dill + * + * BEGIN DATE: November, 2005 + * + * LICENSE: Please view LICENSE file in the home dir of this Program + ********************************************************************/ +// -*- c++ -*- + +#include "../AST/AST.h" +using namespace std; + + // Suppress the bogus warning suppression in bison (it generates + // compile error) +#undef __GNUC_MINOR__ + + extern int yylex(void); + extern char* yytext; + extern int yylineno; + int yyerror(const char *s) { + cout << "syntax error: line " << yylineno << "\n" << s << endl; + BEEV::FatalError(""); + return 1; /* Dill: don't know what it should return */ + }; + +#define YYLTYPE_IS_TRIVIAL 1 +#define YYMAXDEPTH 10485760 +#define YYERROR_VERBOSE 1 +#define YY_EXIT_FAILURE -1 +%} + +%union { + + unsigned int uintval; /* for numerals in types. */ + struct { + //stores the indexwidth and valuewidth + //indexwidth is 0 iff type is bitvector. positive iff type is + //array, and stores the width of the indexing bitvector + unsigned int indexwidth; + //width of the bitvector type + unsigned int valuewidth; + } indexvaluewidth; + + //ASTNode,ASTVec + BEEV::ASTNode *node; + BEEV::ASTVec *vec; + + //Hash_Map to hold Array Updates during parse A map from array index + //to array values. To support the WITH construct + BEEV::ASTNodeMap * Index_To_UpdateValue; +}; + +%start cmd + +%token AND_TOK "AND" +%token OR_TOK "OR" +%token NOT_TOK "NOT" +%token XOR_TOK "XOR" +%token NAND_TOK "NAND" +%token NOR_TOK "NOR" +%token IMPLIES_TOK "=>" +%token IFF_TOK "<=>" + +%token IF_TOK "IF" +%token THEN_TOK "THEN" +%token ELSE_TOK "ELSE" +%token ELSIF_TOK "ELSIF" +%token END_TOK "END" +%token ENDIF_TOK "ENDIF" +%token NEQ_TOK "/=" +%token ASSIGN_TOK ":=" + +%token BV_TOK "BV" +%token BVLEFTSHIFT_TOK "<<" +%token BVRIGHTSHIFT_TOK ">>" +%token BVPLUS_TOK "BVPLUS" +%token BVSUB_TOK "BVSUB" +%token BVUMINUS_TOK "BVUMINUS" +%token BVMULT_TOK "BVMULT" + +%token BVDIV_TOK "BVDIV" +%token BVMOD_TOK "BVMOD" +%token SBVDIV_TOK "SBVDIV" +%token SBVMOD_TOK "SBVMOD" + + +%token BVNEG_TOK "~" +%token BVAND_TOK "&" +%token BVOR_TOK "|" +%token BVXOR_TOK "BVXOR" +%token BVNAND_TOK "BVNAND" +%token BVNOR_TOK "BVNOR" +%token BVXNOR_TOK "BVXNOR" +%token BVCONCAT_TOK "@" + +%token BVLT_TOK "BVLT" +%token BVGT_TOK "BVGT" +%token BVLE_TOK "BVLE" +%token BVGE_TOK "BVGE" + +%token BVSLT_TOK "BVSLT" +%token BVSGT_TOK "BVSGT" +%token BVSLE_TOK "BVSLE" +%token BVSGE_TOK "BVSGE" +%token BOOL_TO_BV_TOK "BOOLBV" +%token BVSX_TOK "BVSX" +%token BOOLEXTRACT_TOK "BOOLEXTRACT" +%token ASSERT_TOK "ASSERT" +%token QUERY_TOK "QUERY" + +%token BOOLEAN_TOK "BOOLEAN" +%token ARRAY_TOK "ARRAY" +%token OF_TOK "OF" +%token WITH_TOK "WITH" + +%token TRUELIT_TOK "TRUE" +%token FALSELIT_TOK "FALSE" + +%token IN_TOK "IN" +%token LET_TOK "LET" +//%token COUNTEREXAMPLE_TOK "COUNTEREXAMPLE" +%token PUSH_TOK "PUSH" +%token POP_TOK "POP" + +%left IN_TOK +%left XOR_TOK +%left IFF_TOK +%right IMPLIES_TOK +%left OR_TOK +%left AND_TOK +%left NAND_TOK +%left NOR_TOK +%left NOT_TOK +%left BVCONCAT_TOK +%left BVOR_TOK +%left BVAND_TOK +%left BVXOR_TOK +%left BVNAND_TOK +%left BVNOR_TOK +%left BVXNOR_TOK +%left BVNEG_TOK +%left BVLEFTSHIFT_TOK BVRIGHTSHIFT_TOK +%left WITH_TOK + +%nonassoc '=' NEQ_TOK ASSIGN_TOK +%nonassoc BVLT_TOK BVLE_TOK BVGT_TOK BVGE_TOK +%nonassoc BVUMINUS_TOK BVPLUS_TOK BVSUB_TOK BVSX_TOK +%nonassoc '[' +%nonassoc '{' '.' '(' +%nonassoc BV_TOK + +%type <vec> Exprs FORM_IDs reverseFORM_IDs +%type <vec> Asserts +%type <node> Expr Formula IfExpr ElseRestExpr IfForm ElseRestForm Assert Query ArrayUpdateExpr +%type <Index_To_UpdateValue> Updates + +%type <indexvaluewidth> BvType BoolType ArrayType Type + +%token <node> BVCONST_TOK +%token <node> TERMID_TOK FORMID_TOK COUNTEREXAMPLE_TOK +%token <uintval> NUMERAL_TOK + +%% + +cmd : other_cmd + | other_cmd counterexample + ; + +counterexample : COUNTEREXAMPLE_TOK ';' + { + BEEV::print_counterexample = true; + BEEV::globalBeevMgr_for_parser->PrintCounterExample(true); + } + ; + +other_cmd : other_cmd1 + | Query + { + BEEV::globalBeevMgr_for_parser->TopLevelSAT(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::TRUE),*$1); + delete $1; + } + | VarDecls Query + { + BEEV::globalBeevMgr_for_parser->TopLevelSAT(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::TRUE),*$2); + delete $2; + } + | other_cmd1 Query + { + BEEV::ASTVec aaa = BEEV::globalBeevMgr_for_parser->GetAsserts(); + if(aaa.size() == 0) + yyerror("Fatal Error: parsing: GetAsserts() call: no assertions: "); + if(aaa.size() == 1) + BEEV::globalBeevMgr_for_parser->TopLevelSAT(aaa[0],*$2); + else + BEEV::globalBeevMgr_for_parser->TopLevelSAT(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::AND,aaa),*$2); + delete $2; + } + ; + +other_cmd1 : VarDecls Asserts + { + delete $2; + } + | Asserts + { + delete $1; + } + | other_cmd1 VarDecls Asserts + { + delete $3; + } + ; + +/* push : PUSH_TOK */ +/* { */ +/* BEEV::globalBeevMgr_for_parser->Push(); */ +/* } */ +/* | */ +/* ; */ + +/* pop : POP_TOK */ +/* { */ +/* BEEV::globalBeevMgr_for_parser->Pop(); */ +/* } */ +/* | */ +/* ; */ + +Asserts : Assert + { + $$ = new BEEV::ASTVec; + $$->push_back(*$1); + BEEV::globalBeevMgr_for_parser->AddAssert(*$1); + delete $1; + } + | Asserts Assert + { + $1->push_back(*$2); + BEEV::globalBeevMgr_for_parser->AddAssert(*$2); + $$ = $1; + delete $2; + } + ; + +Assert : ASSERT_TOK Formula ';' { $$ = $2; } + ; + +Query : QUERY_TOK Formula ';' { BEEV::globalBeevMgr_for_parser->AddQuery(*$2); $$ = $2;} + ; + + +/* Grammar for Variable Declaration */ +VarDecls : VarDecl ';' + { + } + | VarDecls VarDecl ';' + { + } + ; + +VarDecl : FORM_IDs ':' Type + { + for(BEEV::ASTVec::iterator i=$1->begin(),iend=$1->end();i!=iend;i++) { + BEEV::_parser_symbol_table.insert(*i); + i->SetIndexWidth($3.indexwidth); + i->SetValueWidth($3.valuewidth); + + //FIXME: HACK_ATTACK. this vector was hacked into the code to + //support a special request by Dawson' group. They want the + //counterexample to be printed in the order of variables declared. + BEEV::globalBeevMgr_for_parser->_special_print_set.push_back(*i); + } + delete $1; + } + | FORM_IDs ':' Type '=' Expr + { + //do type checking. if doesn't pass then abort + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$5); + if($3.indexwidth != $5->GetIndexWidth()) + yyerror("Fatal Error: parsing: LET Expr: Type check fail: "); + if($3.valuewidth != $5->GetValueWidth()) + yyerror("Fatal Error: parsing: LET Expr: Type check fail: "); + + for(BEEV::ASTVec::iterator i=$1->begin(),iend=$1->end();i!=iend;i++) { + //set the valuewidth of the identifier + i->SetValueWidth($5->GetValueWidth()); + i->SetIndexWidth($5->GetIndexWidth()); + + BEEV::globalBeevMgr_for_parser->LetExprMgr(*i,*$5); + delete $5; + } + } + | FORM_IDs ':' Type '=' Formula + { + //do type checking. if doesn't pass then abort + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$5); + if($3.indexwidth != $5->GetIndexWidth()) + yyerror("Fatal Error: parsing: LET Expr: Type check fail: "); + if($3.valuewidth != $5->GetValueWidth()) + yyerror("Fatal Error: parsing: LET Expr: Type check fail: "); + + for(BEEV::ASTVec::iterator i=$1->begin(),iend=$1->end();i!=iend;i++) { + //set the valuewidth of the identifier + i->SetValueWidth($5->GetValueWidth()); + i->SetIndexWidth($5->GetIndexWidth()); + + BEEV::globalBeevMgr_for_parser->LetExprMgr(*i,*$5); + delete $5; + } + } + ; + +reverseFORM_IDs : FORMID_TOK + { + $$ = new BEEV::ASTVec; + $$->push_back(*$1); + delete $1; + } + | FORMID_TOK ',' reverseFORM_IDs + { + $3->push_back(*$1); + $$ = $3; + delete $1; + } + ; + +FORM_IDs : reverseFORM_IDs + { + $$ = new BEEV::ASTVec($1->rbegin(),$1->rend()); + delete $1; + } + ; + +/* Grammar for Types */ +Type : BvType { $$ = $1; } + | BoolType { $$ = $1; } + | ArrayType { $$ = $1; } + ; + +BvType : BV_TOK '(' NUMERAL_TOK ')' + { + /*((indexwidth is 0) && (valuewidth>0)) iff type is BV*/ + $$.indexwidth = 0; + unsigned int length = $3; + if(length > 0) { + $$.valuewidth = length; + } + else + BEEV::FatalError("Fatal Error: parsing: BITVECTORS must be of positive length: \n"); + } + ; +BoolType : BOOLEAN_TOK + { + $$.indexwidth = 0; + $$.valuewidth = 0; + } + ; +ArrayType : ARRAY_TOK BvType OF_TOK BvType + { + $$.indexwidth = $2.valuewidth; + $$.valuewidth = $4.valuewidth; + } + ; + +/*Grammar for ITEs which are a type of Term*/ +IfExpr : IF_TOK Formula THEN_TOK Expr ElseRestExpr + { + unsigned int width = $4->GetValueWidth(); + if (width != $5->GetValueWidth()) + yyerror("Width mismatch in IF-THEN-ELSE"); + if($4->GetIndexWidth() != $5->GetIndexWidth()) + yyerror("Width mismatch in IF-THEN-ELSE"); + + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$2); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$4); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$5); + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::ITE, width, *$2, *$4, *$5)); + $$->SetIndexWidth($5->GetIndexWidth()); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$$); + delete $2; + delete $4; + delete $5; + } + ; + +ElseRestExpr : ELSE_TOK Expr ENDIF_TOK { $$ = $2; } + | ELSIF_TOK Expr THEN_TOK Expr ElseRestExpr + { + unsigned int width = $2->GetValueWidth(); + if (width != $4->GetValueWidth() || width != $5->GetValueWidth()) + yyerror("Width mismatch in IF-THEN-ELSE"); + if ($2->GetIndexWidth() != $4->GetValueWidth() || $2->GetIndexWidth() != $5->GetValueWidth()) + yyerror("Width mismatch in IF-THEN-ELSE"); + + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$2); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$4); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$5); + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::ITE, width, *$2, *$4, *$5)); + $$->SetIndexWidth($5->GetIndexWidth()); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$$); + delete $2; + delete $4; + delete $5; + } + ; + +/* Grammar for formulas */ +Formula : '(' Formula ')' { $$ = $2; } + | FORMID_TOK { $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->ResolveID(*$1)); delete $1;} + | BOOLEXTRACT_TOK '(' Expr ',' NUMERAL_TOK ')' + { + unsigned int width = $3->GetValueWidth(); + if(0 > (unsigned)$5 || width <= (unsigned)$5) + yyerror("Fatal Error: BOOLEXTRACT: trying to boolextract a bit which beyond range"); + + BEEV::ASTNode hi = BEEV::globalBeevMgr_for_parser->CreateBVConst(32, $5); + BEEV::ASTNode low = BEEV::globalBeevMgr_for_parser->CreateBVConst(32, $5); + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVEXTRACT,1,*$3,hi,low)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + BEEV::ASTNode zero = BEEV::globalBeevMgr_for_parser->CreateBVConst(1,0); + BEEV::ASTNode * out = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::EQ,*n,zero)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*out); + + $$ = out; + delete $3; + } + | Expr '=' Expr + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::EQ, *$1, *$3)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $1; + delete $3; + } + | Expr NEQ_TOK Expr + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::NEQ, *$1, *$3)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $1; + delete $3; + } + | NOT_TOK Formula + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::NOT, *$2)); + delete $2; + } + | Formula OR_TOK Formula %prec OR_TOK + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::OR, *$1, *$3)); + delete $1; + delete $3; + } + | Formula NOR_TOK Formula + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::NOR, *$1, *$3)); + delete $1; + delete $3; + } + | Formula AND_TOK Formula %prec AND_TOK + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::AND, *$1, *$3)); + delete $1; + delete $3; + } + | Formula NAND_TOK Formula + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::NAND, *$1, *$3)); + delete $1; + delete $3; + } + | Formula IMPLIES_TOK Formula + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::IMPLIES, *$1, *$3)); + delete $1; + delete $3; + } + | Formula IFF_TOK Formula + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::IFF, *$1, *$3)); + delete $1; + delete $3; + } + | Formula XOR_TOK Formula + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::XOR, *$1, *$3)); + delete $1; + delete $3; + } + | BVLT_TOK '(' Expr ',' Expr ')' + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::BVLT, *$3, *$5)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $3; + delete $5; + } + | BVGT_TOK '(' Expr ',' Expr ')' + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::BVGT, *$3, *$5)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $3; + delete $5; + } + | BVLE_TOK '(' Expr ',' Expr ')' + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::BVLE, *$3, *$5)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $3; + delete $5; + } + | BVGE_TOK '(' Expr ',' Expr ')' + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::BVGE, *$3, *$5)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $3; + delete $5; + } + | BVSLT_TOK '(' Expr ',' Expr ')' + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::BVSLT, *$3, *$5)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $3; + delete $5; + } + | BVSGT_TOK '(' Expr ',' Expr ')' + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::BVSGT, *$3, *$5)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $3; + delete $5; + } + | BVSLE_TOK '(' Expr ',' Expr ')' + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::BVSLE, *$3, *$5)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $3; + delete $5; + } + | BVSGE_TOK '(' Expr ',' Expr ')' + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::BVSGE, *$3, *$5)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $3; + delete $5; + } + | IfForm + | TRUELIT_TOK + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::TRUE)); + $$->SetIndexWidth(0); + $$->SetValueWidth(0); + } + | FALSELIT_TOK + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::FALSE)); + $$->SetIndexWidth(0); + $$->SetValueWidth(0); + } + + | LET_TOK LetDecls IN_TOK Formula + { + $$ = $4; + //Cleanup the LetIDToExprMap + BEEV::globalBeevMgr_for_parser->CleanupLetIDMap(); + } + ; + +/*Grammar for ITEs which are Formulas */ +IfForm : IF_TOK Formula THEN_TOK Formula ElseRestForm + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::ITE, *$2, *$4, *$5)); + delete $2; + delete $4; + delete $5; + } + ; + +ElseRestForm : ELSE_TOK Formula ENDIF_TOK { $$ = $2; } + | ELSIF_TOK Formula THEN_TOK Formula ElseRestForm + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::ITE, *$2, *$4, *$5)); + delete $2; + delete $4; + delete $5; + } + ; + +/*Grammar for a list of expressions*/ +Exprs : Expr + { + $$ = new BEEV::ASTVec; + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$1); + $$->push_back(*$1); + delete $1; + } + | Exprs ',' Expr + { + $1->push_back(*$3); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$3); + $$ = $1; + delete $3; + } + ; + +/* Grammar for Expr */ +Expr : TERMID_TOK { $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->ResolveID(*$1)); delete $1;} + | '(' Expr ')' { $$ = $2; } + | BVCONST_TOK { $$ = $1; } + | BOOL_TO_BV_TOK '(' Formula ')' + { + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$3); + BEEV::ASTNode one = BEEV::globalBeevMgr_for_parser->CreateBVConst(1,1); + BEEV::ASTNode zero = BEEV::globalBeevMgr_for_parser->CreateBVConst(1,0); + + //return ITE(*$3, length(1), 0bin1, 0bin0) + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::ITE,1,*$3,one,zero)); + delete $3; + } + | Expr '[' Expr ']' + { + // valuewidth is same as array, indexwidth is 0. + unsigned int width = $1->GetValueWidth(); + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::READ, width, *$1, *$3)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + + delete $1; + delete $3; + } + | Expr '(' Expr ')' //array read but in the form of a uninterpreted function application + { + // valuewidth is same as array, indexwidth is 0. + unsigned int width = $1->GetValueWidth(); + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::READ, width, *$1, *$3)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + + delete $1; + delete $3; + } + | Expr '[' NUMERAL_TOK ':' NUMERAL_TOK ']' + { + int width = $3 - $5 + 1; + if (width < 0) + yyerror("Negative width in extract"); + + if((unsigned)$3 >= $1->GetValueWidth() || (unsigned)$5 < 0) + yyerror("Parsing: Wrong width in BVEXTRACT\n"); + + BEEV::ASTNode hi = BEEV::globalBeevMgr_for_parser->CreateBVConst(32, $3); + BEEV::ASTNode low = BEEV::globalBeevMgr_for_parser->CreateBVConst(32, $5); + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVEXTRACT, width, *$1,hi,low)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $1; + } + | BVNEG_TOK Expr + { + unsigned int width = $2->GetValueWidth(); + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVNEG, width, *$2)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $2; + } + | Expr BVAND_TOK Expr + { + unsigned int width = $1->GetValueWidth(); + if (width != $3->GetValueWidth()) { + yyerror("Width mismatch in AND"); + } + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVAND, width, *$1, *$3)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $1; + delete $3; + } + | Expr BVOR_TOK Expr + { + unsigned int width = $1->GetValueWidth(); + if (width != $3->GetValueWidth()) { + yyerror("Width mismatch in OR"); + } + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVOR, width, *$1, *$3)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $1; + delete $3; + } + | BVXOR_TOK '(' Expr ',' Expr ')' + { + unsigned int width = $3->GetValueWidth(); + if (width != $5->GetValueWidth()) { + yyerror("Width mismatch in XOR"); + } + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVXOR, width, *$3, *$5)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $3; + delete $5; + } + | BVNAND_TOK '(' Expr ',' Expr ')' + { + unsigned int width = $3->GetValueWidth(); + if (width != $5->GetValueWidth()) { + yyerror("Width mismatch in NAND"); + } + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVNAND, width, *$3, *$5)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + + delete $3; + delete $5; + } + | BVNOR_TOK '(' Expr ',' Expr ')' + { + unsigned int width = $3->GetValueWidth(); + if (width != $5->GetValueWidth()) { + yyerror("Width mismatch in NOR"); + } + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVNOR, width, *$3, *$5)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + + delete $3; + delete $5; + } + | BVXNOR_TOK '(' Expr ',' Expr ')' + { + unsigned int width = $3->GetValueWidth(); + if (width != $5->GetValueWidth()) { + yyerror("Width mismatch in NOR"); + } + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVXNOR, width, *$3, *$5)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + + delete $3; + delete $5; + } + | BVSX_TOK '(' Expr ',' NUMERAL_TOK ')' + { + //width of the expr which is being sign + //extended. $5 is the resulting length of the + //signextended expr + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$3); + if($3->GetValueWidth() == $5) { + $$ = $3; + } + else { + BEEV::ASTNode width = BEEV::globalBeevMgr_for_parser->CreateBVConst(32,$5); + BEEV::ASTNode *n = + new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVSX, $5,*$3,width)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $3; + } + } + | Expr BVCONCAT_TOK Expr + { + unsigned int width = $1->GetValueWidth() + $3->GetValueWidth(); + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVCONCAT, width, *$1, *$3)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + + delete $1; + delete $3; + } + | Expr BVLEFTSHIFT_TOK NUMERAL_TOK + { + BEEV::ASTNode zero_bits = BEEV::globalBeevMgr_for_parser->CreateZeroConst($3); + BEEV::ASTNode * n = + new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVCONCAT, + $1->GetValueWidth() + $3, *$1, zero_bits)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $1; + } + | Expr BVRIGHTSHIFT_TOK NUMERAL_TOK + { + BEEV::ASTNode len = BEEV::globalBeevMgr_for_parser->CreateZeroConst($3); + unsigned int w = $1->GetValueWidth(); + + //the amount by which you are rightshifting + //is less-than/equal-to the length of input + //bitvector + if((unsigned)$3 < w) { + BEEV::ASTNode hi = BEEV::globalBeevMgr_for_parser->CreateBVConst(32,w-1); + BEEV::ASTNode low = BEEV::globalBeevMgr_for_parser->CreateBVConst(32,$3); + BEEV::ASTNode extract = BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVEXTRACT,w-$3,*$1,hi,low); + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVCONCAT, w,len, extract)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + } + else + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateZeroConst(w)); + + delete $1; + } + | BVPLUS_TOK '(' NUMERAL_TOK ',' Exprs ')' + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVPLUS, $3, *$5)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + + delete $5; + } + | BVSUB_TOK '(' NUMERAL_TOK ',' Expr ',' Expr ')' + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVSUB, $3, *$5, *$7)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + + delete $5; + delete $7; + } + | BVUMINUS_TOK '(' Expr ')' + { + unsigned width = $3->GetValueWidth(); + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVUMINUS,width,*$3)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $3; + } + | BVMULT_TOK '(' NUMERAL_TOK ',' Expr ',' Expr ')' + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVMULT, $3, *$5, *$7)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + + delete $5; + delete $7; + } + | BVDIV_TOK '(' NUMERAL_TOK ',' Expr ',' Expr ')' + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVDIV, $3, *$5, *$7)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + + delete $5; + delete $7; + } + | BVMOD_TOK '(' NUMERAL_TOK ',' Expr ',' Expr ')' + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVMOD, $3, *$5, *$7)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + + delete $5; + delete $7; + } + | SBVDIV_TOK '(' NUMERAL_TOK ',' Expr ',' Expr ')' + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::SBVDIV, $3, *$5, *$7)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + + delete $5; + delete $7; + } + | SBVMOD_TOK '(' NUMERAL_TOK ',' Expr ',' Expr ')' + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::SBVMOD, $3, *$5, *$7)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $5; + delete $7; + } + | IfExpr { $$ = $1; } + | ArrayUpdateExpr + | LET_TOK LetDecls IN_TOK Expr + { + $$ = $4; + //Cleanup the LetIDToExprMap + //BEEV::globalBeevMgr_for_parser->CleanupLetIDMap(); + } + ; + +/*Grammar for Array Update Expr*/ +ArrayUpdateExpr : Expr WITH_TOK Updates + { + BEEV::ASTNode * result; + unsigned int width = $1->GetValueWidth(); + + BEEV::ASTNodeMap::iterator it = $3->begin(); + BEEV::ASTNodeMap::iterator itend = $3->end(); + result = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::WRITE, + width, + *$1, + (*it).first, + (*it).second)); + result->SetIndexWidth($1->GetIndexWidth()); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*result); + for(it++;it!=itend;it++) { + result = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::WRITE, + width, + *result, + (*it).first, + (*it).second)); + result->SetIndexWidth($1->GetIndexWidth()); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*result); + } + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*result); + $$ = result; + delete $3; + } + ; + +Updates : '[' Expr ']' ASSIGN_TOK Expr + { + $$ = new BEEV::ASTNodeMap(); + (*$$)[*$2] = *$5; + } + | Updates WITH_TOK '[' Expr ']' ASSIGN_TOK Expr + { + (*$1)[*$4] = *$7; + } + ; + +/*Grammar for LET Expr*/ +LetDecls : LetDecl + | LetDecls ',' LetDecl + ; + +LetDecl : FORMID_TOK '=' Expr + { + //Expr must typecheck + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$3); + + //set the valuewidth of the identifier + $1->SetValueWidth($3->GetValueWidth()); + $1->SetIndexWidth($3->GetIndexWidth()); + + //populate the hashtable from LET-var --> + //LET-exprs and then process them: + // + //1. ensure that LET variables do not clash + //1. with declared variables. + // + //2. Ensure that LET variables are not + //2. defined more than once + BEEV::globalBeevMgr_for_parser->LetExprMgr(*$1,*$3); + delete $1; + delete $3; + } + | FORMID_TOK ':' Type '=' Expr + { + //do type checking. if doesn't pass then abort + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$5); + + if($3.indexwidth != $5->GetIndexWidth()) + yyerror("Fatal Error: parsing: LET Expr: Type check fail: "); + if($3.valuewidth != $5->GetValueWidth()) + yyerror("Fatal Error: parsing: LET Expr: Type check fail: "); + + //set the valuewidth of the identifier + $1->SetValueWidth($5->GetValueWidth()); + $1->SetIndexWidth($5->GetIndexWidth()); + + BEEV::globalBeevMgr_for_parser->LetExprMgr(*$1,*$5); + delete $1; + delete $5; + } + | FORMID_TOK '=' Formula + { + //Expr must typecheck + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$3); + + //set the valuewidth of the identifier + $1->SetValueWidth($3->GetValueWidth()); + $1->SetIndexWidth($3->GetIndexWidth()); + + //Do LET-expr management + BEEV::globalBeevMgr_for_parser->LetExprMgr(*$1,*$3); + delete $1; + delete $3; + } + | FORMID_TOK ':' Type '=' Formula + { + //do type checking. if doesn't pass then abort + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$5); + + if($3.indexwidth != $5->GetIndexWidth()) + yyerror("Fatal Error: parsing: LET Expr: Type check fail: "); + if($3.valuewidth != $5->GetValueWidth()) + yyerror("Fatal Error: parsing: LET Expr: Type check fail: "); + + //set the valuewidth of the identifier + $1->SetValueWidth($5->GetValueWidth()); + $1->SetIndexWidth($5->GetIndexWidth()); + + //Do LET-expr management + BEEV::globalBeevMgr_for_parser->LetExprMgr(*$1,*$5); + delete $1; + delete $5; + } + ; + +%% diff --git a/stp/parser/let-funcs.cpp b/stp/parser/let-funcs.cpp new file mode 100644 index 00000000..1de10492 --- /dev/null +++ b/stp/parser/let-funcs.cpp @@ -0,0 +1,85 @@ +/******************************************************************** + * AUTHORS: Vijay Ganesh, David L. Dill + * + * BEGIN DATE: November, 2005 + * + * LICENSE: Please view LICENSE file in the home dir of this Program + ********************************************************************/ +// -*- c++ -*- + +#include "../AST/AST.h" +#include <stdlib.h> + +namespace BEEV { + //external parser table for declared symbols. Only symbols which are + //declared are stored here. + ASTNodeSet _parser_symbol_table; + + // FUNC: This function maintains a map between LET-var names and + // LET-expressions + // + //1. if the Let-var is already defined in the LET scope, then the + //1. function returns an error. + // + //2. if the Let-var is already declared variable in the input, then + //2. the function returns an error + // + //3. otherwise add the <var,letExpr> pair to the _letid_expr table. + void BeevMgr::LetExprMgr(const ASTNode& var, const ASTNode& letExpr) { + ASTNodeMap::iterator it; + if(((it = _letid_expr_map.find(var)) != _letid_expr_map.end()) && + it->second != ASTUndefined) { + FatalError("LetExprMgr:The LET-var v has already been defined in this LET scope: v =", var); + } + + if(_parser_symbol_table.find(var) != _parser_symbol_table.end()) { + FatalError("LetExprMgr:This var is already declared. cannot redeclare as a letvar: v =", var); + } + + _letid_expr_map[var] = letExpr; + } + + //this function looksup the "var to letexpr map" and returns the + //corresponding letexpr. if there is no letexpr, then it simply + //returns the var. + ASTNode BeevMgr::ResolveID(const ASTNode& v) { + if(v.GetKind() != SYMBOL) { + return v; + } + + if(_parser_symbol_table.find(v) != _parser_symbol_table.end()) { + return v; + } + + ASTNodeMap::iterator it; + if((it =_letid_expr_map.find(v)) != _letid_expr_map.end()) { + if(it->second == ASTUndefined) + FatalError("Unresolved Identifier: ",v); + else + return it->second; + } + + //this is to mark the let-var as undefined. the let var is defined + //only after the LetExprMgr has completed its work, and until then + //'v' is undefined. + // + //declared variables also get stored in this map, but there value + //is ASTUndefined. This is really a hack. I don't know how to get + //rid of this hack. + _letid_expr_map[v] = ASTUndefined; + return v; + } + + // This function simply cleans up the LetID -> LetExpr Map. + void BeevMgr::CleanupLetIDMap(void) { + ASTNodeMap::iterator it = _letid_expr_map.begin(); + ASTNodeMap::iterator itend = _letid_expr_map.end(); + for(;it!=itend;it++) { + if(it->second != ASTUndefined) { + it->first.SetValueWidth(0); + it->first.SetIndexWidth(0); + } + } + _letid_expr_map.clear(); + } +}; diff --git a/stp/parser/main.cpp b/stp/parser/main.cpp new file mode 100644 index 00000000..ed251ed3 --- /dev/null +++ b/stp/parser/main.cpp @@ -0,0 +1,181 @@ +/******************************************************************** + * AUTHORS: Vijay Ganesh, David L. Dill + * + * BEGIN DATE: November, 2005 + * + * LICENSE: Please view LICENSE file in the home dir of this Program + ********************************************************************/ +// -*- c++ -*- + +#include <iostream> +#include <sstream> +#include <string> +#include <algorithm> +#include <ctime> +#include <unistd.h> +#include <signal.h> +//#include <zlib.h> +#include <stdio.h> +#include "../AST/AST.h" +#include "parsePL_defs.h" +#include "../sat/Solver.h" +#include "../sat/SolverTypes.h" +#include "../sat/VarOrder.h" + +#include <unistd.h> + +#ifdef EXT_HASH_MAP + using namespace __gnu_cxx; +#endif + +/* GLOBAL FUNCTION: parser + */ +extern int yyparse(); +//extern int smtlibparse(); + +/* GLOBAL VARS: Some global vars for the Main function. + * + */ +const char * prog = "stp"; +int linenum = 1; +const char * usage = "Usage: %s [-option] [infile]\n"; +std::string helpstring = "\n\n"; + +// Amount of memory to ask for at beginning of main. +static const intptr_t INITIAL_MEMORY_PREALLOCATION_SIZE = 4000000; + +// Used only in smtlib lexer/parser +BEEV::ASTNode SingleBitOne; +BEEV::ASTNode SingleBitZero; + +/****************************************************************************** + * MAIN FUNCTION: + * + * step 0. Parse the input into an ASTVec. + * step 1. Do BV Rewrites + * step 2. Bitblasts the ASTNode. + * step 3. Convert to CNF + * step 4. Convert to SAT + * step 5. Call SAT to determine if input is SAT or UNSAT + ******************************************************************************/ +int main(int argc, char ** argv) { + char * infile; + extern FILE *yyin; + + // Grab some memory from the OS upfront to reduce system time when individual + // hash tables are being allocated + + if (sbrk(INITIAL_MEMORY_PREALLOCATION_SIZE) == ((void *) -1)) { + // FIXME: figure out how to get and print the real error message. + BEEV::FatalError("Initial allocation of memory failed."); + } + + //populate the help string + helpstring += "-r : switch refinement off (optimizations are ON by default)\n"; + helpstring += "-w : switch wordlevel solver off (optimizations are ON by default)\n"; + helpstring += "-a : switch optimizations off (optimizations are ON by default)\n"; + helpstring += "-s : print function statistics\n"; + helpstring += "-v : print nodes \n"; + helpstring += "-c : construct counterexample\n"; + helpstring += "-d : check counterexample\n"; + helpstring += "-p : print counterexample\n"; + helpstring += "-x : flatten nested XORs\n"; + helpstring += "-h : help\n"; + + for(int i=1; i < argc;i++) { + if(argv[i][0] == '-') + switch(argv[i][1]) { + case 'a' : + BEEV::optimize = false; + BEEV::wordlevel_solve = false; + break; + case 'b': + BEEV::print_STPinput_back = true; + break; + case 'c': + BEEV::construct_counterexample = true; + break; + case 'd': + BEEV::construct_counterexample = true; + BEEV::check_counterexample = true; + break; + case 'e': + BEEV::variable_activity_optimize = true; + break; + case 'f': + BEEV::smtlib_parser_enable = true; + break; + case 'h': + fprintf(stderr,usage,prog); + cout << helpstring; + //BEEV::FatalError(""); + return -1; + break; + case 'l' : + BEEV::linear_search = true; + break; + case 'n': + BEEV::print_output = true; + break; + case 'p': + BEEV::print_counterexample = true; + break; + case 'q': + BEEV::print_arrayval_declaredorder = true; + break; + case 'r': + BEEV::arrayread_refinement = false; + break; + case 's' : + BEEV::stats = true; + break; + case 'u': + BEEV::arraywrite_refinement = false; + break; + case 'v' : + BEEV::print_nodes = true; + break; + case 'w': + BEEV::wordlevel_solve = false; + break; + case 'x': + BEEV::xor_flatten = true; + break; + case 'z': + BEEV::print_sat_varorder = true; + break; + default: + fprintf(stderr,usage,prog); + cout << helpstring; + //BEEV::FatalError(""); + return -1; + break; + } + else { + infile = argv[i]; + yyin = fopen(infile,"r"); + if(yyin == NULL) { + fprintf(stderr,"%s: Error: cannot open %s\n",prog,infile); + BEEV::FatalError(""); + } + } + } + +#ifdef NATIVE_C_ARITH +#else + CONSTANTBV::ErrCode c = CONSTANTBV::BitVector_Boot(); + if(0 != c) { + cout << CONSTANTBV::BitVector_Error(c) << endl; + return 0; + } +#endif + + //want to print the output always from the commandline. + BEEV::print_output = true; + BEEV::globalBeevMgr_for_parser = new BEEV::BeevMgr(); + + SingleBitOne = BEEV::globalBeevMgr_for_parser->CreateOneConst(1); + SingleBitZero = BEEV::globalBeevMgr_for_parser->CreateZeroConst(1); + //BEEV::smtlib_parser_enable = true; + yyparse(); +}//end of Main diff --git a/stp/parser/smtlib.lex b/stp/parser/smtlib.lex new file mode 100644 index 00000000..a03ae94e --- /dev/null +++ b/stp/parser/smtlib.lex @@ -0,0 +1,232 @@ +%{ + /******************************************************************** + * AUTHORS: Vijay Ganesh, David L. Dill + * + * BEGIN DATE: July, 2006 + * + * This file is modified version of the CVCL's smtlib.lex file. Please + * see CVCL license below + ********************************************************************/ + + /******************************************************************** + * \file smtlib.lex + * + * Author: Sergey Berezin, Clark Barrett + * + * Created: Apr 30 2005 + * + * <hr> + * Copyright (C) 2004 by the Board of Trustees of Leland Stanford + * Junior University and by New York University. + * + * License to use, copy, modify, sell and/or distribute this software + * and its documentation for any purpose is hereby granted without + * royalty, subject to the terms and conditions defined in the \ref + * LICENSE file provided with this distribution. In particular: + * + * - The above copyright notice and this permission notice must appear + * in all copies of the software and related documentation. + * + * - THE SOFTWARE IS PROVIDED "AS-IS", WITHOUT ANY WARRANTIES, + * EXPRESSED OR IMPLIED. USE IT AT YOUR OWN RISK. + * + * <hr> + ********************************************************************/ + // -*- c++ -*- +#include <iostream> +#include "../AST/AST.h" +#include "parsePL_defs.h" + + extern char *yytext; + extern int yyerror (char *msg); + + // File-static (local to this file) variables and functions + static std::string _string_lit; + + static char escapeChar(char c) { + switch(c) { + case 'n': return '\n'; + case 't': return '\t'; + default: return c; + } + } + + extern BEEV::ASTNode SingleBitOne; + extern BEEV::ASTNode SingleBitZero; + +/* Changed for smtlib speedup */ +/* bv{DIGIT}+ { yylval.node = new BEEV::ASTNode(BEEV::_bm->CreateBVConst(yytext+2, 10)); return BVCONST_TOK;} */ + +%} + +%option noyywrap +%option nounput +%option noreject +%option noyymore +%option yylineno + +%x COMMENT +%x STRING_LITERAL +%x USER_VALUE + +LETTER ([a-zA-Z]) +DIGIT ([0-9]) +OPCHAR (['\.\_]) +ANYTHING ({LETTER}|{DIGIT}|{OPCHAR}) + +%% +[ \n\t\r\f] { /* sk'ip whitespace */ } +{DIGIT}+ { yylval.uintval = strtoul(yytext, NULL, 10); return NUMERAL_TOK; } + + +bv{DIGIT}+ { yylval.ullval = strtoull(yytext+2, NULL, 10); return BVCONST_TOK; } + +bit{DIGIT}+ { + char c = yytext[3]; + if (c == '1') { + yylval.node = new BEEV::ASTNode(SingleBitOne); + } + else { + yylval.node = new BEEV::ASTNode(SingleBitZero); + } + return BITCONST_TOK; + }; + + +";" { BEGIN COMMENT; } +<COMMENT>"\n" { BEGIN INITIAL; /* return to normal mode */} +<COMMENT>. { /* stay in comment mode */ } + +<INITIAL>"\"" { BEGIN STRING_LITERAL; + _string_lit.erase(_string_lit.begin(), + _string_lit.end()); } +<STRING_LITERAL>"\\". { /* escape characters (like \n or \") */ + _string_lit.insert(_string_lit.end(), + escapeChar(yytext[1])); } +<STRING_LITERAL>"\"" { BEGIN INITIAL; /* return to normal mode */ + yylval.str = new std::string(_string_lit); + return STRING_TOK; } +<STRING_LITERAL>. { _string_lit.insert(_string_lit.end(),*yytext); } + + +<INITIAL>"{" { BEGIN USER_VALUE; + _string_lit.erase(_string_lit.begin(), + _string_lit.end()); } +<USER_VALUE>"\\"[{}] { /* escape characters */ + _string_lit.insert(_string_lit.end(),yytext[1]); } + +<USER_VALUE>"}" { BEGIN INITIAL; /* return to normal mode */ + yylval.str = new std::string(_string_lit); + return USER_VAL_TOK; } +<USER_VALUE>"\n" { _string_lit.insert(_string_lit.end(),'\n');} +<USER_VALUE>. { _string_lit.insert(_string_lit.end(),*yytext); } + +"BitVec" { return BITVEC_TOK;} +"Array" { return ARRAY_TOK;} +"true" { return TRUE_TOK; } +"false" { return FALSE_TOK; } +"not" { return NOT_TOK; } +"implies" { return IMPLIES_TOK; } +"ite" { return ITE_TOK;} +"if_then_else" { return IF_THEN_ELSE_TOK; } +"and" { return AND_TOK; } +"or" { return OR_TOK; } +"xor" { return XOR_TOK; } +"iff" { return IFF_TOK; } +"let" { return LET_TOK; } +"flet" { return FLET_TOK; } +"notes" { return NOTES_TOK; } +"cvc_command" { return CVC_COMMAND_TOK; } +"sorts" { return SORTS_TOK; } +"funs" { return FUNS_TOK; } +"preds" { return PREDS_TOK; } +"extensions" { return EXTENSIONS_TOK; } +"definition" { return DEFINITION_TOK; } +"axioms" { return AXIOMS_TOK; } +"logic" { return LOGIC_TOK; } +"sat" { return SAT_TOK; } +"unsat" { return UNSAT_TOK; } +"unknown" { return UNKNOWN_TOK; } +"assumption" { return ASSUMPTION_TOK; } +"formula" { return FORMULA_TOK; } +"status" { return STATUS_TOK; } +"difficulty" { return DIFFICULTY_TOK; } +"benchmark" { return BENCHMARK_TOK; } +"source" { return SOURCE_TOK;} +"category" { return CATEGORY_TOK;} +"extrasorts" { return EXTRASORTS_TOK; } +"extrafuns" { return EXTRAFUNS_TOK; } +"extrapreds" { return EXTRAPREDS_TOK; } +"language" { return LANGUAGE_TOK; } +"distinct" { return DISTINCT_TOK; } +"select" { return SELECT_TOK; } +"store" { return STORE_TOK; } +":" { return COLON_TOK; } +"\[" { return LBRACKET_TOK; } +"\]" { return RBRACKET_TOK; } +"(" { return LPAREN_TOK; } +")" { return RPAREN_TOK; } +"$" { return DOLLAR_TOK; } +"?" { return QUESTION_TOK; } +"=" {return EQ_TOK;} + +"nand" { return NAND_TOK;} +"nor" { return NOR_TOK;} +"/=" { return NEQ_TOK; } + ":=" { return ASSIGN_TOK;} +"shift_left0" { return BVLEFTSHIFT_TOK;} +"bvshl" { return BVLEFTSHIFT_1_TOK;} +"shift_right0" { return BVRIGHTSHIFT_TOK;} +"bvlshr" { return BVRIGHTSHIFT_1_TOK;} +"bvadd" { return BVPLUS_TOK;} +"bvsub" { return BVSUB_TOK;} +"bvnot" { return BVNOT_TOK;} +"bvmul" { return BVMULT_TOK;} +"bvdiv" { return BVDIV_TOK;} +"bvmod" { return BVMOD_TOK;} +"bvneg" { return BVNEG_TOK;} +"bvand" { return BVAND_TOK;} +"bvor" { return BVOR_TOK;} +"bvxor" { return BVXOR_TOK;} +"bvnand" { return BVNAND_TOK;} +"bvnor" { return BVNOR_TOK;} +"bvxnor" { return BVXNOR_TOK;} +"concat" { return BVCONCAT_TOK;} +"extract" { return BVEXTRACT_TOK;} +"bvlt" { return BVLT_TOK;} +"bvgt" { return BVGT_TOK;} +"bvleq" { return BVLE_TOK;} +"bvgeq" { return BVGE_TOK;} +"bvult" { return BVLT_TOK;} +"bvugt" { return BVGT_TOK;} +"bvuleq" { return BVLE_TOK;} +"bvugeq" { return BVGE_TOK;} +"bvule" { return BVLE_TOK;} +"bvuge" { return BVGE_TOK;} + +"bvslt" { return BVSLT_TOK;} +"bvsgt" { return BVSGT_TOK;} +"bvsleq" { return BVSLE_TOK;} +"bvsgeq" { return BVSGE_TOK;} +"bvsle" { return BVSLE_TOK;} +"bvsge" { return BVSGE_TOK;} + +"sign_extend" { return BVSX_TOK;} +"boolextract" { return BOOLEXTRACT_TOK;} +"boolbv" { return BOOL_TO_BV_TOK;} + +(({LETTER})|(_)({ANYTHING}))({ANYTHING})* { + BEEV::ASTNode nptr = BEEV::globalBeevMgr_for_parser->CreateSymbol(yytext); + + // Check valuesize to see if it's a prop var. I don't like doing + // type determination in the lexer, but it's easier than rewriting + // the whole grammar to eliminate the term/formula distinction. + yylval.node = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->ResolveID(nptr)); + //yylval.node = new BEEV::ASTNode(nptr); + if ((yylval.node)->GetType() == BEEV::BOOLEAN_TYPE) + return FORMID_TOK; + else + return TERMID_TOK; +} +. { yyerror("Illegal input character."); } +%% diff --git a/stp/parser/smtlib.y b/stp/parser/smtlib.y new file mode 100644 index 00000000..0aa22dee --- /dev/null +++ b/stp/parser/smtlib.y @@ -0,0 +1,1036 @@ +%{ + /******************************************************************** + * AUTHORS: Vijay Ganesh, David L. Dill + * + * BEGIN DATE: July, 2006 + * + * This file is modified version of the CVCL's smtlib.y file. Please + * see CVCL license below + ********************************************************************/ + + /******************************************************************** + * + * \file smtlib.y + * + * Author: Sergey Berezin, Clark Barrett + * + * Created: Apr 30 2005 + * + * <hr> + * Copyright (C) 2004 by the Board of Trustees of Leland Stanford + * Junior University and by New York University. + * + * License to use, copy, modify, sell and/or distribute this software + * and its documentation for any purpose is hereby granted without + * royalty, subject to the terms and conditions defined in the \ref + * LICENSE file provided with this distribution. In particular: + * + * - The above copyright notice and this permission notice must appear + * in all copies of the software and related documentation. + * + * - THE SOFTWARE IS PROVIDED "AS-IS", WITHOUT ANY WARRANTIES, + * EXPRESSED OR IMPLIED. USE IT AT YOUR OWN RISK. + * + * <hr> + ********************************************************************/ + // -*- c++ -*- + +#include "../AST/AST.h" + using namespace std; + + // Suppress the bogus warning suppression in bison (it generates + // compile error) +#undef __GNUC_MINOR__ + + extern char* yytext; + extern int yylineno; + + //int yylineno; + + extern int yylex(void); + + int yyerror(char *s) { + //yylineno = 0; + cout << "syntax error: line " << yylineno << "\n" << s << endl; + cout << " token: " << yytext << endl; + BEEV::FatalError(""); + return 1; + } + + BEEV::ASTNode query; +#define YYLTYPE_IS_TRIVIAL 1 +#define YYMAXDEPTH 104857600 +#define YYERROR_VERBOSE 1 +#define YY_EXIT_FAILURE -1 +%} + +%union { + // FIXME: Why is this not an UNSIGNED int? + int uintval; /* for numerals in types. */ + + // for BV32 BVCONST + unsigned long long ullval; + + struct { + //stores the indexwidth and valuewidth + //indexwidth is 0 iff type is bitvector. positive iff type is + //array, and stores the width of the indexing bitvector + unsigned int indexwidth; + //width of the bitvector type + unsigned int valuewidth; + } indexvaluewidth; + + //ASTNode,ASTVec + BEEV::ASTNode *node; + BEEV::ASTVec *vec; + std::string *str; +}; + +%start cmd + +%type <indexvaluewidth> sort_symb sort_symbs +%type <node> status +%type <vec> bench_attributes an_formulas + +%type <node> benchmark bench_name bench_attribute +%type <node> an_term an_nonbvconst_term an_formula + +%type <node> var fvar logic_name +%type <str> user_value + +%token <uintval> NUMERAL_TOK +%token <ullval> BVCONST_TOK +%token <node> BITCONST_TOK +%token <node> FORMID_TOK TERMID_TOK +%token <str> STRING_TOK +%token <str> USER_VAL_TOK +%token SOURCE_TOK +%token CATEGORY_TOK +%token DIFFICULTY_TOK +%token BITVEC_TOK +%token ARRAY_TOK +%token SELECT_TOK +%token STORE_TOK +%token TRUE_TOK +%token FALSE_TOK +%token NOT_TOK +%token IMPLIES_TOK +%token ITE_TOK +%token IF_THEN_ELSE_TOK +%token AND_TOK +%token OR_TOK +%token XOR_TOK +%token IFF_TOK +%token EXISTS_TOK +%token FORALL_TOK +%token LET_TOK +%token FLET_TOK +%token NOTES_TOK +%token CVC_COMMAND_TOK +%token SORTS_TOK +%token FUNS_TOK +%token PREDS_TOK +%token EXTENSIONS_TOK +%token DEFINITION_TOK +%token AXIOMS_TOK +%token LOGIC_TOK +%token COLON_TOK +%token LBRACKET_TOK +%token RBRACKET_TOK +%token LPAREN_TOK +%token RPAREN_TOK +%token SAT_TOK +%token UNSAT_TOK +%token UNKNOWN_TOK +%token ASSUMPTION_TOK +%token FORMULA_TOK +%token STATUS_TOK +%token BENCHMARK_TOK +%token EXTRASORTS_TOK +%token EXTRAFUNS_TOK +%token EXTRAPREDS_TOK +%token LANGUAGE_TOK +%token DOLLAR_TOK +%token QUESTION_TOK +%token DISTINCT_TOK +%token SEMICOLON_TOK +%token EOF_TOK +%token EQ_TOK +/*BV SPECIFIC TOKENS*/ +%token NAND_TOK +%token NOR_TOK +%token NEQ_TOK +%token ASSIGN_TOK +%token BV_TOK +%token BOOLEAN_TOK +%token BVLEFTSHIFT_TOK +%token BVLEFTSHIFT_1_TOK +%token BVRIGHTSHIFT_TOK +%token BVRIGHTSHIFT_1_TOK +%token BVPLUS_TOK +%token BVSUB_TOK +%token BVNOT_TOK //bvneg in CVCL +%token BVMULT_TOK +%token BVDIV_TOK +%token BVMOD_TOK +%token BVNEG_TOK //bvuminus in CVCL +%token BVAND_TOK +%token BVOR_TOK +%token BVXOR_TOK +%token BVNAND_TOK +%token BVNOR_TOK +%token BVXNOR_TOK +%token BVCONCAT_TOK +%token BVLT_TOK +%token BVGT_TOK +%token BVLE_TOK +%token BVGE_TOK +%token BVSLT_TOK +%token BVSGT_TOK +%token BVSLE_TOK +%token BVSGE_TOK +%token BVSX_TOK +%token BOOLEXTRACT_TOK +%token BOOL_TO_BV_TOK +%token BVEXTRACT_TOK + +%left LBRACKET_TOK RBRACKET_TOK + +%% + +cmd: + benchmark + { + if($1 != NULL) { + BEEV::globalBeevMgr_for_parser->TopLevelSAT(*$1,query); + delete $1; + } + YYACCEPT; + } +; + +benchmark: + LPAREN_TOK BENCHMARK_TOK bench_name bench_attributes RPAREN_TOK + { + if($4 != NULL){ + if($4->size() > 1) + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::AND,*$4)); + else + $$ = new BEEV::ASTNode((*$4)[0]); + delete $4; + } + else { + $$ = NULL; + } + } +/* | EOF_TOK */ +/* { */ +/* } */ +; + +bench_name: + FORMID_TOK + { + } +; + +bench_attributes: + bench_attribute + { + $$ = new BEEV::ASTVec; + if ($1 != NULL) { + $$->push_back(*$1); + BEEV::globalBeevMgr_for_parser->AddAssert(*$1); + delete $1; + } + } + | bench_attributes bench_attribute + { + if ($1 != NULL && $2 != NULL) { + $1->push_back(*$2); + BEEV::globalBeevMgr_for_parser->AddAssert(*$2); + $$ = $1; + delete $2; + } + } +; + +bench_attribute: + COLON_TOK ASSUMPTION_TOK an_formula + { + //assumptions are like asserts + $$ = $3; + } + | COLON_TOK FORMULA_TOK an_formula + { + //the query + query = BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::NOT,*$3); + BEEV::globalBeevMgr_for_parser->AddQuery(query); + //dummy true + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::TRUE)); + + + } + | COLON_TOK STATUS_TOK status + { + $$ = NULL; + } + | COLON_TOK LOGIC_TOK logic_name + { + if (!(0 == strcmp($3->GetName(),"QF_UFBV") || + 0 == strcmp($3->GetName(),"QF_AUFBV"))) { + yyerror("Wrong input logic:"); + } + $$ = NULL; + } + | COLON_TOK EXTRAFUNS_TOK LPAREN_TOK var_decls RPAREN_TOK + { + $$ = NULL; + } + | COLON_TOK EXTRAPREDS_TOK LPAREN_TOK var_decls RPAREN_TOK + { + $$ = NULL; + } + | annotation + { + $$ = NULL; + } +; + +logic_name: + FORMID_TOK LBRACKET_TOK NUMERAL_TOK RBRACKET_TOK + { + $$ = $1; + } + | FORMID_TOK + { + $$ = $1; + } +; + +status: + SAT_TOK { $$ = NULL; } + | UNSAT_TOK { $$ = NULL; } + | UNKNOWN_TOK { $$ = NULL; } +; + + +/* annotations: */ +/* annotation */ +/* { */ +/* } */ +/* | annotations annotation */ +/* { */ +/* } */ +/* ; */ + +annotation: + attribute + { + } + | attribute user_value + { + } +; + +user_value: + USER_VAL_TOK + { + //cerr << "Printing user_value: " << *$1 << endl; + } +; + +attribute: + COLON_TOK SOURCE_TOK + { + } + | COLON_TOK CATEGORY_TOK + { + } + | COLON_TOK DIFFICULTY_TOK +; + +sort_symbs: + sort_symb + { + //a single sort symbol here means either a BitVec or a Boolean + $$.indexwidth = $1.indexwidth; + $$.valuewidth = $1.valuewidth; + } + | sort_symb sort_symb + { + //two sort symbols mean f: type --> type + $$.indexwidth = $1.valuewidth; + $$.valuewidth = $2.valuewidth; + } +; + +var_decls: + var_decl + { + } +// | LPAREN_TOK var_decl RPAREN_TOK + | + var_decls var_decl + { + } +; + +var_decl: + LPAREN_TOK FORMID_TOK sort_symbs RPAREN_TOK + { + BEEV::_parser_symbol_table.insert(*$2); + //Sort_symbs has the indexwidth/valuewidth. Set those fields in + //var + $2->SetIndexWidth($3.indexwidth); + $2->SetValueWidth($3.valuewidth); + } + | LPAREN_TOK FORMID_TOK RPAREN_TOK + { + BEEV::_parser_symbol_table.insert(*$2); + //Sort_symbs has the indexwidth/valuewidth. Set those fields in + //var + $2->SetIndexWidth(0); + $2->SetValueWidth(0); + } +; + +an_formulas: + an_formula + { + $$ = new BEEV::ASTVec; + if ($1 != NULL) { + $$->push_back(*$1); + delete $1; + } + } + | + an_formulas an_formula + { + if ($1 != NULL && $2 != NULL) { + $1->push_back(*$2); + $$ = $1; + delete $2; + } + } +; + +an_formula: + TRUE_TOK + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::TRUE)); + $$->SetIndexWidth(0); + $$->SetValueWidth(0); + } + | FALSE_TOK + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::FALSE)); + $$->SetIndexWidth(0); + $$->SetValueWidth(0); + } + | fvar + { + $$ = $1; + } + | LPAREN_TOK EQ_TOK an_term an_term RPAREN_TOK + //| LPAREN_TOK EQ_TOK an_term an_term annotations RPAREN_TOK + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateSimplifiedEQ(*$3, *$4)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $3; + delete $4; + } + | LPAREN_TOK BVSLT_TOK an_term an_term RPAREN_TOK + //| LPAREN_TOK BVSLT_TOK an_term an_term annotations RPAREN_TOK + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::BVSLT, *$3, *$4)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $3; + delete $4; + } + | LPAREN_TOK BVSLE_TOK an_term an_term RPAREN_TOK + //| LPAREN_TOK BVSLE_TOK an_term an_term annotations RPAREN_TOK + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::BVSLE, *$3, *$4)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $3; + delete $4; + } + | LPAREN_TOK BVSGT_TOK an_term an_term RPAREN_TOK + //| LPAREN_TOK BVSGT_TOK an_term an_term annotations RPAREN_TOK + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::BVSGT, *$3, *$4)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $3; + delete $4; + } + | LPAREN_TOK BVSGE_TOK an_term an_term RPAREN_TOK + //| LPAREN_TOK BVSGE_TOK an_term an_term annotations RPAREN_TOK + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::BVSGE, *$3, *$4)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $3; + delete $4; + } + | LPAREN_TOK BVLT_TOK an_term an_term RPAREN_TOK + //| LPAREN_TOK BVLT_TOK an_term an_term annotations RPAREN_TOK + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::BVLT, *$3, *$4)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $3; + delete $4; + } + | LPAREN_TOK BVLE_TOK an_term an_term RPAREN_TOK + //| LPAREN_TOK BVLE_TOK an_term an_term annotations RPAREN_TOK + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::BVLE, *$3, *$4)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $3; + delete $4; + } + | LPAREN_TOK BVGT_TOK an_term an_term RPAREN_TOK + //| LPAREN_TOK BVGT_TOK an_term an_term annotations RPAREN_TOK + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::BVGT, *$3, *$4)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $3; + delete $4; + } + | LPAREN_TOK BVGE_TOK an_term an_term RPAREN_TOK + //| LPAREN_TOK BVGE_TOK an_term an_term annotations RPAREN_TOK + { + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::BVGE, *$3, *$4)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $3; + delete $4; + } + | LPAREN_TOK an_formula RPAREN_TOK + { + $$ = $2; + } + | LPAREN_TOK NOT_TOK an_formula RPAREN_TOK + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::NOT, *$3)); + delete $3; + } + | LPAREN_TOK IMPLIES_TOK an_formula an_formula RPAREN_TOK + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::IMPLIES, *$3, *$4)); + delete $3; + delete $4; + } + | LPAREN_TOK IF_THEN_ELSE_TOK an_formula an_formula an_formula RPAREN_TOK + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::ITE, *$3, *$4, *$5)); + delete $3; + delete $4; + delete $5; + } + | LPAREN_TOK AND_TOK an_formulas RPAREN_TOK + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::AND, *$3)); + delete $3; + } + | LPAREN_TOK OR_TOK an_formulas RPAREN_TOK + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::OR, *$3)); + delete $3; + } + | LPAREN_TOK XOR_TOK an_formula an_formula RPAREN_TOK + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::XOR, *$3, *$4)); + delete $3; + delete $4; + } + | LPAREN_TOK IFF_TOK an_formula an_formula RPAREN_TOK + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateNode(BEEV::IFF, *$3, *$4)); + delete $3; + delete $4; + } + | letexpr_mgmt an_formula RPAREN_TOK + //| letexpr_mgmt an_formula annotations RPAREN_TOK + { + $$ = $2; + //Cleanup the LetIDToExprMap + BEEV::globalBeevMgr_for_parser->CleanupLetIDMap(); + } +; + +letexpr_mgmt: + LPAREN_TOK LET_TOK LPAREN_TOK QUESTION_TOK FORMID_TOK an_term RPAREN_TOK + { + //Expr must typecheck + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$6); + + //set the valuewidth of the identifier + $5->SetValueWidth($6->GetValueWidth()); + $5->SetIndexWidth($6->GetIndexWidth()); + + //populate the hashtable from LET-var --> + //LET-exprs and then process them: + // + //1. ensure that LET variables do not clash + //1. with declared variables. + // + //2. Ensure that LET variables are not + //2. defined more than once + BEEV::globalBeevMgr_for_parser->LetExprMgr(*$5,*$6); + delete $5; + delete $6; + } + | LPAREN_TOK FLET_TOK LPAREN_TOK DOLLAR_TOK FORMID_TOK an_formula RPAREN_TOK + { + //Expr must typecheck + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$6); + + //set the valuewidth of the identifier + $5->SetValueWidth($6->GetValueWidth()); + $5->SetIndexWidth($6->GetIndexWidth()); + + //Do LET-expr management + BEEV::globalBeevMgr_for_parser->LetExprMgr(*$5,*$6); + delete $5; + delete $6; + } + +/* an_terms: */ +/* an_term */ +/* { */ +/* } */ +/* | an_terms an_term */ +/* { */ +/* } */ +/* ; */ + +an_term: + BVCONST_TOK + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateBVConst(64, $1)); + } + | BVCONST_TOK LBRACKET_TOK NUMERAL_TOK RBRACKET_TOK + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateBVConst($3, $1)); + } + | an_nonbvconst_term + ; + +an_nonbvconst_term: + BITCONST_TOK { $$ = $1; } + | var + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->ResolveID(*$1)); + delete $1; + } + | LPAREN_TOK an_term RPAREN_TOK + //| LPAREN_TOK an_term annotations RPAREN_TOK + { + $$ = $2; + //$$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->SimplifyTerm(*$2)); + //delete $2; + } + | LPAREN_TOK TERMID_TOK an_term RPAREN_TOK + { + //ARRAY READ + // valuewidth is same as array, indexwidth is 0. + BEEV::ASTNode in = *$2; + BEEV::ASTNode m = *$3; + unsigned int width = in.GetValueWidth(); + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::READ, width, in, m)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $2; + delete $3; + } + | SELECT_TOK an_term an_term + { + //ARRAY READ + // valuewidth is same as array, indexwidth is 0. + BEEV::ASTNode array = *$2; + BEEV::ASTNode index = *$3; + unsigned int width = array.GetValueWidth(); + BEEV::ASTNode * n = + new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::READ, width, array, index)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $2; + delete $3; + } + | STORE_TOK an_term an_term an_term + { + //ARRAY WRITE + unsigned int width = $4->GetValueWidth(); + BEEV::ASTNode array = *$2; + BEEV::ASTNode index = *$3; + BEEV::ASTNode writeval = *$4; + BEEV::ASTNode write_term = BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::WRITE,width,array,index,writeval); + write_term.SetIndexWidth($2->GetIndexWidth()); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(write_term); + BEEV::ASTNode * n = new BEEV::ASTNode(write_term); + $$ = n; + delete $2; + delete $3; + delete $4; + } +/* | BVEXTRACT_TOK LBRACKET_TOK NUMERAL_TOK COLON_TOK NUMERAL_TOK RBRACKET_TOK BVCONST_TOK */ +/* { */ +/* // This special case is when we have an extract on top of a constant bv, which happens */ +/* // almost all the time in the smt syntax. */ + +/* // $3 is high, $5 is low. They are ints (why not unsigned? See uintval) */ +/* int hi = $3; */ +/* int low = $5; */ +/* int width = hi - low + 1; */ + +/* if (width < 0) */ +/* yyerror("Negative width in extract"); */ + +/* unsigned long long int val = $7; */ + +/* // cut and past from BV32 const evaluator */ + +/* unsigned long long int mask1 = 0xffffffffffffffffLL; */ + +/* mask1 >>= 64-(hi+1); */ + +/* //extract val[hi:0] */ +/* val &= mask1; */ +/* //extract val[hi:low] */ +/* val >>= low; */ + +/* // val is the desired BV32. */ +/* BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateBVConst(width, val)); */ +/* $$ = n; */ +/* } */ + | BVEXTRACT_TOK LBRACKET_TOK NUMERAL_TOK COLON_TOK NUMERAL_TOK RBRACKET_TOK an_term + { + int width = $3 - $5 + 1; + if (width < 0) + yyerror("Negative width in extract"); + + if((unsigned)$3 >= $7->GetValueWidth() || (unsigned)$5 < 0) + yyerror("Parsing: Wrong width in BVEXTRACT\n"); + + BEEV::ASTNode hi = BEEV::globalBeevMgr_for_parser->CreateBVConst(32, $3); + BEEV::ASTNode low = BEEV::globalBeevMgr_for_parser->CreateBVConst(32, $5); + BEEV::ASTNode output = BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVEXTRACT, width, *$7,hi,low); + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->SimplifyTerm(output)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $7; + } + | ITE_TOK an_formula an_term an_term + { + unsigned int width = $3->GetValueWidth(); + if (width != $4->GetValueWidth()) { + cerr << *$3; + cerr << *$4; + yyerror("Width mismatch in IF-THEN-ELSE"); + } + if($3->GetIndexWidth() != $4->GetIndexWidth()) + yyerror("Width mismatch in IF-THEN-ELSE"); + + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$2); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$3); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$4); + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateSimplifiedTermITE(*$2, *$3, *$4)); + //$$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::ITE,width,*$2, *$3, *$4)); + $$->SetIndexWidth($4->GetIndexWidth()); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$$); + delete $2; + delete $3; + delete $4; + } + | BVCONCAT_TOK an_term an_term + { + unsigned int width = $2->GetValueWidth() + $3->GetValueWidth(); + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVCONCAT, width, *$2, *$3)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + + delete $2; + delete $3; + } + | BVNOT_TOK an_term + { + //this is the BVNEG (term) in the CVCL language + unsigned int width = $2->GetValueWidth(); + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVNEG, width, *$2)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $2; + } + | BVNEG_TOK an_term + { + //this is the BVUMINUS term in CVCL langauge + unsigned width = $2->GetValueWidth(); + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVUMINUS,width,*$2)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $2; + } + | BVAND_TOK an_term an_term + { + unsigned int width = $2->GetValueWidth(); + if (width != $3->GetValueWidth()) { + yyerror("Width mismatch in AND"); + } + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVAND, width, *$2, *$3)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $2; + delete $3; + } + | BVOR_TOK an_term an_term + { + unsigned int width = $2->GetValueWidth(); + if (width != $3->GetValueWidth()) { + yyerror("Width mismatch in OR"); + } + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVOR, width, *$2, *$3)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $2; + delete $3; + } + | BVXOR_TOK an_term an_term + { + unsigned int width = $2->GetValueWidth(); + if (width != $3->GetValueWidth()) { + yyerror("Width mismatch in XOR"); + } + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVXOR, width, *$2, *$3)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $2; + delete $3; + } + | BVSUB_TOK an_term an_term + { + unsigned int width = $2->GetValueWidth(); + if (width != $3->GetValueWidth()) { + yyerror("Width mismatch in BVSUB"); + } + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVSUB, width, *$2, *$3)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $2; + delete $3; + } + | BVPLUS_TOK an_term an_term + { + unsigned int width = $2->GetValueWidth(); + if (width != $3->GetValueWidth()) { + yyerror("Width mismatch in BVPLUS"); + } + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVPLUS, width, *$2, *$3)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $2; + delete $3; + + } + | BVMULT_TOK an_term an_term + { + unsigned int width = $2->GetValueWidth(); + if (width != $3->GetValueWidth()) { + yyerror("Width mismatch in BVMULT"); + } + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVMULT, width, *$2, *$3)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $2; + delete $3; + } + | BVNAND_TOK an_term an_term + { + unsigned int width = $2->GetValueWidth(); + if (width != $3->GetValueWidth()) { + yyerror("Width mismatch in BVNAND"); + } + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVNAND, width, *$2, *$3)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $2; + delete $3; + } + | BVNOR_TOK an_term an_term + { + unsigned int width = $2->GetValueWidth(); + if (width != $3->GetValueWidth()) { + yyerror("Width mismatch in BVNOR"); + } + BEEV::ASTNode * n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVNOR, width, *$2, *$3)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $2; + delete $3; + } + | BVLEFTSHIFT_TOK an_term NUMERAL_TOK + { + unsigned int w = $2->GetValueWidth(); + if((unsigned)$3 < w) { + BEEV::ASTNode trailing_zeros = BEEV::globalBeevMgr_for_parser->CreateBVConst($3, 0); + BEEV::ASTNode hi = BEEV::globalBeevMgr_for_parser->CreateBVConst(32, w-$3-1); + BEEV::ASTNode low = BEEV::globalBeevMgr_for_parser->CreateBVConst(32,0); + BEEV::ASTNode m = BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVEXTRACT,w-$3,*$2,hi,low); + BEEV::ASTNode * n = + new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVCONCAT,w,m,trailing_zeros)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + } + else + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateBVConst(w,0)); + delete $2; + } + | BVLEFTSHIFT_1_TOK an_term an_term + { + unsigned int w = $2->GetValueWidth(); + unsigned int shift_amt = GetUnsignedConst(*$3); + if((unsigned)shift_amt < w) { + BEEV::ASTNode trailing_zeros = BEEV::globalBeevMgr_for_parser->CreateBVConst(shift_amt, 0); + BEEV::ASTNode hi = BEEV::globalBeevMgr_for_parser->CreateBVConst(32, w-shift_amt-1); + BEEV::ASTNode low = BEEV::globalBeevMgr_for_parser->CreateBVConst(32,0); + BEEV::ASTNode m = BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVEXTRACT,w-shift_amt,*$2,hi,low); + BEEV::ASTNode * n = + new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVCONCAT,w,m,trailing_zeros)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + } + else { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateBVConst(w,0)); + } + delete $2; + //delete $3; + } + | BVRIGHTSHIFT_TOK an_term NUMERAL_TOK + { + BEEV::ASTNode leading_zeros = BEEV::globalBeevMgr_for_parser->CreateBVConst($3, 0); + unsigned int w = $2->GetValueWidth(); + + //the amount by which you are rightshifting + //is less-than/equal-to the length of input + //bitvector + if((unsigned)$3 < w) { + BEEV::ASTNode hi = BEEV::globalBeevMgr_for_parser->CreateBVConst(32,w-1); + BEEV::ASTNode low = BEEV::globalBeevMgr_for_parser->CreateBVConst(32,$3); + BEEV::ASTNode extract = BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVEXTRACT,w-$3,*$2,hi,low); + BEEV::ASTNode * n = + new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVCONCAT, w,leading_zeros, extract)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + } + else { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateBVConst(w,0)); + } + delete $2; + } + | BVRIGHTSHIFT_1_TOK an_term an_term + { + unsigned int shift_amt = GetUnsignedConst(*$3); + BEEV::ASTNode leading_zeros = BEEV::globalBeevMgr_for_parser->CreateBVConst(shift_amt, 0); + unsigned int w = $2->GetValueWidth(); + + //the amount by which you are rightshifting + //is less-than/equal-to the length of input + //bitvector + if((unsigned)shift_amt < w) { + BEEV::ASTNode hi = BEEV::globalBeevMgr_for_parser->CreateBVConst(32,w-1); + BEEV::ASTNode low = BEEV::globalBeevMgr_for_parser->CreateBVConst(32,shift_amt); + BEEV::ASTNode extract = BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVEXTRACT,w-shift_amt,*$2,hi,low); + BEEV::ASTNode * n = + new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVCONCAT, w,leading_zeros, extract)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + } + else { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateBVConst(w,0)); + } + delete $2; + } + | BVSX_TOK LBRACKET_TOK NUMERAL_TOK RBRACKET_TOK an_term + { + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*$5); + unsigned w = $5->GetValueWidth() + $3; + BEEV::ASTNode width = BEEV::globalBeevMgr_for_parser->CreateBVConst(32,w); + BEEV::ASTNode *n = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->CreateTerm(BEEV::BVSX,w,*$5,width)); + BEEV::globalBeevMgr_for_parser->BVTypeCheck(*n); + $$ = n; + delete $5; + } +; + +sort_symb: + BITVEC_TOK LBRACKET_TOK NUMERAL_TOK RBRACKET_TOK + { + // Just return BV width. If sort is BOOL, width is 0. + // Otherwise, BITVEC[w] returns w. + // + //((indexwidth is 0) && (valuewidth>0)) iff type is BV + $$.indexwidth = 0; + unsigned int length = $3; + if(length > 0) { + $$.valuewidth = length; + } + else { + BEEV::FatalError("Fatal Error: parsing: BITVECTORS must be of positive length: \n"); + } + } + | ARRAY_TOK LBRACKET_TOK NUMERAL_TOK COLON_TOK NUMERAL_TOK RBRACKET_TOK + { + unsigned int index_len = $3; + unsigned int value_len = $5; + if(index_len > 0) { + $$.indexwidth = $3; + } + else { + BEEV::FatalError("Fatal Error: parsing: BITVECTORS must be of positive length: \n"); + } + + if(value_len > 0) { + $$.valuewidth = $5; + } + else { + BEEV::FatalError("Fatal Error: parsing: BITVECTORS must be of positive length: \n"); + } + } +; + +var: + FORMID_TOK + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->ResolveID(*$1)); + delete $1; + } + | TERMID_TOK + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->ResolveID(*$1)); + delete $1; + } + | QUESTION_TOK TERMID_TOK + { + $$ = $2; + } +; + +fvar: + DOLLAR_TOK FORMID_TOK + { + $$ = $2; + } + | FORMID_TOK + { + $$ = new BEEV::ASTNode(BEEV::globalBeevMgr_for_parser->ResolveID(*$1)); + delete $1; + } +; +%% diff --git a/stp/sat/Global.h b/stp/sat/Global.h new file mode 100644 index 00000000..a428975c --- /dev/null +++ b/stp/sat/Global.h @@ -0,0 +1,255 @@ +/****************************************************************************************[Global.h] +MiniSat -- Copyright (c) 2003-2005, Niklas Een, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + +#ifndef Global_h +#define Global_h + +#include <cassert> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <new> + +// PKT: needs to be outside namespace MINISAT or mac os x compilation breaks +#ifdef _MSC_VER +#else +#include <unistd.h> +#endif + +namespace MINISAT { +//================================================================================================= +// Basic Types & Minor Things: + +// DWD: This is needed on darwin. +typedef unsigned int uint; + +#ifdef _MSC_VER + +typedef INT64 int64; +typedef UINT64 uint64; +typedef INT_PTR intp; +typedef UINT_PTR uintp; +#define I64_fmt "I64d" +#else + +typedef long long int64; +typedef unsigned long long uint64; +typedef __PTRDIFF_TYPE__ intp; +typedef unsigned __PTRDIFF_TYPE__ uintp; +#define I64_fmt "lld" +#endif + +template<class T> static inline T min(T x, T y) { return (x < y) ? x : y; } +template<class T> static inline T max(T x, T y) { return (x > y) ? x : y; } + +template <bool> struct STATIC_ASSERTION_FAILURE; +template <> struct STATIC_ASSERTION_FAILURE<true>{}; +#define TEMPLATE_FAIL STATIC_ASSERTION_FAILURE<false>() + + +//================================================================================================= +// 'malloc()'-style memory allocation -- never returns NULL; aborts instead: + + +template<class T> static inline T* xmalloc(size_t size) { + T* tmp = (T*)malloc(size * sizeof(T)); + assert(size == 0 || tmp != NULL); + return tmp; } + +template<class T> static inline T* xrealloc(T* ptr, size_t size) { + T* tmp = (T*)realloc((void*)ptr, size * sizeof(T)); + assert(size == 0 || tmp != NULL); + return tmp; } + +template<class T> static inline void xfree(T *ptr) { + if (ptr != NULL) free((void*)ptr); } + + +//================================================================================================= +// Random numbers: + + +// Returns a random float 0 <= x < 1. Seed must never be 0. +static inline double drand(double& seed) { + seed *= 1389796; + int q = (int)(seed / 2147483647); + seed -= (double)q * 2147483647; + return seed / 2147483647; } + +// Returns a random integer 0 <= x < size. Seed must never be 0. +static inline int irand(double& seed, int size) { + return (int)(drand(seed) * size); } + + +//================================================================================================= +// 'vec' -- automatically resizable arrays (via 'push()' method): + + +// NOTE! Don't use this vector on datatypes that cannot be re-located in memory (with realloc) + +template<class T> +class vec { + T* data; + int sz; + int cap; + + void init(int size, const T& pad); + void grow(int min_cap); + +public: + // Types: + typedef int Key; + typedef T Datum; + + // Constructors: + vec(void) : data(NULL) , sz(0) , cap(0) { } + vec(int size) : data(NULL) , sz(0) , cap(0) { growTo(size); } + vec(int size, const T& pad) : data(NULL) , sz(0) , cap(0) { growTo(size, pad); } + vec(T* array, int size) : data(array), sz(size), cap(size) { } // (takes ownership of array -- will be deallocated with 'xfree()') + ~vec(void) { clear(true); } + + // Ownership of underlying array: + T* release (void) { T* ret = data; data = NULL; sz = 0; cap = 0; return ret; } + operator T* (void) { return data; } // (unsafe but convenient) + operator const T* (void) const { return data; } + + // Size operations: + int size (void) const { return sz; } + void shrink (int nelems) { assert(nelems <= sz); for (int i = 0; i < nelems; i++) sz--, data[sz].~T(); } + void pop (void) { sz--, data[sz].~T(); } + void growTo (int size); + void growTo (int size, const T& pad); + void clear (bool dealloc = false); + void capacity (int size) { grow(size); } + + // Stack interface: + void push (void) { if (sz == cap) grow(sz+1); new (&data[sz]) T() ; sz++; } + void push (const T& elem) { if (sz == cap) grow(sz+1); new (&data[sz]) T(elem); sz++; } + const T& last (void) const { return data[sz-1]; } + T& last (void) { return data[sz-1]; } + + // Vector interface: + const T& operator [] (int index) const { return data[index]; } + T& operator [] (int index) { return data[index]; } + + // Don't allow copying (error prone): + vec<T>& operator = (vec<T>& other) { TEMPLATE_FAIL; } + vec (vec<T>& other) { TEMPLATE_FAIL; } + + // Duplicatation (preferred instead): + void copyTo(vec<T>& copy) const { copy.clear(); copy.growTo(sz); for (int i = 0; i < sz; i++) new (©[i]) T(data[i]); } + void moveTo(vec<T>& dest) { dest.clear(true); dest.data = data; dest.sz = sz; dest.cap = cap; data = NULL; sz = 0; cap = 0; } +}; + +template<class T> +void vec<T>::grow(int min_cap) { + if (min_cap <= cap) return; + if (cap == 0) cap = (min_cap >= 2) ? min_cap : 2; + else do cap = (cap*3+1) >> 1; while (cap < min_cap); + data = xrealloc(data, cap); } + +template<class T> +void vec<T>::growTo(int size, const T& pad) { + if (sz >= size) return; + grow(size); + for (int i = sz; i < size; i++) new (&data[i]) T(pad); + sz = size; } + +template<class T> +void vec<T>::growTo(int size) { + if (sz >= size) return; + grow(size); + for (int i = sz; i < size; i++) new (&data[i]) T(); + sz = size; } + +template<class T> +void vec<T>::clear(bool dealloc) { + if (data != NULL){ + for (int i = 0; i < sz; i++) data[i].~T(); + sz = 0; + if (dealloc) xfree(data), data = NULL, cap = 0; } } + + +//================================================================================================= +// Useful functions on vectors + + +template<class V, class T> +void remove(V& ts, const T& t) +{ + int j = 0; + for (; j < ts.size() && ts[j] != t; j++); + assert(j < ts.size()); + for (; j < ts.size()-1; j++) ts[j] = ts[j+1]; + ts.pop(); +} + + +template<class V, class T> +bool find(V& ts, const T& t) +{ + int j = 0; + for (; j < ts.size() && ts[j] != t; j++); + return j < ts.size(); +} + +//================================================================================================= +// Lifted booleans: + + +class lbool { + int value; + explicit lbool(int v) : value(v) { } + +public: + lbool() : value(0) { } + lbool(bool x) : value((int)x*2-1) { } + int toInt(void) const { return value; } + + bool operator == (const lbool& other) const { return value == other.value; } + bool operator != (const lbool& other) const { return value != other.value; } + lbool operator ~ (void) const { return lbool(-value); } + + friend int toInt (lbool l); + friend lbool toLbool(int v); +}; +inline int toInt (lbool l) { return l.toInt(); } +inline lbool toLbool(int v) { return lbool(v); } + +const lbool l_True = toLbool( 1); +const lbool l_False = toLbool(-1); +const lbool l_Undef = toLbool( 0); + + +//================================================================================================= +// Relation operators -- extend definitions from '==' and '<' + + +#ifndef __SGI_STL_INTERNAL_RELOPS // (be aware of SGI's STL implementation...) +#define __SGI_STL_INTERNAL_RELOPS +template <class T> static inline bool operator != (const T& x, const T& y) { return !(x == y); } +template <class T> static inline bool operator > (const T& x, const T& y) { return y < x; } +template <class T> static inline bool operator <= (const T& x, const T& y) { return !(y < x); } +template <class T> static inline bool operator >= (const T& x, const T& y) { return !(x < y); } +#endif + + +//================================================================================================= +}; +#endif diff --git a/stp/sat/Heap.h b/stp/sat/Heap.h new file mode 100644 index 00000000..acfa1c1e --- /dev/null +++ b/stp/sat/Heap.h @@ -0,0 +1,151 @@ +/******************************************************************************************[Heap.h] +MiniSat -- Copyright (c) 2003-2005, Niklas Een, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + +#ifndef Heap_h +#define Heap_h + +#include "../AST/ASTUtil.h" +namespace MINISAT { + +//================================================================================================= + + +static inline int left (int i) { return i+i; } +static inline int right (int i) { return i+i + 1; } +static inline int parent(int i) { return i >> 1; } + +template<class C> +class Heap { + public: + C comp; + vec<int> heap; // heap of ints + vec<int> indices; // int -> index in heap + + inline void percolateUp(int i) + { + int x = heap[i]; + while (parent(i) != 0 && comp(x,heap[parent(i)])){ + heap[i] = heap[parent(i)]; + indices[heap[i]] = i; + i = parent(i); + } + heap [i] = x; + indices[x] = i; + } + + inline void percolateDown(int i) + { + int x = heap[i]; + while (left(i) < heap.size()){ + int child = right(i) < heap.size() && comp(heap[right(i)],heap[left(i)]) ? right(i) : left(i); + if (!comp(heap[child],x)) break; + heap[i] = heap[child]; + indices[heap[i]] = i; + i = child; + } + heap [i] = x; + indices[x] = i; + } + + bool ok(int n) const { + return n >= 0 && n < (int)indices.size(); } + + public: + Heap(C c) : comp(c) { heap.push(-1); } + + void setBounds (int size) { assert(size >= 0); indices.growTo(size,0); } + void increase (int n) { assert(ok(n)); assert(inHeap(n)); percolateUp(indices[n]); } + bool inHeap (int n) const { assert(ok(n)); return indices[n] != 0; } + int size () const { return heap.size()-1; } + bool empty () const { return size() == 0; } + + + void insert(int n) { + assert(!inHeap(n)); + assert(ok(n)); + indices[n] = heap.size(); + heap.push(n); + percolateUp(indices[n]); + } + + + int getmin() { + //printing heap + if(BEEV::print_sat_varorder) { + // fprintf(stderr, "Vijay: heap before getmin: "); + // for (uint i = 1; i < (uint)heap.size(); i++) + // fprintf(stderr, "%d ", heap[i]); + // fprintf(stderr, "\n"); + } + + int r = heap[1]; + heap[1] = heap.last(); + indices[heap[1]] = 1; + indices[r] = 0; + heap.pop(); + if (heap.size() > 1) + percolateDown(1); + return r; + } + + // fool proof variant of insert/increase + void update (int n) { + //fprintf(stderr, "update heap: "); + //for (uint i = 1; i < (uint)heap.size(); i++) + // fprintf(stderr, "%d ", heap[i]); + //fprintf(stderr, "\n"); + setBounds(n+1); + if (!inHeap(n)) + insert(n); + else { + percolateUp(indices[n]); + percolateDown(indices[n]); + } + } + + + bool heapProperty() { + return heapProperty(1); } + + + bool heapProperty(int i) { + return i >= heap.size() + || ((parent(i) == 0 || !comp(heap[i],heap[parent(i)])) && heapProperty(left(i)) && heapProperty(right(i))); } + + template <class F> void filter(const F& filt) { + int i,j; + for (i = j = 1; i < heap.size(); i++) + if (filt(heap[i])){ + heap[j] = heap[i]; + indices[heap[i]] = j++; + }else + indices[heap[i]] = 0; + + heap.shrink(i - j); + for (int i = heap.size() / 2; i >= 1; i--) + percolateDown(i); + + assert(heapProperty()); + } + +}; + +//================================================================================================= +}; +#endif diff --git a/stp/sat/LICENSE b/stp/sat/LICENSE new file mode 100644 index 00000000..590930bc --- /dev/null +++ b/stp/sat/LICENSE @@ -0,0 +1,20 @@ +MiniSat -- Copyright (c) 2003-2005, Niklas Een, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/stp/sat/Makefile b/stp/sat/Makefile new file mode 100644 index 00000000..8298e05a --- /dev/null +++ b/stp/sat/Makefile @@ -0,0 +1,16 @@ +include ../Makefile.common + +SRCS = Solver.C Simplifier.C +OBJS = $(SRCS:.C=.o) + +libsatsolver.a: $(OBJS) + $(AR) rc $@ $^ + $(RANLIB) $@ + +Solver.o: Solver.C Solver.h Sort.h SolverTypes.h VarOrder.h Global.h Heap.h +Simplifier.o: Simplifier.C Solver.h Sort.h SolverTypes.h VarOrder.h Global.h Heap.h + +clean: + rm -rf *.o *~ *.a depend.mak .#* +depend: + makedepend -- $(CFLAGS) -- $(SRCS) diff --git a/stp/sat/Simplifier.C b/stp/sat/Simplifier.C new file mode 100644 index 00000000..1c192e20 --- /dev/null +++ b/stp/sat/Simplifier.C @@ -0,0 +1,542 @@ +/************************************************************************************[Simplifier.C] +MiniSat -- Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + +#include "Solver.h" + +namespace MINISAT { + +static const int grow = 0; + +//#define WEAKEN +//#define MATING +//#define ASSYMM + +bool Solver::assymmetricBranching(Clause& c) +{ + assert(decisionLevel() == 0); + + //fprintf(stderr, "assymmetric branching on clause: "); printClause(c); fprintf(stderr, "\n"); + if (satisfied(c)){ + //fprintf(stderr, "subsumed.\n"); + return true; } + + int old; + vec<Lit> copy; for (int i = 0; i < c.size(); i++) copy.push(c[i]); + + do { + assert(copy.size() == c.size()); + + old = copy.size(); + + //fprintf(stderr, "checking that clause is normalized\n"); + //for (int i = 0; i < copy.size(); i++) + // assert(value(copy[i]) == l_Undef); + + for (int i = 0; i < copy.size(); i++){ + trail_lim.push(trail.size()); + //fprintf(stderr, " -- trying to delete literal "); printLit(copy[i]); + for (int j = 0; j < copy.size(); j++) + if (j != i) + check(enqueue(~copy[j])); + + if (propagate() != NULL){ + //fprintf(stderr, " succeeded\n"); + cancelUntil(0); + Lit l = copy[i]; + assert(find(copy, l)); + remove(copy, l); + if (!strengthen(c, l)) + return false; + i--; + + if (c.size() == 1) + return propagate() == NULL; + else + assert(qhead == trail.size()); + } + else + //fprintf(stderr, " failed\n"); + + cancelUntil(0); + } + + //} while (false); + } while (copy.size() < old); + + return true; +} + +// Returns FALSE if clause is always satisfied ('out_clause' should not be used). +//bool Solver::merge(const Clause& _ps, const Clause& _qs, Var v, vec<Lit>& out_clause) +bool Solver::merge(const Clause& _ps, const Clause& _qs, Var v, vec<Lit>& out_clause) +{ + stats.merges++; + + bool ps_smallest = _ps.size() < _qs.size(); + const Clause& ps = ps_smallest ? _qs : _ps; + const Clause& qs = ps_smallest ? _ps : _qs; + + for (int i = 0; i < qs.size(); i++){ + if (var(qs[i]) != v){ + for (int j = 0; j < ps.size(); j++) + if (var(ps[j]) == var(qs[i])) { + if (ps[j] == ~qs[i]) + return false; + else + goto next; + } + out_clause.push(qs[i]); + } + next:; + } + + for (int i = 0; i < ps.size(); i++) + if (var(ps[i]) != v) + out_clause.push(ps[i]); + + return true; +} + + +void Solver::gather(vec<Clause*>& clauses) +{ + //fprintf(stderr, "Gathering clauses for backwards subsumption\n"); + int ntouched = 0; + assert(touched.size() == occurs.size()); + clauses.clear(); + for (int i = 0; i < touched.size(); i++) + if (touched[i]){ + const vec<Clause*>& cs = getOccurs(i); + ntouched++; + for (int j = 0; j < cs.size(); j++) + if (cs[j]->mark() == 0){ + clauses.push(cs[j]); + cs[j]->mark(2); + } + touched[i] = 0; + } + + //fprintf(stderr, "Touched variables %d of %d yields %d clauses to check\n", ntouched, touched.size(), clauses.size()); + for (int i = 0; i < clauses.size(); i++) + clauses[i]->mark(0); +} + + +/*_________________________________________________________________________________________________ +| +| subsumes : (_c : ClauseId) (c : Clause&) (_d : ClauseId) (d : Clause&) -> bool +| +| Description: +| Checks if c subsumes d, and at the same time, if c can be used to simplify d by subsumption +| resolution. +| +| Input: +| Indices into the 'clauses' vector _c, _d, and references to the corresponding clauses c, d. +| +| Result: +| lit_Error - No subsumption or simplification +| lit_Undef - Clause c subsumes d +| l - The literal l can be deleted from d +|________________________________________________________________________________________________@*/ +inline Lit Solver::subsumes(const Clause& c, const Clause& d) +{ + stats.subsumption_checks++; + if (d.size() < c.size() || (c.abstraction() & ~d.abstraction()) != 0) + return lit_Error; + + Lit ret = lit_Undef; + + for (int i = 0; i < c.size(); i++) { + // search for c[i] or ~c[i] + for (int j = 0; j < d.size(); j++) + if (c[i] == d[j]) + goto ok; + else if (ret == lit_Undef && c[i] == ~d[j]){ + ret = c[i]; + goto ok; + } + + // did not find it + stats.subsumption_misses++; + return lit_Error; + ok:; + } + + return ret; +} + + +// Backward subsumption + backward subsumption resolution +bool Solver::backwardSubsumptionCheck() +{ + while (subsumption_queue.size() > 0 || qhead < trail.size()){ + + // if propagation queue is non empty, take the first literal and + // create a dummy unit clause + if (qhead < trail.size()){ + Lit l = trail[qhead++]; + (*bwdsub_tmpunit)[0] = l; + assert(bwdsub_tmpunit->mark() == 0); + subsumption_queue.push(bwdsub_tmpunit); + } + Clause& c = *subsumption_queue.last(); subsumption_queue.pop(); + + if (c.mark()) + continue; + + if (c.size() == 1 && !enqueue(c[0])) + return false; + + // (1) find best variable to scan + Var best = var(c[0]); + for (int i = 1; i < c.size(); i++) + if (occurs[var(c[i])].size() < occurs[best].size()) + best = var(c[i]); + + // (2) search all candidates + const vec<Clause*>& cs = getOccurs(best); + + for (int j = 0; j < cs.size(); j++) + if (cs[j] != &c){ + if (cs[j]->mark()) + continue; + if (c.mark()) + break; + + //fprintf(stderr, "backward candidate "); printClause(*cs[j]); fprintf(stderr, "\n"); + Lit l = subsumes(c, *cs[j]); + if (l == lit_Undef){ + //fprintf(stderr, "clause backwards subsumed\n"); + //fprintf(stderr, " >> clause %d: ", cs[j]->mark()); printClause(*cs[j]); fprintf(stderr, "\n"); + //fprintf(stderr, " >> clause %d: ", c.mark()); printClause(c); fprintf(stderr, "\n"); + removeClause(*cs[j], false); + }else if (l != lit_Error){ + //fprintf(stderr, "backwards subsumption resolution\n"); + //fprintf(stderr, " >> clause %d: ", cs[j]->mark()); printClause(*cs[j]); fprintf(stderr, "\n"); + //fprintf(stderr, " >> clause %d: ", c.mark()); printClause(c); fprintf(stderr, "\n"); + + assert(cs[j]->size() > 1); + assert(find(*cs[j], ~l)); + + subsumption_queue.push(cs[j]); + if (!strengthen(*cs[j], ~l)) + return false; + + // did current candidate get deleted from cs? then check candidate at index j again + if (var(l) == best) + j--; + } + } + } + + return true; +} + + +bool Solver::eliminateVar(Var v, bool fail) +{ + assert(hasVarProp(v, p_frozen)); + + vec<Clause*> pos, neg; + const vec<Clause*>& cls = getOccurs(v); + + if (value(v) != l_Undef || cls.size() == 0) + return true; + + //fprintf(stderr, "trying to eliminate var %d\n", v+1); + for (int i = 0; i < cls.size(); i++){ + //fprintf(stderr, "clause: "); printClause(*cls[i]); fprintf(stderr, "\n"); + if (find(*cls[i], Lit(v))) + pos.push(cls[i]); + else{ + assert(find(*cls[i], ~Lit(v))); + neg.push(cls[i]); + } + } + +#ifdef WEAKEN + vec<int> posc(pos.size(), 0); + vec<int> negc(neg.size(), 0); +#endif + // check if number of clauses decreases + int cnt = 0; + vec<Lit> resolvent; + for (int i = 0; i < pos.size(); i++) + for (int j = 0; j < neg.size(); j++){ + resolvent.clear(); + if (merge(*pos[i], *neg[j], v, resolvent)){ + cnt++; +#ifdef WEAKEN + posc[i]++; + negc[j]++; +#endif + } +#ifndef WEAKEN + if (cnt > cls.size() + grow) + return true; +#else +#ifdef MATING + if (cnt > cls.size() + grow) + if (posc[i] > 0) + break; +#endif +#endif + assert(pos.size() <= n_occ[toInt(Lit(v))]); + assert(neg.size() <= n_occ[toInt(~Lit(v))]); + } + +#ifdef WEAKEN +#ifdef MATING + for (int i = 0; i < neg.size(); i++) + if (negc[i] == 0) + for (int j = 0; j < pos.size(); j++){ + resolvent.clear(); + if (merge(*neg[i], *pos[j], v, resolvent)){ + negc[i]++; + break; + } + } +#endif + for (int i = 0; i < pos.size(); i++) + if (posc[i] == 0) + removeClause(*pos[i], false); + + for (int i = 0; i < neg.size(); i++) + if (negc[i] == 0) + removeClause(*neg[i], false); + + if (cnt > cls.size() + grow) + return true; +#endif + //if (pos.size() != n_occ[toInt(Lit(v))]) + // fprintf(stderr, "pos.size() = %d, n_occ[toInt(Lit(v))] = %d\n", pos.size(), n_occ[toInt(Lit(v))]); + assert(pos.size() == n_occ[toInt(Lit(v))]); + //if (neg.size() != n_occ[toInt(~Lit(v))]) + // fprintf(stderr, "neg.size() = %d, n_occ[toInt(Lit(v))] = %d\n", neg.size(), n_occ[toInt(Lit(v))]); + assert(neg.size() == n_occ[toInt(~Lit(v))]); + assert(cnt <= cls.size() + grow); + setVarProp(v, p_decisionvar, false); + + // produce clauses in cross product + int top = clauses.size(); + for (int i = 0; i < pos.size(); i++) + for (int j = 0; j < neg.size(); j++){ + resolvent.clear(); +#ifdef WEAKEN + if (pos[i]->mark() == 1) + break; + if (neg[j]->mark() == 1) + continue; +#endif + + if (merge(*pos[i], *neg[j], v, resolvent)){ + int i, j; + for (i = j = 0; i < resolvent.size(); i++) + if (value(resolvent[i]) == l_True) + goto next; + else if (value(resolvent[i]) == l_Undef) + resolvent[j++] = resolvent[i]; + resolvent.shrink(i - j); + + if (resolvent.size() == 1){ + if (!enqueue(resolvent[0])) + return false; + }else{ + int apa = clauses.size(); + check(newClause(resolvent, false, true)); + assert(apa + 1 == clauses.size()); + } + } + next:; + } + + if (fail){ + fprintf(stderr, "eliminated var %d, %d <= %d\n", v+1, cnt, cls.size()); + fprintf(stderr, "previous clauses:\n"); + for (int i = 0; i < cls.size(); i++){ + printClause(*cls[i]); + fprintf(stderr, "\n"); + } + + fprintf(stderr, "new clauses:\n"); + for (int i = top; i < clauses.size(); i++){ + printClause(*clauses[i]); + fprintf(stderr, "\n"); + } + + assert(0); } + + //fprintf(stderr, "eliminated var %d, %d <= %d\n", v+1, cnt, cls.size()); + //fprintf(stderr, "previous clauses:\n"); + //for (int i = 0; i < cls.size(); i++){ + // printClause(*cls[i]); + // fprintf(stderr, "\n"); + //} + // + //fprintf(stderr, "new clauses:\n"); + //for (int i = top; i < clauses.size(); i++){ + // printClause(*clauses[i]); + // fprintf(stderr, "\n"); + //} + + // delete + store old clauses + eliminated_var.push(v); + eliminated_lim.push(eliminated.size()); + for (int i = 0; i < cls.size(); i++){ + eliminated.push(Clause_new(*cls[i])); + +#ifdef WEAKEN + if (cls[i]->mark() == 0) +#endif + removeClause(*cls[i], false); + + } + + assert(subsumption_queue.size() == 0); + for (int i = top; i < clauses.size(); i++) +#ifdef ASSYMM + if (clauses[i]->mark() == 0) + if (!assymmetricBranching(*clauses[i])) + return false; + else + subsumption_queue.push(clauses[i]); +#else + if (clauses[i]->mark() == 0) + subsumption_queue.push(clauses[i]); +#endif + + return backwardSubsumptionCheck(); +} + + +void Solver::extendModel() +{ + assert(eliminated_var.size() == eliminated_lim.size()); + for (int i = eliminated_var.size()-1; i >= 0; i--){ + Var v = eliminated_var[i]; + Lit l = lit_Undef; + + //fprintf(stderr, "extending var %d\n", v+1); + + for (int j = eliminated_lim[i]; j < (i+1 >= eliminated_lim.size() ? eliminated.size() : eliminated_lim[i+1]); j++){ + assert(j < eliminated.size()); + Clause& c = *eliminated[j]; + + //fprintf(stderr, "checking clause: "); printClause(c); fprintf(stderr, "\n"); + + for (int k = 0; k < c.size(); k++) + if (var(c[k]) == v) + l = c[k]; + else if (value(c[k]) != l_False) + goto next; + + assert(l != lit_Undef); + //fprintf(stderr, "Fixing var %d to %d\n", v+1, !sign(l)); + + assigns[v] = toInt(lbool(!sign(l))); + break; + + next:; + } + + if (value(v) == l_Undef) + assigns[v] = toInt(l_True); + } +} + + +bool Solver::eliminate() +{ + assert(subsumption); + + int cnt = 0; + //fprintf(stderr, "eliminating variables\n"); + +#ifdef INVARIANTS + // check that all clauses are simplified + fprintf(stderr, "Checking that all clauses are normalized prior to variable elimination\n"); + for (int i = 0; i < clauses.size(); i++) + if (clauses[i]->mark() == 0){ + Clause& c = *clauses[i]; + for (int j = 0; j < c.size(); j++) + assert(value(c[j]) == l_Undef); + } + fprintf(stderr, "done.\n"); +#endif + + for (;;){ + gather(subsumption_queue); + + if (subsumption_queue.size() == 0 && heap.size() == 0) + break; + + //fprintf(stderr, "backwards subsumption: %10d\n", subsumption_queue.size()); + if (!backwardSubsumptionCheck()) + return false; + + //fprintf(stderr, "variable elimination: %10d\n", heap.size()); + cnt = 0; + for (;;){ + assert(!heap.empty() || heap.size() == 0); + if (heap.empty()) + break; + + Var elim = heap.getmin(); + + assert(hasVarProp(elim, p_frozen)); + + //for (int i = 1; i < heap.heap.size(); i++) + // assert(heap.comp(elim, heap.heap[i]) || !heap.comp(elim, heap.heap[i])); + + //if (cnt++ % 100 == 0) + // fprintf(stderr, "left %10d\r", heap.size()); + + if (!eliminateVar(elim)) + return false; + } + } +#ifdef INVARIANTS + // check that no more subsumption is possible + fprintf(stderr, "Checking that no more subsumption is possible\n"); + cnt = 0; + for (int i = 0; i < clauses.size(); i++){ + if (cnt++ % 1000 == 0) + fprintf(stderr, "left %10d\r", clauses.size() - i); + for (int j = 0; j < i; j++) + assert(clauses[i]->mark() || + clauses[j]->mark() || + subsumes(*clauses[i], *clauses[j]) == lit_Error); + } + fprintf(stderr, "done.\n"); + + // check that no more elimination is possible + fprintf(stderr, "Checking that no more elimination is possible\n"); + for (int i = 0; i < nVars(); i++){ + if (hasVarProp(i, p_frozen)) + eliminateVar(i, true); + } + fprintf(stderr, "done.\n"); + +#endif + + assert(qhead == trail.size()); + + return true; +} +}; diff --git a/stp/sat/Solver.C b/stp/sat/Solver.C new file mode 100644 index 00000000..0fcb6149 --- /dev/null +++ b/stp/sat/Solver.C @@ -0,0 +1,811 @@ +/****************************************************************************************[Solver.C] +MiniSat -- Copyright (c) 2003-2005, Niklas Een, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + +#include "Solver.h" +#include "Sort.h" +#include <cmath> + +namespace MINISAT { +//================================================================================================= +// Operations on clauses: + + +/*_________________________________________________________________________________________________ +| +| newClause : (ps : const vec<Lit>&) (learnt : bool) -> [void] +| +| Description: +| Allocate and add a new clause to the SAT solvers clause database. +| +| Input: +| ps - The new clause as a vector of literals. +| learnt - Is the clause a learnt clause? For learnt clauses, 'ps[0]' is assumed to be the +| asserting literal. An appropriate 'enqueue()' operation will be performed on this +| literal. One of the watches will always be on this literal, the other will be set to +| the literal with the highest decision level. +| +| Effect: +| Activity heuristics are updated. +|________________________________________________________________________________________________@*/ +bool Solver::newClause(const vec<Lit>& ps_, bool learnt, bool normalized) +{ + vec<Lit> qs; + if (!learnt && !normalized){ + assert(decisionLevel() == 0); + ps_.copyTo(qs); // Make a copy of the input vector. + + // Remove duplicates: + sortUnique(qs); + + // Check if clause is satisfied: + for (int i = 0; i < qs.size()-1; i++){ + if (qs[i] == ~qs[i+1]) + return true; } + for (int i = 0; i < qs.size(); i++){ + if (value(qs[i]) == l_True) + return true; } + + // Remove false literals: + int i, j; + for (i = j = 0; i < qs.size(); i++) + if (value(qs[i]) != l_False) + qs[j++] = qs[i]; + qs.shrink(i - j); + } + const vec<Lit>& ps = learnt || normalized ? ps_ : qs; // 'ps' is now the (possibly) reduced vector of literals. + + if (ps.size() == 0) + return false; + else if (ps.size() == 1){ + assert(decisionLevel() == 0); + return enqueue(ps[0]); + }else{ + // Allocate clause: + Clause* c = Clause_new(ps, learnt); + + if (learnt){ + // Put the second watch on the first literal with highest decision level: + // (requires that this method is called at the level where the clause is asserting!) + int i; + for (i = 1; i < ps.size() && position(trailpos[var(ps[i])]) < trail_lim.last(); i++) + ; + (*c)[1] = ps[i]; + (*c)[i] = ps[1]; + + // Bump, enqueue, store clause: + claBumpActivity(*c); // (newly learnt clauses should be considered active) + check(enqueue((*c)[0], c)); + learnts.push(c); + stats.learnts_literals += c->size(); + }else{ + // Store clause: + clauses.push(c); + stats.clauses_literals += c->size(); + + if (subsumption){ + c->calcAbstraction(); + for (int i = 0; i < c->size(); i++){ + assert(!find(occurs[var((*c)[i])], c)); + occurs[var((*c)[i])].push(c); + n_occ[toInt((*c)[i])]++; + touched[var((*c)[i])] = 1; + + if (heap.inHeap(var((*c)[i]))) + updateHeap(var((*c)[i])); + } + } + + } + // Watch clause: + watches[toInt(~(*c)[0])].push(c); + watches[toInt(~(*c)[1])].push(c); + } + + return true; +} + + +// Disposes a clauses and removes it from watcher lists. NOTE! +// Low-level; does NOT change the 'clauses' and 'learnts' vector. +// +void Solver::removeClause(Clause& c, bool dealloc) +{ + //fprintf(stderr, "delete %d: ", _c); printClause(c); fprintf(stderr, "\n"); + assert(c.mark() == 0); + + if (c.size() > 1){ + assert(find(watches[toInt(~c[0])], &c)); + assert(find(watches[toInt(~c[1])], &c)); + remove(watches[toInt(~c[0])], &c); + remove(watches[toInt(~c[1])], &c); } + + if (c.learnt()) stats.learnts_literals -= c.size(); + else stats.clauses_literals -= c.size(); + + if (subsumption && !c.learnt()){ + for (int i = 0; i < c.size(); i++){ + if (dealloc){ + assert(find(occurs[var(c[i])], &c)); + remove(occurs[var(c[i])], &c); + } + n_occ[toInt(c[i])]--; + updateHeap(var(c[i])); + } + } + + if (dealloc) + xfree(&c); + else + c.mark(1); +} + + +bool Solver::satisfied(Clause& c) const +{ + for (int i = 0; i < c.size(); i++) + if (value(c[i]) == l_True) + return true; + return false; } + + +bool Solver::strengthen(Clause& c, Lit l) +{ + assert(decisionLevel() == 0); + assert(c.size() > 1); + assert(c.mark() == 0); + + assert(toInt(~c[0]) < watches.size()); + assert(toInt(~c[1]) < watches.size()); + + assert(find(watches[toInt(~c[0])], &c)); + assert(find(watches[toInt(~c[1])], &c)); + assert(find(c,l)); + + if (c.learnt()) stats.learnts_literals -= 1; + else stats.clauses_literals -= 1; + + if (c[0] == l || c[1] == l){ + assert(find(watches[toInt(~l)], &c)); + remove(c,l); + remove(watches[toInt(~l)], &c); + if (c.size() > 1){ + assert(!find(watches[toInt(~c[1])], &c)); + watches[toInt(~c[1])].push(&c); } + else { + assert(find(watches[toInt(~c[0])], &c)); + remove(watches[toInt(~c[0])], &c); + removeClause(c, false); + } + } + else + remove(c,l); + + assert(c.size() == 1 || find(watches[toInt(~c[0])], &c)); + assert(c.size() == 1 || find(watches[toInt(~c[1])], &c)); + + if (subsumption){ + assert(find(occurs[var(l)], &c)); + remove(occurs[var(l)], &c); + assert(!find(occurs[var(l)], &c)); + + c.calcAbstraction(); + + n_occ[toInt(l)]--; + updateHeap(var(l)); + } + + return c.size() == 1 ? enqueue(c[0]) : true; +} + + +//================================================================================================= +// Minor methods: + + +// Creates a new SAT variable in the solver. If 'decision_var' is cleared, variable will not be +// used as a decision variable (NOTE! This has effects on the meaning of a SATISFIABLE result). +// +Var Solver::newVar(bool polarity, bool dvar) { + int index; + index = nVars(); + watches .push(); // (list for positive literal) + watches .push(); // (list for negative literal) + reason .push(NULL); + assigns .push(toInt(l_Undef)); + trailpos .push(TrailPos(0,0)); + activity .push(0); + order .newVar(polarity,dvar); + seen .push(0); + touched .push(0); + if (subsumption){ + occurs .push(); + n_occ .push(0); + n_occ .push(0); + heap .setBounds(index+1); + } + return index; } + + +// Returns FALSE if immediate conflict. +bool Solver::assume(Lit p) { + trail_lim.push(trail.size()); + return enqueue(p); } + + +// Revert to the state at given level. +void Solver::cancelUntil(int level) { + if (decisionLevel() > level){ + for (int c = trail.size()-1; c >= trail_lim[level]; c--){ + Var x = var(trail[c]); + assigns[x] = toInt(l_Undef); + reason [x] = NULL; + order.undo(x); } + qhead = trail_lim[level]; + trail.shrink(trail.size() - trail_lim[level]); + trail_lim.shrink(trail_lim.size() - level); + } +} + + +//================================================================================================= +// Major methods: + + +/*_________________________________________________________________________________________________ +| +| analyze : (confl : Clause*) (out_learnt : vec<Lit>&) (out_btlevel : int&) -> [void] +| +| Description: +| Analyze conflict and produce a reason clause. +| +| Pre-conditions: +| * 'out_learnt' is assumed to be cleared. +| * Current decision level must be greater than root level. +| +| Post-conditions: +| * 'out_learnt[0]' is the asserting literal at level 'out_btlevel'. +| +| Effect: +| Will undo part of the trail, upto but not beyond the assumption of the current decision level. +|________________________________________________________________________________________________@*/ +void Solver::analyze(Clause* confl, vec<Lit>& out_learnt, int& out_btlevel) +{ + int pathC = 0; + int btpos = -1; + Lit p = lit_Undef; + + // Generate conflict clause: + // + out_learnt.push(); // (leave room for the asserting literal) + int index = trail.size()-1; + do{ + assert(confl != NULL); // (otherwise should be UIP) + Clause& c = *confl; + + if (c.learnt()) + claBumpActivity(c); + + for (int j = (p == lit_Undef) ? 0 : 1; j < c.size(); j++){ + Lit q = c[j]; + if (!seen[var(q)] && position(trailpos[var(q)]) >= trail_lim[0]){ + varBumpActivity(q); + seen[var(q)] = 1; + if (position(trailpos[var(q)]) >= trail_lim.last()) + pathC++; + else{ + out_learnt.push(q); + btpos = max(btpos, position(trailpos[var(q)])); + } + } + } + + // Select next clause to look at: + while (!seen[var(trail[index--])]); + p = trail[index+1]; + confl = reason[var(p)]; + seen[var(p)] = 0; + pathC--; + + }while (pathC > 0); + out_learnt[0] = ~p; + + // Find correct backtrack level + for (out_btlevel = trail_lim.size()-1; out_btlevel > 0 && trail_lim[out_btlevel-1] > btpos; out_btlevel--) + ; + + int i, j; + if (expensive_ccmin){ + // Simplify conflict clause (a lot): + // + uint min_level = 0; + for (i = 1; i < out_learnt.size(); i++) + min_level |= abstractLevel(trailpos[var(out_learnt[i])]); // (maintain an abstraction of levels involved in conflict) + + out_learnt.copyTo(analyze_toclear); + for (i = j = 1; i < out_learnt.size(); i++) + if (reason[var(out_learnt[i])] == NULL || !analyze_removable(out_learnt[i], min_level)) + out_learnt[j++] = out_learnt[i]; + }else{ + // Simplify conflict clause (a little): + // + out_learnt.copyTo(analyze_toclear); + for (i = j = 1; i < out_learnt.size(); i++){ + Clause& c = *reason[var(out_learnt[i])]; + for (int k = 1; k < c.size(); k++) + if (!seen[var(c[k])] && position(trailpos[var(c[k])]) >= trail_lim[0]){ + out_learnt[j++] = out_learnt[i]; + break; } + } + } + + stats.max_literals += out_learnt.size(); + out_learnt.shrink(i - j); + stats.tot_literals += out_learnt.size(); + + for (int j = 0; j < analyze_toclear.size(); j++) seen[var(analyze_toclear[j])] = 0; // ('seen[]' is now cleared) +} + + +// Check if 'p' can be removed. 'min_level' is used to abort early if visiting literals at a level that cannot be removed. +// +bool Solver::analyze_removable(Lit p, uint min_level) +{ + analyze_stack.clear(); analyze_stack.push(p); + int top = analyze_toclear.size(); + while (analyze_stack.size() > 0){ + assert(reason[var(analyze_stack.last())] != NULL); + Clause& c = *reason[var(analyze_stack.last())]; analyze_stack.pop(); + + for (int i = 1; i < c.size(); i++){ + Lit p = c[i]; + TrailPos tp = trailpos[var(p)]; + if (!seen[var(p)] && position(tp) >= trail_lim[0]){ + if (reason[var(p)] != NULL && (abstractLevel(tp) & min_level) != 0){ + seen[var(p)] = 1; + analyze_stack.push(p); + analyze_toclear.push(p); + }else{ + for (int j = top; j < analyze_toclear.size(); j++) + seen[var(analyze_toclear[j])] = 0; + analyze_toclear.shrink(analyze_toclear.size() - top); + return false; + } + } + } + } + + return true; +} + + +/*_________________________________________________________________________________________________ +| +| analyzeFinal : (p : Lit) -> [void] +| +| Description: +| Specialized analysis procedure to express the final conflict in terms of assumptions. +| Calculates the (possibly empty) set of assumptions that led to the assignment of 'p', and +| stores the result in 'out_conflict'. +|________________________________________________________________________________________________@*/ +void Solver::analyzeFinal(Lit p, vec<Lit>& out_conflict) +{ + out_conflict.clear(); + out_conflict.push(p); + + if (decisionLevel() == 0) + return; + + seen[var(p)] = 1; + + int start = position(trailpos[var(p)]); + for (int i = start; i >= trail_lim[0]; i--){ + Var x = var(trail[i]); + if (seen[x]){ + if (reason[x] == NULL){ + assert(position(trailpos[x]) >= trail_lim[0]); + out_conflict.push(~trail[i]); + }else{ + Clause& c = *reason[x]; + for (int j = 1; j < c.size(); j++) + if (position(trailpos[var(c[j])]) >= trail_lim[0]) + seen[var(c[j])] = 1; + } + seen[x] = 0; + } + } +} + + +/*_________________________________________________________________________________________________ +| +| enqueue : (p : Lit) (from : Clause*) -> [bool] +| +| Description: +| Puts a new fact on the propagation queue as well as immediately updating the variable's value. +| Should a conflict arise, FALSE is returned. +| +| Input: +| p - The fact to enqueue +| from - [Optional] Fact propagated from this (currently) unit clause. Stored in 'reason[]'. +| Default value is NULL (no reason). +| +| Output: +| TRUE if fact was enqueued without conflict, FALSE otherwise. +|________________________________________________________________________________________________@*/ +bool Solver::enqueue(Lit p, Clause* from) +{ + + if (value(p) != l_Undef) + return value(p) != l_False; + else{ + assigns [var(p)] = toInt(lbool(!sign(p))); + trailpos[var(p)] = TrailPos(trail.size(),decisionLevel()); + reason [var(p)] = from; + trail.push(p); + return true; + } +} + + +/*_________________________________________________________________________________________________ +| +| propagate : [void] -> [Clause*] +| +| Description: +| Propagates all enqueued facts. If a conflict arises, the conflicting clause is returned, +| otherwise NULL. +| +| Post-conditions: +| * the propagation queue is empty, even if there was a conflict. +|________________________________________________________________________________________________@*/ +Clause* Solver::propagate() +{ + if (decisionLevel() == 0 && subsumption) + return backwardSubsumptionCheck() ? NULL : propagate_tmpempty; + + Clause* confl = NULL; + //fprintf(stderr, "propagate, qhead = %d, qtail = %d\n", qhead, qtail); + while (qhead < trail.size()){ + stats.propagations++; + simpDB_props--; + + Lit p = trail[qhead++]; // 'p' is enqueued fact to propagate. + vec<Clause*>& ws = watches[toInt(p)]; + Clause **i, **j, **end; + + for (i = j = (Clause**)ws, end = i + ws.size(); i != end;){ + Clause& c = **i++; + + // Make sure the false literal is data[1]: + Lit false_lit = ~p; + if (c[0] == false_lit) + c[0] = c[1], c[1] = false_lit; + + assert(c[1] == false_lit); + + // If 0th watch is true, then clause is already satisfied. + Lit first = c[0]; + if (value(first) == l_True){ + *j++ = &c; + }else{ + // Look for new watch: + for (int k = 2; k < c.size(); k++) + if (value(c[k]) != l_False){ + c[1] = c[k]; c[k] = false_lit; + watches[toInt(~c[1])].push(&c); + goto FoundWatch; } + + // Did not find watch -- clause is unit under assignment: + *j++ = &c; + if (!enqueue(first, &c)){ + confl = &c; + qhead = trail.size(); + // Copy the remaining watches: + while (i < end) + *j++ = *i++; + } + FoundWatch:; + } + } + ws.shrink(i - j); + } + + return confl; +} + + +/*_________________________________________________________________________________________________ +| +| reduceDB : () -> [void] +| +| Description: +| Remove half of the learnt clauses, minus the clauses locked by the current assignment. Locked +| clauses are clauses that are reason to some assignment. Binary clauses are never removed. +|________________________________________________________________________________________________@*/ +struct reduceDB_lt { bool operator () (Clause* x, Clause* y) { return x->size() > 2 && (y->size() == 2 || x->activity() < y->activity()); } }; +void Solver::reduceDB() +{ + int i, j; + double extra_lim = cla_inc / learnts.size(); // Remove any clause below this activity + + sort(learnts, reduceDB_lt()); + for (i = j = 0; i < learnts.size() / 2; i++){ + if (learnts[i]->size() > 2 && !locked(*learnts[i])) + removeClause(*learnts[i]); + else + learnts[j++] = learnts[i]; + } + for (; i < learnts.size(); i++){ + if (learnts[i]->size() > 2 && !locked(*learnts[i]) && learnts[i]->activity() < extra_lim) + removeClause(*learnts[i]); + else + learnts[j++] = learnts[i]; + } + learnts.shrink(i - j); +} + + +/*_________________________________________________________________________________________________ +| +| simplifyDB : [void] -> [bool] +| +| Description: +| Simplify the clause database according to the current top-level assigment. Currently, the only +| thing done here is the removal of satisfied clauses, but more things can be put here. +|________________________________________________________________________________________________@*/ +bool Solver::simplifyDB(bool expensive) +{ + assert(decisionLevel() == 0); + if (!ok || propagate() != NULL) + return ok = false; + + if (nAssigns() == simpDB_assigns || + (!subsumption && simpDB_props > 0)) // (nothing has changed or preformed a simplification too recently) + return true; + + if (subsumption){ + if (expensive && !eliminate()) + return ok = false; + + // Move this cleanup code to its own method ? + int i , j; + vec<Var> dirty; + for (i = 0; i < clauses.size(); i++) + if (clauses[i]->mark() == 1){ + Clause& c = *clauses[i]; + for (int k = 0; k < c.size(); k++) + if (!seen[var(c[k])]){ + seen[var(c[k])] = 1; + dirty.push(var(c[k])); + } + } + + for (i = 0; i < dirty.size(); i++){ + cleanOcc(dirty[i]); + seen[dirty[i]] = 0; + } + + for (i = j = 0; i < clauses.size(); i++) + if (clauses[i]->mark() == 1) + xfree(clauses[i]); + else + clauses[j++] = clauses[i]; + clauses.shrink(i - j); + } + + // Remove satisfied clauses: + for (int type = 0; type < (subsumption ? 1 : 2); type++){ // (only scan learnt clauses if subsumption is on) + vec<Clause*>& cs = type ? learnts : clauses; + int j = 0; + for (int i = 0; i < cs.size(); i++){ + assert(cs[i]->mark() == 0); + if (satisfied(*cs[i])) + removeClause(*cs[i]); + else + cs[j++] = cs[i]; + } + cs.shrink(cs.size()-j); + } + order.cleanup(); + + simpDB_assigns = nAssigns(); + simpDB_props = stats.clauses_literals + stats.learnts_literals; // (shouldn't depend on 'stats' really, but it will do for now) + + return true; +} + + +/*_________________________________________________________________________________________________ +| +| search : (nof_conflicts : int) (nof_learnts : int) (params : const SearchParams&) -> [lbool] +| +| Description: +| Search for a model the specified number of conflicts, keeping the number of learnt clauses +| below the provided limit. NOTE! Use negative value for 'nof_conflicts' or 'nof_learnts' to +| indicate infinity. +| +| Output: +| 'l_True' if a partial assigment that is consistent with respect to the clauseset is found. If +| all variables are decision variables, this means that the clause set is satisfiable. 'l_False' +| if the clause set is unsatisfiable. 'l_Undef' if the bound on number of conflicts is reached. +|________________________________________________________________________________________________@*/ +lbool Solver::search(int nof_conflicts, int nof_learnts) +{ + assert(ok); + int backtrack_level; + int conflictC = 0; + vec<Lit> learnt_clause; + + stats.starts++; + var_decay = 1 / params.var_decay; + cla_decay = 1 / params.clause_decay; + + for (;;){ + Clause* confl = propagate(); + if (confl != NULL){ + // CONFLICT + stats.conflicts++; conflictC++; + if (decisionLevel() == 0) return l_False; + + learnt_clause.clear(); + analyze(confl, learnt_clause, backtrack_level); + cancelUntil(backtrack_level); + newClause(learnt_clause, true); + varDecayActivity(); + claDecayActivity(); + + }else{ + // NO CONFLICT + + if (nof_conflicts >= 0 && conflictC >= nof_conflicts){ + // Reached bound on number of conflicts: + progress_estimate = progressEstimate(); + cancelUntil(0); + return l_Undef; } + + // Simplify the set of problem clauses: + if (decisionLevel() == 0 && !simplifyDB()) + return l_False; + + if (nof_learnts >= 0 && learnts.size()-nAssigns() >= nof_learnts) + // Reduce the set of learnt clauses: + reduceDB(); + + Lit next = lit_Undef; + + if (decisionLevel() < assumptions.size()){ + // Perform user provided assumption: + next = assumptions[decisionLevel()]; + if (value(next) == l_False){ + analyzeFinal(~next, conflict); + return l_False; } + }else{ + // New variable decision: + stats.decisions++; + next = order.select(params.random_var_freq, decisionLevel()); + } + if (next == lit_Undef) + // Model found: + return l_True; + + check(assume(next)); + } + } +} + + +// Return search-space coverage. Not extremely reliable. +// +double Solver::progressEstimate() +{ + double progress = 0; + double F = 1.0 / nVars(); + + for (int i = 0; i <= decisionLevel(); i++){ + int beg = i == 0 ? 0 : trail_lim[i - 1]; + int end = i == decisionLevel() ? trail.size() : trail_lim[i]; + progress += pow(F, i) * (end - beg); + } + + return progress / nVars(); +} + + +// Divide all variable activities by 1e100. +// +void Solver::varRescaleActivity() +{ + for (int i = 0; i < nVars(); i++) + activity[i] *= 1e-100; + var_inc *= 1e-100; +} + + +// Divide all constraint activities by 1e100. +// +void Solver::claRescaleActivity() +{ + for (int i = 0; i < learnts.size(); i++) + learnts[i]->activity() *= 1e-20; + cla_inc *= 1e-20; +} + + +/*_________________________________________________________________________________________________ +| +| solve : (assumps : const vec<Lit>&) -> [bool] +| +| Description: +| Top-level solve. +|________________________________________________________________________________________________@*/ +bool Solver::solve(const vec<Lit>& assumps) +{ + model.clear(); + conflict.clear(); + + if (!simplifyDB(true)) return false; + + + double nof_conflicts = params.restart_first; + double nof_learnts = nClauses() * params.learntsize_factor; + lbool status = l_Undef; + assumps.copyTo(assumptions); + + if (verbosity >= 1){ + reportf("==================================[MINISAT]====================================\n"); + reportf("| Conflicts | ORIGINAL | LEARNT | Progress |\n"); + reportf("| | Vars Clauses Literals | Limit Clauses Lit/Cl | |\n"); + reportf("===============================================================================\n"); + } + + // Search: + while (status == l_Undef){ + if (verbosity >= 1) + //reportf("| %9d | %7d %8d | %7d %7d %8d %7.1f | %6.3f %% |\n", (int)stats.conflicts, nClauses(), (int)stats.clauses_literals, (int)nof_learnts, nLearnts(), (int)stats.learnts_literals, (double)stats.learnts_literals/nLearnts(), progress_estimate*100); + reportf("| %9d | %7d %8d %8d | %8d %8d %6.0f | %6.3f %% |\n", (int)stats.conflicts, order.size(), nClauses(), (int)stats.clauses_literals, (int)nof_learnts, nLearnts(), (double)stats.learnts_literals/nLearnts(), progress_estimate*100); + status = search((int)nof_conflicts, (int)nof_learnts); + nof_conflicts *= params.restart_inc; + nof_learnts *= params.learntsize_inc; + } + + if (verbosity >= 1) + reportf("==============================================================================\n"); + + if (status == l_True){ + // Copy model: + extendModel(); +#if 1 + //fprintf(stderr, "Verifying model.\n"); + for (int i = 0; i < clauses.size(); i++) + assert(satisfied(*clauses[i])); + for (int i = 0; i < eliminated.size(); i++) + assert(satisfied(*eliminated[i])); +#endif + model.growTo(nVars()); + for (int i = 0; i < nVars(); i++) model[i] = value(i); + }else{ + assert(status == l_False); + if (conflict.size() == 0) + ok = false; + } + + cancelUntil(0); + return status == l_True; +} +};//end of MINISAT namespace diff --git a/stp/sat/Solver.h b/stp/sat/Solver.h new file mode 100644 index 00000000..829194cc --- /dev/null +++ b/stp/sat/Solver.h @@ -0,0 +1,359 @@ +/****************************************************************************************[Solver.h] +MiniSat -- Copyright (c) 2003-2005, Niklas Een, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + +#ifndef Solver_h +#define Solver_h + +#include "SolverTypes.h" +#include "VarOrder.h" + +namespace MINISAT { +// Redfine if you want output to go somewhere else: +#define reportf(format, args...) ( printf(format , ## args), fflush(stdout) ) + + +//================================================================================================= +// Solver -- the main class: +struct SolverStats { + int64 starts, decisions, propagations, conflicts; + int64 clauses_literals, learnts_literals, max_literals, tot_literals; + int64 subsumption_checks, subsumption_misses, merges; + SolverStats() : + starts(0), decisions(0), propagations(0), conflicts(0) + , clauses_literals(0), learnts_literals(0), max_literals(0), tot_literals(0) + , subsumption_checks(0), subsumption_misses(0), merges(0) + { } +}; + + +struct SearchParams { + double var_decay, clause_decay, random_var_freq; + double restart_inc, learntsize_inc, learntsize_factor; + int restart_first; + + SearchParams(double v = 0.95, double c = 0.999, double r = 0.02, + double ri = 1.5, double li = 1.1, double lf = (double)1/(double)3, + int rf = 100) : + var_decay(v), clause_decay(c), random_var_freq(r), + restart_inc(ri), learntsize_inc(li), learntsize_factor(lf), + restart_first(rf) { } +}; + + struct ElimLt { + const vec<int>& n_occ; + + ElimLt(const vec<int>& no) : n_occ(no) {} + int cost (Var x) const { return n_occ[toInt(Lit(x))] * n_occ[toInt(~Lit(x))]; } + bool operator()(Var x, Var y) const { return cost(x) < cost(y); } + }; + +class Solver { +protected: + // Solver state: + bool ok; // If FALSE,the constraints are already unsatisfiable. + // No part of solver state may be used! + vec<Clause*> clauses; // List of problem clauses. + vec<Clause*> learnts; // List of learnt clauses. + int n_bin_clauses; // Keep track of number of binary clauses "inlined" into the watcher lists (we do this primarily to get identical behavior to the version without the binary clauses trick). + double cla_inc; // Amount to bump next clause with. + double cla_decay; // INVERSE decay factor for clause activity: stores 1/decay. + + vec<double> activity; // A heuristic measurement of the activity of a variable. + double var_inc; // Amount to bump next variable with. + double var_decay; // INVERSE decay factor for variable activity: stores 1/decay. Use negative value for static variable order. + VarOrder order; // Keeps track of the decision variable order. + vec<char> properties; // TODO: describe!!! + + vec<vec<Clause*> > watches; // 'watches[lit]' is a list of constraints watching 'lit' (will go there if literal becomes true). + vec<char> assigns; // The current assignments (lbool:s stored as char:s). + vec<Lit> trail; // Assignment stack; stores all assigments made in the order they were made. + vec<int> trail_lim; // Separator indices for different decision levels in 'trail'. + vec<Clause*> reason; // 'reason[var]' is the clause that implied the variables current value, or 'NULL' if none. + vec<TrailPos> trailpos; // 'trailpos[var]' contains the position in the trail at wich the assigment was made. + int qhead; // Head of queue (as index into the trail -- no more explicit propagation queue in MiniSat). + int simpDB_assigns; // Number of top-level assignments since last execution of 'simplifyDB()'. + int64 simpDB_props; // Remaining number of propagations that must be made before next execution of 'simplifyDB()'. + vec<Lit> assumptions; // Current set of assumptions provided to solve by the user. + + bool subsumption; + vec<char> touched; + vec<vec<Clause*> > occurs; + vec<int> n_occ; + Heap<ElimLt> heap; + vec<Clause*> subsumption_queue; + + vec<Clause*> eliminated; + vec<int> eliminated_lim; + vec<Var> eliminated_var; + + // Temporaries (to reduce allocation overhead). Each variable is prefixed by the method in which it is + // used, exept 'seen' wich is used in several places. + // + vec<char> seen; + vec<Lit> analyze_stack; + vec<Lit> analyze_toclear; + Clause* propagate_tmpempty; + Clause* propagate_tmpbin; + Clause* analyze_tmpbin; + Clause* bwdsub_tmpunit; + + vec<Lit> addBinary_tmp; + vec<Lit> addTernary_tmp; + + // Main internal methods: + // + bool assume (Lit p); + void cancelUntil (int level); + void record (const vec<Lit>& clause); + + void analyze (Clause* confl, vec<Lit>& out_learnt, int& out_btlevel); // (bt = backtrack) + bool analyze_removable(Lit p, uint min_level); // (helper method for 'analyze()') + void analyzeFinal (Lit p, vec<Lit>& out_conflict); + bool enqueue (Lit fact, Clause* from = NULL); + Clause* propagate (); + void reduceDB (); + Lit pickBranchLit (); + lbool search (int nof_conflicts, int nof_learnts); + double progressEstimate (); + + // Variable properties: + void setVarProp (Var v, uint prop, bool b) { order.setVarProp(v, prop, b); } + bool hasVarProp (Var v, uint prop) const { return order.hasVarProp(v, prop); } + void updateHeap (Var v) { + if (hasVarProp(v, p_frozen)) + heap.update(v); } + + // Simplification methods: + // + void cleanOcc (Var v) { + assert(subsumption); + vec<Clause*>& occ = occurs[v]; + int i, j; + for (i = j = 0; i < occ.size(); i++) + if (occ[i]->mark() != 1) + occ[j++] = occ[i]; + occ.shrink(i - j); + } + + vec<Clause*>& getOccurs (Var x) { cleanOcc(x); return occurs[x]; } + void gather (vec<Clause*>& clauses); + Lit subsumes (const Clause& c, const Clause& d); + bool assymmetricBranching (Clause& c); + bool merge (const Clause& _ps, const Clause& _qs, Var v, vec<Lit>& out_clause); + + bool backwardSubsumptionCheck (); + bool eliminateVar (Var v, bool fail = false); + bool eliminate (); + void extendModel (); + + // Activity: + // + void varBumpActivity(Lit p) { + if (var_decay < 0) return; // (negative decay means static variable order -- don't bump) + if ( (activity[var(p)] += var_inc) > 1e100 ) varRescaleActivity(); + order.update(var(p)); } + void varDecayActivity () { if (var_decay >= 0) var_inc *= var_decay; } + void varRescaleActivity(); + void claDecayActivity () { cla_inc *= cla_decay; } + void claRescaleActivity(); + + // Operations on clauses: + // + bool newClause(const vec<Lit>& ps, bool learnt = false, bool normalized = false); + void claBumpActivity (Clause& c) { if ( (c.activity() += cla_inc) > 1e20 ) claRescaleActivity(); } + bool locked (const Clause& c) const { return reason[var(c[0])] == &c; } + bool satisfied (Clause& c) const; + bool strengthen (Clause& c, Lit l); + void removeClause (Clause& c, bool dealloc = true); + + int decisionLevel() const { return trail_lim.size(); } + +public: + Solver() : ok (true) + , n_bin_clauses (0) + , cla_inc (1) + , cla_decay (1) + , var_inc (1) + , var_decay (1) + , order (assigns, activity) + , qhead (0) + , simpDB_assigns (-1) + , simpDB_props (0) + , subsumption (true) + , heap (n_occ) + , params () + , expensive_ccmin (true) + , verbosity (0) + , progress_estimate(0) + { + vec<Lit> dummy(2,lit_Undef); + propagate_tmpbin = Clause_new(dummy); + analyze_tmpbin = Clause_new(dummy); + dummy.pop(); + bwdsub_tmpunit = Clause_new(dummy); + dummy.pop(); + propagate_tmpempty = Clause_new(dummy); + addBinary_tmp .growTo(2); + addTernary_tmp.growTo(3); + } + + ~Solver() { + xfree(propagate_tmpbin); + xfree(analyze_tmpbin); + xfree(bwdsub_tmpunit); + xfree(propagate_tmpempty); + for (int i = 0; i < eliminated.size(); i++) xfree(eliminated[i]); + for (int i = 0; i < learnts.size(); i++) xfree(learnts[i]); + for (int i = 0; i < clauses.size(); i++) xfree(clauses[i]); } + + // Helpers: (semi-internal) + // + lbool value(Var x) const { return toLbool(assigns[x]); } + lbool value(Lit p) const { return sign(p) ? ~toLbool(assigns[var(p)]) : toLbool(assigns[var(p)]); } + + int nAssigns() { return trail.size(); } + int nClauses() { return clauses.size(); } + int nLearnts() { return learnts.size(); } + int nConflicts() { return (int)stats.conflicts; } + + // Statistics: (read-only member variable) + // + SolverStats stats; + + // Mode of operation: + // + SearchParams params; // Restart frequency etc. + bool expensive_ccmin; // Controls conflict clause minimization. TRUE by default. + int verbosity; // Verbosity level. 0=silent, 1=some progress report, 2=everything + + // Problem specification: + // + Var newVar (bool polarity = true, bool dvar = true); + int nVars () { return assigns.size(); } + bool addUnit (Lit p) { return ok && (ok = enqueue(p)); } + bool addBinary (Lit p, Lit q) { addBinary_tmp [0] = p; addBinary_tmp [1] = q; return addClause(addBinary_tmp); } + bool addTernary(Lit p, Lit q, Lit r) { addTernary_tmp[0] = p; addTernary_tmp[1] = q; addTernary_tmp[2] = r; return addClause(addTernary_tmp); } + bool addClause (const vec<Lit>& ps) { if (ok && !newClause(ps)) ok = false; return ok; } + + // Variable mode: + // + void freezeVar (Var v) { setVarProp(v, p_frozen, true); updateHeap(v); } + + // Solving: + // + bool okay () { return ok; } // FALSE means solver is in a conflicting state + bool simplifyDB (bool expensive = true); + bool solve (const vec<Lit>& assumps); + bool solve () { vec<Lit> tmp; return solve(tmp); } + void turnOffSubsumption() { + subsumption = false; + occurs.clear(true); + n_occ.clear(true); + } + + double progress_estimate; // Set by 'search()'. + vec<lbool> model; // If problem is satisfiable, this vector contains the model (if any). + vec<Lit> conflict; // If problem is unsatisfiable (possibly under assumptions), this vector represent the conflict clause expressed in the assumptions. + + double returnActivity(int i) { return activity[i];} + void updateInitialActivity(int i, double act) {activity[i] = act; order.heap.update(i);} +}; + + +//================================================================================================= +// Debug: + + +#define L_LIT "%sx%d" +#define L_lit(p) sign(p)?"~":"", var(p) + +// Just like 'assert()' but expression will be evaluated in the release version as well. +inline void check(bool expr) { assert(expr); } + +static void printLit(Lit l) +{ + fprintf(stderr, "%s%d", sign(l) ? "-" : "", var(l)+1); +} + +template<class C> +static void printClause(const C& c) +{ + for (int i = 0; i < c.size(); i++){ + printLit(c[i]); + fprintf(stderr, " "); + } +} + +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +#ifdef _MSC_VER + +#include <ctime> + +static inline double cpuTime(void) { + return (double)clock() / CLOCKS_PER_SEC; } + +static inline int64 memUsed() { + return 0; } + +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +#else + +#include <sys/time.h> +#include <sys/resource.h> + +static inline double cpuTime(void) { + struct rusage ru; + getrusage(RUSAGE_SELF, &ru); + return (double)ru.ru_utime.tv_sec + (double)ru.ru_utime.tv_usec / 1000000; } + +#if defined(__linux__) || defined(__CYGWIN__) +static inline int memReadStat(int field) +{ + char name[256]; + pid_t pid = getpid(); + sprintf(name, "/proc/%d/statm", pid); + FILE* in = fopen(name, "rb"); + if (in == NULL) return 0; + int value; + for (; field >= 0; field--) { + int res = fscanf(in, "%d", &value); + (void) res; + } + fclose(in); + return value; +} + +static inline int64 memUsed() { return (int64)memReadStat(0) * (int64)getpagesize(); } +#else +// use this by default. Mac OS X (Darwin) does not define an os type +//defined(__FreeBSD__) + +static inline int64 memUsed(void) { + struct rusage ru; + getrusage(RUSAGE_SELF, &ru); + return ru.ru_maxrss*1024; } + +#endif + +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +#endif + +//================================================================================================= +}; +#endif diff --git a/stp/sat/SolverTypes.h b/stp/sat/SolverTypes.h new file mode 100644 index 00000000..2b98b8ca --- /dev/null +++ b/stp/sat/SolverTypes.h @@ -0,0 +1,127 @@ +/***********************************************************************************[SolverTypes.h] +MiniSat -- Copyright (c) 2003-2005, Niklas Een, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + + +#ifndef SolverTypes_h +#define SolverTypes_h + +#include "Global.h" + +namespace MINISAT { + +//================================================================================================= +// Variables, literals, clause IDs: + + +// NOTE! Variables are just integers. No abstraction here. They should be chosen from 0..N, +// so that they can be used as array indices. + +typedef int Var; +#define var_Undef (-1) + + +struct Lit { + int x; + + Lit() : x(2*var_Undef) { } // (lit_Undef) + explicit Lit(Var var, bool sign = false) : x((var+var) + (int)sign) { } +}; + +// Don't use these for constructing/deconstructing literals. Use the normal constructors instead. +inline int toInt (Lit p) { return p.x; } // A "toInt" method that guarantees small, positive integers suitable for array indexing. +inline Lit toLit (int i) { Lit p; p.x = i; return p; } // Inverse of 'toInt()' + +inline Lit operator ~(Lit p) { Lit q; q.x = p.x ^ 1; return q; } +inline bool sign (Lit p) { return p.x & 1; } +inline int var (Lit p) { return p.x >> 1; } +inline Lit unsign (Lit p) { Lit q; q.x = p.x & ~1; return q; } +inline Lit id (Lit p, bool sgn) { Lit q; q.x = p.x ^ (int)sgn; return q; } + +inline bool operator == (Lit p, Lit q) { return toInt(p) == toInt(q); } +inline bool operator != (Lit p, Lit q) { return toInt(p) != toInt(q); } +inline bool operator < (Lit p, Lit q) { return toInt(p) < toInt(q); } // '<' guarantees that p, ~p are adjacent in the ordering. + + +const Lit lit_Undef(var_Undef, false); // }- Useful special constants. +const Lit lit_Error(var_Undef, true ); // } + + +//================================================================================================= +// Clause -- a simple class for representing a clause: + +class Clause { + uint size_etc; + union { float act; uint abst; } apa; + Lit data[0]; +public: + // NOTE: This constructor cannot be used directly (doesn't allocate enough memory). + template<class V> + Clause(const V& ps, bool learnt) { + size_etc = (ps.size() << 3) | (uint)learnt; + for (int i = 0; i < ps.size(); i++) data[i] = ps[i]; + if (learnt) apa.act = 0; else apa.abst = 0; } + + // -- use this function instead: + template<class V> + friend Clause* Clause_new(const V& ps, bool learnt = false) { + assert(sizeof(Lit) == sizeof(uint)); + assert(sizeof(float) == sizeof(uint)); + void* mem = xmalloc<char>(sizeof(Clause) + sizeof(uint)*(ps.size())); + return new (mem) Clause(ps, learnt); } + + int size () const { return size_etc >> 3; } + void shrink (int i) { assert(i <= size()); size_etc = (((size_etc >> 3) - i) << 3) | (size_etc & 7); } + void pop () { shrink(1); } + bool learnt () const { return size_etc & 1; } + uint mark () const { return (size_etc >> 1) & 3; } + void mark (uint m) { size_etc = (size_etc & ~6) | ((m & 3) << 1); } + Lit operator [] (int i) const { return data[i]; } + Lit& operator [] (int i) { return data[i]; } + + float& activity () { return apa.act; } + + uint abstraction () const { return apa.abst; } + + void calcAbstraction() { + uint abstraction = 0; + for (int i = 0; i < size(); i++) + abstraction |= 1 << (var(data[i]) & 31); + apa.abst = abstraction; } +}; + + +//================================================================================================= +// TrailPos -- Stores an index into the trail as well as an approximation of a level. This data +// is recorded for each assigment. (Replaces the old level information) + + +class TrailPos { + int tp; + public: + explicit TrailPos(int index, int level) : tp( (index << 5) + (level & 31) ) { } + + friend int abstractLevel(const TrailPos& p) { return 1 << (p.tp & 31); } + friend int position (const TrailPos& p) { return p.tp >> 5; } + + bool operator == (TrailPos other) const { return tp == other.tp; } + bool operator < (TrailPos other) const { return tp < other.tp; } +}; + +}; +#endif diff --git a/stp/sat/Sort.h b/stp/sat/Sort.h new file mode 100644 index 00000000..a7011edb --- /dev/null +++ b/stp/sat/Sort.h @@ -0,0 +1,133 @@ +/******************************************************************************************[Sort.h] +MiniSat -- Copyright (c) 2003-2005, Niklas Een, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + +#ifndef Sort_h +#define Sort_h + + +namespace MINISAT { +//================================================================================================= + + +template<class T> +struct LessThan_default { + bool operator () (T x, T y) { return x < y; } +}; + + +//================================================================================================= + + +template <class T, class LessThan> +void selectionSort(T* array, int size, LessThan lt) +{ + int i, j, best_i; + T tmp; + + for (i = 0; i < size-1; i++){ + best_i = i; + for (j = i+1; j < size; j++){ + if (lt(array[j], array[best_i])) + best_i = j; + } + tmp = array[i]; array[i] = array[best_i]; array[best_i] = tmp; + } +} +template <class T> static inline void selectionSort(T* array, int size) { + selectionSort(array, size, LessThan_default<T>()); } + + +template <class T, class LessThan> +void sort(T* array, int size, LessThan lt, double& seed) +{ + if (size <= 15) + selectionSort(array, size, lt); + + else{ + T pivot = array[irand(seed, size)]; + T tmp; + int i = -1; + int j = size; + + for(;;){ + do i++; while(lt(array[i], pivot)); + do j--; while(lt(pivot, array[j])); + + if (i >= j) break; + + tmp = array[i]; array[i] = array[j]; array[j] = tmp; + } + + sort(array , i , lt, seed); + sort(&array[i], size-i, lt, seed); + } +} +template <class T, class LessThan> void sort(T* array, int size, LessThan lt) { + double seed = 91648253; sort(array, size, lt, seed); } +template <class T> static inline void sort(T* array, int size) { + sort(array, size, LessThan_default<T>()); } + + +template <class T, class LessThan> +void sortUnique(T* array, int& size, LessThan lt) +{ + int i, j; + T last; + + if (size == 0) return; + + sort(array, size, lt); + + i = 1; + last = array[0]; + for (j = 1; j < size; j++){ + if (lt(last, array[j])){ + last = array[i] = array[j]; + i++; } + } + + size = i; +} +template <class T> static inline void sortUnique(T* array, int& size) { + sortUnique(array, size, LessThan_default<T>()); } + + +//================================================================================================= +// For 'vec's: + + +template <class T, class LessThan> void sort(vec<T>& v, LessThan lt) { + sort((T*)v, v.size(), lt); } +template <class T> void sort(vec<T>& v) { + sort(v, LessThan_default<T>()); } + + +template <class T, class LessThan> void sortUnique(vec<T>& v, LessThan lt) { + int size = v.size(); + T* data = v.release(); + sortUnique(data, size, lt); + v.~vec<T>(); + new (&v) vec<T>(data, size); } +template <class T> void sortUnique(vec<T>& v) { + sortUnique(v, LessThan_default<T>()); } + + +//================================================================================================= +}; +#endif diff --git a/stp/sat/VarOrder.h b/stp/sat/VarOrder.h new file mode 100644 index 00000000..6ad1bfb1 --- /dev/null +++ b/stp/sat/VarOrder.h @@ -0,0 +1,146 @@ +/**************************************************************************************[VarOrder.h] +MiniSat -- Copyright (c) 2003-2005, Niklas Een, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + +#ifndef VarOrder_h +#define VarOrder_h + +#include "SolverTypes.h" +#include "Solver.h" +#include "Heap.h" +#include "../AST/ASTUtil.h" + +namespace MINISAT { + //================================================================================================= + + struct VarOrder_lt { + const vec<double>& activity; + bool operator () (Var x, Var y) { return activity[x] > activity[y]; } + VarOrder_lt(const vec<double>& act) : activity(act) { } + }; + + + enum { p_decisionvar = 0, p_polarity = 1, p_frozen = 2, p_dontcare = 3 }; + + + class VarOrder { + const vec<char>& assigns; // var->val. Pointer to external assignment table. + const vec<double>& activity; // var->act. Pointer to external activity table. + vec<char> properties; + //Heap<VarOrder_lt> heap; + //double random_seed; // For the internal random number generator + + friend class VarFilter; + public: + //FIXME: Vijay: delete after experiments + Heap<VarOrder_lt> heap; + double random_seed; // For the internal random number generator + //FIXME ENDS HERE + + VarOrder(const vec<char>& ass, const vec<double>& act) : + assigns(ass), activity(act), heap(VarOrder_lt(act)), random_seed(2007) + //assigns(ass), activity(act), heap(VarOrder_lt(act)) + { } + + int size () { return heap.size(); } + void setVarProp (Var v, uint prop, bool b) { properties[v] = (properties[v] & ~(1 << prop)) | (b << prop); } + bool hasVarProp (Var v, uint prop) const { return properties[v] & (1 << prop); } + inline void cleanup (); + + inline void newVar(bool polarity, bool dvar); + inline void update(Var x); // Called when variable increased in activity. + inline void undo(Var x); // Called when variable is unassigned and may be selected again. + //Selects a new, unassigned variable (or 'var_Undef' if none exists). + inline Lit select(double random_freq =.0, int decision_level = 0); + }; + + + struct VarFilter { + const VarOrder& o; + VarFilter(const VarOrder& _o) : o(_o) {} + bool operator()(Var v) const { return toLbool(o.assigns[v]) == l_Undef && o.hasVarProp(v, p_decisionvar); } + //bool operator()(Var v) const { return toLbool(o.assigns[v]) == l_Undef; } + }; + + void VarOrder::cleanup() + { + VarFilter f(*this); + heap.filter(f); + } + + void VarOrder::newVar(bool polarity, bool dvar) + { + Var v = assigns.size()-1; + heap.setBounds(v+1); + properties.push(0); + setVarProp(v, p_decisionvar, dvar); + setVarProp(v, p_polarity, polarity); + undo(v); + } + + + void VarOrder::update(Var x) + { + if (heap.inHeap(x)) + heap.increase(x); + } + + + void VarOrder::undo(Var x) + { + if (!heap.inHeap(x) && hasVarProp(x, p_decisionvar)) + heap.insert(x); + } + + + Lit VarOrder::select(double random_var_freq, int decision_level) + { + Var next = var_Undef; + + if (drand(random_seed) < random_var_freq && !heap.empty()) + next = irand(random_seed,assigns.size()); + + // Activity based decision: + while (next == var_Undef || toLbool(assigns[next]) != l_Undef || !hasVarProp(next, p_decisionvar)) + if (heap.empty()){ + next = var_Undef; + break; + }else + next = heap.getmin(); + + //printing + if(BEEV::print_sat_varorder) { + if (next != var_Undef) { + BEEV::Convert_MINISATVar_To_ASTNode_Print(next, + decision_level, + hasVarProp(next, p_polarity)); + // fprintf(stderr,"var = %d, prop = %d, decision = %d, polarity = %d, frozen = %d\n", + // next+1, properties[next], hasVarProp(next, p_decisionvar), + // hasVarProp(next, p_polarity), hasVarProp(next, p_frozen)); + } + else + fprintf(stderr, "var = undef\n"); + } + + return next == var_Undef ? lit_Undef : Lit(next, hasVarProp(next, p_polarity)); + } + + + //================================================================================================= +}; +#endif diff --git a/stp/simplifier/Makefile b/stp/simplifier/Makefile new file mode 100644 index 00000000..aba07e1b --- /dev/null +++ b/stp/simplifier/Makefile @@ -0,0 +1,11 @@ +include ../Makefile.common + +SRCS = simplifier.cpp bvsolver.cpp +OBJS = $(SRCS:.cpp=.o) + +libsimplifier.a: $(OBJS) + $(AR) rc $@ $^ + $(RANLIB) $@ + +clean: + rm -rf *.o *~ *.a .#* diff --git a/stp/simplifier/bvsolver.cpp b/stp/simplifier/bvsolver.cpp new file mode 100644 index 00000000..1c08f30b --- /dev/null +++ b/stp/simplifier/bvsolver.cpp @@ -0,0 +1,714 @@ +/******************************************************************** + * AUTHORS: Vijay Ganesh, David L. Dill + * + * BEGIN DATE: November, 2005 + * + * LICENSE: Please view LICENSE file in the home dir of this Program + ********************************************************************/ +// -*- c++ -*- + +#include "../AST/AST.h" +#include "../AST/ASTUtil.h" +#include "bvsolver.h" + + //This file contains the implementation of member functions of + //bvsolver class, which represents the bitvector arithmetic linear + //solver. Please also refer the STP's CAV 2007 paper for the + //complete description of the linear solver algorithm + // + //The bitvector solver is a partial solver, i.e. it does not solve + //for all variables in the system of equations. it is + //best-effort. it relies on the SAT solver to be complete. + // + //The BVSolver assumes that the input equations are normalized, and + //have liketerms combined etc. + // + //0. Traverse top-down over the input DAG, looking for a conjunction + //0. of equations. if you find one, then for each equation in the + //0. conjunction, do the following steps. + // + //1. check for Linearity of the input equation + // + //2. Solve for a "chosen" variable. The variable should occur + //2. exactly once and must have an odd coeff. Refer STP's CAV 2007 + //2. paper for actual solving procedure + // + //4. Outside the solver, Substitute and Re-normalize the input DAG +namespace BEEV { + //check the solver map for 'key'. If key is present, then return the + //value by reference in the argument 'output' + bool BVSolver::CheckAlreadySolvedMap(const ASTNode& key, ASTNode& output) { + ASTNodeMap::iterator it; + if((it = FormulasAlreadySolvedMap.find(key)) != FormulasAlreadySolvedMap.end()) { + output = it->second; + return true; + } + return false; + } //CheckAlreadySolvedMap() + + void BVSolver::UpdateAlreadySolvedMap(const ASTNode& key, const ASTNode& value) { + FormulasAlreadySolvedMap[key] = value; + } //end of UpdateAlreadySolvedMap() + + //FIXME This is doing way more arithmetic than it needs to. + //accepts an even number "in", and splits it into an odd number and + //a power of 2. i.e " in = b.(2^k) ". returns the odd number, and + //the power of two by reference + ASTNode BVSolver::SplitEven_into_Oddnum_PowerOf2(const ASTNode& in, + unsigned int& number_shifts) { + if(BVCONST != in.GetKind() || _bm->BVConstIsOdd(in)) { + FatalError("BVSolver:SplitNum_Odd_PowerOf2: input must be a BVCONST and even\n",in); + } + + unsigned int len = in.GetValueWidth(); + ASTNode zero = _bm->CreateZeroConst(len); + ASTNode two = _bm->CreateTwoConst(len); + ASTNode div_by_2 = in; + ASTNode mod_by_2 = + _bm->BVConstEvaluator(_bm->CreateTerm(BVMOD,len,div_by_2,two)); + while(mod_by_2 == zero) { + div_by_2 = + _bm->BVConstEvaluator(_bm->CreateTerm(BVDIV,len,div_by_2,two)); + number_shifts++; + mod_by_2 = + _bm->BVConstEvaluator(_bm->CreateTerm(BVMOD,len,div_by_2,two)); + } + return div_by_2; + } //end of SplitEven_into_Oddnum_PowerOf2() + + //Checks if there are any ARRAYREADS in the term, after the + //alreadyseenmap is cleared, i.e. traversing a new term altogether + bool BVSolver::CheckForArrayReads_TopLevel(const ASTNode& term) { + TermsAlreadySeenMap.clear(); + return CheckForArrayReads(term); + } + + //Checks if there are any ARRAYREADS in the term + bool BVSolver::CheckForArrayReads(const ASTNode& term) { + ASTNode a = term; + ASTNodeMap::iterator it; + if((it = TermsAlreadySeenMap.find(term)) != TermsAlreadySeenMap.end()) { + //if the term has been seen, then simply return true, else + //return false + if(ASTTrue == (it->second)) { + return true; + } + else { + return false; + } + } + + switch(term.GetKind()) { + case READ: + //an array read has been seen. Make an entry in the map and + //return true + TermsAlreadySeenMap[term] = ASTTrue; + return true; + default: { + ASTVec c = term.GetChildren(); + for(ASTVec::iterator it=c.begin(),itend=c.end();it!=itend;it++) { + if(CheckForArrayReads(*it)) { + return true; + } + } + break; + } + } + + //If control is here, then it means that no arrayread was seen for + //the input 'term'. Make an entry in the map with the term as key + //and FALSE as value. + TermsAlreadySeenMap[term] = ASTFalse; + return false; + } //end of CheckForArrayReads() + + //check the solver map for 'key'. If key is present, then return the + //value by reference in the argument 'output' + bool BeevMgr::CheckSolverMap(const ASTNode& key, ASTNode& output) { + ASTNodeMap::iterator it; + if((it = SolverMap.find(key)) != SolverMap.end()) { + output = it->second; + return true; + } + return false; + } //end of CheckSolverMap() + + bool BeevMgr::CheckSolverMap(const ASTNode& key) { + if(SolverMap.find(key) != SolverMap.end()) + return true; + else + return false; + } //end of CheckSolverMap() + + //update solvermap with (key,value) pair + bool BeevMgr::UpdateSolverMap(const ASTNode& key, const ASTNode& value) { + ASTNode var = (BVEXTRACT == key.GetKind()) ? key[0] : key; + if(!CheckSolverMap(var) && key != value) { + SolverMap[key] = value; + return true; + } + return false; + } //end of UpdateSolverMap() + + //collects the vars in the term 'lhs' into the multiset Vars + void BVSolver::VarsInTheTerm_TopLevel(const ASTNode& lhs, ASTNodeMultiSet& Vars) { + TermsAlreadySeenMap.clear(); + VarsInTheTerm(lhs,Vars); + } + + //collects the vars in the term 'lhs' into the multiset Vars + void BVSolver::VarsInTheTerm(const ASTNode& term, ASTNodeMultiSet& Vars) { + ASTNode a = term; + ASTNodeMap::iterator it; + if((it = TermsAlreadySeenMap.find(term)) != TermsAlreadySeenMap.end()) { + //if the term has been seen, then simply return + return; + } + + switch(term.GetKind()) { + case BVCONST: + return; + case SYMBOL: + //cerr << "debugging: symbol added: " << term << endl; + Vars.insert(term); + break; + case READ: + //skip the arrayname, provided the arrayname is a SYMBOL + if(SYMBOL == term[0].GetKind()) { + VarsInTheTerm(term[1],Vars); + } + else { + VarsInTheTerm(term[0],Vars); + VarsInTheTerm(term[1],Vars); + } + break; + default: { + ASTVec c = term.GetChildren(); + for(ASTVec::iterator it=c.begin(),itend=c.end();it!=itend;it++) { + VarsInTheTerm(*it,Vars); + } + break; + } + } + + //ensures that you don't double count. if you have seen the term + //once, then memoize + TermsAlreadySeenMap[term] = ASTTrue; + return; + } //end of VarsInTheTerm() + + bool BVSolver::DoNotSolveThis(const ASTNode& var) { + if(DoNotSolve_TheseVars.find(var) != DoNotSolve_TheseVars.end()) { + return true; + } + return false; + } + + //chooses a variable in the lhs and returns the chosen variable + ASTNode BVSolver::ChooseMonom(const ASTNode& eq, ASTNode& modifiedlhs) { + if(!(EQ == eq.GetKind() && BVPLUS == eq[0].GetKind())) { + FatalError("ChooseMonom: input must be a EQ",eq); + } + + ASTNode lhs = eq[0]; + ASTNode rhs = eq[1]; + ASTNode zero = _bm->CreateZeroConst(32); + + //collect all the vars in the lhs and rhs + ASTNodeMultiSet Vars; + VarsInTheTerm_TopLevel(lhs,Vars); + + //handle BVPLUS case + ASTVec c = lhs.GetChildren(); + ASTVec o; + ASTNode outmonom = _bm->CreateNode(UNDEFINED); + bool chosen_symbol = false; + bool chosen_odd = false; + + //choose variables with no coeffs + for(ASTVec::iterator it=c.begin(),itend=c.end();it!=itend;it++) { + ASTNode monom = *it; + if(SYMBOL == monom.GetKind() && + Vars.count(monom) == 1 && + !_bm->VarSeenInTerm(monom,rhs) && + !DoNotSolveThis(monom) && + !chosen_symbol) { + outmonom = monom; + chosen_symbol = true; + } + else if(BVUMINUS == monom.GetKind() && + SYMBOL == monom[0].GetKind() && + Vars.count(monom[0]) == 1 && + !DoNotSolveThis(monom[0]) && + !_bm->VarSeenInTerm(monom[0],rhs) && + !chosen_symbol) { + //cerr << "Chosen Monom: " << monom << endl; + outmonom = monom; + chosen_symbol = true; + } + else { + o.push_back(monom); + } + } + + //try to choose only odd coeffed variables first + if(!chosen_symbol) { + o.clear(); + for(ASTVec::iterator it=c.begin(),itend=c.end();it!=itend;it++) { + ASTNode monom = *it; + ASTNode var = (BVMULT == monom.GetKind()) ? monom[1] : _bm->CreateNode(UNDEFINED); + + if(BVMULT == monom.GetKind() && + BVCONST == monom[0].GetKind() && + _bm->BVConstIsOdd(monom[0]) && + ((SYMBOL == var.GetKind() && + Vars.count(var) == 1) + || + (BVEXTRACT == var.GetKind() && + SYMBOL == var[0].GetKind() && + BVCONST == var[1].GetKind() && + zero == var[2] && + !_bm->VarSeenInTerm(var[0],rhs) && + !DoNotSolveThis(var[0])) + ) && + !DoNotSolveThis(var) && + !_bm->VarSeenInTerm(var,rhs) && + !chosen_odd) { + //monom[0] is odd. + outmonom = monom; + chosen_odd = true; + } + else { + o.push_back(monom); + } + } + } + + modifiedlhs = (o.size() > 1) ? _bm->CreateTerm(BVPLUS,lhs.GetValueWidth(),o) : o[0]; + return outmonom; + } //end of choosemonom() + + //solver function which solves for variables with odd coefficient + ASTNode BVSolver::BVSolve_Odd(const ASTNode& input) { + ASTNode eq = input; + //cerr << "Input to BVSolve_Odd()" << eq << endl; + if(!(wordlevel_solve && EQ == eq.GetKind())) { + return input; + } + + ASTNode output = input; + if(CheckAlreadySolvedMap(input,output)) { + return output; + } + + //get the lhs and the rhs, and case-split on the lhs kind + ASTNode lhs = eq[0]; + ASTNode rhs = eq[1]; + if(BVPLUS == lhs.GetKind()) { + ASTNode chosen_monom = _bm->CreateNode(UNDEFINED); + ASTNode leftover_lhs; + + //choose monom makes sure that it gets only those vars that + //occur exactly once in lhs and rhs put together + chosen_monom = ChooseMonom(eq, leftover_lhs); + if(chosen_monom == _bm->CreateNode(UNDEFINED)) { + //no monomial was chosen + return eq; + } + + //if control is here then it means that a monom was chosen + // + //construct: rhs - (lhs without the chosen monom) + unsigned int len = lhs.GetValueWidth(); + leftover_lhs = _bm->SimplifyTerm_TopLevel(_bm->CreateTerm(BVUMINUS,len,leftover_lhs)); + ASTNode newrhs = _bm->SimplifyTerm(_bm->CreateTerm(BVPLUS,len,rhs,leftover_lhs)); + lhs = chosen_monom; + rhs = newrhs; + } //end of if(BVPLUS ...) + + if(BVUMINUS == lhs.GetKind()) { + //equation is of the form (-lhs0) = rhs + ASTNode lhs0 = lhs[0]; + rhs = _bm->SimplifyTerm(_bm->CreateTerm(BVUMINUS,rhs.GetValueWidth(),rhs)); + lhs = lhs0; + } + + switch(lhs.GetKind()) { + case SYMBOL: { + //input is of the form x = rhs first make sure that the lhs + //symbol does not occur on the rhs or that it has not been + //solved for + if(_bm->VarSeenInTerm(lhs,rhs)) { + //found the lhs in the rhs. Abort! + DoNotSolve_TheseVars.insert(lhs); + return eq; + } + + //rhs should not have arrayreads in it. it complicates matters + //during transformation + // if(CheckForArrayReads_TopLevel(rhs)) { + // return eq; + // } + + DoNotSolve_TheseVars.insert(lhs); + if(!_bm->UpdateSolverMap(lhs,rhs)) { + return eq; + } + + output = ASTTrue; + break; + } + case BVEXTRACT: { + ASTNode zero = _bm->CreateZeroConst(32); + + if(!(SYMBOL == lhs[0].GetKind() && + BVCONST == lhs[1].GetKind() && + zero == lhs[2] && + !_bm->VarSeenInTerm(lhs[0],rhs) && + !DoNotSolveThis(lhs[0]))) { + return eq; + } + + if(_bm->VarSeenInTerm(lhs[0],rhs)) { + DoNotSolve_TheseVars.insert(lhs[0]); + return eq; + } + + DoNotSolve_TheseVars.insert(lhs[0]); + if(!_bm->UpdateSolverMap(lhs,rhs)) { + return eq; + } + + //if the extract of x[i:0] = t is entered into the solvermap, + //then also add another entry for x = x1@t + ASTNode var = lhs[0]; + ASTNode newvar = NewVar(var.GetValueWidth() - lhs.GetValueWidth()); + newvar = _bm->CreateTerm(BVCONCAT,var.GetValueWidth(),newvar,rhs); + _bm->UpdateSolverMap(var,newvar); + output = ASTTrue; + break; + } + case BVMULT: { + //the input is of the form a*x = t. If 'a' is odd, then compute + //its multiplicative inverse a^-1, multiply 't' with it, and + //update the solver map + if(BVCONST != lhs[0].GetKind()) { + return eq; + } + + if(!(SYMBOL == lhs[1].GetKind() || + (BVEXTRACT == lhs[1].GetKind() && + SYMBOL == lhs[1][0].GetKind()))) { + return eq; + } + + bool ChosenVar_Is_Extract = (BVEXTRACT == lhs[1].GetKind()) ? true : false; + + //if coeff is even, then we know that all the coeffs in the eqn + //are even. Simply return the eqn + if(!_bm->BVConstIsOdd(lhs[0])) { + return eq; + } + + ASTNode a = _bm->MultiplicativeInverse(lhs[0]); + ASTNode chosenvar = (BVEXTRACT == lhs[1].GetKind()) ? lhs[1][0] : lhs[1]; + ASTNode chosenvar_value = + _bm->SimplifyTerm(_bm->CreateTerm(BVMULT,rhs.GetValueWidth(),a,rhs)); + + //if chosenvar is seen in chosenvar_value then abort + if(_bm->VarSeenInTerm(chosenvar,chosenvar_value)) { + //abort solving + DoNotSolve_TheseVars.insert(lhs); + return eq; + } + + //rhs should not have arrayreads in it. it complicates matters + //during transformation + // if(CheckForArrayReads_TopLevel(chosenvar_value)) { + // return eq; + // } + + //found a variable to solve + DoNotSolve_TheseVars.insert(chosenvar); + chosenvar = lhs[1]; + if(!_bm->UpdateSolverMap(chosenvar,chosenvar_value)) { + return eq; + } + + if(ChosenVar_Is_Extract) { + ASTNode var = lhs[1][0]; + ASTNode newvar = NewVar(var.GetValueWidth() - lhs[1].GetValueWidth()); + newvar = _bm->CreateTerm(BVCONCAT,var.GetValueWidth(),newvar,chosenvar_value); + _bm->UpdateSolverMap(var,newvar); + } + output = ASTTrue; + break; + } + default: + output = eq; + break; + } + + UpdateAlreadySolvedMap(input,output); + return output; + } //end of BVSolve_Odd() + + //Create a new variable of ValueWidth 'n' + ASTNode BVSolver::NewVar(unsigned int n) { + std:: string c("v"); + char d[32]; + sprintf(d,"%d",_symbol_count++); + std::string ccc(d); + c += "_solver_" + ccc; + + ASTNode CurrentSymbol = _bm->CreateSymbol(c.c_str()); + CurrentSymbol.SetValueWidth(n); + CurrentSymbol.SetIndexWidth(0); + return CurrentSymbol; + } //end of NewVar() + + //The toplevel bvsolver(). Checks if the formula has already been + //solved. If not, the solver() is invoked. If yes, then simply drop + //the formula + ASTNode BVSolver::TopLevelBVSolve(const ASTNode& input) { + if(!wordlevel_solve) { + return input; + } + + Kind k = input.GetKind(); + if(!(EQ == k || AND == k)) { + return input; + } + + ASTNode output = input; + if(CheckAlreadySolvedMap(input,output)) { + //output is TRUE. The formula is thus dropped + return output; + } + ASTVec o; + ASTVec c; + if(EQ == k) + c.push_back(input); + else + c = input.GetChildren(); + ASTVec eveneqns; + ASTNode solved = ASTFalse; + for(ASTVec::iterator it = c.begin(), itend = c.end();it != itend;it++) { + //_bm->ASTNodeStats("Printing before calling simplifyformula inside the solver:", *it); + ASTNode aaa = (ASTTrue == solved && EQ == it->GetKind()) ? _bm->SimplifyFormula(*it,false) : *it; + //ASTNode aaa = *it; + //_bm->ASTNodeStats("Printing after calling simplifyformula inside the solver:", aaa); + aaa = BVSolve_Odd(aaa); + //_bm->ASTNodeStats("Printing after oddsolver:", aaa); + bool even = false; + aaa = CheckEvenEqn(aaa, even); + if(even) { + eveneqns.push_back(aaa); + } + else { + if(ASTTrue != aaa) { + o.push_back(aaa); + } + } + solved = aaa; + } + + ASTNode evens; + if(eveneqns.size() > 0) { + //if there is a system of even equations then solve them + evens = (eveneqns.size() > 1) ? _bm->CreateNode(AND,eveneqns) : eveneqns[0]; + //evens = _bm->SimplifyFormula(evens,false); + evens = BVSolve_Even(evens); + _bm->ASTNodeStats("Printing after evensolver:", evens); + } + else { + evens = ASTTrue; + } + output = (o.size() > 0) ? ((o.size() > 1) ? _bm->CreateNode(AND,o) : o[0]) : ASTTrue; + output = _bm->CreateNode(AND,output,evens); + + UpdateAlreadySolvedMap(input,output); + return output; + } //end of TopLevelBVSolve() + + ASTNode BVSolver::CheckEvenEqn(const ASTNode& input, bool& evenflag) { + ASTNode eq = input; + //cerr << "Input to BVSolve_Odd()" << eq << endl; + if(!(wordlevel_solve && EQ == eq.GetKind())) { + evenflag = false; + return eq; + } + + ASTNode lhs = eq[0]; + ASTNode rhs = eq[1]; + ASTNode zero = _bm->CreateZeroConst(rhs.GetValueWidth()); + //lhs must be a BVPLUS, and rhs must be a BVCONST + if(!(BVPLUS == lhs.GetKind() && zero == rhs)) { + evenflag = false; + return eq; + } + + ASTVec lhs_c = lhs.GetChildren(); + ASTNode savetheconst = rhs; + for(ASTVec::iterator it=lhs_c.begin(),itend=lhs_c.end();it!=itend;it++) { + ASTNode aaa = *it; + Kind itk = aaa.GetKind(); + + if(BVCONST == itk){ + //check later if the constant is even or not + savetheconst = aaa; + continue; + } + + if(!(BVMULT == itk && + BVCONST == aaa[0].GetKind() && + SYMBOL == aaa[1].GetKind() && + !_bm->BVConstIsOdd(aaa[0]))) { + //If the monomials of the lhs are NOT of the form 'a*x' where + //'a' is even, then return the false + evenflag = false; + return eq; + } + }//end of for loop + + //if control is here then it means that all coeffs are even. the + //only remaining thing is to check if the constant is even or not + if(_bm->BVConstIsOdd(savetheconst)) { + //the constant turned out to be odd. we have UNSAT eqn + evenflag = false; + return ASTFalse; + } + + //all is clear. the eqn in even, through and through + evenflag = true; + return eq; + } //end of CheckEvenEqn + + //solve an eqn whose monomials have only even coefficients + ASTNode BVSolver::BVSolve_Even(const ASTNode& input) { + if(!wordlevel_solve) { + return input; + } + + if(!(EQ == input.GetKind() || AND == input.GetKind())) { + return input; + } + + ASTNode output; + if(CheckAlreadySolvedMap(input,output)) { + return output; + } + + ASTVec input_c; + if(EQ == input.GetKind()) { + input_c.push_back(input); + } + else { + input_c = input.GetChildren(); + } + + //power_of_2 holds the exponent of 2 in the coeff + unsigned int power_of_2 = 0; + //we need this additional variable to find the lowest power of 2 + unsigned int power_of_2_lowest = 0xffffffff; + //the monom which has the least power of 2 in the coeff + ASTNode monom_with_best_coeff; + for(ASTVec::iterator jt=input_c.begin(),jtend=input_c.end();jt!=jtend;jt++) { + ASTNode eq = *jt; + ASTNode lhs = eq[0]; + ASTNode rhs = eq[1]; + ASTNode zero = _bm->CreateZeroConst(rhs.GetValueWidth()); + //lhs must be a BVPLUS, and rhs must be a BVCONST + if(!(BVPLUS == lhs.GetKind() && zero == rhs)) { + return input; + } + + ASTVec lhs_c = lhs.GetChildren(); + ASTNode odd; + for(ASTVec::iterator it=lhs_c.begin(),itend=lhs_c.end();it!=itend;it++) { + ASTNode aaa = *it; + Kind itk = aaa.GetKind(); + if(!(BVCONST == itk && + !_bm->BVConstIsOdd(aaa)) && + !(BVMULT == itk && + BVCONST == aaa[0].GetKind() && + SYMBOL == aaa[1].GetKind() && + !_bm->BVConstIsOdd(aaa[0]))) { + //If the monomials of the lhs are NOT of the form 'a*x' or 'a' + //where 'a' is even, then return the eqn + return input; + } + + //we are gauranteed that if control is here then the monomial is + //of the form 'a*x' or 'a', where 'a' is even + ASTNode coeff = (BVCONST == itk) ? aaa : aaa[0]; + odd = SplitEven_into_Oddnum_PowerOf2(coeff,power_of_2); + if(power_of_2 < power_of_2_lowest) { + power_of_2_lowest = power_of_2; + monom_with_best_coeff = aaa; + } + power_of_2 = 0; + }//end of inner for loop + } //end of outer for loop + + //get the exponent + power_of_2 = power_of_2_lowest; + + //if control is here, we are gauranteed that we have chosen a + //monomial with fewest powers of 2 + ASTVec formula_out; + for(ASTVec::iterator jt=input_c.begin(),jtend=input_c.end();jt!=jtend;jt++) { + ASTNode eq = *jt; + ASTNode lhs = eq[0]; + ASTNode rhs = eq[1]; + ASTNode zero = _bm->CreateZeroConst(rhs.GetValueWidth()); + //lhs must be a BVPLUS, and rhs must be a BVCONST + if(!(BVPLUS == lhs.GetKind() && zero == rhs)) { + return input; + } + + unsigned len = lhs.GetValueWidth(); + ASTNode hi = _bm->CreateBVConst(32,len-1); + ASTNode low = _bm->CreateBVConst(32,len - power_of_2); + ASTNode low_minus_one = _bm->CreateBVConst(32,len - power_of_2 - 1); + ASTNode low_zero = _bm->CreateZeroConst(32); + unsigned newlen = len - power_of_2; + ASTNode two_const = _bm->CreateTwoConst(len); + + unsigned count = power_of_2; + ASTNode two = two_const; + while(--count) { + two = _bm->BVConstEvaluator(_bm->CreateTerm(BVMULT,len,two_const,two)); + } + ASTVec lhs_c = lhs.GetChildren(); + ASTVec lhs_out; + for(ASTVec::iterator it=lhs_c.begin(),itend=lhs_c.end();it!=itend;it++) { + ASTNode aaa = *it; + Kind itk = aaa.GetKind(); + if(BVCONST == itk) { + aaa = _bm->BVConstEvaluator(_bm->CreateTerm(BVDIV,len,aaa,two)); + aaa = _bm->BVConstEvaluator(_bm->CreateTerm(BVEXTRACT,newlen,aaa,low_minus_one,low_zero)); + } + else { + //it must be of the form a*x + ASTNode coeff = _bm->BVConstEvaluator(_bm->CreateTerm(BVDIV,len,aaa[0],two)); + coeff = _bm->BVConstEvaluator(_bm->CreateTerm(BVEXTRACT,newlen,coeff,low_minus_one,low_zero)); + ASTNode upper_x, lower_x; + //upper_x = _bm->SimplifyTerm(_bm->CreateTerm(BVEXTRACT, power_of_2, aaa[1], hi, low)); + lower_x = _bm->SimplifyTerm(_bm->CreateTerm(BVEXTRACT, newlen,aaa[1],low_minus_one,low_zero)); + aaa = _bm->CreateTerm(BVMULT,newlen,coeff,lower_x); + } + lhs_out.push_back(aaa); + }//end of inner forloop() + rhs = _bm->CreateZeroConst(newlen); + lhs = _bm->CreateTerm(BVPLUS,newlen,lhs_out); + formula_out.push_back(_bm->CreateSimplifiedEQ(lhs,rhs)); + } //end of outer forloop() + + output = + (formula_out.size() > 0) ? (formula_out.size() > 1) ? _bm->CreateNode(AND,formula_out) : formula_out[0] : ASTTrue; + + UpdateAlreadySolvedMap(input,output); + return output; + } //end of BVSolve_Even() +};//end of namespace BEEV diff --git a/stp/simplifier/bvsolver.h b/stp/simplifier/bvsolver.h new file mode 100644 index 00000000..a8981b12 --- /dev/null +++ b/stp/simplifier/bvsolver.h @@ -0,0 +1,134 @@ +/******************************************************************** + * AUTHORS: Vijay Ganesh, David L. Dill + * + * BEGIN DATE: November, 2005 + * + * LICENSE: Please view LICENSE file in the home dir of this Program + ********************************************************************/ +// -*- c++ -*- + +#include "../AST/AST.h" +#include "../AST/ASTUtil.h" +namespace BEEV { + + //This class represents the bitvector arithmetic linear solver. + // + //The bitvector solver is a partial solver, i.e. it does not solve + //for all variables in the system of equations. it is + //best-effort. it relies on the SAT solver to be complete. + // + //The BVSolver assumes that the input equations are normalized, and + //have liketerms combined etc. + // + //0. Traverse top-down over the input DAG, looking for a conjunction + //0. of equations. if you find one, then for each equation in the + //0. conjunction, do the following steps. + // + //1. check for Linearity of the input equation + // + //2. Solve for a "chosen" variable. The variable should occur + //2. exactly once and must have an odd coeff. Refer STP's CAV 2007 + //2. paper for actual solving procedure + // + //4. Outside the solver, Substitute and Re-normalize the input DAG + class BVSolver { + //Ptr to toplevel manager that manages bit-vector expressions + //(i.e. construct various kinds of expressions), and also has + //member functions that simplify bit-vector expressions + BeevMgr * _bm; + ASTNode ASTTrue, ASTFalse; + + //Those formulas which have already been solved. If the same + //formula occurs twice then do not solve the second occurence, and + //instead drop it + ASTNodeMap FormulasAlreadySolvedMap; + + //this map is useful while traversing terms and uniquely + //identifying variables in the those terms. Prevents double + //counting. + ASTNodeMap TermsAlreadySeenMap; + ASTNodeMap TermsAlreadySeenMap_ForArrays; + + //count is used in the creation of new variables + unsigned int _symbol_count; + + //solved variables list: If a variable has been solved for then do + //not solve for it again + ASTNodeSet DoNotSolve_TheseVars; + + //checks if var has been solved for or not. if yes, then return + //true else return false + bool DoNotSolveThis(const ASTNode& var); + + //traverses a term, and creates a multiset of all variables in the + //term. Does memoization to avoid double counting. + void VarsInTheTerm(const ASTNode& lhs, ASTNodeMultiSet& v); + void VarsInTheTerm_TopLevel(const ASTNode& lhs, ASTNodeMultiSet& v); + + //choose a suitable var from the term + ASTNode ChooseMonom(const ASTNode& eq, ASTNode& modifiedterm); + //accepts an equation and solves for a variable or a monom in it + ASTNode BVSolve_Odd(const ASTNode& eq); + + //solves equations of the form a*x=t where 'a' is even. Has a + //return value, unlike the normal BVSolve() + ASTNode BVSolve_Even(const ASTNode& eq); + ASTNode CheckEvenEqn(const ASTNode& input, bool& evenflag); + + //Checks for arrayreads in a term. if yes then returns true, else + //return false + bool CheckForArrayReads(const ASTNode& term); + bool CheckForArrayReads_TopLevel(const ASTNode& term); + + //Creates new variables used in solving + ASTNode NewVar(unsigned int n); + + //this function return true if the var occurs in term, else the + //function returns false + bool VarSeenInTerm(const ASTNode& var, const ASTNode& term); + + //takes an even number "in" as input, and returns an odd number + //(return value) and a power of 2 (as number_shifts by reference), + //such that in = (odd_number * power_of_2). + // + //Refer STP's CAV 2007 (or Clark Barrett's 1998 paper on + //bit-vector arithmetic published in DAC 1998) paper for precise + //understanding of the algorithm + ASTNode SplitEven_into_Oddnum_PowerOf2(const ASTNode& in, unsigned int& number_shifts); + + //Once a formula has been solved, then update the alreadysolvedmap + //with the formula, and the solved value. The solved value can be + //described using the following example: Suppose input to the + //solver is + // + // input key: x = 2 AND y = x + t + // + // output value: y = 2 + t + void UpdateAlreadySolvedMap(const ASTNode& key, const ASTNode& value); + + //This function checks if the key (formula) has already been + //solved for. + // + //If yes it returns TRUE and fills the "output" with the + //solved-value (call by reference argument), + // + //else returns FALSE + bool CheckAlreadySolvedMap(const ASTNode& key, ASTNode& output); + public: + //constructor + BVSolver(BeevMgr * bm) : _bm(bm), _symbol_count(0) { + ASTTrue = _bm->CreateNode(TRUE); + ASTFalse = _bm->CreateNode(FALSE); + }; + + //Destructor + ~BVSolver() { + TermsAlreadySeenMap.clear(); + DoNotSolve_TheseVars.clear(); + } + + //Top Level Solver: Goes over the input DAG, identifies the + //equation to be solved, solves them, + ASTNode TopLevelBVSolve(const ASTNode& a); + }; //end of class bvsolver +};//end of namespace BEEV diff --git a/stp/simplifier/simplifier.cpp b/stp/simplifier/simplifier.cpp new file mode 100644 index 00000000..2a627398 --- /dev/null +++ b/stp/simplifier/simplifier.cpp @@ -0,0 +1,2495 @@ +/******************************************************************** + * AUTHORS: Vijay Ganesh, David L. Dill + * + * BEGIN DATE: November, 2005 + * + * LICENSE: Please view LICENSE file in the home dir of this Program + ********************************************************************/ +// -*- c++ -*- + +#include "../AST/AST.h" +#include "../AST/ASTUtil.h" +namespace BEEV { + + bool BeevMgr::CheckSimplifyMap(const ASTNode& key, + ASTNode& output, bool pushNeg) { + ASTNodeMap::iterator it, itend; + it = pushNeg ? SimplifyNegMap.find(key) : SimplifyMap.find(key); + itend = pushNeg ? SimplifyNegMap.end() : SimplifyMap.end(); + + if(it != itend) { + output = it->second; + CountersAndStats("Successful_CheckSimplifyMap"); + return true; + } + + if(pushNeg && (it = SimplifyMap.find(key)) != SimplifyMap.end()) { + output = + (ASTFalse == it->second) ? + ASTTrue : + (ASTTrue == it->second) ? ASTFalse : CreateNode(NOT, it->second); + CountersAndStats("2nd_Successful_CheckSimplifyMap"); + return true; + } + + return false; + } + + void BeevMgr::UpdateSimplifyMap(const ASTNode& key, const ASTNode& value, bool pushNeg) { + if(pushNeg) + SimplifyNegMap[key] = value; + else + SimplifyMap[key] = value; + } + + bool BeevMgr::CheckSubstitutionMap(const ASTNode& key, ASTNode& output) { + ASTNodeMap::iterator it; + if((it = SolverMap.find(key)) != SolverMap.end()) { + output = it->second; + return true; + } + return false; + } + + bool BeevMgr::CheckSubstitutionMap(const ASTNode& key) { + if(SolverMap.find(key) != SolverMap.end()) + return true; + else + return false; + } + + bool BeevMgr::UpdateSubstitutionMap(const ASTNode& e0, const ASTNode& e1) { + int i = TermOrder(e0,e1); + if(0 == i) + return false; + + //e0 is of the form READ(Arr,const), and e1 is const, or + //e0 is of the form var, and e1 is const + if(1 == i && !CheckSubstitutionMap(e0)) { + SolverMap[e0] = e1; + return true; + } + + //e1 is of the form READ(Arr,const), and e0 is const, or + //e1 is of the form var, and e0 is const + if (-1 == i && !CheckSubstitutionMap(e1)) { + SolverMap[e1] = e0; + return true; + } + + return false; + } + + bool BeevMgr::CheckMultInverseMap(const ASTNode& key, ASTNode& output) { + ASTNodeMap::iterator it; + if((it = MultInverseMap.find(key)) != MultInverseMap.end()) { + output = it->second; + return true; + } + return false; + } + + void BeevMgr::UpdateMultInverseMap(const ASTNode& key, const ASTNode& value) { + MultInverseMap[key] = value; + } + + + bool BeevMgr::CheckAlwaysTrueFormMap(const ASTNode& key) { + ASTNodeSet::iterator it = AlwaysTrueFormMap.find(key); + ASTNodeSet::iterator itend = AlwaysTrueFormMap.end(); + + if(it != itend) { + //cerr << "found:" << *it << endl; + CountersAndStats("Successful_CheckAlwaysTrueFormMap"); + return true; + } + + return false; + } + + void BeevMgr::UpdateAlwaysTrueFormMap(const ASTNode& key) { + AlwaysTrueFormMap.insert(key); + } + + //if a is READ(Arr,const) or SYMBOL, and b is BVCONST then return 1 + //if b is READ(Arr,const) or SYMBOL, and a is BVCONST then return -1 + // + //else return 0 by default + int BeevMgr::TermOrder(const ASTNode& a, const ASTNode& b) { + Kind k1 = a.GetKind(); + Kind k2 = b.GetKind(); + + //a is of the form READ(Arr,const), and b is const, or + //a is of the form var, and b is const + if((k1 == READ + && + a[0].GetKind() == SYMBOL && + a[1].GetKind() == BVCONST + ) + && + (k2 == BVCONST) + ) + return 1; + + if(k1 == SYMBOL) + return 1; + + //b is of the form READ(Arr,const), and a is const, or + //b is of the form var, and a is const + if((k1 == BVCONST) + && + ((k2 == READ + && + b[0].GetKind() == SYMBOL && + b[1].GetKind() == BVCONST + ) + || + k2 == SYMBOL + )) + return -1; + return 0; + } + + //This function records all the const-indices seen so far for each + //array. It populates the map '_arrayname_readindices' whose key is + //the arrayname, and vlaue is a vector of read-indices. + // + //fill the arrayname_readindices vector if e0 is a READ(Arr,index) + //and index is a BVCONST. + // + //Since these arrayreads are being nuked and recorded in the + //substitutionmap, we have to also record the fact that each + //arrayread (e0 is of the form READ(Arr,const) here is represented + //by a BVCONST (e1). This is necessary for later Leibnitz Axiom + //generation + void BeevMgr::FillUp_ArrReadIndex_Vec(const ASTNode& e0, const ASTNode& e1) { + int i = TermOrder(e0,e1); + if(0 == i) return; + + if(1 == i && e0.GetKind() != SYMBOL && !CheckSubstitutionMap(e0)) { + _arrayname_readindices[e0[0]].push_back(e0[1]); + //e0 is the array read : READ(A,i) and e1 is a bvconst + _arrayread_symbol[e0] = e1; + return; + } + if(-1 == i && e1.GetKind() != SYMBOL && !CheckSubstitutionMap(e1)) { + _arrayname_readindices[e1[0]].push_back(e1[1]); + //e0 is the array read : READ(A,i) and e1 is a bvconst + _arrayread_symbol[e1] = e0; + return; + } + } + + ASTNode BeevMgr::SimplifyFormula_NoRemoveWrites(const ASTNode& b, bool pushNeg) { + Begin_RemoveWrites = false; + ASTNode out = SimplifyFormula(b,pushNeg); + return out; + } + + ASTNode BeevMgr::SimplifyFormula_TopLevel(const ASTNode& b, bool pushNeg) { + SimplifyMap.clear(); + SimplifyNegMap.clear(); + ASTNode out = SimplifyFormula(b,pushNeg); + SimplifyMap.clear(); + SimplifyNegMap.clear(); + return out; + } + + ASTNode BeevMgr::SimplifyFormula(const ASTNode& b, bool pushNeg){ + if(!optimize) + return b; + + Kind kind = b.GetKind(); + if(BOOLEAN_TYPE != b.GetType()) { + FatalError(" SimplifyFormula: You have input a nonformula kind: ",ASTUndefined,kind); + } + + ASTNode a = b; + ASTVec ca = a.GetChildren(); + if(!(IMPLIES == kind || + ITE == kind || + isAtomic(kind))) { + SortByExprNum(ca); + a = CreateNode(kind,ca); + } + + ASTNode output; + if(CheckSimplifyMap(a,output,pushNeg)) + return output; + + switch(kind){ + case AND: + case OR: + output = SimplifyAndOrFormula(a,pushNeg); + break; + case NOT: + output = SimplifyNotFormula(a,pushNeg); + break; + case XOR: + output = SimplifyXorFormula(a,pushNeg); + break; + case NAND: + output = SimplifyNandFormula(a,pushNeg); + break; + case NOR: + output = SimplifyNorFormula(a,pushNeg); + break; + case IFF: + output = SimplifyIffFormula(a,pushNeg); + break; + case IMPLIES: + output = SimplifyImpliesFormula(a,pushNeg); + break; + case ITE: + output = SimplifyIteFormula(a,pushNeg); + break; + default: + //kind can be EQ,NEQ,BVLT,BVLE,... or a propositional variable + output = SimplifyAtomicFormula(a,pushNeg); + //output = pushNeg ? CreateNode(NOT,a) : a; + break; + } + + //memoize + UpdateSimplifyMap(a,output, pushNeg); + return output; + } + + ASTNode BeevMgr::SimplifyAtomicFormula(const ASTNode& a, bool pushNeg) { + if(!optimize) { + return a; + } + + ASTNode output; + if(CheckSimplifyMap(a,output,pushNeg)) { + return output; + } + + ASTNode left,right; + if(a.Degree() == 2) { + //cerr << "Input to simplifyterm: left: " << a[0] << endl; + left = SimplifyTerm(a[0]); + //cerr << "Output of simplifyterm:left: " << left << endl; + //cerr << "Input to simplifyterm: right: " << a[1] << endl; + right = SimplifyTerm(a[1]); + //cerr << "Output of simplifyterm:left: " << right << endl; + } + + Kind kind = a.GetKind(); + switch(kind) { + case TRUE: + output = pushNeg ? ASTFalse : ASTTrue; + break; + case FALSE: + output = pushNeg ? ASTTrue : ASTFalse; + break; + case SYMBOL: + if(!CheckSolverMap(a,output)) { + output = a; + } + output = pushNeg ? CreateNode(NOT,output) : output; + break; + case BVGETBIT: { + ASTNode term = SimplifyTerm(a[0]); + ASTNode thebit = a[1]; + ASTNode zero = CreateZeroConst(1); + ASTNode one = CreateOneConst(1); + ASTNode getthebit = SimplifyTerm(CreateTerm(BVEXTRACT,1,term,thebit,thebit)); + if(getthebit == zero) + output = pushNeg ? ASTTrue : ASTFalse; + else if(getthebit == one) + output = pushNeg ? ASTFalse : ASTTrue; + else { + output = CreateNode(BVGETBIT,term,thebit); + output = pushNeg ? CreateNode(NOT,output) : output; + } + break; + } + case EQ:{ + output = CreateSimplifiedEQ(left,right); + output = LhsMinusRhs(output); + output = ITEOpt_InEqs(output); + if(output == ASTTrue) + output = pushNeg ? ASTFalse : ASTTrue; + else if (output == ASTFalse) + output = pushNeg ? ASTTrue : ASTFalse; + else + output = pushNeg ? CreateNode(NOT,output) : output; + break; + } + case NEQ: { + output = CreateSimplifiedEQ(left,right); + output = LhsMinusRhs(output); + if(output == ASTTrue) + output = pushNeg ? ASTTrue : ASTFalse; + else if (output == ASTFalse) + output = pushNeg ? ASTFalse : ASTTrue; + else + output = pushNeg ? output : CreateNode(NOT,output); + break; + } + case BVLT: + case BVLE: + case BVGT: + case BVGE: + case BVSLT: + case BVSLE: + case BVSGT: + case BVSGE: { + //output = CreateNode(kind,left,right); + //output = pushNeg ? CreateNode(NOT,output) : output; + output = CreateSimplifiedINEQ(kind,left,right,pushNeg); + break; + } + default: + FatalError("SimplifyAtomicFormula: NO atomic formula of the kind: ",ASTUndefined,kind); + break; + } + + //memoize + UpdateSimplifyMap(a,output,pushNeg); + return output; + } //end of SimplifyAtomicFormula() + + ASTNode BeevMgr::CreateSimplifiedINEQ(Kind k, + const ASTNode& left, + const ASTNode& right, + bool pushNeg) { + ASTNode output; + if(BVCONST == left.GetKind() && BVCONST == right.GetKind()) { + output = BVConstEvaluator(CreateNode(k,left,right)); + output = pushNeg ? (ASTFalse == output) ? ASTTrue : ASTFalse : output; + return output; + } + + unsigned len = left.GetValueWidth(); + ASTNode zero = CreateZeroConst(len); + ASTNode one = CreateOneConst(len); + ASTNode max = CreateMaxConst(len); + switch(k){ + case BVLT: + if(right == zero) { + output = pushNeg ? ASTTrue : ASTFalse; + } + else if(left == right) { + output = pushNeg ? ASTTrue : ASTFalse; + } + else if(one == right) { + output = CreateSimplifiedEQ(left,zero); + output = pushNeg ? CreateNode(NOT,output) : output; + } + else { + output = pushNeg ? CreateNode(BVLE,right,left) : CreateNode(BVLT,left,right); + } + break; + case BVLE: + if(left == zero) { + output = pushNeg ? ASTFalse : ASTTrue; + } + else if(left == right) { + output = pushNeg ? ASTFalse : ASTTrue; + } + else if(max == right) { + output = pushNeg ? ASTFalse : ASTTrue; + } + else if(zero == right) { + output = CreateSimplifiedEQ(left,zero); + output = pushNeg ? CreateNode(NOT,output) : output; + } + else { + output = pushNeg ? CreateNode(BVLT,right,left) : CreateNode(BVLE,left,right); + } + break; + case BVGT: + if(left == zero) { + output = pushNeg ? ASTTrue : ASTFalse; + } + else if(left == right) { + output = pushNeg ? ASTTrue : ASTFalse; + } + else { + output = pushNeg ? CreateNode(BVLE,left,right) : CreateNode(BVLT,right,left); + } + break; + case BVGE: + if(right == zero) { + output = pushNeg ? ASTFalse : ASTTrue; + } + else if(left == right) { + output = pushNeg ? ASTFalse : ASTTrue; + } + else { + output = pushNeg ? CreateNode(BVLT,left,right) : CreateNode(BVLE,right,left); + } + break; + case BVSLT: + case BVSLE: + case BVSGE: + case BVSGT: { + output = CreateNode(k,left,right); + output = pushNeg ? CreateNode(NOT,output) : output; + } + break; + default: + FatalError("Wrong Kind"); + break; + } + + return output; + } + + //takes care of some simple ITE Optimizations in the context of equations + ASTNode BeevMgr::ITEOpt_InEqs(const ASTNode& in) { + CountersAndStats("ITEOpts_InEqs"); + + if(!(EQ == in.GetKind() && optimize)) { + return in; + } + + ASTNode output; + if(CheckSimplifyMap(in,output,false)) { + return output; + } + + ASTNode in1 = in[0]; + ASTNode in2 = in[1]; + Kind k1 = in1.GetKind(); + Kind k2 = in2.GetKind(); + if(in1 == in2) { + //terms are syntactically the same + output = ASTTrue; + } + else if(BVCONST == k1 && BVCONST == k2) { + //here the terms are definitely not syntactically equal but may + //be semantically equal. + output = ASTFalse; + } + else if(ITE == k1 && + BVCONST == in1[1].GetKind() && + BVCONST == in1[2].GetKind() && BVCONST == k2) { + //if one side is a BVCONST and the other side is an ITE over + //BVCONST then we can do the following optimization: + // + // c = ITE(cond,c,d) <=> cond + // + // similarly ITE(cond,c,d) = c <=> cond + // + // c = ITE(cond,d,c) <=> NOT(cond) + // + //similarly ITE(cond,d,c) = d <=> NOT(cond) + ASTNode cond = in1[0]; + if(in1[1] == in2) { + //ITE(cond, c, d) = c <=> cond + output = cond; + } + else if(in1[2] == in2) { + cond = SimplifyFormula(cond,true); + output = cond; + } + else { + //last resort is to CreateNode + output = CreateNode(EQ,in1,in2); + } + } + else if(ITE == k2 && + BVCONST == in2[1].GetKind() && + BVCONST == in2[2].GetKind() && BVCONST == k1) { + ASTNode cond = in2[0]; + if(in2[1] == in1) { + //ITE(cond, c, d) = c <=> cond + output = cond; + } + else if(in2[2] == in1) { + cond = SimplifyFormula(cond,true); + output = cond; + } + else { + //last resort is to CreateNode + output = CreateNode(EQ,in1,in2); + } + } + else { + //last resort is to CreateNode + output = CreateNode(EQ,in1,in2); + } + + UpdateSimplifyMap(in,output,false); + return output; + } //End of ITEOpts_InEqs() + + //Tries to simplify the input to TRUE/FALSE. if it fails, then + //return the constructed equality + ASTNode BeevMgr::CreateSimplifiedEQ(const ASTNode& in1, const ASTNode& in2) { + CountersAndStats("CreateSimplifiedEQ"); + Kind k1 = in1.GetKind(); + Kind k2 = in2.GetKind(); + + if(!optimize) { + return CreateNode(EQ,in1,in2); + } + + if(in1 == in2) + //terms are syntactically the same + return ASTTrue; + + //here the terms are definitely not syntactically equal but may be + //semantically equal. + if(BVCONST == k1 && BVCONST == k2) + return ASTFalse; + + //last resort is to CreateNode + return CreateNode(EQ,in1,in2); + } + + //accepts cond == t1, then part is t2, and else part is t3 + ASTNode BeevMgr::CreateSimplifiedTermITE(const ASTNode& in0, + const ASTNode& in1, const ASTNode& in2) { + ASTNode t0 = in0; + ASTNode t1 = in1; + ASTNode t2 = in2; + CountersAndStats("CreateSimplifiedITE"); + if(!optimize) { + if(t1.GetValueWidth() != t2.GetValueWidth()) { + cerr << "t2 is : = " << t2; + FatalError("CreateSimplifiedTermITE: the lengths of then and else branches don't match",t1); + } + if(t1.GetIndexWidth() != t2.GetIndexWidth()) { + cerr << "t2 is : = " << t2; + FatalError("CreateSimplifiedTermITE: the lengths of then and else branches don't match",t1); + } + return CreateTerm(ITE,t1.GetValueWidth(),t0,t1,t2); + } + + if(t0 == ASTTrue) + return t1; + if (t0 == ASTFalse) + return t2; + if(t1 == t2) + return t1; + if(CheckAlwaysTrueFormMap(t0)) { + return t1; + } + if(CheckAlwaysTrueFormMap(CreateNode(NOT,t0)) || + (NOT == t0.GetKind() && CheckAlwaysTrueFormMap(t0[0]))) { + return t2; + } + + return CreateTerm(ITE,t1.GetValueWidth(),t0,t1,t2); + } + + ASTNode BeevMgr::SimplifyAndOrFormula(const ASTNode& a, bool pushNeg) { + ASTNode output; + //cerr << "input:\n" << a << endl; + + if(CheckSimplifyMap(a,output,pushNeg)) + return output; + + ASTVec c, outvec; + c = a.GetChildren(); + ASTNode flat = FlattenOneLevel(a); + c = flat.GetChildren(); + SortByExprNum(c); + + Kind k = a.GetKind(); + bool isAnd = (k == AND) ? true : false; + + ASTNode annihilator = isAnd ? + (pushNeg ? ASTTrue : ASTFalse): + (pushNeg ? ASTFalse : ASTTrue); + + ASTNode identity = isAnd ? + (pushNeg ? ASTFalse : ASTTrue): + (pushNeg ? ASTTrue : ASTFalse); + + //do the work + ASTVec::const_iterator next_it; + for(ASTVec::const_iterator i=c.begin(),iend=c.end();i!=iend;i++) { + ASTNode aaa = *i; + next_it = i+1; + bool nextexists = (next_it < iend); + + aaa = SimplifyFormula(aaa,pushNeg); + if(annihilator == aaa) { + //memoize + UpdateSimplifyMap(*i,annihilator,pushNeg); + UpdateSimplifyMap(a, annihilator,pushNeg); + //cerr << "annihilator1: output:\n" << annihilator << endl; + return annihilator; + } + ASTNode bbb = ASTFalse; + if(nextexists) { + bbb = SimplifyFormula(*next_it,pushNeg); + } + if(nextexists && bbb == aaa) { + //skip the duplicate aaa. *next_it will be included + } + else if(nextexists && + ((bbb.GetKind() == NOT && bbb[0] == aaa))) { + //memoize + UpdateSimplifyMap(a, annihilator,pushNeg); + //cerr << "annihilator2: output:\n" << annihilator << endl; + return annihilator; + } + else if(identity == aaa) { + // //drop identites + } + else if((!isAnd && !pushNeg) || + (isAnd && pushNeg)) { + outvec.push_back(aaa); + } + else if((isAnd && !pushNeg) || + (!isAnd && pushNeg)) { + outvec.push_back(aaa); + } + else { + outvec.push_back(aaa); + } + } + + switch(outvec.size()) { + case 0: { + //only identities were dropped + output = identity; + break; + } + case 1: { + output = SimplifyFormula(outvec[0],false); + break; + } + default: { + output = (isAnd) ? + (pushNeg ? CreateNode(OR,outvec) : CreateNode(AND,outvec)): + (pushNeg ? CreateNode(AND,outvec) : CreateNode(OR,outvec)); + //output = FlattenOneLevel(output); + break; + } + } + //memoize + UpdateSimplifyMap(a,output,pushNeg); + //cerr << "output:\n" << output << endl; + return output; + } //end of SimplifyAndOrFormula + + + ASTNode BeevMgr::SimplifyNotFormula(const ASTNode& a, bool pushNeg) { + ASTNode output; + if(CheckSimplifyMap(a,output,pushNeg)) + return output; + + if(!(a.Degree() == 1 && NOT == a.GetKind())) + FatalError("SimplifyNotFormula: input vector with more than 1 node",ASTUndefined); + + //if pushNeg is set then there is NOT on top + unsigned int NotCount = pushNeg ? 1 : 0; + ASTNode o = a; + //count the number of NOTs in 'a' + while(NOT == o.GetKind()) { + o = o[0]; + NotCount++; + } + + //pushnegation if there are odd number of NOTs + bool pn = (NotCount % 2 == 0) ? false : true; + + if(CheckAlwaysTrueFormMap(o)) { + output = pn ? ASTFalse : ASTTrue; + return output; + } + + if(CheckSimplifyMap(o,output,pn)) { + return output; + } + + if (ASTTrue == o) { + output = pn ? ASTFalse : ASTTrue; + } + else if (ASTFalse == o) { + output = pn ? ASTTrue : ASTFalse; + } + else { + output = SimplifyFormula(o,pn); + } + //memoize + UpdateSimplifyMap(o,output,pn); + UpdateSimplifyMap(a,output,pushNeg); + return output; + } + + ASTNode BeevMgr::SimplifyXorFormula(const ASTNode& a, bool pushNeg) { + ASTNode output; + if(CheckSimplifyMap(a,output,pushNeg)) + return output; + + if (a.GetChildren().size() > 2) { + FatalError("Simplify got an XOR with more than two children."); + } + + ASTNode a0 = SimplifyFormula(a[0],false); + ASTNode a1 = SimplifyFormula(a[1],false); + output = pushNeg ? CreateNode(IFF,a0,a1) : CreateNode(XOR,a0,a1); + + if(XOR == output.GetKind()) { + a0 = output[0]; + a1 = output[1]; + if(a0 == a1) + output = ASTFalse; + else if((a0 == ASTTrue && a1 == ASTFalse) || + (a0 == ASTFalse && a1 == ASTTrue)) + output = ASTTrue; + } + + //memoize + UpdateSimplifyMap(a,output,pushNeg); + return output; + } + + ASTNode BeevMgr::SimplifyNandFormula(const ASTNode& a, bool pushNeg) { + ASTNode output,a0,a1; + if(CheckSimplifyMap(a,output,pushNeg)) + return output; + + //the two NOTs cancel out + if(pushNeg) { + a0 = SimplifyFormula(a[0],false); + a1 = SimplifyFormula(a[1],false); + output = CreateNode(AND,a0,a1); + } + else { + //push the NOT implicit in the NAND + a0 = SimplifyFormula(a[0],true); + a1 = SimplifyFormula(a[1],true); + output = CreateNode(OR,a0,a1); + } + + //memoize + UpdateSimplifyMap(a,output,pushNeg); + return output; + } + + ASTNode BeevMgr::SimplifyNorFormula(const ASTNode& a, bool pushNeg) { + ASTNode output,a0,a1; + if(CheckSimplifyMap(a,output,pushNeg)) + return output; + + //the two NOTs cancel out + if(pushNeg) { + a0 = SimplifyFormula(a[0],false); + a1 = SimplifyFormula(a[1],false); + output = CreateNode(OR,a0,a1); + } + else { + //push the NOT implicit in the NAND + a0 = SimplifyFormula(a[0],true); + a1 = SimplifyFormula(a[1],true); + output = CreateNode(AND,a0,a1); + } + + //memoize + UpdateSimplifyMap(a,output,pushNeg); + return output; + } + + ASTNode BeevMgr::SimplifyImpliesFormula(const ASTNode& a, bool pushNeg) { + ASTNode output; + if(CheckSimplifyMap(a,output,pushNeg)) + return output; + + if(!(a.Degree()==2 && IMPLIES==a.GetKind())) + FatalError("SimplifyImpliesFormula: vector with wrong num of nodes",ASTUndefined); + + ASTNode c0,c1; + if(pushNeg) { + c0 = SimplifyFormula(a[0],false); + c1 = SimplifyFormula(a[1],true); + output = CreateNode(AND,c0,c1); + } + else { + c0 = SimplifyFormula(a[0],false); + c1 = SimplifyFormula(a[1],false); + if(ASTFalse == c0) { + output = ASTTrue; + } + else if (ASTTrue == c0) { + output = c1; + } + else if (c0 == c1) { + output = ASTTrue; + } + else if(CheckAlwaysTrueFormMap(c0)) { + // c0 AND (~c0 OR c1) <==> c1 + // + //applying modus ponens + output = c1; + } + else if(CheckAlwaysTrueFormMap(c1) || + CheckAlwaysTrueFormMap(CreateNode(NOT,c0)) || + (NOT == c0.GetKind() && CheckAlwaysTrueFormMap(c0[0]))) { + //(~c0 AND (~c0 OR c1)) <==> TRUE + // + //(c0 AND ~c0->c1) <==> TRUE + output = ASTTrue; + } + else if (CheckAlwaysTrueFormMap(CreateNode(NOT,c1)) || + (NOT == c1.GetKind() && CheckAlwaysTrueFormMap(c1[0]))) { + //(~c1 AND c0->c1) <==> (~c1 AND ~c1->~c0) <==> ~c0 + //(c1 AND c0->~c1) <==> (c1 AND c1->~c0) <==> ~c0 + output = CreateNode(NOT,c0); + } + else { + if(NOT == c0.GetKind()) { + output = CreateNode(OR,c0[0],c1); + } + else { + output = CreateNode(OR,CreateNode(NOT,c0),c1); + } + } + } + + //memoize + UpdateSimplifyMap(a,output,pushNeg); + return output; + } + + ASTNode BeevMgr::SimplifyIffFormula(const ASTNode& a, bool pushNeg) { + ASTNode output; + if(CheckSimplifyMap(a,output,pushNeg)) + return output; + + if(!(a.Degree()==2 && IFF==a.GetKind())) + FatalError("SimplifyIffFormula: vector with wrong num of nodes",ASTUndefined); + + ASTNode c0 = a[0]; + ASTNode c1 = SimplifyFormula(a[1],false); + + if(pushNeg) + c0 = SimplifyFormula(c0,true); + else + c0 = SimplifyFormula(c0,false); + + if(ASTTrue == c0) { + output = c1; + } + else if (ASTFalse == c0) { + output = SimplifyFormula(c1,true); + } + else if (ASTTrue == c1) { + output = c0; + } + else if (ASTFalse == c1) { + output = SimplifyFormula(c0,true); + } + else if (c0 == c1) { + output = ASTTrue; + } + else if((NOT == c0.GetKind() && c0[0] == c1) || + (NOT == c1.GetKind() && c0 == c1[0])) { + output = ASTFalse; + } + else if(CheckAlwaysTrueFormMap(c0)) { + output = c1; + } + else if(CheckAlwaysTrueFormMap(c1)) { + output = c0; + } + else if(CheckAlwaysTrueFormMap(CreateNode(NOT,c0))) { + output = CreateNode(NOT,c1); + } + else if(CheckAlwaysTrueFormMap(CreateNode(NOT,c1))) { + output = CreateNode(NOT,c0); + } + else { + output = CreateNode(IFF,c0,c1); + } + + //memoize + UpdateSimplifyMap(a,output,pushNeg); + return output; + } + + ASTNode BeevMgr::SimplifyIteFormula(const ASTNode& b, bool pushNeg) { + if(!optimize) + return b; + + ASTNode output; + if(CheckSimplifyMap(b,output,pushNeg)) + return output; + + if(!(b.Degree() == 3 && ITE == b.GetKind())) + FatalError("SimplifyIteFormula: vector with wrong num of nodes",ASTUndefined); + + ASTNode a = b; + ASTNode t0 = SimplifyFormula(a[0],false); + ASTNode t1,t2; + if(pushNeg) { + t1 = SimplifyFormula(a[1],true); + t2 = SimplifyFormula(a[2],true); + } + else { + t1 = SimplifyFormula(a[1],false); + t2 = SimplifyFormula(a[2],false); + } + + if(ASTTrue == t0) { + output = t1; + } + else if (ASTFalse == t0) { + output = t2; + } + else if (t1 == t2) { + output = t1; + } + else if(ASTTrue == t1 && ASTFalse == t2) { + output = t0; + } + else if(ASTFalse == t1 && ASTTrue == t2) { + output = SimplifyFormula(t0,true); + } + else if(ASTTrue == t1) { + output = CreateNode(OR,t0,t2); + } + else if(ASTFalse == t1) { + output = CreateNode(AND,CreateNode(NOT,t0),t2); + } + else if(ASTTrue == t2) { + output = CreateNode(OR,CreateNode(NOT,t0),t1); + } + else if(ASTFalse == t2) { + output = CreateNode(AND,t0,t1); + } + else if(CheckAlwaysTrueFormMap(t0)) { + output = t1; + } + else if(CheckAlwaysTrueFormMap(CreateNode(NOT,t0)) || + (NOT == t0.GetKind() && CheckAlwaysTrueFormMap(t0[0]))) { + output = t2; + } + else { + output = CreateNode(ITE,t0,t1,t2); + } + + //memoize + UpdateSimplifyMap(a,output,pushNeg); + return output; + } + + //one level deep flattening + ASTNode BeevMgr::FlattenOneLevel(const ASTNode& a) { + Kind k = a.GetKind(); + if(!(BVPLUS == k || + AND == k || OR == k + //|| BVAND == k + //|| BVOR == k + ) + ) { + return a; + } + + ASTNode output; + // if(CheckSimplifyMap(a,output,false)) { + // //check memo table + // //cerr << "output of SimplifyTerm Cache: " << output << endl; + // return output; + // } + + ASTVec c = a.GetChildren(); + ASTVec o; + for(ASTVec::iterator it=c.begin(),itend=c.end();it!=itend;it++) { + ASTNode aaa = *it; + if(k == aaa.GetKind()) { + ASTVec ac = aaa.GetChildren(); + o.insert(o.end(),ac.begin(),ac.end()); + } + else + o.push_back(aaa); + } + + if(is_Form_kind(k)) + output = CreateNode(k,o); + else + output = CreateTerm(k,a.GetValueWidth(),o); + + //UpdateSimplifyMap(a,output,false); + return output; + //memoize + } //end of flattenonelevel() + + ASTNode BeevMgr::SimplifyTerm_TopLevel(const ASTNode& b) { + SimplifyMap.clear(); + SimplifyNegMap.clear(); + ASTNode out = SimplifyTerm(b); + SimplifyNegMap.clear(); + SimplifyMap.clear(); + return out; + } + + //This function simplifies terms based on their kind + ASTNode BeevMgr::SimplifyTerm(const ASTNode& inputterm) { + //cout << "SimplifyTerm: input: " << a << endl; + if(!optimize) { + return inputterm; + } + + BVTypeCheck(inputterm); + ASTNode output; + if(wordlevel_solve && CheckSolverMap(inputterm,output)) { + //cout << "SimplifyTerm: output: " << output << endl; + return SimplifyTerm(output); + } + + if(CheckSimplifyMap(inputterm,output,false)) { + //cerr << "output of SimplifyTerm Cache: " << output << endl; + return output; + } + + Kind k = inputterm.GetKind(); + if(!is_Term_kind(k)) { + FatalError("SimplifyTerm: You have input a Non-term",ASTUndefined); + } + + unsigned int inputValueWidth = inputterm.GetValueWidth(); + switch(k) { + case BVCONST: + output = inputterm; + break; + case SYMBOL: + if(CheckSolverMap(inputterm,output)) { + return SimplifyTerm(output); + } + output = inputterm; + break; + case BVMULT: + case BVPLUS:{ + if(BVMULT == k && 2 != inputterm.Degree()) { + FatalError("SimplifyTerm: We assume that BVMULT is binary",inputterm); + } + + ASTVec c = FlattenOneLevel(inputterm).GetChildren(); + SortByExprNum(c); + ASTVec constkids, nonconstkids; + + //go through the childnodes, and separate constant and + //nonconstant nodes. combine the constant nodes using the + //constevaluator. if the resultant constant is zero and k == + //BVPLUS, then ignore it (similarily for 1 and BVMULT). else, + //add the computed constant to the nonconst vector, flatten, + //sort, and create BVPLUS/BVMULT and return + for(ASTVec::iterator it=c.begin(),itend=c.end();it!=itend;it++) { + ASTNode aaa = SimplifyTerm(*it); + if(BVCONST == aaa.GetKind()) { + constkids.push_back(aaa); + } + else { + nonconstkids.push_back(aaa); + } + } + + ASTNode one = CreateOneConst(inputValueWidth); + ASTNode max = CreateMaxConst(inputValueWidth); + ASTNode zero = CreateZeroConst(inputValueWidth); + + //initialize constoutput to zero, in case there are no elements + //in constkids + ASTNode constoutput = (k == BVPLUS) ? zero : one; + + if(1 == constkids.size()) { + //only one element in constkids + constoutput = constkids[0]; + } + else if (1 < constkids.size()) { + //many elements in constkids. simplify it + constoutput = CreateTerm(k,inputterm.GetValueWidth(),constkids); + constoutput = BVConstEvaluator(constoutput); + } + + if(BVMULT == k && zero == constoutput) { + output = zero; + } + else if(BVMULT == k && + 1 == nonconstkids.size() && + constoutput == max) { + //useful special case opt: when input is BVMULT(max_const,t), + //then output = BVUMINUS(t). this is easier on the bitblaster + output = CreateTerm(BVUMINUS,inputValueWidth,nonconstkids); + } + else { + if(0 < nonconstkids.size()) { + //nonconstkids is not empty. First, combine const and + //nonconstkids + if(BVPLUS == k && constoutput != zero) { + nonconstkids.push_back(constoutput); + } + else if(BVMULT == k && constoutput != one) { + nonconstkids.push_back(constoutput); + } + + if(1 == nonconstkids.size()) { + //exactly one element in nonconstkids. output is exactly + //nonconstkids[0] + output = nonconstkids[0]; + } + else { + //more than 1 element in nonconstkids. create BVPLUS term + SortByExprNum(nonconstkids); + output = CreateTerm(k,inputValueWidth,nonconstkids); + output = FlattenOneLevel(output); + output = DistributeMultOverPlus(output,true); + output = CombineLikeTerms(output); + } + } + else { + //nonconstkids was empty, all childnodes were constant, hence + //constoutput is the output. + output = constoutput; + } + } + if(BVMULT == output.GetKind() + || BVPLUS == output.GetKind() + ) { + ASTVec d = output.GetChildren(); + SortByExprNum(d); + output = CreateTerm(output.GetKind(),output.GetValueWidth(),d); + } + break; + } + case BVSUB: { + ASTVec c = inputterm.GetChildren(); + ASTNode a0 = SimplifyTerm(inputterm[0]); + ASTNode a1 = SimplifyTerm(inputterm[1]); + unsigned int l = inputValueWidth; + if(a0 == a1) + output = CreateZeroConst(l); + else { + //covert x-y into x+(-y) and simplify. this transformation + //triggers more simplifications + a1 = SimplifyTerm(CreateTerm(BVUMINUS,l,a1)); + output = SimplifyTerm(CreateTerm(BVPLUS,l,a0,a1)); + } + break; + } + case BVUMINUS: { + //important to treat BVUMINUS as a special case, because it + //helps in arithmetic transformations. e.g. x + BVUMINUS(x) is + //actually 0. One way to reveal this fact is to strip bvuminus + //out, and replace with something else so that combineliketerms + //can catch this fact. + ASTNode a0 = SimplifyTerm(inputterm[0]); + Kind k1 = a0.GetKind(); + unsigned int l = a0.GetValueWidth(); + ASTNode one = CreateOneConst(l); + switch(k1) { + case BVUMINUS: + output = a0[0]; + break; + case BVCONST: { + output = BVConstEvaluator(CreateTerm(BVUMINUS,l,a0)); + break; + } + case BVNEG: { + output = SimplifyTerm(CreateTerm(BVPLUS,l,a0[0],one)); + break; + } + case BVMULT: { + if(BVUMINUS == a0[0].GetKind()) { + output = CreateTerm(BVMULT,l,a0[0][0],a0[1]); + } + else if(BVUMINUS == a0[1].GetKind()) { + output = CreateTerm(BVMULT,l,a0[0],a0[1][0]); + } + else { + ASTNode a00 = SimplifyTerm(CreateTerm(BVUMINUS,l,a0[0])); + output = CreateTerm(BVMULT,l,a00,a0[1]); + } + break; + } + case BVPLUS: { + //push BVUMINUS over all the monomials of BVPLUS. Simplify + //along the way + // + //BVUMINUS(a1x1 + a2x2 + ...) <=> BVPLUS(BVUMINUS(a1x1) + + //BVUMINUS(a2x2) + ... + ASTVec c = a0.GetChildren(); + ASTVec o; + for(ASTVec::iterator it=c.begin(),itend=c.end();it!=itend;it++) { + //Simplify(BVUMINUS(a1x1)) + ASTNode aaa = SimplifyTerm(CreateTerm(BVUMINUS,l,*it)); + o.push_back(aaa); + } + //simplify the bvplus + output = SimplifyTerm(CreateTerm(BVPLUS,l,o)); + break; + } + case BVSUB: { + //BVUMINUS(BVSUB(x,y)) <=> BVSUB(y,x) + output = SimplifyTerm(CreateTerm(BVSUB,l,a0[1],a0[0])); + break; + } + case ITE: { + //BVUMINUS(ITE(c,t1,t2)) <==> ITE(c,BVUMINUS(t1),BVUMINUS(t2)) + ASTNode c = a0[0]; + ASTNode t1 = SimplifyTerm(CreateTerm(BVUMINUS,l,a0[1])); + ASTNode t2 = SimplifyTerm(CreateTerm(BVUMINUS,l,a0[2])); + output = CreateSimplifiedTermITE(c,t1,t2); + break; + } + default: { + output = CreateTerm(BVUMINUS,l,a0); + break; + } + } + break; + } + case BVEXTRACT:{ + //it is important to take care of wordlevel transformation in + //BVEXTRACT. it exposes oppurtunities for later simplification + //and solving (variable elimination) + ASTNode a0 = SimplifyTerm(inputterm[0]); + Kind k1 = a0.GetKind(); + unsigned int a_len = inputValueWidth; + + //indices for BVEXTRACT + ASTNode i = inputterm[1]; + ASTNode j = inputterm[2]; + ASTNode zero = CreateBVConst(32,0); + //recall that the indices of BVEXTRACT are always 32 bits + //long. therefore doing a GetBVUnsigned is ok. + unsigned int i_val = GetUnsignedConst(i); + unsigned int j_val = GetUnsignedConst(j); + + // a0[i:0] and len(a0)=i+1, then return a0 + if(0 == j_val && a_len == a0.GetValueWidth()) + return a0; + + switch(k1) { + case BVCONST: { + //extract the constant + output = BVConstEvaluator(CreateTerm(BVEXTRACT,a_len,a0,i,j)); + break; + } + case BVCONCAT:{ + //assumes concatenation is binary + // + //input is of the form a0[i:j] + // + //a0 is the conatentation t@u, and a0[0] is t, and a0[1] is u + ASTNode t = a0[0]; + ASTNode u = a0[1]; + unsigned int len_a0 = a0.GetValueWidth(); + unsigned int len_u = u.GetValueWidth(); + + if(len_u > i_val) { + //Apply the following rule: + // (t@u)[i:j] <==> u[i:j], if len(u) > i + // + output = SimplifyTerm(CreateTerm(BVEXTRACT,a_len,u,i,j)); + } + else if(len_a0 > i_val && j_val >= len_u) { + //Apply the rule: + // (t@u)[i:j] <==> t[i-len_u:j-len_u], if len(t@u) > i >= j >= len(u) + i = CreateBVConst(32, i_val - len_u); + j = CreateBVConst(32, j_val - len_u); + output = SimplifyTerm(CreateTerm(BVEXTRACT,a_len,t,i,j)); + } + else { + //Apply the rule: + // (t@u)[i:j] <==> t[i-len_u:0] @ u[len_u-1:j] + i = CreateBVConst(32,i_val-len_u); + ASTNode m = CreateBVConst(32, len_u-1); + t = SimplifyTerm(CreateTerm(BVEXTRACT,i_val-len_u+1,t,i,zero)); + u = SimplifyTerm(CreateTerm(BVEXTRACT,len_u-j_val,u,m,j)); + output = CreateTerm(BVCONCAT,a_len,t,u); + } + break; + } + case BVPLUS: + case BVMULT: { + // (BVMULT(n,t,u))[i:j] <==> BVMULT(i+1,t[i:0],u[i:0])[i:j] + //similar rule for BVPLUS + ASTVec c = a0.GetChildren(); + ASTVec o; + for(ASTVec::iterator jt=c.begin(),jtend=c.end();jt!=jtend;jt++) { + ASTNode aaa = *jt; + aaa = SimplifyTerm(CreateTerm(BVEXTRACT,i_val+1,aaa,i,zero)); + o.push_back(aaa); + } + output = CreateTerm(a0.GetKind(),i_val+1,o); + if(j_val != 0) { + //add extraction only if j is not zero + output = CreateTerm(BVEXTRACT,a_len,output,i,j); + } + break; + } + case BVAND: + case BVOR: + case BVXOR: { + //assumes these operators are binary + // + // (t op u)[i:j] <==> t[i:j] op u[i:j] + ASTNode t = a0[0]; + ASTNode u = a0[1]; + t = SimplifyTerm(CreateTerm(BVEXTRACT,a_len,t,i,j)); + u = SimplifyTerm(CreateTerm(BVEXTRACT,a_len,u,i,j)); + BVTypeCheck(t); + BVTypeCheck(u); + output = CreateTerm(k1,a_len,t,u); + break; + } + case BVNEG:{ + // (~t)[i:j] <==> ~(t[i:j]) + ASTNode t = a0[0]; + t = SimplifyTerm(CreateTerm(BVEXTRACT,a_len,t,i,j)); + output = CreateTerm(BVNEG,a_len,t); + break; + } + // case BVSX:{ +// //(BVSX(t,n)[i:j] <==> BVSX(t,i+1), if n >= i+1 and j=0 +// ASTNode t = a0[0]; +// unsigned int bvsx_len = a0.GetValueWidth(); +// if(bvsx_len < a_len) { +// FatalError("SimplifyTerm: BVEXTRACT over BVSX:" +// "the length of BVSX term must be greater than extract-len",inputterm); +// } +// if(j != zero) { +// output = CreateTerm(BVEXTRACT,a_len,a0,i,j); +// } +// else { +// output = CreateTerm(BVSX,a_len,t,CreateBVConst(32,a_len)); +// } +// break; +// } + case ITE: { + ASTNode t0 = a0[0]; + ASTNode t1 = SimplifyTerm(CreateTerm(BVEXTRACT,a_len,a0[1],i,j)); + ASTNode t2 = SimplifyTerm(CreateTerm(BVEXTRACT,a_len,a0[2],i,j)); + output = CreateSimplifiedTermITE(t0,t1,t2); + break; + } + default: { + output = CreateTerm(BVEXTRACT,a_len,a0,i,j); + break; + } + } + break; + } + case BVNEG: { + ASTNode a0 = SimplifyTerm(inputterm[0]); + unsigned len = inputValueWidth; + switch(a0.GetKind()) { + case BVCONST: + output = BVConstEvaluator(CreateTerm(BVNEG,len,a0)); + break; + case BVNEG: + output = a0[0]; + break; + // case ITE: { +// ASTNode cond = a0[0]; +// ASTNode thenpart = SimplifyTerm(CreateTerm(BVNEG,len,a0[1])); +// ASTNode elsepart = SimplifyTerm(CreateTerm(BVNEG,len,a0[2])); +// output = CreateSimplifiedTermITE(cond,thenpart,elsepart); +// break; +// } + default: + output = CreateTerm(BVNEG,len,a0); + break; + } + break; + } + case BVSX:{ + //a0 is the expr which is being sign extended + ASTNode a0 = SimplifyTerm(inputterm[0]); + //a1 represents the length of the term BVSX(a0) + ASTNode a1 = inputterm[1]; + //output length of the BVSX term + unsigned len = inputValueWidth; + + if(a0.GetValueWidth() == len) { + //nothing to signextend + return a0; + } + + switch(a0.GetKind()) { + case BVCONST: + output = BVConstEvaluator(CreateTerm(BVSX,len,a0,a1)); + break; + case BVNEG: + output = CreateTerm(a0.GetKind(),len,CreateTerm(BVSX,len,a0[0],a1)); + break; + case BVAND: + case BVOR: + //assuming BVAND and BVOR are binary + output = CreateTerm(a0.GetKind(),len, + CreateTerm(BVSX,len,a0[0],a1), + CreateTerm(BVSX,len,a0[1],a1)); + break; + case BVPLUS: { + //BVSX(m,BVPLUS(n,BVSX(t1),BVSX(t2))) <==> BVPLUS(m,BVSX(m,t1),BVSX(m,t2)) + ASTVec c = a0.GetChildren(); + bool returnflag = false; + for(ASTVec::iterator it=c.begin(),itend=c.end();it!=itend;it++) { + if(BVSX != it->GetKind()) { + returnflag = true; + break; + } + } + if(returnflag) { + output = CreateTerm(BVSX,len,a0,a1); + } + else { + ASTVec o; + for(ASTVec::iterator it=c.begin(),itend=c.end();it!=itend;it++) { + ASTNode aaa = SimplifyTerm(CreateTerm(BVSX,len,*it,a1)); + o.push_back(aaa); + } + output = CreateTerm(a0.GetKind(),len,o); + } + break; + } + case BVSX: { + //if you have BVSX(m,BVSX(n,a)) then you can drop the inner + //BVSX provided m is greater than n. + a0 = SimplifyTerm(a0[0]); + output = CreateTerm(BVSX,len,a0,a1); + break; + } + case ITE: { + ASTNode cond = a0[0]; + ASTNode thenpart = SimplifyTerm(CreateTerm(BVSX,len,a0[1],a1)); + ASTNode elsepart = SimplifyTerm(CreateTerm(BVSX,len,a0[2],a1)); + output = CreateSimplifiedTermITE(cond,thenpart,elsepart); + break; + } + default: + output = CreateTerm(BVSX,len,a0,a1); + break; + } + break; + } + case BVAND: + case BVOR:{ + ASTNode max = CreateMaxConst(inputValueWidth); + ASTNode zero = CreateZeroConst(inputValueWidth); + + ASTNode identity = (BVAND == k) ? max : zero; + ASTNode annihilator = (BVAND == k) ? zero : max; + ASTVec c = FlattenOneLevel(inputterm).GetChildren(); + SortByExprNum(c); + ASTVec o; + bool constant = true; + for(ASTVec::iterator it=c.begin(),itend=c.end();it!=itend;it++) { + ASTNode aaa = SimplifyTerm(*it); + if(BVCONST != aaa.GetKind()) { + constant = false; + } + + if(aaa == annihilator) { + output = annihilator; + //memoize + UpdateSimplifyMap(inputterm,output,false); + //cerr << "output of SimplifyTerm: " << output << endl; + return output; + } + + if(aaa != identity) { + o.push_back(aaa); + } + } + + switch(o.size()) { + case 0: + output = identity; + break; + case 1: + output = o[0]; + break; + default: + SortByExprNum(o); + output = CreateTerm(k,inputValueWidth,o); + if(constant) { + output = BVConstEvaluator(output); + } + break; + } + break; + } + case BVCONCAT:{ + ASTNode t = SimplifyTerm(inputterm[0]); + ASTNode u = SimplifyTerm(inputterm[1]); + Kind tkind = t.GetKind(); + Kind ukind = u.GetKind(); + + + if(BVCONST == tkind && BVCONST == ukind) { + output = BVConstEvaluator(CreateTerm(BVCONCAT,inputValueWidth,t,u)); + } + else if(BVEXTRACT == tkind && + BVEXTRACT == ukind && + t[0] == u[0]) { + //to handle the case x[m:n]@x[n-1:k] <==> x[m:k] + ASTNode t_hi = t[1]; + ASTNode t_low = t[2]; + ASTNode u_hi = u[1]; + ASTNode u_low = u[2]; + ASTNode c = BVConstEvaluator(CreateTerm(BVPLUS,32,u_hi,CreateOneConst(32))); + if(t_low == c) { + output = CreateTerm(BVEXTRACT,inputValueWidth,t[0],t_hi,u_low); + } + else { + output = CreateTerm(BVCONCAT,inputValueWidth,t,u); + } + } + else { + output = CreateTerm(BVCONCAT,inputValueWidth,t,u); + } + break; + } + case BVXOR: + case BVXNOR: + case BVNAND: + case BVNOR: + case BVLEFTSHIFT: + case BVRIGHTSHIFT: + case BVVARSHIFT: + case BVSRSHIFT: + case BVDIV: + case BVMOD: { + ASTVec c = inputterm.GetChildren(); + ASTVec o; + bool constant = true; + for(ASTVec::iterator it=c.begin(),itend=c.end();it!=itend;it++) { + ASTNode aaa = SimplifyTerm(*it); + if(BVCONST != aaa.GetKind()) { + constant = false; + } + o.push_back(aaa); + } + output = CreateTerm(k,inputValueWidth,o); + if(constant) + output = BVConstEvaluator(output); + break; + } + case READ: { + ASTNode out1; + //process only if not in the substitution map. simplifymap + //has been checked already + if(!CheckSubstitutionMap(inputterm,out1)) { + if(WRITE == inputterm[0].GetKind()) { + //get rid of all writes + ASTNode nowrites = RemoveWrites_TopLevel(inputterm); + out1 = nowrites; + } + else if (ITE == inputterm[0].GetKind()){ + ASTNode cond = SimplifyFormula(inputterm[0][0],false); + ASTNode arr1 = SimplifyTerm(inputterm[0][1]); + ASTNode arr2 = SimplifyTerm(inputterm[0][2]); + + ASTNode index = SimplifyTerm(inputterm[1]); + + ASTNode read1 = CreateTerm(READ,inputValueWidth,arr1,index); + ASTNode read2 = CreateTerm(READ,inputValueWidth,arr2,index); + out1 = CreateSimplifiedTermITE(cond,read1,read2); + } + else { + //arr is a SYMBOL for sure + ASTNode arr = inputterm[0]; + ASTNode index = SimplifyTerm(inputterm[1]); + out1 = CreateTerm(READ,inputValueWidth,arr,index); + } + } + //it is possible that after all the procesing the READ term + //reduces to READ(Symbol,const) and hence we should check the + //substitutionmap once again. + if(!CheckSubstitutionMap(out1,output)) + output = out1; + break; + } + case ITE: { + ASTNode t0 = SimplifyFormula(inputterm[0],false); + ASTNode t1 = SimplifyTerm(inputterm[1]); + ASTNode t2 = SimplifyTerm(inputterm[2]); + output = CreateSimplifiedTermITE(t0,t1,t2); + break; + } + case SBVMOD: + case SBVDIV: { + ASTVec c = inputterm.GetChildren(); + ASTVec o; + for(ASTVec::iterator it=c.begin(),itend=c.end();it!=itend;it++) { + ASTNode aaa = SimplifyTerm(*it); + o.push_back(aaa); + } + output = CreateTerm(k,inputValueWidth,o); + break; + } + case WRITE: + default: + FatalError("SimplifyTerm: Control should never reach here:", inputterm, k); + return inputterm; + break; + } + + //memoize + UpdateSimplifyMap(inputterm,output,false); + //cerr << "SimplifyTerm: output" << output << endl; + return output; + } //end of SimplifyTerm() + + + //At the end of each simplification call, we want the output to be + //always smaller or equal to the input in size. + void BeevMgr::CheckSimplifyInvariant(const ASTNode& a, const ASTNode& output) { + //Don't do the check in optimized mode + if(optimize) + return; + + if(NodeSize(a,true) < NodeSize(output,true)) { + cerr << "lhs := " << a << endl; + cerr << "NodeSize of lhs is: " << NodeSize(a, true) << endl; + cerr << endl; + cerr << "rhs := " << output << endl; + cerr << "NodeSize of rhs is: " << NodeSize(output, true) << endl; + FatalError("SimplifyFormula: The nodesize shoudl decrease from lhs to rhs: ",ASTUndefined); + } + } + + //this function assumes that the input is a vector of childnodes of + //a BVPLUS term. it combines like terms and returns a bvplus + //term. e.g. 1.x + 2.x is converted to 3.x + ASTNode BeevMgr::CombineLikeTerms(const ASTNode& a) { + if(BVPLUS != a.GetKind()) + return a; + + ASTNode output; + if(CheckSimplifyMap(a,output,false)) { + //check memo table + //cerr << "output of SimplifyTerm Cache: " << output << endl; + return output; + } + + ASTVec c = a.GetChildren(); + //map from variables to vector of constants + ASTNodeToVecMap vars_to_consts; + //vector to hold constants + ASTVec constkids; + ASTVec outputvec; + + //useful constants + unsigned int len = c[0].GetValueWidth(); + ASTNode one = CreateOneConst(len); + ASTNode zero = CreateZeroConst(len); + ASTNode max = CreateMaxConst(len); + + //go over the childnodes of the input bvplus, and collect like + //terms in a map. the key of the map are the variables, and the + //values are stored in a ASTVec + for(ASTVec::const_iterator it=c.begin(),itend=c.end();it!=itend;it++){ + ASTNode aaa = *it; + if(SYMBOL == aaa.GetKind()) { + vars_to_consts[aaa].push_back(one); + } + else if(BVMULT == aaa.GetKind() && + BVUMINUS == aaa[0].GetKind() && + BVCONST == aaa[0][0].GetKind()) { + //(BVUMINUS(c))*(y) <==> compute(BVUMINUS(c))*y + ASTNode compute_const = BVConstEvaluator(aaa[0]); + vars_to_consts[aaa[1]].push_back(compute_const); + } + else if(BVMULT == aaa.GetKind() && + BVUMINUS == aaa[1].GetKind() && + BVCONST == aaa[0].GetKind()) { + //c*(BVUMINUS(y)) <==> compute(BVUMINUS(c))*y + ASTNode cccc = BVConstEvaluator(CreateTerm(BVUMINUS,len,aaa[0])); + vars_to_consts[aaa[1][0]].push_back(cccc); + } + else if(BVMULT == aaa.GetKind() && BVCONST == aaa[0].GetKind()) { + //assumes that BVMULT is binary + vars_to_consts[aaa[1]].push_back(aaa[0]); + } + else if(BVMULT == aaa.GetKind() && BVUMINUS == aaa[0].GetKind()) { + //(-1*x)*(y) <==> -1*(xy) + ASTNode cccc = CreateTerm(BVMULT,len,aaa[0][0],aaa[1]); + ASTVec cNodes = cccc.GetChildren(); + SortByExprNum(cNodes); + vars_to_consts[cccc].push_back(max); + } + else if(BVMULT == aaa.GetKind() && BVUMINUS == aaa[1].GetKind()) { + //x*(-1*y) <==> -1*(xy) + ASTNode cccc = CreateTerm(BVMULT,len,aaa[0],aaa[1][0]); + ASTVec cNodes = cccc.GetChildren(); + SortByExprNum(cNodes); + vars_to_consts[cccc].push_back(max); + } + else if(BVCONST == aaa.GetKind()) { + constkids.push_back(aaa); + } + else if(BVUMINUS == aaa.GetKind()) { + //helps to convert BVUMINUS into a BVMULT. here the max + //constant represents -1. this transformation allows us to + //conclude that x + BVUMINUS(x) is 0. + vars_to_consts[aaa[0]].push_back(max); + } + else + vars_to_consts[aaa].push_back(one); + } //end of for loop + + //go over the map from variables to vector of values. combine the + //vector of values, multiply to the variable, and put the + //resultant monomial in the output BVPLUS. + for(ASTNodeToVecMap::iterator it=vars_to_consts.begin(),itend=vars_to_consts.end(); + it!=itend;it++){ + ASTVec ccc = it->second; + + ASTNode constant; + if(1 < ccc.size()) { + constant = CreateTerm(BVPLUS,ccc[0].GetValueWidth(),ccc); + constant = BVConstEvaluator(constant); + } + else + constant = ccc[0]; + + //constant * var + ASTNode monom; + if(zero == constant) + monom = zero; + else if (one == constant) + monom = it->first; + else + monom = + SimplifyTerm(CreateTerm(BVMULT,constant.GetValueWidth(),constant,it->first)); + if(zero != monom) { + outputvec.push_back(monom); + } + } //end of for loop + + if(constkids.size() > 1) { + ASTNode output = CreateTerm(BVPLUS,constkids[0].GetValueWidth(),constkids); + output = BVConstEvaluator(output); + if(output != zero) + outputvec.push_back(output); + } + else if (constkids.size() == 1) { + if(constkids[0] != zero) + outputvec.push_back(constkids[0]); + } + + if (outputvec.size() > 1) { + output = CreateTerm(BVPLUS,len,outputvec); + } + else if(outputvec.size() == 1) { + output = outputvec[0]; + } + else { + output = zero; + } + + //memoize + //UpdateSimplifyMap(a,output,false); + return output; + } //end of CombineLikeTerms() + + //accepts lhs and rhs, and returns lhs - rhs = 0. The function + //assumes that lhs and rhs have already been simplified. although + //this assumption is not needed for correctness, it is essential for + //performance. The function also assumes that lhs is a BVPLUS + ASTNode BeevMgr::LhsMinusRhs(const ASTNode& eq) { + //if input is not an equality, simply return it + if(EQ != eq.GetKind()) + return eq; + + ASTNode lhs = eq[0]; + ASTNode rhs = eq[1]; + Kind k_lhs = lhs.GetKind(); + Kind k_rhs = rhs.GetKind(); + //either the lhs has to be a BVPLUS or the rhs has to be a + //BVPLUS + if(!(BVPLUS == k_lhs || + BVPLUS == k_rhs || + (BVMULT == k_lhs && + BVMULT == k_rhs) + )) { + return eq; + } + + ASTNode output; + if(CheckSimplifyMap(eq,output,false)) { + //check memo table + //cerr << "output of SimplifyTerm Cache: " << output << endl; + return output; + } + + //if the lhs is not a BVPLUS, but the rhs is a BVPLUS, then swap + //the lhs and rhs + bool swap_flag = false; + if(BVPLUS != k_lhs && BVPLUS == k_rhs) { + ASTNode swap = lhs; + lhs = rhs; + rhs = swap; + swap_flag = true; + } + + unsigned int len = lhs.GetValueWidth(); + ASTNode zero = CreateZeroConst(len); + //right is -1*(rhs): Simplify(-1*rhs) + rhs = SimplifyTerm(CreateTerm(BVUMINUS,len,rhs)); + + ASTVec lvec = lhs.GetChildren(); + ASTVec rvec = rhs.GetChildren(); + ASTNode lhsplusrhs; + if(BVPLUS != lhs.GetKind() && BVPLUS != rhs.GetKind()) { + lhsplusrhs = CreateTerm(BVPLUS,len,lhs,rhs); + } + else if(BVPLUS == lhs.GetKind() && BVPLUS == rhs.GetKind()) { + //combine the childnodes of the left and the right + lvec.insert(lvec.end(),rvec.begin(),rvec.end()); + lhsplusrhs = CreateTerm(BVPLUS,len,lvec); + } + else if(BVPLUS == lhs.GetKind() && BVPLUS != rhs.GetKind()){ + lvec.push_back(rhs); + lhsplusrhs = CreateTerm(BVPLUS,len,lvec); + } + else { + //Control should never reach here + FatalError("LhsMinusRhs: Control should never reach here\n"); + } + + //combine like terms + output = CombineLikeTerms(lhsplusrhs); + output = SimplifyTerm(output); + // + //Now make output into: lhs-rhs = 0 + output = CreateSimplifiedEQ(output,zero); + //sort if BVPLUS + if(BVPLUS == output.GetKind()) { + ASTVec outv = output.GetChildren(); + SortByExprNum(outv); + output = CreateTerm(BVPLUS,len,outv); + } + + //memoize + //UpdateSimplifyMap(eq,output,false); + return output; + } //end of LhsMinusRHS() + + //THis function accepts a BVMULT(t1,t2) and distributes the mult + //over plus if either or both t1 and t2 are BVPLUSes. + // + // x*(y1 + y2 + ...+ yn) <==> x*y1 + x*y2 + ... + x*yn + // + // (y1 + y2 + ...+ yn)*x <==> x*y1 + x*y2 + ... + x*yn + // + // The function assumes that the BVPLUSes have been flattened + ASTNode BeevMgr::DistributeMultOverPlus(const ASTNode& a, bool startdistribution) { + if(!startdistribution) + return a; + Kind k = a.GetKind(); + if(BVMULT != k) + return a; + + ASTNode left = a[0]; + ASTNode right = a[1]; + Kind left_kind = left.GetKind(); + Kind right_kind = right.GetKind(); + + ASTNode output; + if(CheckSimplifyMap(a,output,false)) { + //check memo table + //cerr << "output of SimplifyTerm Cache: " << output << endl; + return output; + } + + //special case optimization: c1*(c2*t1) <==> (c1*c2)*t1 + if(BVCONST == left_kind && + BVMULT == right_kind && + BVCONST == right[0].GetKind()) { + ASTNode c = BVConstEvaluator(CreateTerm(BVMULT,a.GetValueWidth(),left,right[0])); + c = CreateTerm(BVMULT,a.GetValueWidth(),c,right[1]); + return c; + left = c[0]; + right = c[1]; + left_kind = left.GetKind(); + right_kind = right.GetKind(); + } + + //special case optimization: c1*(t1*c2) <==> (c1*c2)*t1 + if(BVCONST == left_kind && + BVMULT == right_kind && + BVCONST == right[1].GetKind()) { + ASTNode c = BVConstEvaluator(CreateTerm(BVMULT,a.GetValueWidth(),left,right[1])); + c = CreateTerm(BVMULT,a.GetValueWidth(),c,right[0]); + return c; + left = c[0]; + right = c[1]; + left_kind = left.GetKind(); + right_kind = right.GetKind(); + } + + //atleast one of left or right have to be BVPLUS + if(!(BVPLUS == left_kind || BVPLUS == right_kind)) { + return a; + } + + //if left is BVPLUS and right is not, then swap left and right. we + //can do this since BVMULT is communtative + ASTNode swap; + if(BVPLUS == left_kind && BVPLUS != right_kind) { + swap = left; + left = right; + right = swap; + } + left_kind = left.GetKind(); + right_kind = right.GetKind(); + + //by this point we are gauranteed that right is a BVPLUS, but left + //may not be + ASTVec rightnodes = right.GetChildren(); + ASTVec outputvec; + unsigned len = a.GetValueWidth(); + ASTNode zero = CreateZeroConst(len); + ASTNode one = CreateOneConst(len); + if(BVPLUS != left_kind) { + //if the multiplier is not a BVPLUS then we have a special case + // x*(y1 + y2 + ...+ yn) <==> x*y1 + x*y2 + ... + x*yn + if(zero == left) { + outputvec.push_back(zero); + } + else if(one == left) { + outputvec.push_back(left); + } + else { + for(ASTVec::iterator j=rightnodes.begin(),jend=rightnodes.end(); + j!=jend;j++) { + ASTNode out = SimplifyTerm(CreateTerm(BVMULT,len,left,*j)); + outputvec.push_back(out); + } + } + } + else { + ASTVec leftnodes = left.GetChildren(); + // (x1 + x2 + ... + xm)*(y1 + y2 + ...+ yn) <==> x1*y1 + x1*y2 + + // ... + x2*y1 + ... + xm*yn + for(ASTVec::iterator i=leftnodes.begin(),iend=leftnodes.end(); + i!=iend;i++) { + ASTNode multiplier = *i; + for(ASTVec::iterator j=rightnodes.begin(),jend=rightnodes.end(); + j!=jend;j++) { + ASTNode out = SimplifyTerm(CreateTerm(BVMULT,len,multiplier,*j)); + outputvec.push_back(out); + } + } + } + + //compute output here + if(outputvec.size() > 1) { + output = CombineLikeTerms(CreateTerm(BVPLUS,len,outputvec)); + output = SimplifyTerm(output); + } + else + output = SimplifyTerm(outputvec[0]); + + //memoize + //UpdateSimplifyMap(a,output,false); + return output; + } //end of distributemultoverplus() + + //converts the BVSX(len, a0) operator into ITE( check top bit, + //extend a0 by 1, extend a0 by 0) + ASTNode BeevMgr::ConvertBVSXToITE(const ASTNode& a) { + if(BVSX != a.GetKind()) + return a; + + ASTNode output; + if(CheckSimplifyMap(a,output,false)) { + //check memo table + //cerr << "output of ConvertBVSXToITE Cache: " << output << endl; + return output; + } + + ASTNode a0 = a[0]; + unsigned a_len = a.GetValueWidth(); + unsigned a0_len = a0.GetValueWidth(); + + if(a0_len > a_len){ + FatalError("Trying to sign_extend a larger BV into a smaller BV"); + return ASTUndefined; //to stop the compiler from producing bogus warnings + } + + //sign extend + unsigned extensionlen = a_len-a0_len; + if(0 == extensionlen) { + UpdateSimplifyMap(a,output,false); + return a; + } + + std::string ones; + for(unsigned c=0; c < extensionlen;c++) + ones += '1'; + std::string zeros; + for(unsigned c=0; c < extensionlen;c++) + zeros += '0'; + + //string of oness of length extensionlen + BEEV::ASTNode BVOnes = CreateBVConst(ones.c_str(),2); + //string of zeros of length extensionlen + BEEV::ASTNode BVZeros = CreateBVConst(zeros.c_str(),2); + + //string of ones BVCONCAT a0 + BEEV::ASTNode concatOnes = CreateTerm(BEEV::BVCONCAT,a_len,BVOnes,a0); + //string of zeros BVCONCAT a0 + BEEV::ASTNode concatZeros = CreateTerm(BEEV::BVCONCAT,a_len,BVZeros,a0); + + //extract top bit of a0 + BEEV::ASTNode hi = CreateBVConst(32,a0_len-1); + BEEV::ASTNode low = CreateBVConst(32,a0_len-1); + BEEV::ASTNode topBit = CreateTerm(BEEV::BVEXTRACT,1,a0,hi,low); + + //compare topBit of a0 with 0bin1 + BEEV::ASTNode condition = CreateSimplifiedEQ(CreateBVConst(1,1),topBit); + + //ITE(topbit = 0bin1, 0bin1111...a0, 0bin000...a0) + output = CreateSimplifiedTermITE(condition,concatOnes,concatZeros); + UpdateSimplifyMap(a,output,false); + return output; + } //end of ConvertBVSXToITE() + + + ASTNode BeevMgr::RemoveWrites_TopLevel(const ASTNode& term) { + if(READ != term.GetKind() && WRITE != term[0].GetKind()) { + FatalError("RemovesWrites: Input must be a READ over a WRITE",term); + } + + if(!Begin_RemoveWrites && + !SimplifyWrites_InPlace_Flag && + !start_abstracting) { + return term; + } + else if(!Begin_RemoveWrites && + SimplifyWrites_InPlace_Flag && + !start_abstracting) { + //return term; + return SimplifyWrites_InPlace(term); + } + else { + return RemoveWrites(term); + } + } //end of RemoveWrites_TopLevel() + + ASTNode BeevMgr::SimplifyWrites_InPlace(const ASTNode& term) { + ASTNodeMultiSet WriteIndicesSeenSoFar; + bool SeenNonConstWriteIndex = false; + + if(READ != term.GetKind() && + WRITE != term[0].GetKind()) { + FatalError("RemovesWrites: Input must be a READ over a WRITE",term); + } + + ASTNode output; + if(CheckSimplifyMap(term,output,false)) { + return output; + } + + ASTVec writeIndices, writeValues; + unsigned int width = term.GetValueWidth(); + ASTNode write = term[0]; + unsigned indexwidth = write.GetIndexWidth(); + ASTNode readIndex = SimplifyTerm(term[1]); + + do { + ASTNode writeIndex = SimplifyTerm(write[1]); + ASTNode writeVal = SimplifyTerm(write[2]); + + //compare the readIndex and the current writeIndex and see if they + //simplify to TRUE or FALSE or UNDETERMINABLE at this stage + ASTNode compare_readwrite_indices = + SimplifyFormula(CreateSimplifiedEQ(writeIndex,readIndex),false); + + //if readIndex and writeIndex are equal + if(ASTTrue == compare_readwrite_indices && !SeenNonConstWriteIndex) { + UpdateSimplifyMap(term,writeVal,false); + return writeVal; + } + + if(!(ASTTrue == compare_readwrite_indices || + ASTFalse == compare_readwrite_indices)) { + SeenNonConstWriteIndex = true; + } + + //if (readIndex=writeIndex <=> FALSE) + if(ASTFalse == compare_readwrite_indices + || + (WriteIndicesSeenSoFar.find(writeIndex) != WriteIndicesSeenSoFar.end()) + ) { + //drop the current level write + //do nothing + } + else { + writeIndices.push_back(writeIndex); + writeValues.push_back(writeVal); + } + + //record the write indices seen so far + //if(BVCONST == writeIndex.GetKind()) { + WriteIndicesSeenSoFar.insert(writeIndex); + //} + + //Setup the write for the new iteration, one level inner write + write = write[0]; + }while (SYMBOL != write.GetKind()); + + ASTVec::reverse_iterator it_index = writeIndices.rbegin(); + ASTVec::reverse_iterator itend_index = writeIndices.rend(); + ASTVec::reverse_iterator it_values = writeValues.rbegin(); + ASTVec::reverse_iterator itend_values = writeValues.rend(); + + //"write" must be a symbol at the control point before the + //begining of the "for loop" + + for(;it_index!=itend_index;it_index++,it_values++) { + write = CreateTerm(WRITE,width,write,*it_index,*it_values); + write.SetIndexWidth(indexwidth); + } + + output = CreateTerm(READ,width,write,readIndex); + UpdateSimplifyMap(term,output,false); + return output; + } //end of SimplifyWrites_In_Place() + + //accepts a read over a write and returns a term without the write + //READ(WRITE(A i val) j) <==> ITE(i=j,val,READ(A,j)). We use a memo + //table for this function called RemoveWritesMemoMap + ASTNode BeevMgr::RemoveWrites(const ASTNode& input) { + //unsigned int width = input.GetValueWidth(); + if(READ != input.GetKind() || WRITE != input[0].GetKind()) { + FatalError("RemovesWrites: Input must be a READ over a WRITE",input); + } + + ASTNodeMap::iterator it; + ASTNode output = input; + if(CheckSimplifyMap(input,output,false)) { + return output; + } + + if(!start_abstracting && Begin_RemoveWrites) { + output= ReadOverWrite_To_ITE(input); + } + + if(start_abstracting) { + ASTNode newVar; + if(!CheckSimplifyMap(input,newVar,false)) { + newVar = NewVar(input.GetValueWidth()); + ReadOverWrite_NewName_Map[input] = newVar; + NewName_ReadOverWrite_Map[newVar] = input; + + UpdateSimplifyMap(input,newVar,false); + ASTNodeStats("New Var Name which replace Read_Over_Write: ", newVar); + } + output = newVar; + } //end of start_abstracting if condition + + //memoize + UpdateSimplifyMap(input,output,false); + return output; + } //end of RemoveWrites() + + ASTNode BeevMgr::ReadOverWrite_To_ITE(const ASTNode& term) { + unsigned int width = term.GetValueWidth(); + ASTNode input = term; + if(READ != term.GetKind() || WRITE != term[0].GetKind()) { + FatalError("RemovesWrites: Input must be a READ over a WRITE",term); + } + + ASTNodeMap::iterator it; + ASTNode output; + // if(CheckSimplifyMap(term,output,false)) { + // return output; + // } + + ASTNode partialITE = term; + ASTNode writeA = ASTTrue; + ASTNode oldRead = term; + //iteratively expand read-over-write + do { + ASTNode write = input[0]; + ASTNode readIndex = SimplifyTerm(input[1]); + //DO NOT CALL SimplifyTerm() on write[0]. You will go into an + //infinite loop + writeA = write[0]; + ASTNode writeIndex = SimplifyTerm(write[1]); + ASTNode writeVal = SimplifyTerm(write[2]); + + ASTNode cond = SimplifyFormula(CreateSimplifiedEQ(writeIndex,readIndex),false); + ASTNode newRead = CreateTerm(READ,width,writeA,readIndex); + ASTNode newRead_memoized = newRead; + if(CheckSimplifyMap(newRead, newRead_memoized,false)) { + newRead = newRead_memoized; + } + + if(ASTTrue == cond && (term == partialITE)) { + //found the write-value in the first iteration itself. return + //it + output = writeVal; + UpdateSimplifyMap(term,output,false); + return output; + } + + if(READ == partialITE.GetKind() && WRITE == partialITE[0].GetKind()) { + //first iteration or (previous cond==ASTFALSE and partialITE is a "READ over WRITE") + partialITE = CreateSimplifiedTermITE(cond, writeVal, newRead); + } + else if (ITE == partialITE.GetKind()){ + //ITE(i1 = j, v1, R(A,j)) + ASTNode ElseITE = CreateSimplifiedTermITE(cond, writeVal, newRead); + //R(W(A,i1,v1),j) <==> ITE(i1 = j, v1, R(A,j)) + UpdateSimplifyMap(oldRead,ElseITE,false); + //ITE(i2 = j, v2, R(W(A,i1,v1),j)) <==> ITE(i2 = j, v2, ITE(i1 = j, v1, R(A,j))) + partialITE = SimplifyTerm(partialITE); + } + else { + FatalError("RemoveWrites: Control should not reach here\n"); + } + + if(ASTTrue == cond) { + //no more iterations required + output = partialITE; + UpdateSimplifyMap(term,output,false); + return output; + } + + input = newRead; + oldRead = newRead; + } while(READ == input.GetKind() && WRITE == input[0].GetKind()); + + output = partialITE; + + //memoize + //UpdateSimplifyMap(term,output,false); + return output; + } //ReadOverWrite_To_ITE() + + //compute the multiplicative inverse of the input + ASTNode BeevMgr::MultiplicativeInverse(const ASTNode& d) { + ASTNode c = d; + if(BVCONST != c.GetKind()) { + FatalError("Input must be a constant", c); + } + + if(!BVConstIsOdd(c)) { + FatalError("MultiplicativeInverse: Input must be odd: ",c); + } + + //cerr << "input to multinverse function is: " << d << endl; + ASTNode inverse; + if(CheckMultInverseMap(d,inverse)) { + //cerr << "found the inverse of: " << d << "and it is: " << inverse << endl; + return inverse; + } + + inverse = c; + unsigned inputwidth = c.GetValueWidth(); + +#ifdef NATIVE_C_ARITH + ASTNode one = CreateOneConst(inputwidth); + while(c != one) { + //c = c*c + c = BVConstEvaluator(CreateTerm(BVMULT,inputwidth,c,c)); + //inverse = invsere*c + inverse = BVConstEvaluator(CreateTerm(BVMULT,inputwidth,inverse,c)); + } +#else + //Compute the multiplicative inverse of c using the extended + //euclidian algorithm + // + //create a '0' which is 1 bit long + ASTNode onebit_zero = CreateZeroConst(1); + //zero pad t0, i.e. 0 @ t0 + c = BVConstEvaluator(CreateTerm(BVCONCAT,inputwidth+1,onebit_zero,c)); + + //construct 2^(inputwidth), i.e. a bitvector of length + //'inputwidth+1', which is max(inputwidth)+1 + // + //all 1's + ASTNode max = CreateMaxConst(inputwidth); + //zero pad max + max = BVConstEvaluator(CreateTerm(BVCONCAT,inputwidth+1,onebit_zero,max)); + //Create a '1' which has leading zeros of length 'inputwidth' + ASTNode inputwidthplusone_one = CreateOneConst(inputwidth+1); + //add 1 to max + max = CreateTerm(BVPLUS,inputwidth+1,max,inputwidthplusone_one); + max = BVConstEvaluator(max); + + ASTNode zero = CreateZeroConst(inputwidth+1); + ASTNode max_bvgt_0 = CreateNode(BVGT,max,zero); + ASTNode quotient, remainder; + ASTNode x, x1, x2; + + //x1 initialized to zero + x1 = zero; + //x2 initialized to one + x2 = CreateOneConst(inputwidth+1); + while (ASTTrue == BVConstEvaluator(max_bvgt_0)) { + //quotient = (c divided by max) + quotient = BVConstEvaluator(CreateTerm(BVDIV,inputwidth+1, c, max)); + + //remainder of (c divided by max) + remainder = BVConstEvaluator(CreateTerm(BVMOD,inputwidth+1, c, max)); + + //x = x2 - q*x1 + x = CreateTerm(BVSUB,inputwidth+1,x2,CreateTerm(BVMULT,inputwidth+1,quotient,x1)); + x = BVConstEvaluator(x); + + //fix the inputs to the extended euclidian algo + c = max; + max = remainder; + max_bvgt_0 = CreateNode(BVGT,max,zero); + + x2 = x1; + x1 = x; + } + + ASTNode hi = CreateBVConst(32,inputwidth-1); + ASTNode low = CreateZeroConst(32); + inverse = CreateTerm(BVEXTRACT,inputwidth,x2,hi,low); + inverse = BVConstEvaluator(inverse); +#endif + + UpdateMultInverseMap(d,inverse); + //cerr << "output of multinverse function is: " << inverse << endl; + return inverse; + } //end of MultiplicativeInverse() + + //returns true if the input is odd + bool BeevMgr::BVConstIsOdd(const ASTNode& c) { + if(BVCONST != c.GetKind()) { + FatalError("Input must be a constant", c); + } + + ASTNode zero = CreateZeroConst(1); + ASTNode hi = CreateZeroConst(32); + ASTNode low = hi; + ASTNode lowestbit = CreateTerm(BVEXTRACT,1,c,hi,low); + lowestbit = BVConstEvaluator(lowestbit); + + if(lowestbit == zero) { + return false; + } + else { + return true; + } + } //end of BVConstIsOdd() + + //The big substitution function + ASTNode BeevMgr::CreateSubstitutionMap(const ASTNode& a){ + if(!optimize) + return a; + + ASTNode output = a; + //if the variable has been solved for, then simply return it + if(CheckSolverMap(a,output)) + return output; + + //traverse a and populate the SubstitutionMap + Kind k = a.GetKind(); + if(SYMBOL == k && BOOLEAN_TYPE == a.GetType()) { + bool updated = UpdateSubstitutionMap(a,ASTTrue); + output = updated ? ASTTrue : a; + return output; + } + if(NOT == k + && SYMBOL == a[0].GetKind()) { + bool updated = UpdateSubstitutionMap(a[0],ASTFalse); + output = updated ? ASTTrue : a; + return output; + } + + if(IFF == k) { + ASTVec c = a.GetChildren(); + SortByExprNum(c); + if(SYMBOL != c[0].GetKind() || + VarSeenInTerm(c[0],SimplifyFormula_NoRemoveWrites(c[1],false))) { + return a; + } + bool updated = UpdateSubstitutionMap(c[0],c[1]); + output = updated ? ASTTrue : a; + return output; + } + + if(EQ == k) { + //fill the arrayname readindices vector if e0 is a + //READ(Arr,index) and index is a BVCONST + ASTVec c = a.GetChildren(); + SortByExprNum(c); + FillUp_ArrReadIndex_Vec(c[0],c[1]); + + if(SYMBOL == c[0].GetKind() && + VarSeenInTerm(c[0],SimplifyTerm(c[1]))) { + return a; + } + + if(1 == TermOrder(c[0],c[1]) && + READ == c[0].GetKind() && + VarSeenInTerm(c[0][0],SimplifyTerm(c[1]))) { + return a; + } + bool updated = UpdateSubstitutionMap(c[0],c[1]); + output = updated ? ASTTrue : a; + return output; + } + + if(AND == k){ + ASTVec o; + ASTVec c = a.GetChildren(); + for(ASTVec::iterator it = c.begin(),itend=c.end();it!=itend;it++) { + UpdateAlwaysTrueFormMap(*it); + ASTNode aaa = CreateSubstitutionMap(*it); + + if(ASTTrue != aaa) { + if(ASTFalse == aaa) + return ASTFalse; + else + o.push_back(aaa); + } + } + if(o.size() == 0) + return ASTTrue; + + if(o.size() == 1) + return o[0]; + + return CreateNode(AND,o); + } + return output; + } //end of CreateSubstitutionMap() + + + bool BeevMgr::VarSeenInTerm(const ASTNode& var, const ASTNode& term) { + if(READ == term.GetKind() && + WRITE == term[0].GetKind() && !Begin_RemoveWrites) { + return false; + } + + if(READ == term.GetKind() && + WRITE == term[0].GetKind() && Begin_RemoveWrites) { + return true; + } + + ASTNodeMap::iterator it; + if((it = TermsAlreadySeenMap.find(term)) != TermsAlreadySeenMap.end()) { + if(it->second == var) { + return false; + } + } + + if(var == term) { + return true; + } + + for(ASTVec::const_iterator it=term.begin(),itend=term.end();it!=itend;it++){ + if(VarSeenInTerm(var,*it)) { + return true; + } + else { + TermsAlreadySeenMap[*it] = var; + } + } + + TermsAlreadySeenMap[term] = var; + return false; + } +};//end of namespace diff --git a/test/CXX/ArrayNew.cpp b/test/CXX/ArrayNew.cpp new file mode 100644 index 00000000..e6a41ddf --- /dev/null +++ b/test/CXX/ArrayNew.cpp @@ -0,0 +1,38 @@ +// RUN: %llvmgxx %s --emit-llvm -O0 -c -o %t1.bc +// RUN: %klee --no-output --exit-on-error --no-externals %t1.bc + +#include <cassert> + +static int decon = 0; + +class Test { + int x; + +public: + Test() {} + Test(int _x) : x(_x) { } + ~Test() { decon += x; } + + int getX() { return x; } + void setX(int _x) { x = _x; } +}; + +int main(int argc) { + Test *rt = new Test[4]; + int i; + + for (i=0; i<4; i++) + rt[i].setX(i+1); + + int sum = 0; + for (i=0; i<4; i++) + sum += rt[i].getX(); + + assert(sum==10); + + delete[] rt; + + assert(decon==10); + + return 0; +} diff --git a/test/CXX/New.cpp b/test/CXX/New.cpp new file mode 100644 index 00000000..148dfed6 --- /dev/null +++ b/test/CXX/New.cpp @@ -0,0 +1,29 @@ +// RUN: %llvmgxx %s --emit-llvm -O0 -c -o %t1.bc +// RUN: %klee --no-output --exit-on-error --no-externals %t1.bc + +#include <cassert> + +class Test { + int x; + +public: + Test(int _x) : x(_x) { + } + ~Test() { + } + + int getX() { return x; } +}; + +// This doesn't do what I want because +// it is being changed to alloca, but +// it is also failing. +int main(int argc) { + Test *rt = new Test(2); + + assert(rt->getX()==2); + + delete rt; + + return 0; +} diff --git a/test/CXX/SimpleVirtual.cpp b/test/CXX/SimpleVirtual.cpp new file mode 100644 index 00000000..9dc2a0ac --- /dev/null +++ b/test/CXX/SimpleVirtual.cpp @@ -0,0 +1,38 @@ +// RUN: %llvmgxx %s --emit-llvm -O0 -c -o %t1.bc +// RUN: %klee --no-output --exit-on-error --no-externals %t1.bc + +#include <cassert> + +static int decon = 0; + +class Thing { +public: + Thing() {} + virtual ~Thing() { decon += getX(); } + + virtual int getX() { return 1; }; +}; + +class Thing2 : public Thing { +public: + virtual int getX() { return 2; }; +}; + +Thing *getThing(bool which) { + return which ? new Thing() : new Thing2(); +} + +int main(int argc) { + Thing *one = getThing(false); + Thing *two = getThing(true); + + int x = one->getX() + two->getX(); + assert(x==3); + + delete two; + delete one; + + assert(decon==2); + + return 0; +} diff --git a/test/CXX/StaticConstructor.cpp b/test/CXX/StaticConstructor.cpp new file mode 100644 index 00000000..d4992ffe --- /dev/null +++ b/test/CXX/StaticConstructor.cpp @@ -0,0 +1,25 @@ +// RUN: %llvmgxx %s --emit-llvm -O0 -c -o %t1.bc +// RUN: %klee --libc=klee --no-output --exit-on-error %t1.bc + +#include <cassert> + +// to make sure globals are initialized +int aGlobal = 21; + +class Test { + int x; + +public: + Test() : x(aGlobal + 1) {} + ~Test() {} + + int getX() { return x; } +}; + +Test t; + +int main() { + assert(t.getX()==22); + + return 0; +} diff --git a/test/CXX/StaticDestructor.cpp b/test/CXX/StaticDestructor.cpp new file mode 100644 index 00000000..7a765a8f --- /dev/null +++ b/test/CXX/StaticDestructor.cpp @@ -0,0 +1,24 @@ +// don't optimize this, llvm likes to turn the *p into unreachable + +// RUN: %llvmgxx %s --emit-llvm -g -O0 -c -o %t1.bc +// RUN: %klee --libc=klee --no-output %t1.bc 2> %t1.log +// RUN: grep ":16: memory error" %t1.log + +#include <cassert> + +class Test { + int *p; + +public: + Test() : p(0) {} + ~Test() { + assert(!p); + assert(*p == 10); // crash here + } +}; + +Test t; + +int main() { + return 0; +} diff --git a/test/CXX/Trivial.cpp b/test/CXX/Trivial.cpp new file mode 100644 index 00000000..b50e82b2 --- /dev/null +++ b/test/CXX/Trivial.cpp @@ -0,0 +1,22 @@ +// RUN: %llvmgxx %s --emit-llvm -O0 -c -o %t1.bc +// RUN: %klee --no-output --exit-on-error %t1.bc + +#include <cassert> + +class Test { + int x; + +public: + Test(int _x) : x(_x) {} + ~Test() {} + + int getX() { return x; } +}; + +int main() { + Test rt(2); + + assert(rt.getX()==2); + + return 0; +} diff --git a/test/CXX/dg.exp b/test/CXX/dg.exp new file mode 100644 index 00000000..879685ca --- /dev/null +++ b/test/CXX/dg.exp @@ -0,0 +1,3 @@ +load_lib llvm.exp + +RunLLVMTests [lsort [glob -nocomplain $srcdir/$subdir/*.{ll,llx,c,cpp,tr}]] diff --git a/test/Concrete/BitwiseOps.ll b/test/Concrete/BitwiseOps.ll new file mode 100644 index 00000000..cf5e7e6b --- /dev/null +++ b/test/Concrete/BitwiseOps.ll @@ -0,0 +1,15 @@ +declare void @print_i32(i32) + +define i32 @main() { + %a = or i32 12345678, 87654321 + %b = and i32 %a, 87654321 + %check = xor i32 %b, 87654321 + %test = icmp eq i32 %check, 0 + br i1 %test, label %exitTrue, label %exitFalse +exitTrue: + call void @print_i32(i32 1) + ret i32 0 +exitFalse: + call void @print_i32(i32 0) + ret i32 0 +} diff --git a/test/Concrete/BoolReadWrite.ll b/test/Concrete/BoolReadWrite.ll new file mode 100644 index 00000000..f37359f8 --- /dev/null +++ b/test/Concrete/BoolReadWrite.ll @@ -0,0 +1,13 @@ +declare void @print_i1(i1) + +define i32 @main() { + %mem = alloca i1 + store i1 1, i1* %mem + %v = load i1* %mem + br i1 %v, label %ok, label %exit +ok: + call void @print_i1(i1 %v) + br label %exit +exit: + ret i32 0 +} diff --git a/test/Concrete/Casts.ll b/test/Concrete/Casts.ll new file mode 100755 index 00000000..1329a127 --- /dev/null +++ b/test/Concrete/Casts.ll @@ -0,0 +1,28 @@ +declare void @print_i32(i32) + +define i32 @main() { +entry: + %a = add i32 315904, 128 + %b = trunc i32 %a to i8 + %c0 = icmp eq i8 %b, 128 + %d = zext i8 %b to i32 + %c1 = icmp eq i32 %d, 128 + %e = sext i8 %b to i32 + %c2 = icmp eq i32 %e, -128 + %c0i = zext i1 %c0 to i32 + %c1i = zext i1 %c1 to i32 + %c2i = zext i1 %c2 to i32 + %c0is = shl i32 %c0i, 0 + %c1is = shl i32 %c1i, 1 + %c2is = shl i32 %c2i, 2 + %tmp = add i32 %c0is, %c1is + %res = add i32 %tmp, %c2is + %p = icmp eq i32 %res, 7 + br i1 %p, label %exitTrue, label %exitFalse +exitTrue: + call void @print_i32(i32 1) + ret i32 0 +exitFalse: + call void @print_i32(i32 0) + ret i32 0 +} diff --git a/test/Concrete/CmpEq.ll b/test/Concrete/CmpEq.ll new file mode 100644 index 00000000..a8c2fe7a --- /dev/null +++ b/test/Concrete/CmpEq.ll @@ -0,0 +1,14 @@ +declare void @print_i32(i32) + +define i32 @main() { + %a = add i8 0, 1 + %b = add i8 %a, -1 + %c = icmp eq i8 %b, 0 + br i1 %c, label %exitTrue, label %exitFalse +exitTrue: + call void @print_i32(i32 1) + ret i32 0 +exitFalse: + call void @print_i32(i32 0) + ret i32 0 +} diff --git a/test/Concrete/ConcreteTest.py b/test/Concrete/ConcreteTest.py new file mode 100755 index 00000000..758d0caa --- /dev/null +++ b/test/Concrete/ConcreteTest.py @@ -0,0 +1,99 @@ +#!/usr/bin/python + +import os +import sys +import popen2 + +class TestError(Exception): + pass + +kLLIPath = '../../llvm/Debug/bin/lli' +kKleePath = '../../Debug/bin/klee' + +def getFiles(): + for name in os.listdir('.'): + if (name[0]!='.' and name[0]!='_' and + (name.endswith('.ll') or name.endswith('.c'))): + yield name + +def readFile(f): + s = "" + while 1: + data = f.read() + if not data: + break + s += data + return s + +def testFile(name, printOutput=False): + baseName,ext = os.path.splitext(name) + exeFile = 'Output/linked_%s.bc'%baseName + if printOutput: + redirectStderr = '' + else: + redirectStderr = '2> /dev/null' + + if os.system('make %s > /dev/null %s'%(exeFile,redirectStderr)): + raise TestError('make failed') + + if printOutput: + print '-- running lli --' + lli = popen2.Popen3('%s -force-interpreter=true %s %s'%(kLLIPath,exeFile,redirectStderr)) + lliOut = readFile(lli.fromchild) + if lli.wait(): + raise TestError('lli execution failed') + + if printOutput: + print lliOut + + if printOutput: + print '-- running klee --' + klee = popen2.Popen3('%s --no-output %s %s'%(kKleePath,exeFile,redirectStderr)) + kleeOut = readFile(klee.fromchild) + if klee.wait(): + raise TestError('klee execution failed') + if printOutput: + print kleeOut + + if lliOut!=kleeOut: + raise TestError('outputs differ') + +def testOneFile(f, printOutput=False, log=None): + try: + testFile(f, printOutput) + code = ['pass','xpass'][f.startswith('broken')] + extra = '' + except TestError,e: + code = ['fail','xfail'][f.startswith('broken')] + extra = str(e) + + print '%s: %s -- %s'%(code,f,extra) + if log: + print >>log,'%s: %s -- %s'%(code,f,extra) + +def test(): + if not os.path.exists('Output'): + os.mkdir('Output') + log = open("Output/test.log","w") + files = list(getFiles()) + files.sort(key = lambda n: n.lower()) + for f in files: + testOneFile(f, log=log) + log.close() + +if __name__=='__main__': + args = sys.argv + args.pop(0) + + runAll = not args + + while args: + arg = args.pop(0) + if arg=='--run': + testFile(args.pop(0), printOutput=True) + else: + raise ValueError,'invalid argument: %s'%arg + + if runAll: + test() + diff --git a/test/Concrete/ConstantExpr.ll b/test/Concrete/ConstantExpr.ll new file mode 100644 index 00000000..2bc33a1e --- /dev/null +++ b/test/Concrete/ConstantExpr.ll @@ -0,0 +1,166 @@ +@gInt = global i32 10 +@gIntWithConstant = global i32 sub(i32 ptrtoint(i32* @gInt to i32), + i32 ptrtoint(i32* @gInt to i32)) + +define void @"test_int_to_ptr"() +begin + %t1 = add i8 ptrtoint(i8* inttoptr(i32 100 to i8*) to i8), 0 + %t2 = add i32 ptrtoint(i32* inttoptr(i8 100 to i32*) to i32), 0 + %t3 = add i32 ptrtoint(i32* inttoptr(i64 100 to i32*) to i32), 0 + %t4 = add i64 ptrtoint(i8* inttoptr(i32 100 to i8*) to i64), 0 + + call void @print_i8(i8 %t1) + call void @print_i32(i32 %t2) + call void @print_i32(i32 %t3) + call void @print_i64(i64 %t4) + + ret void +end + +define void @"test_constant_ops"() +begin + %t1 = add i8 trunc(i64 add(i64 ptrtoint(i32* @gInt to i64), i64 -10) to i8), 10 + %t2 = sub i64 sext(i32 ptrtoint(i32* @gInt to i32) to i64), ptrtoint(i32* @gInt to i64) + %t3 = sub i64 zext(i32 ptrtoint(i32* @gInt to i32) to i64), ptrtoint(i32* @gInt to i64) + + %t4 = icmp eq i8 trunc(i64 ptrtoint(i32* @gInt to i64) to i8), %t1 + %t5 = zext i1 %t4 to i8 + + call void @print_i8(i8 %t5) + call void @print_i64(i64 %t2) + call void @print_i64(i64 %t3) + + ret void +end + +define void @"test_logical_ops"() +begin + %t1 = add i32 -10, and(i32 ptrtoint(i32* @gInt to i32), i32 xor(i32 ptrtoint(i32* @gInt to i32), i32 -1)) + %t2 = add i32 -10, or(i32 ptrtoint(i32* @gInt to i32), i32 xor(i32 ptrtoint(i32* @gInt to i32), i32 -1)) + %t3 = add i32 -10, xor(i32 xor(i32 ptrtoint(i32* @gInt to i32), i32 1024), i32 ptrtoint(i32* @gInt to i32)) + + call void @print_i32(i32 %t1) + call void @print_i32(i32 %t2) + call void @print_i32(i32 %t3) + + %t4 = shl i32 lshr(i32 ptrtoint(i32* @gInt to i32), i32 8), 8 + %t5 = shl i32 ashr(i32 ptrtoint(i32* @gInt to i32), i32 8), 8 + %t6 = lshr i32 shl(i32 ptrtoint(i32* @gInt to i32), i32 8), 8 + + %t7 = icmp eq i32 %t4, %t5 + %t8 = icmp ne i32 %t4, %t6 + + %t9 = zext i1 %t7 to i8 + %t10 = zext i1 %t8 to i8 + + call void @print_i8(i8 %t9) + call void @print_i8(i8 %t10) + + ret void +end + +%test.struct.type = type { i32, i32 } +@test_struct = global %test.struct.type { i32 0, i32 10 } + +define void @"test_misc"() +begin + ; probability that @gInt == 100 is very very low + %t1 = add i32 select(i1 icmp eq (i32* @gInt, i32* inttoptr(i32 100 to i32*)), i32 10, i32 0), 0 + call void @print_i32(i32 %t1) + + %t2 = load i32* getelementptr(%test.struct.type* @test_struct, i32 0, i32 1) + call void @print_i32(i32 %t2) + + ret void +end + +define void @"test_simple_arith"() +begin + %t1 = add i32 add(i32 ptrtoint(i32* @gInt to i32), i32 0), 0 + %t2 = add i32 sub(i32 0, i32 ptrtoint(i32* @gInt to i32)), %t1 + %t3 = mul i32 mul(i32 ptrtoint(i32* @gInt to i32), i32 10), %t2 + + call void @print_i32(i32 %t3) + + ret void +end + +define void @"test_div_and_mod"() +begin + %t1 = add i32 udiv(i32 ptrtoint(i32* @gInt to i32), i32 13), 0 + %t2 = add i32 urem(i32 ptrtoint(i32* @gInt to i32), i32 13), 0 + %t3 = add i32 sdiv(i32 ptrtoint(i32* @gInt to i32), i32 13), 0 + %t4 = add i32 srem(i32 ptrtoint(i32* @gInt to i32), i32 13), 0 + + %p = ptrtoint i32* @gInt to i32 + + %i1 = udiv i32 %p, 13 + %i2 = urem i32 %p, 13 + %i3 = sdiv i32 %p, 13 + %i4 = srem i32 %p, 13 + + %x1 = sub i32 %t1, %i1 + %x2 = sub i32 %t2, %i2 + %x3 = sub i32 %t3, %i3 + %x4 = sub i32 %t4, %i4 + + call void @print_i32(i32 %x1) + call void @print_i32(i32 %x2) + call void @print_i32(i32 %x3) + call void @print_i32(i32 %x4) + + ret void +end + +define void @test_cmp() +begin + %t1 = add i8 zext(i1 icmp ult (i32 ptrtoint(i32* @gInt to i32), i32 0) to i8), 1 + %t2 = add i8 zext(i1 icmp ule (i32 ptrtoint(i32* @gInt to i32), i32 0) to i8), 1 + %t3 = add i8 zext(i1 icmp uge (i32 ptrtoint(i32* @gInt to i32), i32 0) to i8), 1 + %t4 = add i8 zext(i1 icmp ugt (i32 ptrtoint(i32* @gInt to i32), i32 0) to i8), 1 + %t5 = add i8 zext(i1 icmp slt (i32 ptrtoint(i32* @gInt to i32), i32 0) to i8), 1 + %t6 = add i8 zext(i1 icmp sle (i32 ptrtoint(i32* @gInt to i32), i32 0) to i8), 1 + %t7 = add i8 zext(i1 icmp sge (i32 ptrtoint(i32* @gInt to i32), i32 0) to i8), 1 + %t8 = add i8 zext(i1 icmp sgt (i32 ptrtoint(i32* @gInt to i32), i32 0) to i8), 1 + %t9 = add i8 zext(i1 icmp eq (i32 ptrtoint(i32* @gInt to i32), i32 10) to i8), 1 + %t10 = add i8 zext(i1 icmp ne (i32 ptrtoint(i32* @gInt to i32), i32 10) to i8), 1 + + call void @print_i1(i8 %t1) + call void @print_i1(i8 %t2) + call void @print_i1(i8 %t3) + call void @print_i1(i8 %t4) + call void @print_i1(i8 %t5) + call void @print_i1(i8 %t6) + call void @print_i1(i8 %t7) + call void @print_i1(i8 %t8) + call void @print_i1(i8 %t9) + call void @print_i1(i8 %t10) + + ret void +end + +define i32 @main() +begin + call void @test_simple_arith() + + call void @test_div_and_mod() + + call void @test_cmp() + + call void @test_int_to_ptr() + + call void @test_constant_ops() + + call void @test_logical_ops() + + call void @test_misc() + + ret i32 0 +end + +; defined in print_int.c +declare void @print_i1(i8) +declare void @print_i8(i8) +declare void @print_i16(i16) +declare void @print_i32(i32) +declare void @print_i64(i64) diff --git a/test/Concrete/FloatingPointOps.ll b/test/Concrete/FloatingPointOps.ll new file mode 100644 index 00000000..7f23dcef --- /dev/null +++ b/test/Concrete/FloatingPointOps.ll @@ -0,0 +1,685 @@ +%struct.stdout = type { i32, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, %struct._IO_marker*, %struct.stdout*, i32, i32, i32, i16, i8, [1 x i8], i8*, i64, i8*, i8*, i8*, i8*, i32, i32, [40 x i8] } +%struct._IO_marker = type { %struct._IO_marker*, %struct.stdout*, i32 } +@stdout = external global %struct.stdout* + +; casting error messages +@.strTrunc = internal constant [15 x i8] c"FPTrunc broken\00" +@.strExt = internal constant [13 x i8] c"FPExt broken\00" +@.strFPToUIFlt = internal constant [20 x i8] c"FPToUI float broken\00" +@.strFPToUIDbl = internal constant [21 x i8] c"FPToUI double broken\00" +@.strFPToSIFlt = internal constant [20 x i8] c"FPToSI float broken\00" +@.strFPToSIDbl = internal constant [21 x i8] c"FPToSI double broken\00" +@.strUIToFPFlt = internal constant [20 x i8] c"UIToFP float broken\00" +@.strUIToFPDbl = internal constant [21 x i8] c"UIToFP double broken\00" +@.strSIToFPFlt = internal constant [20 x i8] c"SIToFP float broken\00" +@.strSIToFPDbl = internal constant [21 x i8] c"SIToFP double broken\00" + +; mathematical operator error messages +@.strDivFlt = internal constant [18 x i8] c"FDiv float broken\00" +@.strDivDbl = internal constant [19 x i8] c"FDiv double broken\00" +@.strRemFlt = internal constant [18 x i8] c"FRem float broken\00" +@.strRemDbl = internal constant [19 x i8] c"FRem double broken\00" +@.strAddInt = internal constant [16 x i8] c"Add ints broken\00" +@.strAddFlt = internal constant [18 x i8] c"Add floats broken\00" +@.strAddDbl = internal constant [19 x i8] c"Add doubles broken\00" +@.strSubInt = internal constant [16 x i8] c"Sub ints broken\00" +@.strSubFlt = internal constant [18 x i8] c"Sub floats broken\00" +@.strSubDbl = internal constant [19 x i8] c"Sub doubles broken\00" +@.strMulInt = internal constant [16 x i8] c"Mul ints broken\00" +@.strMulFlt = internal constant [18 x i8] c"Mul floats broken\00" +@.strMulDbl = internal constant [19 x i8] c"Mul doubles broken\00" + +; fcmp error messages +@.strCmpTrFlt = internal constant [19 x i8] c"floats TRUE broken\00" ; fcmp::generic broken msgs +@.strCmpFaFlt = internal constant [20 x i8] c"floats FALSE broken\00" +@.strCmpTrDbl = internal constant [19 x i8] c"double TRUE broken\00" +@.strCmpFaDbl = internal constant [20 x i8] c"double FALSE broken\00" +@.strCmpEqFlt = internal constant [17 x i8] c"floats == broken\00" ; fcmp::ordered broken msgs +@.strCmpGeFlt = internal constant [17 x i8] c"floats >= broken\00" +@.strCmpGtFlt = internal constant [17 x i8] c"floats > broken\00" +@.strCmpLeFlt = internal constant [17 x i8] c"floats <= broken\00" +@.strCmpLtFlt = internal constant [17 x i8] c"floats < broken\00" +@.strCmpNeFlt = internal constant [17 x i8] c"floats != broken\00" +@.strCmpOrdFlt = internal constant [18 x i8] c"floats ORD broken\00" +@.strCmpEqDbl = internal constant [18 x i8] c"doubles == broken\00" +@.strCmpGeDbl = internal constant [18 x i8] c"doubles >= broken\00" +@.strCmpGtDbl = internal constant [18 x i8] c"doubles > broken\00" +@.strCmpLeDbl = internal constant [18 x i8] c"doubles <= broken\00" +@.strCmpLtDbl = internal constant [18 x i8] c"doubles < broken\00" +@.strCmpNeDbl = internal constant [18 x i8] c"doubles != broken\00" +@.strCmpOrdDbl = internal constant [19 x i8] c"doubles ORD broken\00" +@.strCmpEqFltU = internal constant [17 x i8] c"U:floats==broken\00" ; fcmp::unordered broken msgs +@.strCmpGeFltU = internal constant [17 x i8] c"U:floats>=broken\00" +@.strCmpGtFltU = internal constant [17 x i8] c"U:floats> broken\00" +@.strCmpLeFltU = internal constant [17 x i8] c"U:floats<=broken\00" +@.strCmpLtFltU = internal constant [17 x i8] c"U:floats< broken\00" +@.strCmpNeFltU = internal constant [17 x i8] c"U:floats!=broken\00" +@.strCmpUnoFlt = internal constant [20 x i8] c"U:floats UNO broken\00" +@.strCmpEqDblU = internal constant [18 x i8] c"U:doubles==broken\00" +@.strCmpGeDblU = internal constant [18 x i8] c"U:doubles>=broken\00" +@.strCmpGtDblU = internal constant [18 x i8] c"U:doubles> broken\00" +@.strCmpLeDblU = internal constant [18 x i8] c"U:doubles<=broken\00" +@.strCmpLtDblU = internal constant [18 x i8] c"U:doubles< broken\00" +@.strCmpNeDblU = internal constant [18 x i8] c"U:doubles!=broken\00" +@.strCmpUnoDbl = internal constant [21 x i8] c"U:doubles UNO broken\00" + +@.strWorks = internal constant [20 x i8] c"Everything works!\0D\0A\00" +@.strNL = internal constant [3 x i8] c"\0D\0A\00" + +declare i32 @fprintf(%struct.stdout*, i8*, ...) +declare void @exit(i32) +declare void @llvm.memcpy.i32(i8*, i8*, i32, i32) + +; if isOk is false, then print errMsg to stdout and exit(1) +define void @failCheck(i1 %isOk, i8* %errMsg) { +entry: + %fail = icmp eq i1 %isOk, 0 + br i1 %fail, label %failed, label %return + +failed: + ; print the error msg + %err_stream = load %struct.stdout** @stdout + %ret = call i32 (%struct.stdout*, i8*, ...)* @fprintf( %struct.stdout* %err_stream, i8* %errMsg ) + + ; add a newline to the ostream + %nl = getelementptr [3 x i8]* @.strNL, i32 0, i32 0 + %ret2 = call i32 (%struct.stdout*, i8*, ...)* @fprintf( %struct.stdout* %err_stream, i8* %nl ) + + ; exit with return value 1 to denote that an error occurred + call void @exit( i32 1 ) + unreachable + +return: + ret void +} + +; test FPTrunc which casts doubles to floats +define void @testFPTrunc() { +entry: + %d_addr = alloca double, align 8 + store double 8.000000e+00, double* %d_addr + %d = load double* %d_addr + %f = fptrunc double %d to float + %matches = fcmp oeq float %f, 8.000000e+00 + %err_msg = getelementptr [15 x i8]* @.strTrunc, i32 0, i32 0 + call void @failCheck( i1 %matches, i8* %err_msg ) + ret void +} + +; test FPExt which casts floats to doubles +define void @testFPExt() { +entry: + %f_addr = alloca float, align 4 + store float 8.000000e+00, float* %f_addr + %f = load float* %f_addr + %d = fpext float %f to double + %matches = fcmp oeq double %d, 8.000000e+00 + %err_msg = getelementptr [13 x i8]* @.strExt, i32 0, i32 0 + call void @failCheck( i1 %matches, i8* %err_msg ) + ret void +} + +; test casting fp to an unsigned int +define void @testFPToUI() { +entry: + %f_addr = alloca float, align 4 + %d_addr = alloca double, align 8 + + ; test float to UI + store float 0x4020333340000000, float* %f_addr; %f = 8.1 + %f = load float* %f_addr + %uf = fptoui float %f to i32 + %matchesf = icmp eq i32 %uf, 8 + %err_msgf = getelementptr [20 x i8]* @.strFPToUIFlt, i32 0, i32 0 + call void @failCheck( i1 %matchesf, i8* %err_msgf ) + + ; test double to UI + store double 8.100000e+00, double* %d_addr + %d = load double* %d_addr + %ud = fptoui double %d to i32 + %matchesd = icmp eq i32 %ud, 8 + %err_msgd = getelementptr [21 x i8]* @.strFPToUIDbl, i32 0, i32 0 + call void @failCheck( i1 %matchesd, i8* %err_msgd ) + + ret void +} + +; test casting fp to a signed int +define void @testFPToSI() { +entry: + %f_addr = alloca float, align 4 + %d_addr = alloca double, align 8 + + ; test float 8.1 to signed int + store float 0x4020333340000000, float* %f_addr + %f = load float* %f_addr + %sf = fptosi float %f to i32 + %matchesf = icmp eq i32 %sf, 8 + %err_msgf = getelementptr [20 x i8]* @.strFPToSIFlt, i32 0, i32 0 + call void @failCheck( i1 %matchesf, i8* %err_msgf ) + + ; test double -8.1 to signed int + store double -8.100000e+00, double* %d_addr + %d = load double* %d_addr + %sd = fptosi double %d to i32 + %matchesd = icmp eq i32 %sd, -8 + %err_msgd = getelementptr [21 x i8]* @.strFPToSIDbl, i32 0, i32 0 + call void @failCheck( i1 %matchesd, i8* %err_msgd ) + + ret void +} + +; test casting unsigned int to fp +define void @testUIToFP() { +entry: + ; unsigned int to float + %f = uitofp i32 7 to float + %matchesf = fcmp oeq float %f, 7.000000e+00 + %err_msgf = getelementptr [20 x i8]* @.strUIToFPFlt, i32 0, i32 0 + call void @failCheck( i1 %matchesf, i8* %err_msgf ) + + ; unsigned int to double + %d = uitofp i32 7 to double + %matchesd = fcmp oeq double %d, 7.000000e+00 + %err_msgd = getelementptr [21 x i8]* @.strUIToFPDbl, i32 0, i32 0 + call void @failCheck( i1 %matchesd, i8* %err_msgd ) + + ret void +} + +; test casting signed int to fp +define void @testSIToFP() { +entry: + ; signed int to float + %f = sitofp i32 -7 to float + %matchesf = fcmp oeq float %f, -7.000000e+00 + %err_msgf = getelementptr [20 x i8]* @.strSIToFPFlt, i32 0, i32 0 + call void @failCheck( i1 %matchesf, i8* %err_msgf ) + + ; signed int to double + %d = sitofp i32 -7 to double + %matchesd = fcmp oeq double %d, -7.000000e+00 + %err_msgd = getelementptr [21 x i8]* @.strSIToFPDbl, i32 0, i32 0 + call void @failCheck( i1 %matchesd, i8* %err_msgd ) + + ret void +} + +; testing fp division +define void @testFDiv() { +entry: + %fN_addr = alloca float, align 4 + %fD_addr = alloca float, align 4 + %dN_addr = alloca double, align 8 + %dD_addr = alloca double, align 8 + + ; float division + store float 2.200000e+01, float* %fN_addr + store float 4.000000e+00, float* %fD_addr + %fN = load float* %fN_addr + %fD = load float* %fD_addr + %f = fdiv float %fN, %fD + %matchesf = fcmp oeq float %f, 5.500000e+00 + %err_msgf = getelementptr [18 x i8]* @.strDivFlt, i32 0, i32 0 + call void @failCheck( i1 %matchesf, i8* %err_msgf ) + + ; double division + store double 2.200000e+01, double* %dN_addr + store double -4.000000e+00, double* %dD_addr + %dN = load double* %dN_addr + %dD = load double* %dD_addr + %d = fdiv double %dN, %dD + %matchesd = fcmp oeq double %d, -5.500000e+00 + %err_msgd = getelementptr [19 x i8]* @.strDivDbl, i32 0, i32 0 + call void @failCheck( i1 %matchesd, i8* %err_msgd ) + + ret void +} + +; testing fp modulo +define void @testFRem() { +entry: + %fN_addr = alloca float, align 4 + %fD_addr = alloca float, align 4 + %dN_addr = alloca double, align 8 + %dD_addr = alloca double, align 8 + + ; float modoulo + store float 2.200000e+01, float* %fN_addr + store float 4.000000e+00, float* %fD_addr + %fN = load float* %fN_addr + %fD = load float* %fD_addr + %f = frem float %fN, %fD + %matchesf = fcmp oeq float %f, 2.000000e+00 + %err_msgf = getelementptr [18 x i8]* @.strRemFlt, i32 0, i32 0 + call void @failCheck( i1 %matchesf, i8* %err_msgf ) + + ; double modulo + store double -2.200000e+01, double* %dN_addr + store double 4.000000e+00, double* %dD_addr + %dN = load double* %dN_addr + %dD = load double* %dD_addr + %d = frem double %dN, %dD + %matchesd = fcmp oeq double %d, -2.000000e+00 + %err_msgd = getelementptr [19 x i8]* @.strRemDbl, i32 0, i32 0 + call void @failCheck( i1 %matchesd, i8* %err_msgd ) + + ret void +} + +; test addition (fp and int since add is polymorphic) +define void @testAdd() { +entry: + %f1_addr = alloca float, align 4 + %f2_addr = alloca float, align 4 + %d1_addr = alloca double, align 8 + %d2_addr = alloca double, align 8 + + ; test integer addition (3 + 4) + %sumi = add i32 3, 4 + %matchesi = icmp eq i32 %sumi, 7 + %err_msgi = getelementptr [16 x i8]* @.strAddInt, i32 0, i32 0 + call void @failCheck( i1 %matchesi, i8* %err_msgi ) + + ; test float addition (3.5 + 4.2) + store float 3.500000e+00, float* %f1_addr + store float 0x4010CCCCC0000000, float* %f2_addr + %f1 = load float* %f1_addr + %f2 = load float* %f2_addr + %sumf = add float %f1, %f2 + %matchesf = fcmp oeq float %sumf, 0x401ECCCCC0000000 + %err_msgf = getelementptr [18 x i8]* @.strAddFlt, i32 0, i32 0 + call void @failCheck( i1 %matchesf, i8* %err_msgf ) + + ; test double addition (3.5 + -4.2) + store double 3.500000e+00, double* %d1_addr + store double -4.200000e+00, double* %d2_addr + %d1 = load double* %d1_addr + %d2 = load double* %d2_addr + %sumd = add double %d1, %d2 + %matchesd = fcmp oeq double %sumd, 0xBFE6666666666668 + %err_msgd = getelementptr [19 x i8]* @.strAddDbl, i32 0, i32 0 + call void @failCheck( i1 %matchesd, i8* %err_msgd ) + + ret void +} + +; test subtraction (fp and int since sub is polymorphic) +define void @testSub() { +entry: + %f1_addr = alloca float, align 4 + %f2_addr = alloca float, align 4 + %d1_addr = alloca double, align 8 + %d2_addr = alloca double, align 8 + + ; test integer subtraction (3 - 4) + %subi = sub i32 3, 4 + %matchesi = icmp eq i32 %subi, -1 + %err_msgi = getelementptr [16 x i8]* @.strSubInt, i32 0, i32 0 + call void @failCheck( i1 %matchesi, i8* %err_msgi ) + + ; test float subtraction (3.5 - 4.2) + store float 3.500000e+00, float* %f1_addr + store float 0x4010CCCCC0000000, float* %f2_addr + %f1 = load float* %f1_addr + %f2 = load float* %f2_addr + %subf = sub float %f1, %f2 + %matchesf = fcmp oeq float %subf, 0xBFE6666600000000 + %err_msgf = getelementptr [18 x i8]* @.strSubFlt, i32 0, i32 0 + call void @failCheck( i1 %matchesf, i8* %err_msgf ) + + ; test double subtraction (3.5 - -4.2) + store double 3.500000e+00, double* %d1_addr + store double -4.200000e+00, double* %d2_addr + %d1 = load double* %d1_addr + %d2 = load double* %d2_addr + %subd = sub double %d1, %d2 + %matchesd = fcmp oeq double %subd, 7.700000e+00 + %err_msgd = getelementptr [19 x i8]* @.strSubDbl, i32 0, i32 0 + call void @failCheck( i1 %matchesd, i8* %err_msgd ) + + ret void +} + +; test multiplication (fp and int since mul is polymorphic) +define void @testMul() { +entry: + %f1_addr = alloca float, align 4 + %f2_addr = alloca float, align 4 + %d1_addr = alloca double, align 8 + %d2_addr = alloca double, align 8 + + ; test integer multiplication (3 * 4) + %muli = mul i32 3, 4 + %matchesi = icmp eq i32 %muli, 12 + %err_msgi = getelementptr [16 x i8]* @.strMulInt, i32 0, i32 0 + call void @failCheck( i1 %matchesi, i8* %err_msgi ) + + ; test float multiplication (3.5 * 4.2) + store float 3.500000e+00, float* %f1_addr + store float 0x4010CCCCC0000000, float* %f2_addr + %f1 = load float* %f1_addr + %f2 = load float* %f2_addr + %mulf = mul float %f1, %f2 + %matchesf = fcmp oeq float %mulf, 0x402D666640000000 + %err_msgf = getelementptr [18 x i8]* @.strMulFlt, i32 0, i32 0 + call void @failCheck( i1 %matchesf, i8* %err_msgf ) + + ; test double multiplication (3.5 * -4.2) + store double 3.500000e+00, double* %d1_addr + store double -4.200000e+00, double* %d2_addr + %d1 = load double* %d1_addr + %d2 = load double* %d2_addr + %muld = mul double %d1, %d2 + %matchesd = fcmp oeq double %muld, 0xC02D666666666667 + %err_msgd = getelementptr [19 x i8]* @.strMulDbl, i32 0, i32 0 + call void @failCheck( i1 %matchesd, i8* %err_msgd ) + + ret void +} + +; test float comparisons (ordered) +define void @testFCmpFOrdered(float %f1, float %f2, i1 %eq, i1 %ge, i1 %gt, i1 %le, i1 %lt, i1 %ne, i1 %ord) { +entry: + ; test fcmp::true -- should always return true + %cmp_t = fcmp true float %f1, %f2 + %cmp_t_ok = icmp eq i1 %cmp_t, 1 + %cmp_t_em = getelementptr [19 x i8]* @.strCmpTrFlt, i32 0, i32 0 + call void @failCheck( i1 %cmp_t_ok, i8* %cmp_t_em ) + + ; test fcmp::false -- should always return false + %cmp_f = fcmp false float %f1, %f2 + %cmp_f_ok = icmp eq i1 %cmp_f, 0 + %cmp_f_em = getelementptr [20 x i8]* @.strCmpFaFlt, i32 0, i32 0 + call void @failCheck( i1 %cmp_f_ok, i8* %cmp_f_em ) + + ; test fcmp::ord -- should return true if neither operand is NaN + %cmp_o = fcmp ord float %f1, %f2 + %cmp_o_ok = icmp eq i1 %cmp_o, %ord + %cmp_o_em = getelementptr [18 x i8]* @.strCmpOrdFlt, i32 0, i32 0 + call void @failCheck( i1 %cmp_o_ok, i8* %cmp_o_em ) + + ; test fcmp::oeq -- should return true if neither operand is a NaN and they are equal + %cmp_eq = fcmp oeq float %f1, %f2 + %cmp_eq_ok = icmp eq i1 %cmp_eq, %eq + %cmp_eq_em = getelementptr [17 x i8]* @.strCmpEqFlt, i32 0, i32 0 + call void @failCheck( i1 %cmp_eq_ok, i8* %cmp_eq_em ) + + ; test fcmp::oge -- should return true if neither operand is a NaN and the first is greater or equal + %cmp_ge = fcmp oge float %f1, %f2 + %cmp_ge_ok = icmp eq i1 %cmp_ge, %ge + %cmp_ge_em = getelementptr [17 x i8]* @.strCmpGeFlt, i32 0, i32 0 + call void @failCheck( i1 %cmp_ge_ok, i8* %cmp_ge_em ) + + ; test fcmp::ogt -- should return true if neither operand is a NaN and the first is greater + %cmp_gt = fcmp ogt float %f1, %f2 + %cmp_gt_ok = icmp eq i1 %cmp_gt, %gt + %cmp_gt_em = getelementptr [17 x i8]* @.strCmpGtFlt, i32 0, i32 0 + call void @failCheck( i1 %cmp_gt_ok, i8* %cmp_gt_em ) + + ; test fcmp::ole -- should return true if neither operand is a NaN and the first is less or equal + %cmp_le = fcmp ole float %f1, %f2 + %cmp_le_ok = icmp eq i1 %cmp_le, %le + %cmp_le_em = getelementptr [17 x i8]* @.strCmpLeFlt, i32 0, i32 0 + call void @failCheck( i1 %cmp_le_ok, i8* %cmp_le_em ) + + ; test fcmp::olt -- should return true if neither operand is a NaN and the first is less + %cmp_lt = fcmp olt float %f1, %f2 + %cmp_lt_ok = icmp eq i1 %cmp_lt, %lt + %cmp_lt_em = getelementptr [17 x i8]* @.strCmpLtFlt, i32 0, i32 0 + call void @failCheck( i1 %cmp_lt_ok, i8* %cmp_lt_em ) + + ; test fcmp::one -- should return true if neither operand is a NaN and they are not equal + %cmp_ne = fcmp one float %f1, %f2 + %cmp_ne_ok = icmp eq i1 %cmp_ne, %ne + %cmp_ne_em = getelementptr [17 x i8]* @.strCmpNeFlt, i32 0, i32 0 + call void @failCheck( i1 %cmp_ne_ok, i8* %cmp_ne_em ) + + ret void +} + +; test double comparisons (ordered) +define void @testFCmpDOrdered(double %d1, double %d2, i1 %eq, i1 %ge, i1 %gt, i1 %le, i1 %lt, i1 %ne, i1 %ord) { +entry: + ; test fcmp::true -- should always return true + %cmp_t = fcmp true double %d1, %d2 + %cmp_t_ok = icmp eq i1 %cmp_t, 1 + %cmp_t_em = getelementptr [19 x i8]* @.strCmpTrDbl, i32 0, i32 0 + call void @failCheck( i1 %cmp_t_ok, i8* %cmp_t_em ) + + ; test fcmp::false -- should always return false + %cmp_f = fcmp false double %d1, %d2 + %cmp_f_ok = icmp eq i1 %cmp_f, 0 + %cmp_f_em = getelementptr [20 x i8]* @.strCmpFaDbl, i32 0, i32 0 + call void @failCheck( i1 %cmp_f_ok, i8* %cmp_f_em ) + + ; test fcmp::ord -- should return true if neither operand is NaN + %cmp_o = fcmp ord double %d1, %d2 + %cmp_o_ok = icmp eq i1 %cmp_o, %ord + %cmp_o_em = getelementptr [19 x i8]* @.strCmpOrdDbl, i32 0, i32 0 + call void @failCheck( i1 %cmp_o_ok, i8* %cmp_o_em ) + + ; test fcmp::oeq -- should return true if neither operand is a NaN and they are equal + %cmp_eq = fcmp oeq double %d1, %d2 + %cmp_eq_ok = icmp eq i1 %cmp_eq, %eq + %cmp_eq_em = getelementptr [18 x i8]* @.strCmpEqDbl, i32 0, i32 0 + call void @failCheck( i1 %cmp_eq_ok, i8* %cmp_eq_em ) + + ; test fcmp::oge -- should return true if neither operand is a NaN and the first is greater or equal + %cmp_ge = fcmp oge double %d1, %d2 + %cmp_ge_ok = icmp eq i1 %cmp_ge, %ge + %cmp_ge_em = getelementptr [18 x i8]* @.strCmpGeDbl, i32 0, i32 0 + call void @failCheck( i1 %cmp_ge_ok, i8* %cmp_ge_em ) + + ; test fcmp::ogt -- should return true if neither operand is a NaN and the first is greater + %cmp_gt = fcmp ogt double %d1, %d2 + %cmp_gt_ok = icmp eq i1 %cmp_gt, %gt + %cmp_gt_em = getelementptr [18 x i8]* @.strCmpGtDbl, i32 0, i32 0 + call void @failCheck( i1 %cmp_gt_ok, i8* %cmp_gt_em ) + + ; test fcmp::ole -- should return true if neither operand is a NaN and the first is less or equal + %cmp_le = fcmp ole double %d1, %d2 + %cmp_le_ok = icmp eq i1 %cmp_le, %le + %cmp_le_em = getelementptr [18 x i8]* @.strCmpLeDbl, i32 0, i32 0 + call void @failCheck( i1 %cmp_le_ok, i8* %cmp_le_em ) + + ; test fcmp::olt -- should return true if neither operand is a NaN and the first is less + %cmp_lt = fcmp olt double %d1, %d2 + %cmp_lt_ok = icmp eq i1 %cmp_lt, %lt + %cmp_lt_em = getelementptr [18 x i8]* @.strCmpLtDbl, i32 0, i32 0 + call void @failCheck( i1 %cmp_lt_ok, i8* %cmp_lt_em ) + + ; test fcmp::one -- should return true if neither operand is a NaN and they are not equal + %cmp_ne = fcmp one double %d1, %d2 + %cmp_ne_ok = icmp eq i1 %cmp_ne, %ne + %cmp_ne_em = getelementptr [18 x i8]* @.strCmpNeDbl, i32 0, i32 0 + call void @failCheck( i1 %cmp_ne_ok, i8* %cmp_ne_em ) + + ret void +} + +; test floating point comparisons (ordered) +define void @testFCmpBothOrdered(double %d1, double %d2, i1 %eq, i1 %ge, i1 %gt, i1 %le, i1 %lt, i1 %ne, i1 %ord) { +entry: + call void @testFCmpDOrdered( double %d1, double %d2, i1 %eq, i1 %ge, i1 %gt, i1 %le, i1 %lt, i1 %ne, i1 %ord ) + + %f1 = fptrunc double %d1 to float + %f2 = fptrunc double %d2 to float + call void @testFCmpFOrdered( float %f1, float %f2, i1 %eq, i1 %ge, i1 %gt, i1 %le, i1 %lt, i1 %ne, i1 %ord ) + + ret void +} + +; test float comparisons (unordered) +define void @testFCmpFUnordered(float %f1, float %f2, i1 %eq, i1 %ge, i1 %gt, i1 %le, i1 %lt, i1 %ne, i1 %uno) { +entry: + ; test fcmp::uno -- should return true if either operand is NaN + %cmp_o = fcmp uno float %f1, %f2 + %cmp_o_ok = icmp eq i1 %cmp_o, %uno + %cmp_o_em = getelementptr [20 x i8]* @.strCmpUnoFlt, i32 0, i32 0 + call void @failCheck( i1 %cmp_o_ok, i8* %cmp_o_em ) + + ; test fcmp::oeq -- should return true if either operand is a NaN and they are equal + %cmp_eq = fcmp ueq float %f1, %f2 + %cmp_eq_ok = icmp eq i1 %cmp_eq, %eq + %cmp_eq_em = getelementptr [17 x i8]* @.strCmpEqFltU, i32 0, i32 0 + call void @failCheck( i1 %cmp_eq_ok, i8* %cmp_eq_em ) + + ; test fcmp::oge -- should return true if either operand is a NaN and the first is greater or equal + %cmp_ge = fcmp uge float %f1, %f2 + %cmp_ge_ok = icmp eq i1 %cmp_ge, %ge + %cmp_ge_em = getelementptr [17 x i8]* @.strCmpGeFltU, i32 0, i32 0 + call void @failCheck( i1 %cmp_ge_ok, i8* %cmp_ge_em ) + + ; test fcmp::ogt -- should return true if either operand is a NaN and the first is greater + %cmp_gt = fcmp ugt float %f1, %f2 + %cmp_gt_ok = icmp eq i1 %cmp_gt, %gt + %cmp_gt_em = getelementptr [17 x i8]* @.strCmpGtFltU, i32 0, i32 0 + call void @failCheck( i1 %cmp_gt_ok, i8* %cmp_gt_em ) + + ; test fcmp::ole -- should return true if either operand is a NaN and the first is less or equal + %cmp_le = fcmp ule float %f1, %f2 + %cmp_le_ok = icmp eq i1 %cmp_le, %le + %cmp_le_em = getelementptr [17 x i8]* @.strCmpLeFltU, i32 0, i32 0 + call void @failCheck( i1 %cmp_le_ok, i8* %cmp_le_em ) + + ; test fcmp::olt -- should return true if either operand is a NaN and the first is less + %cmp_lt = fcmp ult float %f1, %f2 + %cmp_lt_ok = icmp eq i1 %cmp_lt, %lt + %cmp_lt_em = getelementptr [17 x i8]* @.strCmpLtFltU, i32 0, i32 0 + call void @failCheck( i1 %cmp_lt_ok, i8* %cmp_lt_em ) + + ; test fcmp::one -- should return true if either operand is a NaN and they are not equal + %cmp_ne = fcmp une float %f1, %f2 + %cmp_ne_ok = icmp eq i1 %cmp_ne, %ne + %cmp_ne_em = getelementptr [17 x i8]* @.strCmpNeFltU, i32 0, i32 0 + call void @failCheck( i1 %cmp_ne_ok, i8* %cmp_ne_em ) + + ret void +} + +; test double comparisons (unordered) +define void @testFCmpDUnordered(double %d1, double %d2, i1 %eq, i1 %ge, i1 %gt, i1 %le, i1 %lt, i1 %ne, i1 %uno) { +entry: + ; test fcmp::uno -- should return true if either operand is NaN + %cmp_o = fcmp uno double %d1, %d2 + %cmp_o_ok = icmp eq i1 %cmp_o, %uno + %cmp_o_em = getelementptr [21 x i8]* @.strCmpUnoDbl, i32 0, i32 0 + call void @failCheck( i1 %cmp_o_ok, i8* %cmp_o_em ) + + ; test fcmp::ueq -- should return true if either operand is a NaN and they are equal + %cmp_eq = fcmp ueq double %d1, %d2 + %cmp_eq_ok = icmp eq i1 %cmp_eq, %eq + %cmp_eq_em = getelementptr [18 x i8]* @.strCmpEqDblU, i32 0, i32 0 + call void @failCheck( i1 %cmp_eq_ok, i8* %cmp_eq_em ) + + ; test fcmp::uge -- should return true if either operand is a NaN and the first is greater or equal + %cmp_ge = fcmp uge double %d1, %d2 + %cmp_ge_ok = icmp eq i1 %cmp_ge, %ge + %cmp_ge_em = getelementptr [18 x i8]* @.strCmpGeDblU, i32 0, i32 0 + call void @failCheck( i1 %cmp_ge_ok, i8* %cmp_ge_em ) + + ; test fcmp::ugt -- should return true if either operand is a NaN and the first is greater + %cmp_gt = fcmp ugt double %d1, %d2 + %cmp_gt_ok = icmp eq i1 %cmp_gt, %gt + %cmp_gt_em = getelementptr [18 x i8]* @.strCmpGtDblU, i32 0, i32 0 + call void @failCheck( i1 %cmp_gt_ok, i8* %cmp_gt_em ) + + ; test fcmp::ule -- should return true if either operand is a NaN and the first is less or equal + %cmp_le = fcmp ule double %d1, %d2 + %cmp_le_ok = icmp eq i1 %cmp_le, %le + %cmp_le_em = getelementptr [18 x i8]* @.strCmpLeDblU, i32 0, i32 0 + call void @failCheck( i1 %cmp_le_ok, i8* %cmp_le_em ) + + ; test fcmp::ult -- should return true if either operand is a NaN and the first is less + %cmp_lt = fcmp ult double %d1, %d2 + %cmp_lt_ok = icmp eq i1 %cmp_lt, %lt + %cmp_lt_em = getelementptr [18 x i8]* @.strCmpLtDblU, i32 0, i32 0 + call void @failCheck( i1 %cmp_lt_ok, i8* %cmp_lt_em ) + + ; test fcmp::une -- should return true if either operand is a NaN and they are not equal + %cmp_ne = fcmp une double %d1, %d2 + %cmp_ne_ok = icmp eq i1 %cmp_ne, %ne + %cmp_ne_em = getelementptr [18 x i8]* @.strCmpNeDblU, i32 0, i32 0 + call void @failCheck( i1 %cmp_ne_ok, i8* %cmp_ne_em ) + + ret void +} + +; test floating point comparisons (unordered) +define void @testFCmpBothUnordered(double %d1, double %d2, i1 %eq, i1 %ge, i1 %gt, i1 %le, i1 %lt, i1 %ne, i1 %uno) { +entry: + call void @testFCmpDUnordered( double %d1, double %d2, i1 %eq, i1 %ge, i1 %gt, i1 %le, i1 %lt, i1 %ne, i1 %uno ) + + %f1 = fptrunc double %d1 to float + %f2 = fptrunc double %d2 to float + call void @testFCmpFUnordered( float %f1, float %f2, i1 %eq, i1 %ge, i1 %gt, i1 %le, i1 %lt, i1 %ne, i1 %uno ) + + ret void +} + +; test floating point comparisons (ordered and unordered) +define void @testFCmpBoth(double %d1, double %d2, i1 %eq, i1 %ge, i1 %gt, i1 %le, i1 %lt, i1 %ne, i1 %ord, i1 %uno) { +entry: + call void @testFCmpBothOrdered( double %d1, double %d2, i1 %eq, i1 %ge, i1 %gt, i1 %le, i1 %lt, i1 %ne, i1 %ord ) + call void @testFCmpBothUnordered( double %d1, double %d2, i1 %eq, i1 %ge, i1 %gt, i1 %le, i1 %lt, i1 %ne, i1 %uno ) + + ret void +} + +; test floating point comparisons (ordered and unordered) with a variety of real numbers and NaNs as operands +define void @testFCmp() { +entry: + %x = alloca i64, align 8 + %nan = alloca double, align 8 + + ; test FCmp on some real number inputs + call void @testFCmpBoth( double 0.000000e+00, double 0.000000e+00, i1 1, i1 1, i1 0, i1 1, i1 0, i1 0, i1 1, i1 0 ) + call void @testFCmpBoth( double 0.000000e+00, double 1.000000e+00, i1 0, i1 0, i1 0, i1 1, i1 1, i1 1, i1 1, i1 0 ) + call void @testFCmpBoth( double 1.000000e+00, double 0.000000e+00, i1 0, i1 1, i1 1, i1 0, i1 0, i1 1, i1 1, i1 0 ) + + ; build NaN + store i64 -1, i64* %x + %nan_as_i8 = bitcast double* %nan to i8* + %x_as_i8 = bitcast i64* %x to i8* + call void @llvm.memcpy.i32( i8* %nan_as_i8, i8* %x_as_i8, i32 8, i32 8 ) + + ; load two copies of our NaN + %nan1 = load double* %nan + %nan2 = load double* %nan + + ; Warning: NaN comparisons with normal operators is BROKEN in LLVM JIT v2.0. Fixed in v2.1. + ; NaNs do different things depending on ordered vs unordered +; call void @testFCmpBothOrdered( double %nan1, double 0.000000e+00, i1 0, i1 0, i1 0, i1 0, i1 0, i1 0, i1 0 ) +; call void @testFCmpBothOrdered( double %nan1, double %nan2, i1 0, i1 0, i1 0, i1 0, i1 0, i1 0, i1 0 ) +; call void @testFCmpBothUnordered( double %nan1, double 0.000000e+00, i1 1, i1 1, i1 1, i1 1, i1 1, i1 1, i1 1 ) +; call void @testFCmpBothUnordered( double %nan1, double %nan2, i1 1, i1 1, i1 1, i1 1, i1 1, i1 1, i1 1 ) + + ret void +} + +; tes all floating point instructions +define i32 @main() { +entry: + call void @testFPTrunc( ) + call void @testFPExt( ) + call void @testFPToUI( ) + call void @testFPToSI( ) + call void @testUIToFP( ) + call void @testSIToFP( ) + + call void @testFDiv( ) + call void @testFRem( ) + call void @testAdd( ) + call void @testSub( ) + call void @testMul( ) + + call void @testFCmp( ) + + ; everything worked -- print a message saying so + %works_msg = getelementptr [20 x i8]* @.strWorks, i32 0, i32 0 + %err_stream = load %struct.stdout** @stdout + %ret = call i32 (%struct.stdout*, i8*, ...)* @fprintf( %struct.stdout* %err_stream, i8* %works_msg ) + + ret i32 0 +} diff --git a/test/Concrete/GlobalInitializers.ll b/test/Concrete/GlobalInitializers.ll new file mode 100755 index 00000000..b4d95de6 --- /dev/null +++ b/test/Concrete/GlobalInitializers.ll @@ -0,0 +1,47 @@ +; (cd .. && make) && ../llvm/Release/bin/llvm-as test.ll -o=- | ../Debug/bin/klee + +declare void @print_i32(i32) + +%simple = type { i8, i16, i32, i64 } +@gInt = global i32 2 +@gInts = global [2 x i32] [ i32 3, i32 5 ] +@gStruct = global %simple { i8 7, i16 11, i32 13, i64 17 } +@gZero = global %simple zeroinitializer + +define i32 @main() { +entry: + %addr0 = getelementptr i32* @gInt, i32 0 + %addr1 = getelementptr [2 x i32]* @gInts, i32 0, i32 0 + %addr2 = getelementptr [2 x i32]* @gInts, i32 0, i32 1 + %addr3 = getelementptr %simple* @gStruct, i32 0, i32 0 + %addr4 = getelementptr %simple* @gStruct, i32 0, i32 1 + %addr5 = getelementptr %simple* @gStruct, i32 0, i32 2 + %addr6 = getelementptr %simple* @gStruct, i32 0, i32 3 + %addr7 = getelementptr %simple* @gZero, i32 0, i32 2 + %contents0 = load i32* %addr0 + %contents1 = load i32* %addr1 + %contents2 = load i32* %addr2 + %contents3tmp = load i8* %addr3 + %contents3 = zext i8 %contents3tmp to i32 + %contents4tmp = load i16* %addr4 + %contents4 = zext i16 %contents4tmp to i32 + %contents5 = load i32* %addr5 + %contents6tmp = load i64* %addr6 + %contents6 = trunc i64 %contents6tmp to i32 + %contents7 = load i32* %addr7 + %tmp0 = mul i32 %contents0, %contents1 + %tmp1 = mul i32 %tmp0, %contents2 + %tmp2 = mul i32 %tmp1, %contents3 + %tmp3 = mul i32 %tmp2, %contents4 + %tmp4 = mul i32 %tmp3, %contents5 + %tmp5 = mul i32 %tmp4, %contents6 + %tmp6 = add i32 %tmp5, %contents7 + %p = icmp eq i32 %tmp5, 510510 + br i1 %p, label %exitTrue, label %exitFalse +exitTrue: + call void @print_i32(i32 1) + ret i32 0 +exitFalse: + call void @print_i32(i32 0) + ret i32 0 +} diff --git a/test/Concrete/GlobalVariable.ll b/test/Concrete/GlobalVariable.ll new file mode 100644 index 00000000..d15df064 --- /dev/null +++ b/test/Concrete/GlobalVariable.ll @@ -0,0 +1,9 @@ +declare void @print_i32(i32) + +@anInt = global i32 1 +@aRef = global i32* @anInt + +define i32 @main() { + call void @print_i32(i32 0) + ret i32 0 +} diff --git a/test/Concrete/ICmp.ll b/test/Concrete/ICmp.ll new file mode 100644 index 00000000..e2a1ef24 --- /dev/null +++ b/test/Concrete/ICmp.ll @@ -0,0 +1,245 @@ +declare void @print_i32(i32) + +define i1 @checkSlt() { + %c0 = icmp slt i8 -1, 1 ; 1 + %c1 = icmp slt i8 0, 1 ; 1 + %c2 = icmp slt i8 1, 1 ; 0 + %c3 = icmp slt i8 1, -1 ; 0 + %c4 = icmp slt i8 1, 0 ; 0 + %c5 = icmp slt i8 1, 1 ; 0 + %a0 = select i1 %c0, i8 1, i8 0 + %a1 = select i1 %c1, i8 2, i8 0 + %a2 = select i1 %c2, i8 4, i8 0 + %a3 = select i1 %c3, i8 8, i8 0 + %a4 = select i1 %c4, i8 16, i8 0 + %a5 = select i1 %c5, i8 32, i8 0 + %sum0 = add i8 %a0, %a1 + %sum1 = add i8 %sum0, %a2 + %sum2 = add i8 %sum1, %a3 + %sum3 = add i8 %sum2, %a4 + %sum = add i8 %sum3, %a5 + %test = icmp eq i8 %sum, 3 ; 0bin000011 + ret i1 %test +} + +define i1 @checkSle() { + %c0 = icmp sle i8 -1, 1 ; 1 + %c1 = icmp sle i8 0, 1 ; 1 + %c2 = icmp sle i8 1, 1 ; 1 + %c3 = icmp sle i8 1, -1 ; 0 + %c4 = icmp sle i8 1, 0 ; 0 + %c5 = icmp sle i8 1, 1 ; 1 + %a0 = select i1 %c0, i8 1, i8 0 + %a1 = select i1 %c1, i8 2, i8 0 + %a2 = select i1 %c2, i8 4, i8 0 + %a3 = select i1 %c3, i8 8, i8 0 + %a4 = select i1 %c4, i8 16, i8 0 + %a5 = select i1 %c5, i8 32, i8 0 + %sum0 = add i8 %a0, %a1 + %sum1 = add i8 %sum0, %a2 + %sum2 = add i8 %sum1, %a3 + %sum3 = add i8 %sum2, %a4 + %sum = add i8 %sum3, %a5 + %test = icmp eq i8 %sum, 39 ; 0bin100111 + ret i1 %test +} + +define i1 @checkSgt() { + %c0 = icmp sgt i8 -1, 1 ; 0 + %c1 = icmp sgt i8 0, 1 ; 0 + %c2 = icmp sgt i8 1, 1 ; 0 + %c3 = icmp sgt i8 1, -1 ; 1 + %c4 = icmp sgt i8 1, 0 ; 1 + %c5 = icmp sgt i8 1, 1 ; 0 + %a0 = select i1 %c0, i8 1, i8 0 + %a1 = select i1 %c1, i8 2, i8 0 + %a2 = select i1 %c2, i8 4, i8 0 + %a3 = select i1 %c3, i8 8, i8 0 + %a4 = select i1 %c4, i8 16, i8 0 + %a5 = select i1 %c5, i8 32, i8 0 + %sum0 = add i8 %a0, %a1 + %sum1 = add i8 %sum0, %a2 + %sum2 = add i8 %sum1, %a3 + %sum3 = add i8 %sum2, %a4 + %sum = add i8 %sum3, %a5 + %test = icmp eq i8 %sum, 24 ; 0bin011000 + ret i1 %test +} + +define i1 @checkSge() { + %c0 = icmp sge i8 -1, 1 ; 0 + %c1 = icmp sge i8 0, 1 ; 0 + %c2 = icmp sge i8 1, 1 ; 1 + %c3 = icmp sge i8 1, -1 ; 1 + %c4 = icmp sge i8 1, 0 ; 1 + %c5 = icmp sge i8 1, 1 ; 1 + %a0 = select i1 %c0, i8 1, i8 0 + %a1 = select i1 %c1, i8 2, i8 0 + %a2 = select i1 %c2, i8 4, i8 0 + %a3 = select i1 %c3, i8 8, i8 0 + %a4 = select i1 %c4, i8 16, i8 0 + %a5 = select i1 %c5, i8 32, i8 0 + %sum0 = add i8 %a0, %a1 + %sum1 = add i8 %sum0, %a2 + %sum2 = add i8 %sum1, %a3 + %sum3 = add i8 %sum2, %a4 + %sum = add i8 %sum3, %a5 + %test = icmp eq i8 %sum, 60 ; 0bin111100 + ret i1 %test +} + +define i1 @checkUlt() { + %c0 = icmp ult i8 -1, 1 ; 0 + %c1 = icmp ult i8 0, 1 ; 1 + %c2 = icmp ult i8 1, 1 ; 0 + %c3 = icmp ult i8 1, -1 ; 1 + %c4 = icmp ult i8 1, 0 ; 0 + %c5 = icmp ult i8 1, 1 ; 0 + %a0 = select i1 %c0, i8 1, i8 0 + %a1 = select i1 %c1, i8 2, i8 0 + %a2 = select i1 %c2, i8 4, i8 0 + %a3 = select i1 %c3, i8 8, i8 0 + %a4 = select i1 %c4, i8 16, i8 0 + %a5 = select i1 %c5, i8 32, i8 0 + %sum0 = add i8 %a0, %a1 + %sum1 = add i8 %sum0, %a2 + %sum2 = add i8 %sum1, %a3 + %sum3 = add i8 %sum2, %a4 + %sum = add i8 %sum3, %a5 + %test = icmp eq i8 %sum, 10 ; 0bin001010 + ret i1 %test +} + +define i1 @checkUle() { + %c0 = icmp ule i8 -1, 1 ; 0 + %c1 = icmp ule i8 0, 1 ; 1 + %c2 = icmp ule i8 1, 1 ; 1 + %c3 = icmp ule i8 1, -1 ; 1 + %c4 = icmp ule i8 1, 0 ; 0 + %c5 = icmp ule i8 1, 1 ; 1 + %a0 = select i1 %c0, i8 1, i8 0 + %a1 = select i1 %c1, i8 2, i8 0 + %a2 = select i1 %c2, i8 4, i8 0 + %a3 = select i1 %c3, i8 8, i8 0 + %a4 = select i1 %c4, i8 16, i8 0 + %a5 = select i1 %c5, i8 32, i8 0 + %sum0 = add i8 %a0, %a1 + %sum1 = add i8 %sum0, %a2 + %sum2 = add i8 %sum1, %a3 + %sum3 = add i8 %sum2, %a4 + %sum = add i8 %sum3, %a5 + %test = icmp eq i8 %sum, 46 ; 0bin101110 + ret i1 %test +} + +define i1 @checkUgt() { + %c0 = icmp ugt i8 -1, 1 ; 1 + %c1 = icmp ugt i8 0, 1 ; 0 + %c2 = icmp ugt i8 1, 1 ; 0 + %c3 = icmp ugt i8 1, -1 ; 0 + %c4 = icmp ugt i8 1, 0 ; 1 + %c5 = icmp ugt i8 1, 1 ; 0 + %a0 = select i1 %c0, i8 1, i8 0 + %a1 = select i1 %c1, i8 2, i8 0 + %a2 = select i1 %c2, i8 4, i8 0 + %a3 = select i1 %c3, i8 8, i8 0 + %a4 = select i1 %c4, i8 16, i8 0 + %a5 = select i1 %c5, i8 32, i8 0 + %sum0 = add i8 %a0, %a1 + %sum1 = add i8 %sum0, %a2 + %sum2 = add i8 %sum1, %a3 + %sum3 = add i8 %sum2, %a4 + %sum = add i8 %sum3, %a5 + %test = icmp eq i8 %sum, 17 ; 0bin010001 + ret i1 %test +} + +define i1 @checkUge() { + %c0 = icmp uge i8 -1, 1 ; 1 + %c1 = icmp uge i8 0, 1 ; 0 + %c2 = icmp uge i8 1, 1 ; 1 + %c3 = icmp uge i8 1, -1 ; 0 + %c4 = icmp uge i8 1, 0 ; 1 + %c5 = icmp uge i8 1, 1 ; 1 + %a0 = select i1 %c0, i8 1, i8 0 + %a1 = select i1 %c1, i8 2, i8 0 + %a2 = select i1 %c2, i8 4, i8 0 + %a3 = select i1 %c3, i8 8, i8 0 + %a4 = select i1 %c4, i8 16, i8 0 + %a5 = select i1 %c5, i8 32, i8 0 + %sum0 = add i8 %a0, %a1 + %sum1 = add i8 %sum0, %a2 + %sum2 = add i8 %sum1, %a3 + %sum3 = add i8 %sum2, %a4 + %sum = add i8 %sum3, %a5 + %test = icmp eq i8 %sum, 53 ; 0bin110101 + ret i1 %test +} + +define i1 @checkEq() { + %c0 = icmp eq i8 -1, 1 ; 0 + %c1 = icmp eq i8 1, 1 ; 1 + %c2 = icmp eq i8 1, -1 ; 0 + %a0 = select i1 %c0, i8 1, i8 0 + %a1 = select i1 %c1, i8 2, i8 0 + %a2 = select i1 %c2, i8 4, i8 0 + %sum0 = add i8 %a0, %a1 + %sum = add i8 %sum0, %a2 + %test = icmp eq i8 %sum, 2 + ret i1 %test +} + +define i1 @checkNe() { + %c0 = icmp ne i8 -1, 1 ; 1 + %c1 = icmp ne i8 1, 1 ; 0 + %c2 = icmp ne i8 1, -1 ; 1 + %a0 = select i1 %c0, i8 1, i8 0 + %a1 = select i1 %c1, i8 2, i8 0 + %a2 = select i1 %c2, i8 4, i8 0 + %sum0 = add i8 %a0, %a1 + %sum = add i8 %sum0, %a2 + %test = icmp eq i8 %sum, 5 + ret i1 %test +} + +define i32 @main() { + %c0 = call i1 @checkSlt () + %c1 = call i1 @checkSle () + %c2 = call i1 @checkSgt () + %c3 = call i1 @checkSge () + %c4 = call i1 @checkUlt () + %c5 = call i1 @checkUle () + %c6 = call i1 @checkUgt () + %c7 = call i1 @checkUge () + %c8 = call i1 @checkEq () + %c9 = call i1 @checkNe () + %a0 = select i1 %c0, i16 1, i16 0 + %a1 = select i1 %c1, i16 2, i16 0 + %a2 = select i1 %c2, i16 4, i16 0 + %a3 = select i1 %c3, i16 8, i16 0 + %a4 = select i1 %c4, i16 16, i16 0 + %a5 = select i1 %c5, i16 32, i16 0 + %a6 = select i1 %c6, i16 64, i16 0 + %a7 = select i1 %c7, i16 128, i16 0 + %a8 = select i1 %c8, i16 256, i16 0 + %a9 = select i1 %c9, i16 512, i16 0 + %sum0 = add i16 %a0, %a1 + %sum1 = add i16 %sum0, %a2 + %sum2 = add i16 %sum1, %a3 + %sum3 = add i16 %sum2, %a4 + %sum4 = add i16 %sum3, %a5 + %sum5 = add i16 %sum4, %a6 + %sum6 = add i16 %sum5, %a7 + %sum7 = add i16 %sum6, %a8 + %sum8 = add i16 %sum7, %a9 + %t = shl i16 63, 10 + %sum = add i16 %sum8, %t + %test = icmp eq i16 %sum, -1 + br i1 %test, label %exitTrue, label %exitFalse +exitTrue: + call void @print_i32(i32 1) + ret i32 0 +exitFalse: + call void @print_i32(i32 0) + ret i32 0 +} diff --git a/test/Concrete/InvokeAndReturn.ll b/test/Concrete/InvokeAndReturn.ll new file mode 100644 index 00000000..f73c242a --- /dev/null +++ b/test/Concrete/InvokeAndReturn.ll @@ -0,0 +1,18 @@ +declare void @print_i32(i32) + +define i8 @sum(i8 %a, i8 %b) { + %c = add i8 %a, %b + ret i8 %c +} + +define i32 @main() { + invoke i8 @sum(i8 1, i8 2) + to label %continue + unwind label %error +continue: + call void @print_i32(i32 1) + ret i32 0 +error: + call void @print_i32(i32 0) + ret i32 0 +} diff --git a/test/Concrete/InvokeAndUnwindOnce.ll b/test/Concrete/InvokeAndUnwindOnce.ll new file mode 100644 index 00000000..2834ec82 --- /dev/null +++ b/test/Concrete/InvokeAndUnwindOnce.ll @@ -0,0 +1,18 @@ +declare void @print_i32(i32) + +define i8 @sum(i8 %a, i8 %b) { + %c = add i8 %a, %b + unwind +} + +define i32 @main() { + invoke i8 @sum(i8 1, i8 2) + to label %continue + unwind label %error +continue: + call void @print_i32(i32 1) + ret i32 0 +error: + call void @print_i32(i32 0) + ret i32 0 +} diff --git a/test/Concrete/InvokeAndUnwindTwice.ll b/test/Concrete/InvokeAndUnwindTwice.ll new file mode 100644 index 00000000..81760b86 --- /dev/null +++ b/test/Concrete/InvokeAndUnwindTwice.ll @@ -0,0 +1,22 @@ +declare void @print_i32(i32) + +define i8 @myadd(i8 %a, i8 %b) { + unwind +} + +define i8 @sum(i8 %a, i8 %b) { + %c = call i8 @myadd(i8 %a, i8 %b) + ret i8 %c +} + +define i32 @main() { + invoke i8 @sum(i8 1, i8 2) + to label %continue + unwind label %error +continue: + call void @print_i32(i32 1) + ret i32 0 +error: + call void @print_i32(i32 0) + ret i32 0 +} diff --git a/test/Concrete/Makefile b/test/Concrete/Makefile new file mode 100644 index 00000000..4acfadad --- /dev/null +++ b/test/Concrete/Makefile @@ -0,0 +1,55 @@ +LEVEL = ../.. + +# hard-coding bad. will get fixed. +LCCFLAGS += -O0 -Wall +LCXXFLAGS += -O0 -Wall +LLCFLAGS = + +test: + ./ConcreteTest.py + +clean:: + -rm -rf Output klee-last klee-out* test.log + -rm -rf *.bc + +# We do not want to make .d files for tests! +DISABLE_AUTO_DEPENDENCIES=1 + +include ${LEVEL}/Makefile.common + +# Compile from X.c to Output/X.ll +Output/%.ll: %.c $(LCC1) Output/.dir $(INCLUDES) + $(LLVMGCCWITHPATH) --emit-llvm $(CPPFLAGS) $(LCCFLAGS) -S $< -o $@ + +# Compile from X.cpp to Output/X.ll +Output/%.ll: %.cpp $(LCC1XX) Output/.dir $(INCLUDES) + $(LLVMGXXWITHPATH) --emit-llvm $(CPPFLAGS) $(LCXXFLAGS) -S $< -o $@ + +# Compile from X.cc to Output/X.ll +Output/%.ll: %.cc $(LCC1XX) Output/.dir $(INCLUDES) + $(LLVMGXXWITHPATH) --emit-llvm $(CPPFLAGS) $(LCXXFLAGS) -S $< -o $@ + +# LLVM Assemble from Output/X.ll to Output/X.bc. Output/X.ll must have come +# from GCC output, so use GCCAS. +# +Output/%.bc: Output/%.ll $(LLVMAS) + $(LLVMAS) -f $< -o $@ + +# LLVM Assemble from X.ll to Output/X.bc. Because we are coming directly from +# LLVM source, use the non-transforming assembler. +# +Output/%.bc: %.ll $(LLVMAS) Output/.dir + $(LLVMAS) -f $< -o $@ + +Output/linked_%.bc: Output/%.bc Output/_testingUtils.bc + $(LLVMLD) -disable-opt -link-as-library Output/_testingUtils.bc $< -o $@ + +.PRECIOUS: Output/.dir + +## Cancel built-in implicit rules that override above rules +%: %.s + +%: %.c + +%.o: %.c + diff --git a/test/Concrete/OneCall.ll b/test/Concrete/OneCall.ll new file mode 100644 index 00000000..d8b94b37 --- /dev/null +++ b/test/Concrete/OneCall.ll @@ -0,0 +1,12 @@ +declare void @print_i32(i32) + +define i32 @sum(i32 %a, i32 %b) { + %c = sub i32 %a, %b + ret i32 %c +} + +define i32 @main() { + %a = call i32 @sum(i32 54, i32 2) + call void @print_i32(i32 %a) + ret i32 0 +} diff --git a/test/Concrete/OverlappingPhiNodes.ll b/test/Concrete/OverlappingPhiNodes.ll new file mode 100644 index 00000000..d3dc76ab --- /dev/null +++ b/test/Concrete/OverlappingPhiNodes.ll @@ -0,0 +1,15 @@ +declare void @print_i32(i32) + +define i32 @main() { +entry: + br label %test +test: + %a = phi i32 [10, %entry], [%b, %test] + %b = phi i32 [20, %entry], [%a, %test] + %c = phi i32 [0, %entry], [1, %test] + %d = icmp eq i32 %c, 1 + br i1 %d, label %exit, label %test +exit: + call void @print_i32(i32 %b) + ret i32 0 +} diff --git a/test/Concrete/Select.ll b/test/Concrete/Select.ll new file mode 100644 index 00000000..29b81e3e --- /dev/null +++ b/test/Concrete/Select.ll @@ -0,0 +1,15 @@ +declare void @print_i32(i32) + +define i32 @main() { + %ten = select i1 true, i32 10, i32 0 + %five = select i1 false, i32 0, i32 5 + %check = add i32 %ten, %five + %test = icmp eq i32 %check, 15 + br i1 %test, label %exitTrue, label %exitFalse +exitTrue: + call void @print_i32(i32 1) + ret i32 0 +exitFalse: + call void @print_i32(i32 0) + ret i32 0 +} diff --git a/test/Concrete/Shifts.ll b/test/Concrete/Shifts.ll new file mode 100644 index 00000000..2430f879 --- /dev/null +++ b/test/Concrete/Shifts.ll @@ -0,0 +1,21 @@ +declare void @print_i32(i32) + +define i32 @main() { + %amt = add i8 2, 5 + %a = shl i8 1, 5 + %b = lshr i8 %a, 5 + %c = shl i8 %b, %amt + %d = lshr i8 %c, %amt + %e = shl i8 %d, 7 + %f = ashr i8 %e, 7 + %g = shl i8 %f, %amt + %h = ashr i8 %g, %amt + %test = icmp eq i8 %h, -1 + br i1 %test, label %exitTrue, label %exitFalse +exitTrue: + call void @print_i32(i32 1) + ret i32 0 +exitFalse: + call void @print_i32(i32 0) + ret i32 0 +} diff --git a/test/Concrete/SimpleStoreAndLoad.ll b/test/Concrete/SimpleStoreAndLoad.ll new file mode 100644 index 00000000..0201ba15 --- /dev/null +++ b/test/Concrete/SimpleStoreAndLoad.ll @@ -0,0 +1,17 @@ +declare void @print_i32(i32) + +define i32 @main() { +entry: + %a = alloca i32, i32 4 + %tmp1 = getelementptr i32* %a, i32 0 + store i32 0, i32* %tmp1 + %tmp2 = load i32* %tmp1 + %tmp3 = icmp eq i32 %tmp2, 0 + br i1 %tmp3, label %exitTrue, label %exitFalse +exitTrue: + call void @print_i32(i32 1) + ret i32 0 +exitFalse: + call void @print_i32(i32 0) + ret i32 0 +} diff --git a/test/Concrete/UnconditionalBranch.ll b/test/Concrete/UnconditionalBranch.ll new file mode 100644 index 00000000..81e2f931 --- /dev/null +++ b/test/Concrete/UnconditionalBranch.ll @@ -0,0 +1,10 @@ +declare void @print_i32(i32) + +define i32 @main() { +entry: + %a = add i32 0, 1 + br label %exit +exit: + call void @print_i32(i32 %a) + ret i32 0 +} diff --git a/test/Concrete/UnconditionalBranchWithSimplePhi.ll b/test/Concrete/UnconditionalBranchWithSimplePhi.ll new file mode 100644 index 00000000..551ddf5d --- /dev/null +++ b/test/Concrete/UnconditionalBranchWithSimplePhi.ll @@ -0,0 +1,14 @@ +declare void @print_i32(i32) + +define i32 @main() { +entry: + %a = add i32 0, 1 + br label %exit +unused: + %b = add i32 1, 2 + br label %exit +exit: + %c = phi i32 [%a, %entry], [%b, %unused] + call void @print_i32(i32 %c) + ret i32 0 +} diff --git a/test/Concrete/UnorderedPhiNodes.ll b/test/Concrete/UnorderedPhiNodes.ll new file mode 100644 index 00000000..3ecd5083 --- /dev/null +++ b/test/Concrete/UnorderedPhiNodes.ll @@ -0,0 +1,15 @@ +declare void @print_i32(i32) + +define i32 @main() { +entry: + br label %test +test: + %a = phi i32 [10, %entry], [%b, %test] + %b = phi i32 [%a, %test], [20, %entry] + %c = phi i32 [0, %entry], [1, %test] + %d = icmp eq i32 %c, 1 + br i1 %d, label %exit, label %test +exit: + call void @print_i32(i32 %b) + ret i32 0 +} diff --git a/test/Concrete/_testingUtils.c b/test/Concrete/_testingUtils.c new file mode 100644 index 00000000..02d0529e --- /dev/null +++ b/test/Concrete/_testingUtils.c @@ -0,0 +1,32 @@ +int putchar(int x); + +void print_int(unsigned long long val); + +#define TYPED_PRINT(_name_type, _arg_type) \ + void print_ ## _name_type(_arg_type val) { print_int(val); } + +TYPED_PRINT(i1, unsigned char) +TYPED_PRINT(i8, unsigned char) +TYPED_PRINT(i16, unsigned short) +TYPED_PRINT(i32, unsigned int) +TYPED_PRINT(i64, unsigned long long) + +void print_int(unsigned long long val) { + int cur = 1; + + // effectively do a log (can't call log because it returns floats) + // do the nasty divide to prevent overflow + while (cur <= val / 10) + cur *= 10; + + while (cur) { + int digit = val / cur; + + putchar(digit + '0'); + + val = val % cur; + cur /= 10; + } + + putchar('\n'); +} diff --git a/test/Concrete/ackermann.c b/test/Concrete/ackermann.c new file mode 100644 index 00000000..0f21a3ab --- /dev/null +++ b/test/Concrete/ackermann.c @@ -0,0 +1,16 @@ +// llvm-gcc -O2 --emit-llvm -c ackermann.c && ../../Debug/bin/klee ackermann.o 2 2 + +#include <stdio.h> + +int ackermann(int m, int n) { + if (m == 0) + return n+1; + else + return ackermann(m-1, (n==0) ? 1 : ackermann(m, n-1)); + } + +int main() { + printf("ackerman(%d, %d) = %d\n", 2, 2, ackermann(2, 2)); + + return 0; +} diff --git a/test/Concrete/arith_test.ll b/test/Concrete/arith_test.ll new file mode 100644 index 00000000..5624462b --- /dev/null +++ b/test/Concrete/arith_test.ll @@ -0,0 +1,76 @@ +define void @"test_simple_arith"(i16 %i0, i16 %j0) +begin + %t1 = add i16 %i0, %j0 + %t2 = sub i16 %i0, %j0 + %t3 = mul i16 %t1, %t2 + + call void @print_i16(i16 %t3) + + ret void +end + +define void @"test_div_and_mod"(i16 %op1, i16 %op2) +begin + %t1 = udiv i16 %op1, %op2 + %t2 = urem i16 %op1, %op2 + %t3 = sdiv i16 %op1, %op2 + %t4 = srem i16 %op1, %op2 + + call void @print_i16(i16 %t1) + call void @print_i16(i16 %t2) + call void @print_i16(i16 %t3) + call void @print_i16(i16 %t4) + + ret void +end + +define void @test_cmp(i16 %op1, i16 %op2) +begin + %t1 = icmp ule i16 %op1, %op2 + %t2 = icmp ult i16 %op1, %op2 + %t3 = icmp uge i16 %op1, %op2 + %t4 = icmp ugt i16 %op1, %op2 + %t6 = icmp slt i16 %op1, %op2 + %t5 = icmp sle i16 %op1, %op2 + %t7 = icmp sge i16 %op1, %op2 + %t8 = icmp sgt i16 %op1, %op2 + %t9 = icmp eq i16 %op1, %op2 + %t10 = icmp ne i16 %op1, %op2 + + call void @print_i1(i1 %t1) + call void @print_i1(i1 %t2) + call void @print_i1(i1 %t3) + call void @print_i1(i1 %t4) + call void @print_i1(i1 %t5) + call void @print_i1(i1 %t6) + call void @print_i1(i1 %t7) + call void @print_i1(i1 %t8) + call void @print_i1(i1 %t9) + call void @print_i1(i1 %t10) + + ret void +end + +define i32 @main() +begin + call void @test_simple_arith(i16 111, i16 100) + + call void @test_div_and_mod(i16 63331, i16 3123) + call void @test_div_and_mod(i16 1000, i16 55444) + call void @test_div_and_mod(i16 49012, i16 55444) + call void @test_div_and_mod(i16 1000, i16 25) + + call void @test_cmp(i16 63331, i16 3123) + call void @test_cmp(i16 1000, i16 55444) + call void @test_cmp(i16 49012, i16 55444) + call void @test_cmp(i16 1000, i16 25) + + ret i32 0 +end + +; defined in print_int.c +declare void @print_i1(i1) +declare void @print_i8(i8) +declare void @print_i16(i16) +declare void @print_i32(i32) +declare void @print_i64(i64) diff --git a/test/Coverage/ReadArgs.c b/test/Coverage/ReadArgs.c new file mode 100644 index 00000000..7ebf056e --- /dev/null +++ b/test/Coverage/ReadArgs.c @@ -0,0 +1,9 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: rm -rf xxx +// RUN: echo " --output-dir=xxx " > %t1.args +// RUN: %klee --read-args %t1.args %t1.bc +// RUN: test -d xxx + +int main() { + return 0; +} diff --git a/test/Coverage/ReplayOutDir.c b/test/Coverage/ReplayOutDir.c new file mode 100644 index 00000000..3ca6fb80 --- /dev/null +++ b/test/Coverage/ReplayOutDir.c @@ -0,0 +1,11 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: rm -rf %t1.out %t1.replay +// RUN: %klee --output-dir=%t1.out %t1.bc +// RUN: %klee --output-dir=%t1.replay --replay-out-dir=%t1.out %t1.bc + +int main() { + int i; + klee_make_symbolic(&i, sizeof i); + klee_print_range("i", i); + return 0; +} diff --git a/test/Coverage/dg.exp b/test/Coverage/dg.exp new file mode 100644 index 00000000..879685ca --- /dev/null +++ b/test/Coverage/dg.exp @@ -0,0 +1,3 @@ +load_lib llvm.exp + +RunLLVMTests [lsort [glob -nocomplain $srcdir/$subdir/*.{ll,llx,c,cpp,tr}]] diff --git a/test/Dogfood/ImmutableSet.cpp b/test/Dogfood/ImmutableSet.cpp new file mode 100644 index 00000000..7b816de4 --- /dev/null +++ b/test/Dogfood/ImmutableSet.cpp @@ -0,0 +1,134 @@ +// RUN: llvm-g++ -I../../../include -g -DMAX_ELEMENTS=4 -fno-exceptions --emit-llvm -c -o %t1.bc %s +// RUN: %klee --libc=klee --max-forks=200 --no-output --exit-on-error --optimize --disable-inlining --use-non-uniform-random-search --use-cex-cache %t1.bc + +#include "klee/klee.h" +#include "klee/Internal/ADT/ImmutableSet.h" + +using namespace klee; + +typedef ImmutableSet<unsigned> T; + +bool iff(bool a, bool b) { + return !!a == !!b; +} + +template<typename InputIterator, typename T> +bool contains(InputIterator begin, InputIterator end, T item) { + for (; begin!=end; ++begin) + if (*begin == item) + return true; + return false; +} + +bool equal(T &a, T &b) { + T::iterator aIt=a.begin(), ae=a.end(), bIt=b.begin(), be=b.end(); + for (; aIt!=ae && bIt!=be; ++aIt, ++bIt) + if (*aIt != *bIt) + return false; + if (aIt!=ae) return false; + if (bIt!=be) return false; + return true; +} + +template<typename InputIterator, typename T> +void remove(InputIterator begin, InputIterator end, T item) { + InputIterator out = begin; + for (; begin!=end; ++begin) { + if (*begin!=item) { + if (out!=begin) + *out = *begin; + ++out; + } + } +} + +void check_set(T &set, unsigned num, unsigned *values) { + assert(set.size() == num); + + // must contain only values + unsigned item = klee_range(0, 256, "range"); + assert(iff(contains(values, values+num, item), + set.count(item))); + + // each value must be findable, must be its own lower bound, and + // must be one behind its upper bound + for (unsigned i=0; i<num; i++) { + unsigned item = values[i]; + assert(set.count(item)); + T::iterator it = set.find(item); + T::iterator lb = set.lower_bound(item); + T::iterator ub = set.upper_bound(item); + assert(it != set.end()); // must exit + assert(*it == item); // must be itself + assert(lb == it); // must be own lower bound + assert(--ub == it); // must be one behind upper + } + + // for items not in the set... + unsigned item2 = klee_range(0, 256, "range"); + if (!set.count(item2)) { + assert(set.find(item2) == set.end()); + + T::iterator lb = set.lower_bound(item2); + for (T::iterator it=set.begin(); it!=lb; ++it) + assert(*it < item2); + for (T::iterator it=lb, ie=set.end(); it!=ie; ++it) + assert(*it >= item2); + + T::iterator ub = set.upper_bound(item2); + for (T::iterator it=set.begin(); it!=ub; ++it) + assert(*it <= item2); + for (T::iterator it=ub, ie=set.end(); it!=ie; ++it) + assert(*it > item2); + } +} + +#ifndef MAX_ELEMENTS +#define MAX_ELEMENTS 4 +#endif + +void test() { + unsigned num=0, values[MAX_ELEMENTS]; + T set; + + assert(MAX_ELEMENTS >= 0); + for (unsigned i=0; i<klee_range(0,MAX_ELEMENTS+1, "range"); i++) { + unsigned item = klee_range(0, 256, "range"); + if (contains(values, values+num, item)) + klee_silent_exit(0); + + set = set.insert(item); + values[num++] = item; + } + + check_set(set, num, values); + + unsigned item = klee_range(0, 256, "range"); + if (contains(values, values+num, item)) { // in tree + // insertion is invariant + T set2 = set.insert(item); + assert(equal(set2, set)); + + // check remove + T set3 = set.remove(item); + assert(!equal(set3, set)); // mostly just for coverage + assert(set3.size() + 1 == set.size()); + assert(!set3.count(item)); + } else { // not in tree + // removal is invariant + T set2 = set.remove(item); + assert(equal(set2, set)); + + // check insert + T set3 = set.insert(item); + assert(!equal(set3, set)); // mostly just for coverage + assert(set3.size() == set.size() + 1); + assert(set3.count(item)); + } +} + +int main(int argc, char **argv) { + test(); + assert(T::getAllocated() == 0); + return 0; +} diff --git a/test/Dogfood/dg.exp b/test/Dogfood/dg.exp new file mode 100644 index 00000000..879685ca --- /dev/null +++ b/test/Dogfood/dg.exp @@ -0,0 +1,3 @@ +load_lib llvm.exp + +RunLLVMTests [lsort [glob -nocomplain $srcdir/$subdir/*.{ll,llx,c,cpp,tr}]] diff --git a/test/Expr/dg.exp b/test/Expr/dg.exp new file mode 100644 index 00000000..879685ca --- /dev/null +++ b/test/Expr/dg.exp @@ -0,0 +1,3 @@ +load_lib llvm.exp + +RunLLVMTests [lsort [glob -nocomplain $srcdir/$subdir/*.{ll,llx,c,cpp,tr}]] diff --git a/test/Feature/Alias.c b/test/Feature/Alias.c new file mode 100644 index 00000000..9300e2c7 --- /dev/null +++ b/test/Feature/Alias.c @@ -0,0 +1,25 @@ +// RUN: %llvmgcc %s -emit-llvm -g -c -o %t1.bc +// RUN: %klee --exit-on-error %t1.bc + +// Darwin does not have strong aliases. +// XFAIL: darwin + +#include <assert.h> + +// alias for global +int b = 52; +extern int a __attribute__((alias("b"))); + +// alias for function +int __foo() { return 52; } +extern int foo() __attribute__((alias("__foo"))); + +int *c = &a; + +int main() { + assert(a == 52); + assert(foo() == 52); + assert(*c == 52); + + return 0; +} diff --git a/test/Feature/AliasFunction.c b/test/Feature/AliasFunction.c new file mode 100644 index 00000000..e7acfc19 --- /dev/null +++ b/test/Feature/AliasFunction.c @@ -0,0 +1,33 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee %t1.bc > %t1.log +// RUN: grep -c foo %t1.log | grep 5 +// RUN: grep -c bar %t1.log | grep 3 + +#include <stdio.h> +#include <stdlib.h> + +void foo() { printf(" foo()\n"); } +void bar() { printf(" bar()\n"); } + +int main() { + int x; + klee_make_symbolic_name(&x, sizeof(x), "x"); + + // no aliases + foo(); + + if (x > 10) + { + // foo -> bar + klee_alias_function("foo", "bar"); + + if (x > 20) + foo(); + } + + foo(); + + // undo + klee_alias_function("foo", "foo"); + foo(); +} diff --git a/test/Feature/AliasFunctionExit.c b/test/Feature/AliasFunctionExit.c new file mode 100644 index 00000000..fcaf7e6c --- /dev/null +++ b/test/Feature/AliasFunctionExit.c @@ -0,0 +1,30 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee %t1.bc > %t1.log +// RUN: grep -c START %t1.log | grep 1 +// RUN: grep -c END %t1.log | grep 2 + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +void start(int x) { + printf("START\n"); + if (x == 53) + exit(1); +} + +void end(int status) { + klee_alias_function("exit", "exit"); + printf("END: status = %d\n", status); + exit(status); +} + + +int main() { + int x; + klee_make_symbolic_name(&x, sizeof(x), "x"); + + klee_alias_function("exit", "end"); + start(x); + end(0); +} diff --git a/test/Feature/AsmAddresses.c b/test/Feature/AsmAddresses.c new file mode 100644 index 00000000..a58fc059 --- /dev/null +++ b/test/Feature/AsmAddresses.c @@ -0,0 +1,22 @@ +// RUN: %llvmgcc -g -c -o %t.bc %s +// RUN: %klee --exit-on-error --use-asm-addresses %t.bc +// RUN: %llvmgcc -DOVERLAP -g -c -o %t.bc %s +// RUN: not %klee --exit-on-error --use-asm-addresses %t.bc + +#include <assert.h> + + +volatile unsigned char x0 __asm ("0x0021"); +volatile unsigned char whee __asm ("0x0WHEE"); + +#ifdef OVERLAP +volatile unsigned int y0 __asm ("0x0030"); +volatile unsigned int y1 __asm ("0x0032"); +#endif + +int main() { + assert(&x0 == (void*) 0x0021); + assert(&whee != (void*) 0x0); + + return 0; +} diff --git a/test/Feature/ByteSwap.c b/test/Feature/ByteSwap.c new file mode 100644 index 00000000..f85ac3b1 --- /dev/null +++ b/test/Feature/ByteSwap.c @@ -0,0 +1,16 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee --libc=klee --exit-on-error %t1.bc + +#include <arpa/inet.h> +#include <assert.h> + +int main() { + + uint32_t n = 0; + klee_make_symbolic(&n, sizeof(n)); + + uint32_t h = ntohl(n); + assert(htonl(h) == n); + + return 0; +} diff --git a/test/Feature/CallToUndefinedExternal.cpp b/test/Feature/CallToUndefinedExternal.cpp new file mode 100644 index 00000000..2f11f29d --- /dev/null +++ b/test/Feature/CallToUndefinedExternal.cpp @@ -0,0 +1,11 @@ +// RUN: %llvmgcc %s -emit-llvm -g -c -o %t1.bc +// RUN: %klee %t1.bc +// RUN: test -f klee-last/test000001.external.err + +extern "C" void poof(void); + +int main() { + poof(); + + return 0; +} diff --git a/test/Feature/CheckForImpliedValue.c.failing b/test/Feature/CheckForImpliedValue.c.failing new file mode 100644 index 00000000..7a088354 --- /dev/null +++ b/test/Feature/CheckForImpliedValue.c.failing @@ -0,0 +1,23 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: rm -f %t1.log +// RUN: %klee --log-file %t1.log --debug-check-for-implied-values %t1.bc +// RUN: grep "= 0 (ok)" %t1.log +// RUN: grep "= 2 (missed)" %t1.log + +#define swap(x) (((x)>>16) | (((x)&0xFFFF)<<16)) +int main() { + unsigned x, y; + + klee_make_symbolic(&x, sizeof x); + klee_make_symbolic(&y, sizeof y); + + if (!x) { // should give x = 0 hit by ivc + printf("ok\n"); + } else { + if (swap(y) == 0x00020000) { // should give y = 2 missed by ivc + printf("ok\n"); + } + } + + return 0; +} diff --git a/test/Feature/CheckMemoryAccess.c b/test/Feature/CheckMemoryAccess.c new file mode 100644 index 00000000..dd6e8745 --- /dev/null +++ b/test/Feature/CheckMemoryAccess.c @@ -0,0 +1,26 @@ +// RUN: %llvmgcc -g -c %s -o %t.bc +// RUN: %klee %t.bc > %t.log +// RUN: grep -q "good" %t.log +// RUN: not grep -q "bad" %t.log + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> + +int main() { + char buf[4]; + + klee_check_memory_access(&buf, 1); + printf("good\n"); + if (klee_range(0, 2, "range1")) { + klee_check_memory_access(0, 1); + printf("null pointer deref: bad\n"); + } + + if (klee_range(0, 2, "range2")) { + klee_check_memory_access(buf, 5); + printf("oversize access: bad\n"); + } + + return 0; +} diff --git a/test/Feature/CopyOnWrite.c b/test/Feature/CopyOnWrite.c new file mode 100644 index 00000000..ee3ea15e --- /dev/null +++ b/test/Feature/CopyOnWrite.c @@ -0,0 +1,26 @@ +// RUN: %llvmgcc %s -emit-llvm -g -c -o %t1.bc +// RUN: %klee --use-random-search --exit-on-error %t1.bc + +#include <assert.h> + +#define N 5 + +unsigned branches = 0; + +void explode(int *ap, int i, int *result) { + if (i<N) { + (*result)++; + if (ap[i]) // just cause a fork + branches++; + return explode(ap, i+1, result); + } +} + +int main() { + int result = 0; + int a[N]; + klee_make_symbolic(a, sizeof a); + explode(a,0,&result); + assert(result==N); + return 0; +} diff --git a/test/Feature/DanglingConcreteReadExpr.c b/test/Feature/DanglingConcreteReadExpr.c new file mode 100644 index 00000000..1bf44c3f --- /dev/null +++ b/test/Feature/DanglingConcreteReadExpr.c @@ -0,0 +1,22 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee %t1.bc +// RUN: grep "total queries = 2" klee-last/info + +#include <assert.h> + +int main() { + unsigned char x, y; + + klee_make_symbolic(&x, sizeof x); + + y = x; + + // should be exactly two queries (prove x is/is not 10) + // eventually should be 0 when we have fast solver + if (x==10) { + assert(y==10); + } + + klee_silent_exit(0); + return 0; +} diff --git a/test/Feature/DefineFixedObject.c b/test/Feature/DefineFixedObject.c new file mode 100644 index 00000000..36822434 --- /dev/null +++ b/test/Feature/DefineFixedObject.c @@ -0,0 +1,17 @@ +// RUN: %llvmgcc -c -o %t1.bc %s +// RUN: %klee --exit-on-error %t1.bc + +#include <stdio.h> + +#define ADDRESS ((int*) 0x0080) + +int main() { + klee_define_fixed_object(ADDRESS, 4); + + int *p = ADDRESS; + + *p = 10; + printf("*p: %d\n", *p); + + return 0; +} diff --git a/test/Feature/DoubleFree.c b/test/Feature/DoubleFree.c new file mode 100644 index 00000000..3727ef2b --- /dev/null +++ b/test/Feature/DoubleFree.c @@ -0,0 +1,10 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee %t1.bc +// RUN: test -f klee-last/test000001.ptr.err + +int main() { + int *x = malloc(4); + free(x); + free(x); + return 0; +} diff --git a/test/Feature/DumpStatesOnHalt.c b/test/Feature/DumpStatesOnHalt.c new file mode 100644 index 00000000..7e9cf46a --- /dev/null +++ b/test/Feature/DumpStatesOnHalt.c @@ -0,0 +1,8 @@ +// RUN: %llvmgcc %s -g -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee --stop-after-n-instructions=1 --dump-states-on-halt=true %t1.bc +// RUN: test -f klee-last/test000001.bout + +int main() { + int x = 10; + return x; +} diff --git a/test/Feature/Envp.c b/test/Feature/Envp.c new file mode 100644 index 00000000..f1f62a72 --- /dev/null +++ b/test/Feature/Envp.c @@ -0,0 +1,18 @@ +// RUN: %llvmgcc %s -emit-llvm -g -c -o %t1.bc +// RUN: %klee --exit-on-error %t1.bc + +#include <assert.h> + +int main(int argc, char **argv, char **envp) { + unsigned i; + assert(argv[argc] == 0); + printf("argc: %d, argv: %p, envp: %p\n", argc, argv, envp); + printf("--environ--\n"); + int haspwd = 0; + for (i=0; envp[i]; i++) { + printf("%d: %s\n", i, envp[i]); + haspwd |= strncmp(envp[i], "PWD=", 4)==0; + } + assert(haspwd); + return 0; +} diff --git a/test/Feature/ExprLogging.c b/test/Feature/ExprLogging.c new file mode 100644 index 00000000..2abf0070 --- /dev/null +++ b/test/Feature/ExprLogging.c @@ -0,0 +1,43 @@ +// RUN: %llvmgcc %s -emit-llvm -g -O0 -c -o %t1.bc +// RUN: %klee --use-query-pc-log --write-pcs --write-cvcs %t1.bc 2> %t2.log +// RUN: %kleaver -print-ast klee-last/test000001.pc + +// FIXME: Ideally we would verify a roundtrip that we parsed the pc +// file and can dump it back out as the same file. + +#include <assert.h> + +int constantArr[16 ] = { + 1 << 0, 1 << 1, 1 << 2, 1 << 3, + 1 << 4, 1 << 5, 1 << 6, 1 << 7, + 1 << 8, 1 << 9, 1 << 10, 1 << 11, + 1 << 12, 1 << 13, 1 << 14, 1 << 15 +}; + + +int main() { + char buf[4]; + klee_make_symbolic(buf, sizeof buf); + + buf[1] = 'a'; + + constantArr[klee_range(0, 16, "idx.0")] = buf[0]; + + // Use this to trigger an interior update list usage. + int y = constantArr[klee_range(0, 16, "idx.1")]; + + constantArr[klee_range(0, 16, "idx.2")] = buf[3]; + + buf[klee_range(0, 4, "idx.3")] = 0; + klee_assume(buf[0] == 'h'); + + int x = *((int*) buf); + klee_assume(x > 2); + klee_assume(x == constantArr[12]); + + klee_assume(y != (1 << 5)); + + assert(0); + + return 0; +} diff --git a/test/Feature/ExternalWeakLinkage.c b/test/Feature/ExternalWeakLinkage.c new file mode 100644 index 00000000..c2008136 --- /dev/null +++ b/test/Feature/ExternalWeakLinkage.c @@ -0,0 +1,11 @@ +// RUN: %llvmgcc %s -emit-llvm -g -c -o %t1.bc +// RUN: %klee --exit-on-error %t1.bc + +#include <assert.h> + +void __attribute__((weak)) IAmSoWeak(int); + +int main() { + assert(IAmSoWeak==0); + return 0; +} diff --git a/test/Feature/FunctionPointer.c b/test/Feature/FunctionPointer.c new file mode 100644 index 00000000..e1ae1e37 --- /dev/null +++ b/test/Feature/FunctionPointer.c @@ -0,0 +1,36 @@ +// RUN: %llvmgcc %s -emit-llvm -g -c -o %t1.bc +// RUN: %klee --no-output --exit-on-error %t1.bc + +#include <stdio.h> + +void foo(const char *msg) { printf("foo: %s\n", msg); } +void baz(const char *msg) { printf("baz: %s\n", msg); } + +void (*xx)(const char *) = foo; + +void bar(void (*fp)(const char *)) { fp("called via bar"); } + +int main(int argc, char **argv) { + void (*fp)(const char *) = foo; + + printf("going to call through fp\n"); + fp("called via fp"); + + printf("calling via pass through\n"); + bar(foo); + + fp = baz; + fp("called via fp"); + + xx("called via xx"); + +#if 0 + klee_make_symbolic(&fp, sizeof fp); + if(fp == baz) { + printf("fp = %p, baz = %p\n", fp, baz); + fp("calling via symbolic!"); + } +#endif + + return 0; +} diff --git a/test/Feature/GetValue.c b/test/Feature/GetValue.c new file mode 100644 index 00000000..391b68e3 --- /dev/null +++ b/test/Feature/GetValue.c @@ -0,0 +1,17 @@ +// RUN: %llvmgcc -c -o %t1.bc %s +// RUN: %klee --exit-on-error %t1.bc + +#include <stdio.h> +#include <assert.h> + +int main() { + int x = klee_int("x"); + klee_assume(x > 10); + klee_assume(x < 20); + + assert(!klee_is_symbolic(klee_get_value(x))); + assert(klee_get_value(x) > 10); + assert(klee_get_value(x) < 20); + + return 0; +} diff --git a/test/Feature/ImpliedValue.c.failing b/test/Feature/ImpliedValue.c.failing new file mode 100644 index 00000000..2f169970 --- /dev/null +++ b/test/Feature/ImpliedValue.c.failing @@ -0,0 +1,147 @@ +// RUN: rm -f %t4.out %t4.err %t4.log +// RUN: %llvmgcc %s -emit-llvm -O2 -c -o %t1.bc +// RUN: llvm-as -f ../_utils._ll -o %t2.bc +// RUN: llvm-ld -disable-opt -link-as-library %t1.bc %t2.bc -o %t3.bc +// RUN: %klee --log-file %t4.log --debug-check-for-implied-values %t3.bc > %t4.out 2> %t4.err +// RUN: ls klee-last | not grep .err +// RUN: not grep "(missed)" %t4.log + +#include <assert.h> + +#include "utils.h" + +int main() { + unsigned char which; + volatile unsigned char a,b,c,d,e,f,g,h; + + klee_make_symbolic(&which, sizeof which); + klee_make_symbolic(&a, sizeof a); + klee_make_symbolic(&b, sizeof b); + klee_make_symbolic(&c, sizeof c); + klee_make_symbolic(&d, sizeof d); + klee_make_symbolic(&e, sizeof e); + klee_make_symbolic(&f, sizeof f); + klee_make_symbolic(&g, sizeof g); + klee_make_symbolic(&h, sizeof h); + + switch (which) { +// RUN: grep "simple read(2) = value case" %t4.out + case 0: + if (a == 2) { + assert(!klee_is_symbolic(a)); + printf("simple read(%d) = value case\n", a); + } + break; + +// RUN: grep "select(0) has distinct constant result case (false)" %t4.out + case 1: + if (util_make_select(a, 12, 14) == 14) { + assert(!klee_is_symbolic(a)); + printf("select(%d) has distinct constant result case (false)\n", a); + } + break; + +// RUN: grep "select(0) has distinct constant result case (true)" %t4.out + case 2: + if (util_make_select(!a, 12, 14) == 12) { + assert(!klee_is_symbolic(a)); + printf("select(%d) has distinct constant result case (true)\n", a); + } + break; + +// RUN: grep "concat2(0xBE,0xEF) = value case" %t4.out + case 3: + if (util_make_concat2(a,b) == 0xBEEF) { + assert(!klee_is_symbolic(a)); + assert(!klee_is_symbolic(b)); + printf("concat2(0x%X,0x%X) = value case\n", + a, b); + } + break; + +// RUN: grep "concat4(0xDE,0xAD,0xBE,0xEF) = value case" %t4.out + case 4: + if (util_make_concat4(a,b,c,d) == 0xDEADBEEF) { + assert(!klee_is_symbolic(a)); + assert(!klee_is_symbolic(b)); + assert(!klee_is_symbolic(c)); + assert(!klee_is_symbolic(d)); + printf("concat4(0x%X,0x%X,0x%X,0x%X) = value case\n", + a, b, c, d); + } + break; + +// RUN: grep "concat8(0xDE,0xAD,0xBE,0xEF,0xAB,0xCD,0xAB,0xCD) = value case" %t4.out + case 5: + if (util_make_concat8(a,b,c,d,e,f,g,h) == 0xDEADBEEFABCDABCDLL) { + assert(!klee_is_symbolic(a)); + assert(!klee_is_symbolic(b)); + assert(!klee_is_symbolic(c)); + assert(!klee_is_symbolic(d)); + assert(!klee_is_symbolic(e)); + assert(!klee_is_symbolic(f)); + assert(!klee_is_symbolic(g)); + assert(!klee_is_symbolic(h)); + printf("concat8(0x%X,0x%X,0x%X,0x%X,0x%X,0x%X,0x%X,0x%X) = value case\n", + a, b, c, d, e, f, g, h); + } + break; + +// RUN: grep "and(0,0) = true case" %t4.out + case 6: + if (util_make_and_i1(!a, !b)) { + assert(!klee_is_symbolic(a)); + assert(!klee_is_symbolic(b)); + printf("and(%d,%d) = true case\n", a, b); + } + break; + +// RUN: grep "or(0,0) = false case" %t4.out + case 7: + if (!util_make_or_i1(a, b)) { + assert(!klee_is_symbolic(a)); + assert(!klee_is_symbolic(b)); + printf("or(%d,%d) = false case\n", a, b); + } + break; + + // we use concat to prevent folding, will need to fix if that gets + // partial evaluated +// RUN: grep "add constant case: 0xDE" %t4.out + case 8: + if (util_make_concat2(a+0xCD,0xCD) == 0xABCD) { + assert(!klee_is_symbolic(a)); + printf("add constant case: 0x%X\n", a); + } + break; + +// RUN: grep "sub constant case: 0x60" %t4.out + case 9: + if (util_make_concat2(0x0B-a,0xCD) == 0xABCD) { + assert(!klee_is_symbolic(a)); + printf("sub constant case: 0x%X\n", a); + } + break; + +// RUN: grep "xor constant case: 0xA0" %t4.out + case 10: + if (util_make_concat2(0x0B ^ a,0xCD) == 0xABCD) { + assert(!klee_is_symbolic(a)); + printf("xor constant case: 0x%X\n", a); + } + break; + +// RUN: grep "sext constant case: 244" %t4.out + case 11: + if (! util_make_or_i1(((short) (signed char) a) + 12,b)) { + assert(!klee_is_symbolic(a)); + printf("sext constant case: %d\n", a); + } + break; + + default: + break; + } + + return 0; +} diff --git a/test/Feature/InAndOutOfBounds.c b/test/Feature/InAndOutOfBounds.c new file mode 100644 index 00000000..18e5d2b2 --- /dev/null +++ b/test/Feature/InAndOutOfBounds.c @@ -0,0 +1,19 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee %t1.bc +// RUN: test -f klee-last/test000001.ptr.err -o -f klee-last/test000002.ptr.err +// RUN: test ! -f klee-last/test000001.ptr.err -o ! -f klee-last/test000002.ptr.err +// RUN: test ! -f klee-last/test000003.bout + +unsigned klee_urange(unsigned start, unsigned end) { + unsigned x; + klee_make_symbolic(&x, sizeof x); + if (x-start>=end-start) klee_silent_exit(0); + return x; +} + +int main() { + int *x = malloc(4); + x[klee_urange(0,2)] = 1; + free(x); + return 0; +} diff --git a/test/Feature/IndirectCallToBuiltin.c b/test/Feature/IndirectCallToBuiltin.c new file mode 100644 index 00000000..591a5601 --- /dev/null +++ b/test/Feature/IndirectCallToBuiltin.c @@ -0,0 +1,15 @@ +// RUN: %llvmgcc %s -emit-llvm -g -c -o %t1.bc +// RUN: %klee %t1.bc + +#include <stdlib.h> +#include <stdio.h> + +int main() { + void *(*allocator)(size_t) = malloc; + int *mem = allocator(10); + + printf("mem: %p\n", mem); + printf("mem[0]: %d\n", mem[0]); + + return 0; +} diff --git a/test/Feature/IndirectCallToExternal.c b/test/Feature/IndirectCallToExternal.c new file mode 100644 index 00000000..4603213b --- /dev/null +++ b/test/Feature/IndirectCallToExternal.c @@ -0,0 +1,15 @@ +// RUN: %llvmgcc %s -emit-llvm -g -c -o %t1.bc +// RUN: %klee %t1.bc + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> + +int main() { + int (*scmp)(char*,char*) = strcmp; + + assert(scmp("hello","hi") < 0); + + return 0; +} diff --git a/test/Feature/InvalidBitfieldAccess.c.failing b/test/Feature/InvalidBitfieldAccess.c.failing new file mode 100644 index 00000000..ae8bfe5e --- /dev/null +++ b/test/Feature/InvalidBitfieldAccess.c.failing @@ -0,0 +1,28 @@ +// RUN: %llvmgcc -c -o %t1.bc %s +// RUN: %klee --exit-on-error %t1.bc + +// This is a bug in llvm-gcc4.0 but seems to be fixed in llvm-gcc4.2, +// its included here mostly as a reminder. + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> + +struct foo { + unsigned int a : 5; + unsigned int b : 10; + unsigned int c : 1; +} __attribute__((packed)); + +int main() { + struct foo *a = malloc(sizeof *a); + + a->b = 12; // problem here is that llvm-gcc emits a 4 byte access + // which is out of bounds. + + int x = a->b; + + assert(x == 12); + + return 0; +} diff --git a/test/Feature/IsSymbolic.c b/test/Feature/IsSymbolic.c new file mode 100644 index 00000000..4a86368a --- /dev/null +++ b/test/Feature/IsSymbolic.c @@ -0,0 +1,16 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee %t1.bc + +#include <assert.h> + +int main() { + int x, y, z = 0; + klee_make_symbolic(&x, sizeof x); + klee_make_symbolic(&y, sizeof y); + if (x) { + assert(klee_is_symbolic(y)); + } else { + assert(!klee_is_symbolic(z)); + } + return 0; +} diff --git a/test/Feature/KleeReportError.c b/test/Feature/KleeReportError.c new file mode 100644 index 00000000..dda72fd0 --- /dev/null +++ b/test/Feature/KleeReportError.c @@ -0,0 +1,23 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t2.bc +// RUN: %klee --emit-all-errors %t2.bc > %t3.log +// RUN: ls klee-last/ | grep .my.err | wc -l | grep 2 +#include <stdio.h> +#include <assert.h> + +int main(int argc, char** argv) { + int x, y, *p = 0; + + klee_make_symbolic(&x, sizeof x); + klee_make_symbolic(&y, sizeof y); + + if (x) + fprintf(stderr, "x\n"); + else fprintf(stderr, "!x\n"); + + if (y) { + fprintf(stderr, "My error\n"); + klee_report_error(__FILE__, __LINE__, "My error", "my.err"); + } + + return 0; +} diff --git a/test/Feature/LongDoubleSupport.c b/test/Feature/LongDoubleSupport.c new file mode 100644 index 00000000..b4631832 --- /dev/null +++ b/test/Feature/LongDoubleSupport.c @@ -0,0 +1,20 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee --exit-on-error %t1.bc > %t2.out + +#include <stdio.h> +#include <float.h> + +// FIXME: This doesn't really work at all, it just doesn't +// crash. Until we have wide constant support, that is all we care +// about; the only reason this comes up is in initialization of +// constants, we don't actually end up seeing much code which uses long +// double. +int main() { + long double a = LDBL_MAX; + long double b = -1; + long double c = a + b; + printf("a = %Lg\n", a); + printf("b = %Lg\n", b); + printf("c = %Lg\n", c); + return 0; +} diff --git a/test/Feature/LowerSwitch.c b/test/Feature/LowerSwitch.c new file mode 100644 index 00000000..c4d9644a --- /dev/null +++ b/test/Feature/LowerSwitch.c @@ -0,0 +1,30 @@ +// RUN: %llvmgcc %s -emit-llvm -g -c -o %t.bc +// RUN: %klee --exit-on-error --allow-external-sym-calls --switch-type=internal %t.bc +// RUN: not test -f klee-last/test000010.bout +// RUN: %klee --exit-on-error --allow-external-sym-calls --switch-type=simple %t.bc +// RUN: test -f klee-last/test000010.bout + +#include <stdio.h> + +int main(int argc, char **argv) { + int c = klee_range(0, 256, "range"); + + switch(c) { + case 0: printf("0\n"); break; + case 10: printf("10\n"); break; + case 16: printf("16\n"); break; + case 17: printf("17\n"); break; + case 18: printf("18\n"); break; + case 19: printf("19\n"); break; +#define C(x) case x: case x+1: case x+2: case x+3 +#define C2(x) C(x): C(x+4): C(x+8): C(x+12) +#define C3(x) C2(x): C2(x+16): C2(x+32): C2(x+48) + C3(128): + printf("bignums: %d\n", c); break; + default: + printf("default\n"); + break; + } + + return 0; +} diff --git a/test/Feature/MakeConcreteSymbolic.c b/test/Feature/MakeConcreteSymbolic.c new file mode 100644 index 00000000..29b43a04 --- /dev/null +++ b/test/Feature/MakeConcreteSymbolic.c @@ -0,0 +1,19 @@ +// RUN: %llvmgcc %s -emit-llvm -g -c -o %t1.bc +// RUN: %klee --exit-on-error %t1.bc +// RUN: grep "done: total queries = 0" klee-last/info +// RUN: %klee --make-concrete-symbolic=1 --exit-on-error %t1.bc +// RUN: grep "done: total queries = 2" klee-last/info + + +#include <assert.h> + +#define N 2 +int main() { + int i; + char a; + + a = 10; + assert(a == 10); + + return 0; +} diff --git a/test/Feature/MakeSymbolicName.c b/test/Feature/MakeSymbolicName.c new file mode 100644 index 00000000..c1f11424 --- /dev/null +++ b/test/Feature/MakeSymbolicName.c @@ -0,0 +1,17 @@ +// RUN: %llvmgcc %s -emit-llvm -g -c -o %t1.bc +// RUN: %klee --use-random-search --exit-on-error %t1.bc + +#include <assert.h> + +int main() { + int a[4] = {1, 2, 3, 4}; + unsigned i; + + klee_make_symbolic_name(&i, sizeof(i), "index"); + if (i > 3) + klee_silent_exit(0); + + assert(a[i] << 1 != 5); + if (a[i] << 1 == 6) + assert(i == 2); +} diff --git a/test/Feature/MemoryLimit.c b/test/Feature/MemoryLimit.c new file mode 100644 index 00000000..3b1bacaf --- /dev/null +++ b/test/Feature/MemoryLimit.c @@ -0,0 +1,37 @@ +// RUN: %llvmgcc -DLITTLE_ALLOC -g -c %s -o %t.little.bc +// RUN: %llvmgcc -g -c %s -o %t.big.bc +// RUN: %klee --max-memory=20 %t.little.bc > %t.little.log +// RUN: %klee --max-memory=20 %t.big.bc > %t.big.log +// RUN: not grep -q "DONE" %t.little.log +// RUN: not grep -q "DONE" %t.big.log + +#include <stdlib.h> + +int main() { + int i, j, x=0; + +#ifdef LITTLE_ALLOC + printf("IN LITTLE ALLOC\n"); + + // 200 MBs total (in 32 byte chunks) + for (i=0; i<100; i++) { + for (j=0; j<(1<<16); j++) + malloc(1<<5); + } +#else + printf("IN BIG ALLOC\n"); + + // 200 MBs total + for (i=0; i<100; i++) { + malloc(1<<21); + + // Ensure we hit the periodic check + for (j=0; j<10000; j++) + x++; + } +#endif + + printf("DONE!\n"); + + return x; +} diff --git a/test/Feature/MultipleFreeResolution.c b/test/Feature/MultipleFreeResolution.c new file mode 100644 index 00000000..872d6856 --- /dev/null +++ b/test/Feature/MultipleFreeResolution.c @@ -0,0 +1,39 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee --emit-all-errors %t1.bc +// RUN: ls klee-last/ | grep .out | wc -l | grep 4 +// RUN: ls klee-last/ | grep .err | wc -l | grep 3 + +#include <stdlib.h> +#include <stdio.h> + +unsigned klee_urange(unsigned start, unsigned end) { + unsigned x; + klee_make_symbolic(&x, sizeof x); + if (x-start>=end-start) klee_silent_exit(0); + return x; +} + +int *make_int(int i) { + int *x = malloc(sizeof(*x)); + *x = i; + return x; +} + +int main() { + int *buf[4]; + int i,s; + + for (i=0; i<3; i++) + buf[i] = make_int(i); + buf[3] = 0; + + s = klee_urange(0,4); + + free(buf[s]); + + for (i=0; i<3; i++) { + printf("*buf[%d] = %d\n", i, *buf[i]); + } + + return 0; +} diff --git a/test/Feature/MultipleReadResolution.c b/test/Feature/MultipleReadResolution.c new file mode 100644 index 00000000..9297cf8d --- /dev/null +++ b/test/Feature/MultipleReadResolution.c @@ -0,0 +1,43 @@ +// RUN: echo "x" > %t1.res +// RUN: echo "x" >> %t1.res +// RUN: echo "x" >> %t1.res +// RUN: echo "x" >> %t1.res +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee %t1.bc > %t1.log +// RUN: diff %t1.res %t1.log + +#include <stdio.h> + +unsigned klee_urange(unsigned start, unsigned end) { + unsigned x; + klee_make_symbolic(&x, sizeof x); + if (x-start>=end-start) klee_silent_exit(0); + return x; +} + +int *make_int(int i) { + int *x = malloc(sizeof(*x)); + *x = i; + return x; +} + +int main() { + int *buf[4]; + int i,s,t; + + for (i=0; i<4; i++) + buf[i] = make_int((i+1)*2); + + s = klee_urange(0,4); + + int x = *buf[s]; + + if (x == 4) + if (s!=1) + abort(); + + printf("x\n"); + fflush(stdout); + + return 0; +} diff --git a/test/Feature/MultipleReallocResolution.c b/test/Feature/MultipleReallocResolution.c new file mode 100644 index 00000000..b1a14ace --- /dev/null +++ b/test/Feature/MultipleReallocResolution.c @@ -0,0 +1,63 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee %t1.bc +// RUN: ls klee-last/ | grep .err | wc -l | grep 2 +// RUN: ls klee-last/ | grep .ptr.err | wc -l | grep 2 + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> + +unsigned klee_urange(unsigned start, unsigned end) { + unsigned x; + klee_make_symbolic(&x, sizeof x); + if (x-start>=end-start) klee_silent_exit(0); + return x; +} + +int *make_int(int i, int N) { + int *x = malloc(sizeof(*x) * N); + *x = i; + return x; +} + +int main() { + int *buf[4]; + + buf[0] = malloc(sizeof(int)*4); + buf[1] = malloc(sizeof(int)); + buf[2] = 0; // gets malloc'd + buf[3] = (int*) 0xdeadbeef; // boom + + buf[0][0] = 10; + buf[0][1] = 42; + buf[1][0] = 20; + + int i = klee_urange(0,4); + int new_size = 2 * sizeof(int) * klee_urange(0,2); // 0 or 8 + + // whee, party time, needs to: + // Fork if size == 0, in which case all buffers get free'd and + // we will crash in prints below. + // Fork if buf[s] == 0, in which case the buffer gets malloc'd (for s==2) + // Fork on other buffers (s in [0,1]) and do realloc + // for s==0 this is shrinking + // for s==1 this is growing + buf[i] = realloc(buf[i], new_size); + + if (new_size == 0) { + assert(!buf[2]); // free(0) is a no-op + if (i==0) assert(!buf[0] && buf[1]); + if (i==1) assert(buf[0] && !buf[1]); + assert(i != 3); // we should have crashed on free of invalid + } else { + assert(new_size == sizeof(int)*2); + assert(buf[0][0] == 10); + assert(buf[0][1] == 42); // make sure copied + assert(buf[1][0] == 20); + if (i==1) { int x = buf[1][1]; } // should be safe + if (i==2) { int x = buf[2][0]; } // should be safe + assert(i != 3); // we should have crashed on realloc of invalid + } + + return 0; +} diff --git a/test/Feature/MultipleWriteResolution.c b/test/Feature/MultipleWriteResolution.c new file mode 100644 index 00000000..f07b9710 --- /dev/null +++ b/test/Feature/MultipleWriteResolution.c @@ -0,0 +1,43 @@ +// RUN: echo "x" > %t1.res +// RUN: echo "x" >> %t1.res +// RUN: echo "x" >> %t1.res +// RUN: echo "x" >> %t1.res +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee %t1.bc > %t1.log +// RUN: diff %t1.res %t1.log + +#include <stdio.h> + +unsigned klee_urange(unsigned start, unsigned end) { + unsigned x; + klee_make_symbolic(&x, sizeof x); + if (x-start>=end-start) klee_silent_exit(0); + return x; +} + +int *make_int(int i) { + int *x = malloc(sizeof(*x)); + *x = i; + return x; +} + +int main() { + int *buf[4]; + int i,s,t; + + for (i=0; i<4; i++) + buf[i] = make_int((i+1)*2); + + s = klee_urange(0,4); + + *buf[s] = 5; + + if ((*buf[0] + *buf[1] + *buf[2] + *buf[3]) == 17) + if (s!=3) + abort(); + + printf("x\n"); + fflush(stdout); + + return 0; +} diff --git a/test/Feature/NamedSeedMatching.c b/test/Feature/NamedSeedMatching.c new file mode 100644 index 00000000..6d52e7a4 --- /dev/null +++ b/test/Feature/NamedSeedMatching.c @@ -0,0 +1,41 @@ +// RUN: %llvmgcc -c -g %s -o %t.bc +// RUN: rm -rf %t.out +// RUN: %klee --output-dir=%t.out %t.bc "initial" +// RUN: test -f %t.out/test000001.bout +// RUN: not test -f %t.out/test000002.bout +// RUN: %klee --only-replay-seeds --named-seed-matching --seed-out %t.out/test000001.bout %t.bc > %t.log +// RUN: grep -q "a==3" %t.log +// RUN: grep -q "b==4" %t.log +// RUN: grep -q "c==5" %t.log +// RUN: grep -q "x==6" %t.log + +#include <string.h> +#include <stdio.h> + +int main(int argc, char **argv) { + int a, b, c, x; + + if (argc==2 && strcmp(argv[1], "initial") == 0) { + klee_make_symbolic_name(&a, sizeof a, "a"); + klee_make_symbolic_name(&b, sizeof b, "b"); + klee_make_symbolic_name(&c, sizeof c, "c"); + klee_make_symbolic_name(&x, sizeof x, "a"); + + klee_assume(a == 3); + klee_assume(b == 4); + klee_assume(c == 5); + klee_assume(x == 6); + } else { + klee_make_symbolic_name(&a, sizeof a, "a"); + klee_make_symbolic_name(&c, sizeof c, "c"); + klee_make_symbolic_name(&b, sizeof b, "b"); + klee_make_symbolic_name(&x, sizeof x, "a"); + } + + if (a==3) printf("a==3\n"); + if (b==4) printf("b==4\n"); + if (c==5) printf("c==5\n"); + if (x==6) printf("x==6\n"); + + return 0; +} diff --git a/test/Feature/OneFreeError.c b/test/Feature/OneFreeError.c new file mode 100644 index 00000000..8eb13298 --- /dev/null +++ b/test/Feature/OneFreeError.c @@ -0,0 +1,10 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee %t1.bc +// RUN: test -f klee-last/test000001.ptr.err + +int main() { + int *x = malloc(4); + free(x); + x[0] = 1; + return 0; +} diff --git a/test/Feature/OneOutOfBounds.c b/test/Feature/OneOutOfBounds.c new file mode 100644 index 00000000..11a9eecb --- /dev/null +++ b/test/Feature/OneOutOfBounds.c @@ -0,0 +1,10 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee %t1.bc +// RUN: test -f klee-last/test000001.ptr.err + +int main() { + int *x = malloc(4); + x[1] = 1; + free(x); + return 0; +} diff --git a/test/Feature/Optimize.c b/test/Feature/Optimize.c new file mode 100644 index 00000000..3c9159c7 --- /dev/null +++ b/test/Feature/Optimize.c @@ -0,0 +1,22 @@ +// RUN: %llvmgcc %s --emit-llvm -O0 -c -o %t2.bc +// RUN: rm -f %t2.log +// RUN: %klee --stop-after-n-instructions=100 --optimize %t2.bc > %t3.log +// RUN: echo "good" > %t3.good +// RUN: diff %t3.log %t3.good + +// should complete by 100 instructions if opt is on + +int main() { + int i, res = 0; + + for (i=1; i<=1000; i++) + res += i; + + if (res == (1000*1001)/2) { + printf("good\n"); + } else { + printf("bad\n"); + } + + return 0; +} diff --git a/test/Feature/OverlappedError.c b/test/Feature/OverlappedError.c new file mode 100644 index 00000000..3c79380c --- /dev/null +++ b/test/Feature/OverlappedError.c @@ -0,0 +1,19 @@ +// RUN: %llvmgcc %s -g -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee %t1.bc +// RUN: ls klee-last/ | grep .out | wc -l | grep 4 +// RUN: ls klee-last/ | grep .ptr.err | wc -l | grep 2 + +#include <stdlib.h> + +int main() { + if (klee_range(0,2, "range")) { + char *x = malloc(8); + *((int*) &x[klee_range(0,6, "range")]) = 1; + free(x); + } else { + char *x = malloc(8); + *((int*) &x[klee_range(-1,5, "range")]) = 1; + free(x); + } + return 0; +} diff --git a/test/Feature/PreferCex.c b/test/Feature/PreferCex.c new file mode 100644 index 00000000..d73b6076 --- /dev/null +++ b/test/Feature/PreferCex.c @@ -0,0 +1,18 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee --exit-on-error %t1.bc +// RUN: klee-bout-tool klee-last/test000001.bout | grep -F 'Hi\x00\x00' + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> + +int main() { + char buf[4]; + + klee_make_symbolic(buf, sizeof buf); + klee_prefer_cex(buf, buf[0]=='H'); + klee_prefer_cex(buf, buf[1]=='i'); + klee_prefer_cex(buf, buf[2]=='\0'); + + return 0; +} diff --git a/test/Feature/RaiseAsm.c b/test/Feature/RaiseAsm.c new file mode 100644 index 00000000..5b8acab4 --- /dev/null +++ b/test/Feature/RaiseAsm.c @@ -0,0 +1,39 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee --exit-on-error %t1.bc + +#include <assert.h> + +typedef unsigned short uint16; +typedef unsigned int uint32; + +uint16 byteswap_uint16(uint16 x) { + return (x << 8) | (x >> 8); +} +uint32 byteswap_uint32(uint32 x) { + return ((byteswap_uint16(x) << 16) | + (byteswap_uint16(x >> 16))); +} + +uint16 byteswap_uint16_asm(uint16 x) { + uint16 res; + __asm__("rorw $8, %w0" : "=r" (res) : "0" (x) : "cc"); + return res; +} + +uint32 byteswap_uint32_asm(uint32 x) { + uint32 res; + __asm__("rorw $8, %w0;" + "rorl $16, %0;" + "rorw $8, %w0" : "=r" (res) : "0" (x) : "cc"); + return res; +} + +int main() { + uint16 ui16 = klee_int("ui16"); + uint32 ui32 = klee_int("ui32"); + + assert(ui16 == byteswap_uint16(byteswap_uint16_asm(ui16))); + assert(ui32 == byteswap_uint32(byteswap_uint32_asm(ui32))); + + return 0; +} diff --git a/test/Feature/ReallocFailure.c b/test/Feature/ReallocFailure.c new file mode 100644 index 00000000..e5105c33 --- /dev/null +++ b/test/Feature/ReallocFailure.c @@ -0,0 +1,18 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee --exit-on-error %t1.bc + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> + +int main() { + int *p = malloc(sizeof(int)*2); + assert(p); + p[1] = 52; + + int *p2 = realloc(p, 1<<30); + assert(p2 == 0); + assert(p[1] == 52); + + return 0; +} diff --git a/test/Feature/ReplayPath.c b/test/Feature/ReplayPath.c new file mode 100644 index 00000000..3f01fa80 --- /dev/null +++ b/test/Feature/ReplayPath.c @@ -0,0 +1,27 @@ +// RUN: echo "1" > %t1.path +// RUN: echo "0" >> %t1.path +// RUN: echo "1" >> %t1.path +// RUN: echo "0" >> %t1.path +// RUN: echo "1" >> %t1.path +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t2.bc +// RUN: %klee --replay-path %t1.path %t2.bc > %t3.log +// RUN: echo "res: 110" > %t3.good +// RUN: diff %t3.log %t3.good + +int main() { + int res = 1; + int x; + + klee_make_symbolic(&x, sizeof x); + + if (x&1) res *= 2; + if (x&2) res *= 3; + if (x&4) res *= 5; + + // get forced branch coverage + if (x&2) res *= 7; + if (!(x&2)) res *= 11; + printf("res: %d\n", res); + + return 0; +} diff --git a/test/Feature/Searchers.c b/test/Feature/Searchers.c new file mode 100644 index 00000000..95ebddf2 --- /dev/null +++ b/test/Feature/Searchers.c @@ -0,0 +1,52 @@ +// RUN: %llvmgcc %s --emit-llvm -O0 -c -o %t2.bc +// RUN: %klee %t2.bc +// RUN: %klee --use-random-search %t2.bc +// RUN: %klee --use-non-uniform-random-search %t2.bc +// RUN: %klee --use-non-uniform-random-search --weight-type=query-cost %t2.bc +// RUN: %klee --use-batching-search %t2.bc +// RUN: %klee --use-batching-search --use-random-search %t2.bc +// RUN: %klee --use-batching-search --use-non-uniform-random-search %t2.bc +// RUN: %klee --use-batching-search --use-non-uniform-random-search --weight-type=query-cost %t2.bc +// RUN: %klee --use-merge --debug-log-merge --debug-log-state-merge %t2.bc +// RUN: %klee --use-merge --use-batching-search %t2.bc +// RUN: %klee --use-merge --use-batching-search --use-random-search %t2.bc +// RUN: %klee --use-merge --use-batching-search --use-non-uniform-random-search %t2.bc +// RUN: %klee --use-merge --use-batching-search --use-non-uniform-random-search --weight-type=query-cost %t2.bc +// RUN: %klee --use-iterative-deepening-time-search --use-batching-search %t2.bc +// RUN: %klee --use-iterative-deepening-time-search --use-batching-search --use-random-search %t2.bc +// RUN: %klee --use-iterative-deepening-time-search --use-batching-search --use-non-uniform-random-search %t2.bc +// RUN: %klee --use-iterative-deepening-time-search --use-batching-search --use-non-uniform-random-search --weight-type=query-cost %t2.bc + + +/* this test is basically just for coverage and doesn't really do any + correctness check (aside from testing that the various combinations + don't crash) */ + +int validate(char *buf, int N) { + + int i; + + for (i=0; i<N; i++) { + if (buf[i]==0) { + klee_merge(); + return 0; + } + } + + klee_merge(); + return 1; +} + +#ifndef SYMBOLIC_SIZE +#define SYMBOLIC_SIZE 15 +#endif +int main(int argc, char **argv) { + int N = SYMBOLIC_SIZE; + unsigned char *buf = malloc(N); + int i; + + klee_make_symbolic(buf, N); + if (validate(buf, N)) + return buf[0]; + return 0; +} diff --git a/test/Feature/SetForking.c b/test/Feature/SetForking.c new file mode 100644 index 00000000..8620110d --- /dev/null +++ b/test/Feature/SetForking.c @@ -0,0 +1,28 @@ +// RUN: %llvmgcc -g -c %s -o %t.bc +// RUN: %klee %t.bc > %t.log +// RUN: sort %t.log | uniq -c > %t.uniq.log +// RUN: grep "1 A" %t.uniq.log +// RUN: grep "1 B" %t.uniq.log +// RUN: grep "1 C" %t.uniq.log + +#include <stdio.h> + +int main() { + klee_set_forking(0); + + if (klee_range(0, 2, "range")) { + printf("A\n"); + } else { + printf("A\n"); + } + + klee_set_forking(1); + + if (klee_range(0, 2, "range")) { + printf("B\n"); + } else { + printf("C\n"); + } + + return 0; +} diff --git a/test/Feature/Vararg.c b/test/Feature/Vararg.c new file mode 100644 index 00000000..f782c15e --- /dev/null +++ b/test/Feature/Vararg.c @@ -0,0 +1,82 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee %t1.bc > %t2.out +// RUN: grep "types: (52, 37, 2.00, (9,12,15))" %t2.out +// RUN: test -f klee-last/test000001.ptr.err + +#include <stdarg.h> +#include <assert.h> +#include <stdio.h> + +struct triple { + int first, second, third; +}; + +int test1(int x, ...) { + va_list ap; + va_start(ap, x); + int i32 = va_arg(ap, int); + long long i64 = va_arg(ap, long long); + double d = va_arg(ap, double); + struct triple p = va_arg(ap, struct triple); + printf("types: (%d, %lld, %.2f, (%d,%d,%d))\n", i32, i64, d, p.first, p.second, p.third); + va_end(ap); +} + +int sum(int N, ...) { + int i, res = 0; + va_list ap,ap2; + + va_start(ap, N); + for (i=0; i<N; i++) { + if (i==1) + va_copy(ap2, ap); + res += va_arg(ap, int); + } + for (i=0; i<N-1; i++) + res += va_arg(ap2, int); + va_end(ap); + + return res; +} + +// test using aps in an array (no multiple resolution +// testing, though) +int va_array(int N, ...) { + va_list aps[2]; + unsigned i; + unsigned sum1 = 0, sum2 = 0; + + for (i=0; i<2; i++) + va_start(aps[i], N); + + for (i=0; i<N; i++) { + unsigned cmd = va_arg(aps[0], int); + + if (cmd==0) { + sum1 += va_arg(aps[0],int); + } else if (cmd==1) { + sum2 += va_arg(aps[1],int); + } else if (cmd==2) { + va_copy(aps[1], aps[0]); + } + } + + for (i=0; i<2; i++) + va_end(aps[i]); + + return 3*sum1 + 5*sum2; +} + +int main() { + struct triple p = { 9, 12, 15 }; + test1(-1, 52, 37ll, 2.0, p); + + assert(sum(2, 3, 4) == 11); + assert(sum(0) == 0); + assert(va_array(5, 0, 5, 1, 1, 2, 1)==45); // 15 + 30 + + // should give memory error + test1(-1, 52, 37, 2.0, p); + + return 0; +} diff --git a/test/Feature/WithLibc.c b/test/Feature/WithLibc.c new file mode 100644 index 00000000..5e84eb4a --- /dev/null +++ b/test/Feature/WithLibc.c @@ -0,0 +1,22 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t2.bc +// RUN: %klee --libc=klee %t2.bc > %t3.log +// RUN: echo "good" > %t3.good +// RUN: diff %t3.log %t3.good + +int main() { + char buf[4]; + char *s = "foo"; + + klee_make_symbolic(buf, sizeof buf); + buf[3] = 0; + + if (strcmp(buf, s)==0) { + if (buf[0]=='f' && buf[1]=='o' && buf[2]=='o' && buf[3]==0) { + printf("good\n"); + } else { + printf("bad\n"); + } + } + + return 0; +} diff --git a/test/Feature/WriteCov.c b/test/Feature/WriteCov.c new file mode 100644 index 00000000..defc7e59 --- /dev/null +++ b/test/Feature/WriteCov.c @@ -0,0 +1,15 @@ +// RUN: %llvmgcc %s -emit-llvm -g -c -o %t2.bc +// RUN: %klee --exit-on-error --write-cov %t2.bc +// RUN: grep WriteCov.c:10 klee-last/test000002.cov +// RUN: grep WriteCov.c:12 klee-last/test000001.cov + +#include <assert.h> + +int main() { + if (klee_range(0,2, "range")) { + assert(__LINE__ == 10); printf("__LINE__ = %d\n", __LINE__); + } else { + assert(__LINE__ == 12); printf("__LINE__ = %d\n", __LINE__); + } + return 0; +} diff --git a/test/Feature/_utils._ll b/test/Feature/_utils._ll new file mode 100644 index 00000000..32a73bb1 --- /dev/null +++ b/test/Feature/_utils._ll @@ -0,0 +1,71 @@ +define i32 @util_make_and_i1(i32 %a, i32 %b) { + %a_i1 = icmp ne i32 %a, 0 + %b_i1 = icmp ne i32 %b, 0 + %res_i1 = and i1 %a_i1, %b_i1 + %res = zext i1 %res_i1 to i32 + ret i32 %res +} + +define i32 @util_make_or_i1(i32 %a, i32 %b) { + %a_i1 = icmp ne i32 %a, 0 + %b_i1 = icmp ne i32 %b, 0 + %res_i1 = or i1 %a_i1, %b_i1 + %res = zext i1 %res_i1 to i32 + ret i32 %res +} + +define i16 @util_make_concat2(i8 %a, i8 %b) { + %tmp = alloca i16 + %tmp8 = bitcast i16* %tmp to i8* + %p0 = getelementptr i8* %tmp8, i32 0 + %p1 = getelementptr i8* %tmp8, i32 1 + store i8 %b, i8* %p0 + store i8 %a, i8* %p1 + %concat = load i16* %tmp + ret i16 %concat +} + +define i32 @util_make_concat4(i8 %a, i8 %b, i8 %c, i8 %d) { + %tmp = alloca i32 + %tmp8 = bitcast i32* %tmp to i8* + %p0 = getelementptr i8* %tmp8, i32 0 + %p1 = getelementptr i8* %tmp8, i32 1 + %p2 = getelementptr i8* %tmp8, i32 2 + %p3 = getelementptr i8* %tmp8, i32 3 + store i8 %d, i8* %p0 + store i8 %c, i8* %p1 + store i8 %b, i8* %p2 + store i8 %a, i8* %p3 + %concat = load i32* %tmp + ret i32 %concat +} + +define i64 @util_make_concat8(i8 %a, i8 %b, i8 %c, i8 %d, + i8 %e, i8 %f, i8 %g, i8 %h) { + %tmp = alloca i64 + %tmp8 = bitcast i64* %tmp to i8* + %p0 = getelementptr i8* %tmp8, i32 0 + %p1 = getelementptr i8* %tmp8, i32 1 + %p2 = getelementptr i8* %tmp8, i32 2 + %p3 = getelementptr i8* %tmp8, i32 3 + %p4 = getelementptr i8* %tmp8, i32 4 + %p5 = getelementptr i8* %tmp8, i32 5 + %p6 = getelementptr i8* %tmp8, i32 6 + %p7 = getelementptr i8* %tmp8, i32 7 + store i8 %h, i8* %p0 + store i8 %g, i8* %p1 + store i8 %f, i8* %p2 + store i8 %e, i8* %p3 + store i8 %d, i8* %p4 + store i8 %c, i8* %p5 + store i8 %b, i8* %p6 + store i8 %a, i8* %p7 + %concat = load i64* %tmp + ret i64 %concat +} + +define i32 @util_make_select(i32 %cond, i32 %t, i32 %f) { + %cond_i1 = icmp ne i32 %cond, 0 + %res = select i1 %cond_i1, i32 %t, i32 %f + ret i32 %res +} \ No newline at end of file diff --git a/test/Feature/const_array_opt1.c b/test/Feature/const_array_opt1.c new file mode 100644 index 00000000..96c46fb9 --- /dev/null +++ b/test/Feature/const_array_opt1.c @@ -0,0 +1,37 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t.bc +// RUN: %klee --const-array-opt --max-time=10 --only-output-states-covering-new %t.bc >%t.log +// grep -q "Finished successfully!\n" + +/* This is testing the const array optimization. On my 2.3GHz machine + this takes under 2 seconds w/ the optimization and almost 6 minutes + w/o. So we kill it in 10 sec and check if it has finished + successfully. */ + +#include <unistd.h> +#include <assert.h> +#include <stdio.h> + +int main() { +#define N 8192 +#define N_IDX 16 + unsigned char a[N]; + unsigned i, k[N_IDX]; + + for (i=0; i<N; i++) + a[i] = i % 256; + + klee_make_symbolic_name(k, sizeof(k), "k"); + + for (i=0; i<N_IDX; i++) { + if (k[i] >= N) + klee_silent_exit(0); + + if (a[k[i]] == i) + assert(k[i] % 256 == i); + else klee_silent_exit(0); + } + + printf("Finished successfully!\n"); + + return 0; +} diff --git a/test/Feature/dg.exp b/test/Feature/dg.exp new file mode 100644 index 00000000..879685ca --- /dev/null +++ b/test/Feature/dg.exp @@ -0,0 +1,3 @@ +load_lib llvm.exp + +RunLLVMTests [lsort [glob -nocomplain $srcdir/$subdir/*.{ll,llx,c,cpp,tr}]] diff --git a/test/Feature/utils.h b/test/Feature/utils.h new file mode 100644 index 00000000..5423a0dc --- /dev/null +++ b/test/Feature/utils.h @@ -0,0 +1,16 @@ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +uint32_t util_make_and_i1(uint32_t a, uint32_t b); +uint32_t util_make_or_i1(uint32_t a, uint32_t b); + +uint16_t util_make_concat2(uint8_t a, uint8_t b); +uint32_t util_make_concat4(uint8_t a, uint8_t b, + uint8_t c, uint8_t d); +uint64_t util_make_concat8(uint8_t a, uint8_t b, + uint8_t c, uint8_t d, + uint8_t e, uint8_t f, + uint8_t g, uint8_t h); +uint32_t util_make_select(uint32_t cond, uint32_t true, uint32_t false); diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 00000000..0c42a6f6 --- /dev/null +++ b/test/Makefile @@ -0,0 +1,101 @@ +#===- test/Makefile ----------------------------------------*- Makefile -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file was developed by the LLVM research group and is distributed under +# the University of Illinois Open Source License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +LEVEL = .. +DIRS = + +# +# Make Dejagnu the default for testing +# +all:: check-local + +# Include other test rules +include Makefile.tests + +#===------------------------------------------------------------------------===# +# DejaGNU testing support +#===------------------------------------------------------------------------===# + +ifdef TESTSUITE +CLEANED_TESTSUITE := $(patsubst %/,%,$(TESTSUITE)) +CLEANED_TESTSUITE := $(patsubst test/%,%,$(CLEANED_TESTSUITE)) +RUNTESTFLAGS := --tool $(CLEANED_TESTSUITE) +endif + +ifneq ($(RUNTEST),) +check-local:: site.exp + ( ulimit -t 600 ; ulimit -d 512000 ; \ + PATH="$(ToolDir):$(LLVMToolDir):$(LLVM_SRC_ROOT)/test/Scripts:$(PATH)" \ + $(RUNTEST) $(RUNTESTFLAGS) ; \ + ! grep FAIL testrun.log ) +else +check-local:: site.exp + @echo "*** dejagnu not found. Make sure runtest is in your PATH, then reconfigure llvm." +endif + +ifdef TESTONE +CLEANED_TESTONE := $(patsubst %/,%,$(TESTONE)) +CLEANED_TESTONE := $(patsubst test/%,%,$(CLEANED_TESTONE)) +SUBDIR := $(shell dirname $(CLEANED_TESTONE)) +TESTPATH := $(PROJ_SRC_ROOT)/test/$(CLEANED_TESTONE) +check-one: site.exp $(TCLSH) + $(Verb)( echo "source $(PROJ_OBJ_ROOT)/test/site.exp" ; \ + echo "set subdir $(SUBDIR)" ; \ + echo "proc pass { msg } { puts \"PASS: \$$msg\" } "; \ + echo "proc fail { msg } { puts \"FAIL: \$$msg\" }" ; \ + echo "proc xfail { msg } { puts \"XFAIL: \$$msg\" }" ; \ + echo "proc xpass { msg } { puts \"XPASS: \$$msg\" }" ; \ + echo "source $(PROJ_SRC_ROOT)/test/lib/llvm.exp" ; \ + echo "RunLLVMTests $(TESTPATH)" ) | \ + ( ulimit -t 600 ; ulimit -d 512000 ; \ + PATH="$(ToolDir):$(LLVMToolDir):$(LLVM_SRC_ROOT)/test/Scripts:$(PATH)" \ + $(TCLSH) ) +endif + +clean:: + $(RM) -rf `find $(PROJ_OBJ_ROOT)/test -name Output -type d -print` + $(RM) -rf `find $(PROJ_OBJ_ROOT)/test -name 'ft-out*' -type d -print` + $(RM) -rf `find $(PROJ_OBJ_ROOT)/test -name 'ft-last' -print` + $(RM) -rf `find $(PROJ_OBJ_ROOT)/test -name 'klee-last'` + $(RM) -rf `find $(PROJ_OBJ_ROOT)/test -name 'klee-out*'` + $(RM) -rf `find $(PROJ_OBJ_ROOT)/test -name '*~'` + $(RM) -rf `find $(PROJ_OBJ_ROOT)/test -name test.log` + rm -f site.exp + +site.exp: Makefile $(LEVEL)/Makefile.config + @echo 'Making a new site.exp file...' + @echo '## these variables are automatically generated by make ##' >site.tmp + @echo '# Do not edit here. If you wish to override these values' >>site.tmp + @echo '# edit the last section' >>site.tmp + @echo 'set target_triplet "$(TARGET_TRIPLE)"' >> site.tmp + @echo 'set ENABLE_UCLIBC "$(ENABLE_UCLIBC)"' >> site.tmp + @echo 'set ENABLE_POSIX_RUNTIME "$(ENABLE_POSIX_RUNTIME)"' >> site.tmp + @echo 'set TEST_FEATURE_LIST "$(TEST_FEATURE_LIST)"' >> site.tmp + @echo 'set prcontext "$(TCLSH) $(LLVM_SRC_ROOT)/test/Scripts/prcontext.tcl"' >> site.tmp + @echo 'set llvmtoolsdir "$(ToolDir)"' >>site.tmp + @echo 'set llvmlibsdir "$(LibDir)"' >>site.tmp + @echo 'set srcroot "$(PROJ_SRC_ROOT)"' >>site.tmp + @echo 'set objroot "$(PROJ_OBJ_ROOT)"' >>site.tmp + @echo 'set srcdir "$(PROJ_SRC_ROOT)/test"' >>site.tmp + @echo 'set objdir "$(PROJ_OBJ_ROOT)/test"' >>site.tmp + @echo 'set gccpath "$(CC)"' >>site.tmp + @echo 'set gxxpath "$(CXX)"' >>site.tmp + @echo 'set compile_c "$(CC) $(CPP.Flags) $(C.Flags) $(CompileCommonOpts) -c "' >>site.tmp + @echo 'set compile_cxx "$(CXX) $(CPP.Flags) $(CXX.Flags) $(CompileCommonOpts) - c"' >> site.tmp + @echo 'set link "$(CXX) $(CPP.Flags) $(CXX.Flags) $(CompileCommonOpts) $(LD.Flags)"' >>site.tmp + @echo 'set llvmgcc "$(LLVMGCC)"' >> site.tmp + @echo 'set llvmgxx "$(LLVMGCC)"' >> site.tmp + @echo 'set llvmgccmajvers "$(LLVMGCC_MAJVERS)"' >> site.tmp + @echo 'set shlibext "$(SHLIBEXT)"' >> site.tmp + @echo '## All variables above are generated by configure. Do Not Edit ## ' >>site.tmp + @test ! -f site.exp || \ + sed '1,/^## All variables above are.*##/ d' site.exp >> site.tmp + @-rm -f site.bak + @test ! -f site.exp || mv site.exp site.bak + @mv site.tmp site.exp diff --git a/test/Makefile.tests b/test/Makefile.tests new file mode 100644 index 00000000..ad9f2eab --- /dev/null +++ b/test/Makefile.tests @@ -0,0 +1,80 @@ +##----------------------------------------------------------*- Makefile -*-===## +## +## Common rules for generating, linking, and compiling via LLVM. This is +## used to implement a robust testing framework for LLVM +## +##-------------------------------------------------------------------------===## + +# If the user specified a TEST= option on the command line, we do not want to do +# the default testing type. Instead, we change the default target to be the +# test:: target. +# +ifdef TEST +test:: +endif + +# We do not want to make .d files for tests! +DISABLE_AUTO_DEPENDENCIES=1 + +include ${LEVEL}/Makefile.common + +# Specify ENABLE_STATS on the command line to enable -stats and -time-passes +# output from gccas and gccld. +ifdef ENABLE_STATS +STATS = -stats -time-passes +endif + +.PHONY: clean default + +# These files, which might be intermediate results, should not be deleted by +# make +.PRECIOUS: Output/%.bc Output/%.ll +.PRECIOUS: Output/%.tbc Output/%.tll +.PRECIOUS: Output/.dir +.PRECIOUS: Output/%.llvm.bc +.PRECIOUS: Output/%.llvm + +LCCFLAGS += -O2 -Wall +LCXXFLAGS += -O2 -Wall +LLCFLAGS = +TESTRUNR = @echo Running test: $<; \ + PATH="$(LLVMTOOLCURRENT):$(LLVM_SRC_ROOT)/test/Scripts:$(PATH)" \ + $(LLVM_SRC_ROOT)/test/TestRunner.sh + +LLCLIBS := $(LLCLIBS) -lm + +clean:: + $(RM) -f a.out core + $(RM) -rf Output/ + +# Compile from X.c to Output/X.ll +Output/%.ll: %.c $(LCC1) Output/.dir $(INCLUDES) + -$(LLVMGCCWITHPATH) $(CPPFLAGS) $(LCCFLAGS) -S $< -o $@ + +# Compile from X.cpp to Output/X.ll +Output/%.ll: %.cpp $(LCC1XX) Output/.dir $(INCLUDES) + -$(LLVMGXXWITHPATH) $(CPPFLAGS) $(LCXXFLAGS) -S $< -o $@ + +# Compile from X.cc to Output/X.ll +Output/%.ll: %.cc $(LCC1XX) Output/.dir $(INCLUDES) + -$(LLVMGXXWITHPATH) $(CPPFLAGS) $(LCXXFLAGS) -S $< -o $@ + +# LLVM Assemble from Output/X.ll to Output/X.bc. Output/X.ll must have come +# from GCC output, so use GCCAS. +# +Output/%.bc: Output/%.ll $(LGCCAS) + -$(LGCCAS) $(STATS) $< -o $@ + +# LLVM Assemble from X.ll to Output/X.bc. Because we are coming directly from +# LLVM source, use the non-transforming assembler. +# +Output/%.bc: %.ll $(LLVMAS) Output/.dir + -$(LLVMAS) -f $< -o $@ + +## Cancel built-in implicit rules that override above rules +%: %.s + +%: %.c + +%.o: %.c + diff --git a/test/README b/test/README new file mode 100644 index 00000000..53d30ce2 --- /dev/null +++ b/test/README @@ -0,0 +1 @@ +about tests.... diff --git a/test/Runtime/POSIX/DirConsistency.c b/test/Runtime/POSIX/DirConsistency.c new file mode 100644 index 00000000..613655e9 --- /dev/null +++ b/test/Runtime/POSIX/DirConsistency.c @@ -0,0 +1,64 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t.bc +// RUN: %klee --run-in=/tmp --use-random-search --init-env --libc=uclibc --posix-runtime --exit-on-error %t.bc --sym-files 1 1 > %t1.log +// RUN: %llvmgcc -D_FILE_OFFSET_BITS=64 %s -emit-llvm -O0 -c -o %t.bc +// RUN: %klee --run-in=/tmp --use-random-search --init-env --libc=uclibc --posix-runtime --exit-on-error %t.bc --sym-files 1 1 > %t2.log +// RUN: sort %t1.log %t2.log | uniq -c > %t3.log +// RUN: grep -q "4 COUNT" %t3.log + +// For this test really to work as intended it needs to be run in a +// directory large enough to cause uclibc to do multiple getdents +// calls (otherwise uclibc will handle the seeks itself). We should +// create a bunch of files or something. +// +// It is even more important for this test because it requires the +// directory not to change while running, which might be a lot to +// demand of /tmp. + +#define _LARGEFILE64_SOURCE +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <dirent.h> +#include <sys/stat.h> +#include <errno.h> + +int main(int argc, char **argv) { + struct stat s; + int res = stat("A", &s); + int hasA = !(res!=0 && errno==ENOENT); + + //printf("sizeof(dirent) = %d\n", sizeof(struct dirent)); + //printf("sizeof(dirent64) = %d\n", sizeof(struct dirent64)); + + //printf("\"A\" exists: %d\n", hasA); + + DIR *d = opendir("."); + assert(d); + + int snum = 1; + if (klee_range(0,2,"range")) { + snum = 2; + printf("state%d\n", snum); + } + + int foundA = 0, count = 0; + struct dirent *de; + while ((de = readdir(d))) { + // printf("state%d: dirent: %s\n", snum, de->d_name); + if (strcmp(de->d_name, "A") == 0) + foundA = 1; + count++; + } + + closedir(d); + + //printf("found A: %d\n", foundA); + + // Ensure atomic write + char buf[64]; + sprintf(buf, "COUNT: %d\n", count); + fputs(buf, stdout); + assert(hasA == foundA); + + return 0; +} diff --git a/test/Runtime/POSIX/DirSeek.c b/test/Runtime/POSIX/DirSeek.c new file mode 100644 index 00000000..1735fdb8 --- /dev/null +++ b/test/Runtime/POSIX/DirSeek.c @@ -0,0 +1,57 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t2.bc +// RUN: %klee --run-in=/tmp --init-env --libc=uclibc --posix-runtime --exit-on-error %t2.bc --sym-files 2 2 +// RUN: %klee --run-in=/tmp --init-env --libc=uclibc --posix-runtime --exit-on-error %t2.bc --sym-files 1 2 +// RUN: %klee --run-in=/tmp --init-env --libc=uclibc --posix-runtime --exit-on-error %t2.bc --sym-files 0 2 + +// For this test really to work as intended it needs to be run in a +// directory large enough to cause uclibc to do multiple getdents +// calls (otherwise uclibc will handle the seeks itself). We should +// create a bunch of files or something. + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <dirent.h> +#include <sys/stat.h> +#include <errno.h> +#include <string.h> + +int main(int argc, char **argv) { + struct stat s; + + char first[256], second[256]; + DIR *d = opendir("."); + struct dirent *de = readdir(d); + assert(de); + strcpy(first, de->d_name); + off_t pos = telldir(d); + printf("pos: %d\n", telldir(d)); + de = readdir(d); + assert(de); + strcpy(second, de->d_name); + + // Go back to second and check + seekdir(d, pos); + de = readdir(d); + assert(de); + assert(strcmp(de->d_name, second) == 0); + + // Go to end, then back to 2nd + while (de) + de = readdir(d); + assert(!errno); + seekdir(d, pos); + assert(telldir(d) == pos); + de = readdir(d); + assert(de); + assert(strcmp(de->d_name, second) == 0); + + // Go to beginning and check + rewinddir(d); + de = readdir(d); + assert(de); + assert(strcmp(de->d_name, first) == 0); + closedir(d); + + return 0; +} diff --git a/test/Runtime/POSIX/FDNumbers.c b/test/Runtime/POSIX/FDNumbers.c new file mode 100644 index 00000000..e576f9bb --- /dev/null +++ b/test/Runtime/POSIX/FDNumbers.c @@ -0,0 +1,24 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t2.bc +// RUN: %klee --init-env --posix-runtime --exit-on-error %t2.bc --sym-files 1 10 + +#include <assert.h> +#include <fcntl.h> +#include <errno.h> + +int main(int argc, char **argv) { + int fd = open("A", O_TRUNC); + assert(fd == 3); + assert(!close(0)); + assert(!close(1)); + assert(close(0) == -1); + assert(close(1) == -1); + assert(open("A", O_TRUNC) == 0); + assert(dup(0) == 1); + assert(open("A", O_TRUNC) == 4); + assert(!close(1)); + assert(open("A", O_TRUNC) == 1); + assert(dup(0) != 1); + assert(dup2(0,1) == 1); + + return 0; +} diff --git a/test/Runtime/POSIX/FD_Fail.c b/test/Runtime/POSIX/FD_Fail.c new file mode 100644 index 00000000..cf1d4d5a --- /dev/null +++ b/test/Runtime/POSIX/FD_Fail.c @@ -0,0 +1,25 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee --libc=uclibc --posix-runtime --init-env %t1.bc --sym-files 0 0 --max-fail 1 > %t.log +// RUN: grep -q "fread(): ok" %t.log +// RUN: grep -q "fread(): fail" %t.log +// RUN: grep -q "fclose(): ok" %t.log +// RUN: grep -q "fclose(): fail" %t.log + +#include <stdio.h> +#include <assert.h> + +int main(int argc, char** argv) { + char buf[1024]; + FILE* f = fopen("/etc/fstab", "r"); + assert(f); + + int r = fread(buf, 1, 100, f); + printf("fread(): %s\n", + r ? "ok" : "fail"); + + r = fclose(f); + printf("fclose(): %s\n", + r ? "ok" : "fail"); + + return 0; +} diff --git a/test/Runtime/POSIX/FD_Fail2.c b/test/Runtime/POSIX/FD_Fail2.c new file mode 100644 index 00000000..c9195f66 --- /dev/null +++ b/test/Runtime/POSIX/FD_Fail2.c @@ -0,0 +1,34 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee --libc=uclibc --posix-runtime --init-env %t1.bc --sym-files 0 0 --max-fail 1 +// RUN: test -f klee-last/test000001.bout +// RUN: test -f klee-last/test000002.bout +// RUN: test -f klee-last/test000003.bout +// RUN: test -f klee-last/test000004.bout +// RUN: test -f klee-last/test000005.bout +// RUN: test -f klee-last/test000006.bout +// RUN: test -f klee-last/test000007.bout + +#include <stdio.h> +#include <assert.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +int main(int argc, char** argv) { + char buf[1024]; + int fd = open("/etc/fstab", O_RDONLY); + assert(fd != -1); + + int r = read(fd, buf, 1, 100); + if (r != -1) + printf("read() succeeded\n"); + else printf("read() failed with errno %s\n", strerror(errno)); + + r = close(fd); + if (r != -1) + printf("close() succeeded\n"); + else printf("close() failed with errno %s\n", strerror(errno)); + + return 0; +} diff --git a/test/Runtime/POSIX/Fcntl.c b/test/Runtime/POSIX/Fcntl.c new file mode 100644 index 00000000..139fb1f3 --- /dev/null +++ b/test/Runtime/POSIX/Fcntl.c @@ -0,0 +1,17 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t2.bc +// RUN: %klee --init-env --posix-runtime --exit-on-error %t2.bc --sym-files 1 10 + +#include <assert.h> +#include <fcntl.h> + +int main(int argc, char **argv) { + int fd = open("A", O_RDWR|O_TRUNC); + if (fd == -1) + klee_silent_exit(0); + assert(fd == 3); + assert((fcntl(fd, F_GETFD) & FD_CLOEXEC) == 0); + assert(fcntl(fd, F_SETFD, FD_CLOEXEC, 1) == 0); + assert((fcntl(fd, F_GETFD) & FD_CLOEXEC) != 0); + + return 0; +} diff --git a/test/Runtime/POSIX/FilePerm.c b/test/Runtime/POSIX/FilePerm.c new file mode 100644 index 00000000..818d482f --- /dev/null +++ b/test/Runtime/POSIX/FilePerm.c @@ -0,0 +1,20 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t.bc +// RUN: %klee --posix-runtime --init-env %t.bc --sym-files 1 10 --sym-stdout 2>%t.log +// RUN: test -f klee-last/test000001.bout +// RUN: test -f klee-last/test000002.bout +// RUN: test -f klee-last/test000003.bout + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +int main(int argc, char** argv) { + int fd = open("A", O_RDWR); + if (fd != -1) + fprintf(stderr, "File 'A' opened successfully\n"); + else fprintf(stderr, "Cannot open file 'A'\n"); + + if (fd != -1) + close(fd); +} diff --git a/test/Runtime/POSIX/FreeArgv.c b/test/Runtime/POSIX/FreeArgv.c new file mode 100644 index 00000000..37909b80 --- /dev/null +++ b/test/Runtime/POSIX/FreeArgv.c @@ -0,0 +1,20 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t.bc +// RUN: %klee --init-env --posix-runtime %t.bc --sym-args 1 1 1 +// RUN: test -f klee-last/test000001.free.err +// RUN: test -f klee-last/test000002.free.err +// RUN: test -f klee-last/test000003.free.err + +int main(int argc, char **argv) { + switch(klee_range(0, 3, "range")) { + case 0: + free(argv); + break; + case 1: + free(argv[0]); + break; + case 2: + free(argv[1]); + break; + } + return 0; +} diff --git a/test/Runtime/POSIX/Getenv.c b/test/Runtime/POSIX/Getenv.c new file mode 100644 index 00000000..7ec66788 --- /dev/null +++ b/test/Runtime/POSIX/Getenv.c @@ -0,0 +1,21 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t2.bc +// RUN: %klee --init-env --libc=klee --posix-runtime --exit-on-error %t2.bc --sym-files 1 10 + +#include <assert.h> + +int main(int argc, char **argv) { + char *g = getenv("PWD"); + if (g) { + printf("have PWD\n"); + } else { + printf("have no PWD\n"); + } + + g = getenv("HELLO"); + if (!g || strcmp(g, "nice")==0) { + printf("getenv(\"HELLO\") = %p\n", g); + if (g) assert(strcmp(getenv("HELLO"),"nice") == 0); + } + + return 0; +} diff --git a/test/Runtime/POSIX/Ioctl.c b/test/Runtime/POSIX/Ioctl.c new file mode 100644 index 00000000..0e7b2cad --- /dev/null +++ b/test/Runtime/POSIX/Ioctl.c @@ -0,0 +1,31 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t.bc +// RUN: %klee --init-env --posix-runtime --exit-on-error %t.bc --sym-files 0 4 + +#include <assert.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <termios.h> +#include <asm/ioctls.h> +#include <errno.h> +#include <stdio.h> + +int main(int argc, char **argv) { + struct stat s; + struct termios ts; + + assert(fstat(0, &s) == 0); + + assert(ioctl(10, FIONREAD, &ts) == -1 && errno == EBADF); + + if (S_ISCHR(s.st_mode)) { + printf("is chr\n"); + assert(ioctl(0, TIOCGWINSZ, &ts) == 0); + } else { + printf("not chr\n"); + // I think TC* ioctls basically always fail on nonchr? + assert(ioctl(0, TIOCGWINSZ, &ts) == -1); + assert(errno == ENOTTY); + } + + return 0; +} diff --git a/test/Runtime/POSIX/Isatty.c b/test/Runtime/POSIX/Isatty.c new file mode 100644 index 00000000..6a78dc96 --- /dev/null +++ b/test/Runtime/POSIX/Isatty.c @@ -0,0 +1,33 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t.bc +// RUN: %klee --libc=uclibc --posix-runtime --init-env %t.bc --sym-files 0 10 --sym-stdout 2>%t.log + +// RUN: test -f klee-last/test000001.bout +// RUN: test -f klee-last/test000002.bout +// RUN: test -f klee-last/test000003.bout +// RUN: test -f klee-last/test000004.bout + +// RUN: grep -q "stdin is a tty" %t.log +// RUN: grep -q "stdin is NOT a tty" %t.log +// RUN: grep -q "stdout is a tty" %t.log +// RUN: grep -q "stdout is NOT a tty" %t.log + +#include <unistd.h> +#include <stdio.h> +#include <assert.h> + +int main(int argc, char** argv) { + int fd0 = 0; // stdin + int fd1 = 1; // stdout + + int r = isatty(fd0); + if (r) + fprintf(stderr, "stdin is a tty\n"); + else fprintf(stderr, "stdin is NOT a tty\n"); + + r = isatty(fd1); + if (r) + fprintf(stderr, "stdout is a tty\n"); + else fprintf(stderr, "stdout is NOT a tty\n"); + + return 0; +} diff --git a/test/Runtime/POSIX/PrgName.c b/test/Runtime/POSIX/PrgName.c new file mode 100644 index 00000000..19a56889 --- /dev/null +++ b/test/Runtime/POSIX/PrgName.c @@ -0,0 +1,31 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t2.bc +// RUN: %klee --init-env --posix-runtime --exit-on-error %t2.bc --sym-arg 10 >%t.log +// RUN: test -f klee-last/test000001.bout +// RUN: test -f klee-last/test000002.bout +// RUN: grep -q "No" %t.log +// RUN: grep -qv "Yes" %t.log + + +/* Simple test for argv[0] */ + +#include <stdio.h> + +int f(int argc, char **argv) { + + if (argv[0][5] == '7') + printf("Yes\n"); + else printf("No\n"); + + printf("%c\n", argv[0][5]); + + if (argv[1][1] == 4) + printf("4\n"); + printf("not 4\n"); + + return 0; +} + + +int main(int argc, char **argv) { + f(argc, argv); +} diff --git a/test/Runtime/POSIX/Read1.c b/test/Runtime/POSIX/Read1.c new file mode 100644 index 00000000..3b24bfb9 --- /dev/null +++ b/test/Runtime/POSIX/Read1.c @@ -0,0 +1,34 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t.bc +// RUN: %klee --exit-on-error --posix-runtime --init-env %t.bc --sym-files 1 8 >%t.log + +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <assert.h> + +int main(int argc, char** argv) { + char buf[32]; + + // If count is zero, read() returns zero and has no other results. (man page) + int x = read(0, 0, 0); + assert(x == 0); + + int fd = open("A", O_RDONLY); + assert(fd != -1); + + // EFAULT buf is outside your accessible address space. (man page) + x = read(fd, 0, 1); + assert(x == -1 && errno == EFAULT); + + // EBADF fd is not a valid file descriptor (man page) + x = read(-1, buf, 1); + assert(x == -1 && errno == EBADF); + + fd = open("A", O_RDONLY); + assert(fd != -1); + x = read(fd, buf, 1); + assert(x == 1); +} + diff --git a/test/Runtime/POSIX/SELinux.c b/test/Runtime/POSIX/SELinux.c new file mode 100644 index 00000000..65dd1a7f --- /dev/null +++ b/test/Runtime/POSIX/SELinux.c @@ -0,0 +1,30 @@ +/* Very basic test, as right now SELinux support is extremely basic */ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee --posix-runtime --exit-on-error %t1.bc --sym-arg 2 > %t.log +// XFAIL: no-selinux + +#include <selinux/selinux.h> +#include <stdio.h> +#include <assert.h> + +int main(int argc, char** argv) { + + security_context_t con; + + assert(argc == 2); + + int selinux = is_selinux_enabled(); + printf("selinux enabled = %d\n", selinux); + + if (setfscreatecon(argv[1]) < 0) + printf("Error: set\n"); + else printf("Success: set\n"); + + if (getfscreatecon(&con) < 0) + printf("Error: get\n"); + else printf("Success: get\n"); + + printf("create_con = %s\n", con); + + return 0; +} diff --git a/test/Runtime/POSIX/SeedAndFail.c b/test/Runtime/POSIX/SeedAndFail.c new file mode 100644 index 00000000..64cf79bd --- /dev/null +++ b/test/Runtime/POSIX/SeedAndFail.c @@ -0,0 +1,38 @@ +// RUN: %llvmgcc %s -emit-llvm -g -O0 -c -o %t.bc +// RUN: rm -rf tmp-123 +// RUN: %klee --libc=klee --output-dir=tmp-123 --posix-runtime --init-env %t.bc --sym-files 1 10 2>%t.log +// RUN: klee --seed-out-dir=tmp-123 --zero-seed-extension --libc=uclibc --posix-runtime --init-env %t.bc --sym-files 1 10 --max-fail 1 +// RUN: ls klee-last | grep -c assert | grep 4 + + + +#include <stdio.h> +#include <assert.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + + +int main(int argc, char** argv) { + char buf[32]; + + int fd = open("A", O_RDWR | O_CREAT, S_IRWXU); + assert(fd != -1); + int nbytes = write(fd, "Hello", sizeof("Hello")); + assert(nbytes == sizeof("Hello")); + + off_t off = lseek(fd, 0, SEEK_SET); + assert(off != (off_t) -1); + + nbytes = read(fd, buf, sizeof("Hello")); + assert(nbytes == sizeof("Hello")); + + int r = close(fd); + assert(r == 0); + + r = memcmp(buf, "Hello", sizeof("Hello")); + assert(r == 0); + + return 0; +} diff --git a/test/Runtime/POSIX/Stdin.c b/test/Runtime/POSIX/Stdin.c new file mode 100644 index 00000000..065533a1 --- /dev/null +++ b/test/Runtime/POSIX/Stdin.c @@ -0,0 +1,56 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t.bc +// RUN: %klee --init-env --libc=klee --posix-runtime --exit-on-error %t.bc --sym-files 0 4 > %t.log +// RUN: grep "mode:file" %t.log +// RUN: grep "mode:dir" %t.log +// RUN: grep "mode:chr" %t.log +// RUN: grep "mode:blk" %t.log +// RUN: grep "mode:fifo" %t.log +// RUN: grep "mode:lnk" %t.log +// RUN: grep "read:sym:yes" %t.log +// RUN: grep "read:sym:no" %t.log + +#include <stdio.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <assert.h> + +int main(int argc, char **argv) { + struct stat stats; + assert(fstat(0, &stats) == 0); + + if (S_ISREG(stats.st_mode)) { + printf("mode:file\n"); + } else if (S_ISDIR(stats.st_mode)) { + printf("mode:dir\n"); + } else if (S_ISCHR(stats.st_mode)) { + printf("mode:chr\n"); + } else if (S_ISBLK(stats.st_mode)) { + printf("mode:blk\n"); + } else if (S_ISFIFO(stats.st_mode)) { + printf("mode:fifo\n"); + } else if (S_ISLNK(stats.st_mode)) { + printf("mode:lnk\n"); + } else if (S_ISSOCK(stats.st_mode)) { + printf("mode:sock\n"); + } else { + printf("unknown mode\n"); + } + + assert(stats.st_size==4); + + if (S_ISREG(stats.st_mode)) { + char buf[10]; + int n = read(0, buf, 5); + assert(n == 4); + + if (strcmp(buf, "HI!")) { + printf("read:sym:yes\n"); + } else { + printf("read:sym:no\n"); + } + } + + fflush(stdout); + + return 0; +} diff --git a/test/Runtime/POSIX/Write1.c b/test/Runtime/POSIX/Write1.c new file mode 100644 index 00000000..73902363 --- /dev/null +++ b/test/Runtime/POSIX/Write1.c @@ -0,0 +1,23 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t.bc +// RUN: %klee --exit-on-error --libc=uclibc --posix-runtime --init-env %t.bc --sym-files 1 10 --sym-stdout 2>%t.log + +#include <stdio.h> +#include <assert.h> + +int main(int argc, char** argv) { + char buf[32]; + + FILE* f = fopen("A", "w"); + if (!f) + klee_silent_exit(0); + fwrite("Hello", sizeof("Hello"), 1, f); + fclose(f); + + f = fopen("A", "r"); + fread(buf, sizeof("Hello"), 1, f); + fclose(f); + + assert(memcmp(buf, "Hello", sizeof("Hello")) == 0); + + return 0; +} diff --git a/test/Runtime/POSIX/Write2.c b/test/Runtime/POSIX/Write2.c new file mode 100644 index 00000000..4865b479 --- /dev/null +++ b/test/Runtime/POSIX/Write2.c @@ -0,0 +1,21 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t.bc +// RUN: %klee --exit-on-error --libc=uclibc --posix-runtime --init-env %t.bc --sym-files 1 10 --sym-stdout 2>%t.log + +#include <stdio.h> +#include <assert.h> + +int main(int argc, char** argv) { + const char* msg = "This will eventually overflow stdout. "; + char buf[32]; + int i; + + FILE* f = stdout;//fopen("A", "w"); + if (!f) + klee_silent_exit(0); + + for (i = 0; i < 300; i++) + fwrite(msg, sizeof(msg), 1, f); + fclose(f); + + return 0; +} diff --git a/test/Runtime/POSIX/dg.exp b/test/Runtime/POSIX/dg.exp new file mode 100644 index 00000000..88406208 --- /dev/null +++ b/test/Runtime/POSIX/dg.exp @@ -0,0 +1,5 @@ +load_lib llvm.exp + +if { [klee_supports_posix_runtime] } { + RunLLVMTests [lsort [glob -nocomplain $srcdir/$subdir/*.{ll,llx,c,cpp,tr}]] +} diff --git a/test/Runtime/Uclibc/2007-10-08-optimization-calls-wrong-libc-functions.c b/test/Runtime/Uclibc/2007-10-08-optimization-calls-wrong-libc-functions.c new file mode 100644 index 00000000..83e6a57a --- /dev/null +++ b/test/Runtime/Uclibc/2007-10-08-optimization-calls-wrong-libc-functions.c @@ -0,0 +1,19 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee --exit-on-error --optimize --libc=uclibc %t1.bc + +#include <string.h> +#include <assert.h> + +int main() { + int a; + + klee_make_symbolic(&a, sizeof a); + + memset(&a, 0, sizeof a); + + if (a) { + assert(0); + } + + return 0; +} diff --git a/test/Runtime/Uclibc/2008-03-04-libc-atexit-uses-dso-handle.c b/test/Runtime/Uclibc/2008-03-04-libc-atexit-uses-dso-handle.c new file mode 100644 index 00000000..686bec19 --- /dev/null +++ b/test/Runtime/Uclibc/2008-03-04-libc-atexit-uses-dso-handle.c @@ -0,0 +1,12 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee --exit-on-error --libc=uclibc %t1.bc + +// just make sure atexit works ok + +void boo() { +} + +int main() { + atexit(boo); + return 0; +} diff --git a/test/Runtime/Uclibc/Environ.c b/test/Runtime/Uclibc/Environ.c new file mode 100644 index 00000000..63892db5 --- /dev/null +++ b/test/Runtime/Uclibc/Environ.c @@ -0,0 +1,10 @@ +// RUN: %llvmgcc %s -emit-llvm -g -c -o %t1.bc +// RUN: %klee --libc=uclibc --exit-on-error %t1.bc + +#include <assert.h> + +int main() { + printf("HOME: %s\n", getenv("HOME")); + assert(getenv("HOME") != 0); + return 0; +} diff --git a/test/Runtime/Uclibc/dg.exp b/test/Runtime/Uclibc/dg.exp new file mode 100644 index 00000000..9c1663c3 --- /dev/null +++ b/test/Runtime/Uclibc/dg.exp @@ -0,0 +1,5 @@ +load_lib llvm.exp + +if { [klee_supports_uclibc] } { + RunLLVMTests [lsort [glob -nocomplain $srcdir/$subdir/*.{ll,llx,c,cpp,tr}]] +} diff --git a/test/TestRunner.sh b/test/TestRunner.sh new file mode 100755 index 00000000..3e2cace4 --- /dev/null +++ b/test/TestRunner.sh @@ -0,0 +1,36 @@ +#!/bin/sh +# +# TestRunner.sh - This script is used to run the deja-gnu tests exactly like +# deja-gnu does, by executing the Tcl script specified in the test case's +# RUN: lines. This is made possible by a simple make target supported by the +# test/Makefile. All this script does is invoke that make target. +# +# Usage: +# TestRunner.sh {script_names} +# +# This script is typically used by cd'ing to a test directory and then +# running TestRunner.sh with a list of test file names you want to run. +# +TESTPATH=`pwd` +SUBDIR="" +if test `dirname $1` = "." ; then + while test `basename $TESTPATH` != "test" -a ! -z "$TESTPATH" ; do + tmp=`basename $TESTPATH` + SUBDIR="$tmp/$SUBDIR" + TESTPATH=`dirname $TESTPATH` + done +fi + +for TESTFILE in "$@" ; do + if test `dirname $TESTFILE` = . ; then + if test -d "$TESTPATH" ; then + cd $TESTPATH + make check-one TESTONE="$SUBDIR$TESTFILE" + cd $PWD + else + echo "Can't find klee/test directory in " `pwd` + fi + else + make check-one TESTONE=$TESTFILE + fi +done diff --git a/test/lib/llvm.exp b/test/lib/llvm.exp new file mode 100644 index 00000000..2857dd82 --- /dev/null +++ b/test/lib/llvm.exp @@ -0,0 +1,213 @@ +# This procedure executes one line of a test case's execution script. +proc execOneLine { test PRS outcome lineno line } { + set status 0 + set resultmsg "" + set retval [ catch { eval exec -keepnewline -- $line } errmsg ] + if { $retval != 0 } { + set code [lindex $::errorCode 0] + set lineno [expr $lineno + 1] + if { $PRS != ""} { + set PRS " for $PRS" + } + set errmsg " at line $lineno\nwhile running: $line\n$errmsg" + switch "$code" { + CHILDSTATUS { + set status [lindex $::errorCode 2] + if { $status != 0 } { + set resultmsg "$test$PRS\nFailed with exit($status)$errmsg" + } + } + CHILDKILLED { + set signal [lindex $::errorCode 2] + set resultmsg "$test$PRS\nFailed with signal($signal)$errmsg" + } + CHILDSUSP { + set signal [lindex $::errorCode 2] + set resultmsg "$test$PRS\nFailed with suspend($signal)$errmsg" + } + POSIX { + set posixNum [lindex $::errorCode 1] + set posixMsg [lindex $::errorCode 2] + set resultmsg "$test$PRS\nFailed with posix($posixNum,$posixMsg)$errmsg" + } + NONE { + } + default { + } + } + } + return $resultmsg +} + +# This procedure performs variable substitutions on the RUN: lines of a test +# cases. +proc substitute { line test tmpFile } { + global srcroot objroot srcdir objdir subdir target_triplet prcontext + global llvmgcc llvmgxx llvmgcc_version llvmgccmajvers + global gccpath gxxpath compile_c compile_cxx link shlibext llvmlibsdir + global llvmtoolsdir + set path [file join $srcdir $subdir] + + # Substitute all Tcl variables. + set new_line [subst $line ] + + #replace %prcontext with prcontext.tcl (Must replace before %p) + regsub -all {%prcontext} $new_line $prcontext new_line + #replace %llvmgcc with actual path to llvmgcc + regsub -all {%llvmgcc} $new_line "$llvmgcc -emit-llvm" new_line + #replace %llvmgxx with actual path to llvmg++ + regsub -all {%llvmgxx} $new_line "$llvmgxx -emit-llvm" new_line + #replace %compile_c with C compilation command + regsub -all {%compile_c} $new_line "$compile_c" new_line + #replace %compile_cxx with C++ compilation command + regsub -all {%compile_cxx} $new_line "$compile_cxx" new_line + #replace %link with C++ link command + regsub -all {%link} $new_line "$link" new_line + #replace %shlibext with shared library extension + regsub -all {%shlibext} $new_line "$shlibext" new_line + #replace %llvmlibsdir with configure library directory + regsub -all {%llvmlibsdir} $new_line "$llvmlibsdir" new_line + #replace %klee with klee binary + regsub -all {%klee} $new_line "klee" new_line + #replace %kleaver with kleaver binary + regsub -all {%kleaver} $new_line "kleaver" new_line + #replace %p with path to source, + regsub -all {%p} $new_line [file join $srcdir $subdir] new_line + #replace %s with filename + regsub -all {%s} $new_line $test new_line + #replace %t with temp filenames + regsub -all {%t} $new_line $tmpFile new_line + #replace %% with % + regsub -all {%%} $new_line % new_line + return $new_line +} + +# This procedure runs the set of tests for the test_source_files array. +proc RunLLVMTests { test_source_files } { + global srcroot objroot srcdir objdir subdir TEST_FEATURE_LIST target_triplet + set timeout 60 + + set path [file join $objdir $subdir] + + #Make Output Directory if it does not exist already + if { [file exists path] } { + cd $path + } else { + file mkdir $path + cd $path + } + + file mkdir Output + cd Output + + foreach test $test_source_files { + #Should figure out best way to set the timeout + #set timeout 40 + + set filename [file tail $test] + set outcome PASS + set tmpFile "$filename.tmp" + + #set hasRunline bool to check if testcase has a runline + set numLines 0 + + # Open the test file and start reading lines + set testFileId [ open $test r] + set runline "" + set PRNUMS "" + foreach line [split [read $testFileId] \n] { + + # if its the END. line then stop parsing (optimization for big files) + if {[regexp {END.[ *]$} $line match endofscript]} { + break + + # if the line is continued, concatenate and continue the loop + } elseif {[regexp {RUN: *(.+)(\\)$} $line match oneline suffix]} { + set runline "$runline$oneline " + + # if its a terminating RUN: line then do substitution on the whole line + # and then save the line. + } elseif {[regexp {RUN: *([^&]+)(&&)?} $line match oneline suffix]} { + set runline "$runline$oneline" + set runline [ substitute $runline $test $tmpFile ] + set lines($numLines) $runline + set numLines [expr $numLines + 1] + set runline "" + + # if its an PR line, save the problem report number + } elseif {[regexp {PR([0-9]+)} $line match prnum]} { + if {$PRNUMS == ""} { + set PRNUMS "PR$prnum" + } else { + set PRNUMS "$PRNUMS,$prnum" + } + # if its an XFAIL line, see if we should be XFAILing or not. + } elseif {[regexp {XFAIL:[ *](.+)} $line match features]} { + set features + + foreach feature [split $features ,] { + if { [regexp {\*} $feature match] } { + set outcome XFAIL + } elseif { [regexp $feature $target_triplet match] } { + set outcome XFAIL + } elseif { [regexp $feature $TEST_FEATURE_LIST match] } { + set outcome XFAIL + } + } + } + } + + # Done reading the script + close $testFileId + + + if { $numLines == 0 } { + fail "$test: \nDoes not have a RUN line\n" + } else { + set failed 0 + for { set i 0 } { $i < $numLines } { set i [ expr $i + 1 ] } { + regsub ^.*RUN:(.*) $lines($i) \1 theLine + set resultmsg [execOneLine $test $PRNUMS $outcome $i $theLine ] + if { $resultmsg != "" } { + if { $outcome == "XFAIL" } { + xfail "$resultmsg" + } else { + fail "$resultmsg" + } + set failed 1 + break + } + } + if { $failed } { + continue + } else { + if { $PRNUMS != "" } { + set PRNUMS " for $PRNUMS" + } + if { $outcome == "XFAIL" } { + xpass "$test$PRNUMS" + } else { + pass "$test$PRNUMS" + } + } + } + } +} + +# Check if klee was configured with POSIX runtime support. +proc klee_supports_posix_runtime { } { + global ENABLE_POSIX_RUNTIME + if { $ENABLE_POSIX_RUNTIME == "1" } { + return 1 + } + return 0 +} + +# Check if klee was configured with uclibc support. +proc klee_supports_uclibc { } { + global ENABLE_UCLIBC + if { $ENABLE_UCLIBC == "1" } { + return 1 + } + return 0 +} diff --git a/test/regression/2007-07-25-invalid-stp-array-binding-to-objectstate.c b/test/regression/2007-07-25-invalid-stp-array-binding-to-objectstate.c new file mode 100644 index 00000000..ad585ddd --- /dev/null +++ b/test/regression/2007-07-25-invalid-stp-array-binding-to-objectstate.c @@ -0,0 +1,20 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee %t1.bc + +#include <assert.h> + +int main(void) { + char c[2]; + + klee_make_symbolic(&c, sizeof(c)); + + if (c[0] > 10) { + int x; + + c[1] = 1; // copy object state + + assert(c[0] > 10); + } + + return 0; +} diff --git a/test/regression/2007-07-30-unflushed-byte.c b/test/regression/2007-07-30-unflushed-byte.c new file mode 100644 index 00000000..ba8a08a7 --- /dev/null +++ b/test/regression/2007-07-30-unflushed-byte.c @@ -0,0 +1,18 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee %t1.bc + +#include <assert.h> + +int main() { + char i, x[3]; + + klee_make_symbolic(&i, sizeof i); + + x[0] = i; + + // DEMAND FAILED:Memory.cpp:read8:199: <isByteFlushed(offset)> is false: "unflushed byte without cache value" + char y = x[1]; + + return 0; +} + diff --git a/test/regression/2007-08-01-bool-zext-in-call.ll b/test/regression/2007-08-01-bool-zext-in-call.ll new file mode 100644 index 00000000..3f3e26ab --- /dev/null +++ b/test/regression/2007-08-01-bool-zext-in-call.ll @@ -0,0 +1,22 @@ +; RUN: llvm-as -f %s -o - | %klee 2> %t1.log +; RUN: not test -f klee-last/test0001.abort.err + +declare void @klee_abort() + +define i32 @foo(i8 signext %val) { + %tmp = zext i8 %val to i32 + ret i32 %tmp +} + +define i32 @main() { + %res = call i32 bitcast (i32 (i8 signext)* @foo to i32 (i1)*)( i1 1 ) + %check = icmp ne i32 %res, 255 + br i1 %check, label %error, label %exit + +error: + call void @klee_abort() + unreachable + +exit: + ret i32 0 +} diff --git a/test/regression/2007-08-01-cache-unclear-on-overwrite-flushed.c b/test/regression/2007-08-01-cache-unclear-on-overwrite-flushed.c new file mode 100644 index 00000000..15f4e90e --- /dev/null +++ b/test/regression/2007-08-01-cache-unclear-on-overwrite-flushed.c @@ -0,0 +1,25 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee %t1.bc + +#include <assert.h> +#include <stdio.h> + +int main() { + unsigned char x; + + klee_make_symbolic(&x, sizeof x); + if (x >= 2) klee_silent_exit(0); + + char delete[2] = {0,1}; + + char tmp = delete[ x ]; + char tmp2 = delete[0]; + delete[ x ] = tmp2; + + if (x==1) { + assert(delete[1] == 0); + return 0; + } + + return 0; +} diff --git a/test/regression/2007-08-06-64bit-shift.c b/test/regression/2007-08-06-64bit-shift.c new file mode 100644 index 00000000..958e56c0 --- /dev/null +++ b/test/regression/2007-08-06-64bit-shift.c @@ -0,0 +1,20 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee %t1.bc + +#include <assert.h> + +int main() { + int d; + + klee_make_symbolic( &d, sizeof(d) ); + + int l = d - 1; + unsigned long long m = ((unsigned long long) l << 32) / d; + if (d==2) { + assert(m == 2147483648u); + } + + klee_silent_exit(0); + + return 0; +} diff --git a/test/regression/2007-08-06-access-after-free.c b/test/regression/2007-08-06-access-after-free.c new file mode 100644 index 00000000..a1812062 --- /dev/null +++ b/test/regression/2007-08-06-access-after-free.c @@ -0,0 +1,29 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee %t1.bc + +#include <assert.h> + +int main() { + int a; + unsigned char *p = malloc(4); + + klee_make_symbolic(&a, sizeof a); + klee_make_symbolic(p, sizeof p); + + p[0] |= 16; + + if (a) { + free(p); + + // this should give an error instead of + // pulling the state from the parent, where + // it is not free + assert(p[0] > 10); + + return 0; + } + + assert(p[0] > 10); + + return 0; +} diff --git a/test/regression/2007-08-08-free-zero.c b/test/regression/2007-08-08-free-zero.c new file mode 100644 index 00000000..964889a1 --- /dev/null +++ b/test/regression/2007-08-08-free-zero.c @@ -0,0 +1,8 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee %t1.bc +// RUN: ls klee-last | not grep *.err + +int main() { + free(0); + return 0; +} diff --git a/test/regression/2007-08-16-invalid-constant-value.c b/test/regression/2007-08-16-invalid-constant-value.c new file mode 100644 index 00000000..ecb3283f --- /dev/null +++ b/test/regression/2007-08-16-invalid-constant-value.c @@ -0,0 +1,31 @@ +// RUN: rm -f %t4.out %t4.err %t4.log +// RUN: %llvmgcc %s -emit-llvm -O2 -c -o %t1.bc +// RUN: llvm-as -f ../../Feature/_utils._ll -o %t2.bc +// RUN: llvm-ld -disable-opt %t1.bc %t2.bc -o %t3 +// RUN: %klee %t3.bc + +#include <assert.h> + +#include "../Feature/utils.h" + +int main() { + unsigned char a; + + klee_make_symbolic(&a, sizeof a); + + // demand was firing here because an invalid constant + // value was being created when implied value did not + // subtract using the proper type (so overflowed into + // invalid bits) + if (util_make_concat2(a+0xCD,0xCD) == 0xABCD) { + assert(!klee_is_symbolic(a)); + printf("add constant case: %d\n", a); + } + + if (util_make_concat2(0x0B-a,0xCD) == 0xABCD) { + assert(!klee_is_symbolic(a)); + printf("sub constant case: %d\n", a); + } + + return 0; +} diff --git a/test/regression/2007-08-16-valid-write-to-freed-object.c b/test/regression/2007-08-16-valid-write-to-freed-object.c new file mode 100644 index 00000000..472b7de8 --- /dev/null +++ b/test/regression/2007-08-16-valid-write-to-freed-object.c @@ -0,0 +1,24 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee %t1.bc + +unsigned sym() { + unsigned x; + klee_make_symbolic(&x, sizeof x); + return x; +} + +int main() { + unsigned x, y; + + // sym returns a symbolic object, but because it is + // alloca'd it is freed on sym()s return. thats fine, + // but the problem is that IVC is going to try to write + // into the object right here. + // + // to support this we need to have a facility for making + // state local copies of a freed object. + if (sym() == 0) + printf("ok\n"); + + return 0; +} diff --git a/test/regression/2007-10-11-free-of-alloca.c b/test/regression/2007-10-11-free-of-alloca.c new file mode 100644 index 00000000..71a16f6b --- /dev/null +++ b/test/regression/2007-10-11-free-of-alloca.c @@ -0,0 +1,9 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee %t1.bc +// RUN: test -f klee-last/test000001.free.err + +int main() { + int buf[4]; + free(buf); // this should give runtime error, not crash + return 0; +} diff --git a/test/regression/2007-10-11-illegal-access-after-free-and-branch.c b/test/regression/2007-10-11-illegal-access-after-free-and-branch.c new file mode 100644 index 00000000..fbbb99c3 --- /dev/null +++ b/test/regression/2007-10-11-illegal-access-after-free-and-branch.c @@ -0,0 +1,19 @@ +// RUN: %llvmgcc %s -emit-llvm -g -c -o %t1.bc +// RUN: %klee --optimize %t1.bc +// RUN: test -f klee-last/test000001.ptr.err + +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> + +int main(int argc, char **argv) { + unsigned char *buf = malloc(3); + klee_make_symbolic(buf, 3); + if (buf[0]>4) klee_silent_exit(0); + unsigned char x = buf[1]; + free(buf); + if (x) + return buf[2]; + klee_silent_exit(0); + return 0; +} diff --git a/test/regression/2007-10-12-failed-make-symbolic-after-copy.c b/test/regression/2007-10-12-failed-make-symbolic-after-copy.c new file mode 100644 index 00000000..144281fa --- /dev/null +++ b/test/regression/2007-10-12-failed-make-symbolic-after-copy.c @@ -0,0 +1,22 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee %t1.bc +// RUN: test -f klee-last/test000001.bout + +int main() { + unsigned x, y[4]; + + klee_make_symbolic(&x,sizeof x); + if (x>=4) klee_silent_exit(0); + + y[x] = 0; + + if (x) { // force branch so y is copied + klee_make_symbolic(&y, sizeof y); + if (y[x]==0) klee_silent_exit(0); + return 0; // should be reachable + } else { + // force read here in case we try to optimize copies smartly later + if (y[x]==0) klee_silent_exit(0); + return 0; // not reachable + } +} diff --git a/test/regression/2008-02-11-phi-nodes-after-invoke.ll b/test/regression/2008-02-11-phi-nodes-after-invoke.ll new file mode 100644 index 00000000..f6077f25 --- /dev/null +++ b/test/regression/2008-02-11-phi-nodes-after-invoke.ll @@ -0,0 +1,47 @@ +; RUN: llvm-as -f %s -o - | %klee --no-output --exit-on-error + +declare void @klee_abort() + +define i32 @foo(i32 %val, i32 %fail) { + %code = icmp ne i32 0, %fail + br i1 %code, label %failing, label %return +failing: + unwind +return: + ret i32 %val +} + +define void @test(i32 %should_fail) { +entry: + %res0 = invoke i32 (i32, i32)* @foo(i32 0, i32 %should_fail) + to label %check_phi unwind label %error + +error: + %res1 = zext i8 1 to i32 + br label %check_phi + +check_phi: + %val = phi i32 [%never_used, %never_used_label], [%res0, %entry], [%res1, %error] + %ok = icmp eq i32 %val, %should_fail + br i1 %ok, label %exit, label %on_error + call void @klee_abort() + unreachable + +on_error: + call void @klee_abort() + unreachable + +exit: + ret void + + ;; this is so we hopefully fail if incomingBBIndex isn't set properly +never_used_label: + %never_used = zext i8 undef to i32 + br label %check_phi +} + +define i32 @main() { + call void (i32)* @test(i32 0) + call void (i32)* @test(i32 1) + ret i32 0 +} diff --git a/test/regression/2008-03-04-free-of-global.c b/test/regression/2008-03-04-free-of-global.c new file mode 100644 index 00000000..7821398d --- /dev/null +++ b/test/regression/2008-03-04-free-of-global.c @@ -0,0 +1,10 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee %t1.bc +// RUN: test -f klee-last/test000001.free.err + +int buf[4]; + +int main() { + free(buf); // this should give runtime error, not crash + return 0; +} diff --git a/test/regression/2008-03-11-free-of-malloc-zero.c b/test/regression/2008-03-11-free-of-malloc-zero.c new file mode 100644 index 00000000..d096818b --- /dev/null +++ b/test/regression/2008-03-11-free-of-malloc-zero.c @@ -0,0 +1,16 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee --exit-on-error %t1.bc + +#include <stdlib.h> + +int main() { + // concrete case + void *p = malloc(0); + free(p); + + p = malloc(0); + void *arr[4] = { p, 0, 0, 0 }; + + // symbolic case + free(arr[klee_range(0, 4, "range")]); +} diff --git a/test/regression/2008-04-10-bad-alloca-free.c b/test/regression/2008-04-10-bad-alloca-free.c new file mode 100644 index 00000000..46e2b0cf --- /dev/null +++ b/test/regression/2008-04-10-bad-alloca-free.c @@ -0,0 +1,12 @@ +// RUN: %llvmgcc %s -emit-llvm -O0 -c -o %t1.bc +// RUN: %klee --exit-on-error %t1.bc + +void f(int *addr) { + klee_make_symbolic_name(addr, sizeof *addr, "moo"); +} + +int main() { + int x; + f(&x); + return x; +} diff --git a/test/regression/2008-05-23-gep-with-global-const.c b/test/regression/2008-05-23-gep-with-global-const.c new file mode 100644 index 00000000..5e03ec1d --- /dev/null +++ b/test/regression/2008-05-23-gep-with-global-const.c @@ -0,0 +1,15 @@ +// RUN: %llvmgcc -O0 -c -o %t.bc %s +// RUN: %klee --exit-on-error %t.bc + +#include <assert.h> + +int a; + +int main() { + void *p1 = &((char*) 0)[(long) &a]; + void *p2 = &a; + + assert(p1 == p2); + + return 0; +} diff --git a/test/regression/dg.exp b/test/regression/dg.exp new file mode 100644 index 00000000..879685ca --- /dev/null +++ b/test/regression/dg.exp @@ -0,0 +1,3 @@ +load_lib llvm.exp + +RunLLVMTests [lsort [glob -nocomplain $srcdir/$subdir/*.{ll,llx,c,cpp,tr}]] diff --git a/tools/Makefile b/tools/Makefile new file mode 100644 index 00000000..8c3501da --- /dev/null +++ b/tools/Makefile @@ -0,0 +1,21 @@ +#===-- tools/Makefile --------------------------------------*- Makefile -*--===# +# +# The KLEE Symbolic Virtual Machine +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +# +# Relative path to the top of the source tree. +# +LEVEL=.. + +# +# List all of the subdirectories that we will compile. +# +PARALLEL_DIRS=klee kleaver klee-bout-tool gen-random-bout +# FIXME: Move qplayer functionality into kleaver + +include $(LEVEL)/Makefile.common diff --git a/tools/gen-random-bout/Makefile b/tools/gen-random-bout/Makefile new file mode 100644 index 00000000..97315af2 --- /dev/null +++ b/tools/gen-random-bout/Makefile @@ -0,0 +1,7 @@ +##===- tools/klee/Makefile ---------------*- Makefile -*-===## + +LEVEL=../.. +TOOLNAME = gen-random-bout +USEDLIBS = kleeBasic.a + +include $(LEVEL)/Makefile.common diff --git a/tools/gen-random-bout/gen-random-bout.cpp b/tools/gen-random-bout/gen-random-bout.cpp new file mode 100644 index 00000000..044c3d24 --- /dev/null +++ b/tools/gen-random-bout/gen-random-bout.cpp @@ -0,0 +1,124 @@ +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <time.h> +#include <unistd.h> + +#include "klee/Internal/ADT/BOut.h" + +// --sym-args 0 1 10 --sym-args 0 2 2 --sym-files 1 8 --sym-stdout +static int getint(char *i) { + if(!i) { + fprintf(stderr, "ran out of arguments!\n"); + assert(i); + } + return atoi(i); +} + +#define MAX 64 +static void push_obj(BOut *b, const char *name, unsigned non_zero_bytes, + unsigned total_bytes) { + BOutObject *o = &b->objects[b->numObjects++]; + assert(b->numObjects < MAX); + + o->name = strdup(name); + o->numBytes = total_bytes; + o->bytes = (unsigned char *)malloc(o->numBytes); + + unsigned i; + for(i = 0; i < non_zero_bytes; i++) + o->bytes[i] = random(); + + for(i = non_zero_bytes; i < total_bytes; i++) + o->bytes[i] = 0; +} + + +static void push_range(BOut *b, const char *name, unsigned value) { + BOutObject *o = &b->objects[b->numObjects++]; + assert(b->numObjects < MAX); + + o->name = strdup(name); + o->numBytes = 4; + o->bytes = (unsigned char *)malloc(o->numBytes); + + *(unsigned*)o->bytes = value; +} + +int main(int argc, char *argv[]) { + unsigned i, narg; + unsigned sym_stdout = 0; + + if (argc < 2) { + fprintf(stderr, "Usage: %s <random-seed> <argument-types>\n", argv[0]); + fprintf(stderr, " If <random-seed> is 0, time(NULL)*getpid() is used as a seed\n"); + fprintf(stderr, " <argument-types> are the ones accepted by KLEE: --sym-args, --sym-files etc.\n"); + fprintf(stderr, " Ex: %s 100 --sym-args 0 2 2 --sym-files 1 8\n", argv[0]); + exit(1); + } + + unsigned seed = atoi(argv[1]); + if (seed) + srandom(seed); + else srandom(time(NULL) * getpid()); + + BOut b; + b.numArgs = argc; + b.args = argv; + b.symArgvs = 0; + b.symArgvLen = 0; + + b.numObjects = 0; + b.objects = (BOutObject *)malloc(MAX * sizeof *b.objects); + + for(i = 2; i < (unsigned)argc; i++) { + if(strcmp(argv[i], "--sym-args") == 0) { + unsigned lb = getint(argv[++i]); + unsigned ub = getint(argv[++i]); + unsigned nbytes = getint(argv[++i]); + + narg = random() % (ub - lb) + lb; + push_range(&b, "range", narg); + + while(narg-- > 0) { + unsigned x = random() % (nbytes + 1); + + // A little different than how klee does it but more natural + // for random. + static int total_args; + char arg[1024]; + + sprintf(arg, "arg%d", total_args++); + push_obj(&b, arg, x, nbytes+1); + } + } else if(strcmp(argv[i], "--sym-stdout") == 0) { + if(!sym_stdout) { + sym_stdout = 1; + push_obj(&b, "stdout", 1024, 1024); + push_obj(&b, "stdout-stat", sizeof(struct stat64), + sizeof(struct stat64)); + } + } else if(strcmp(argv[i], "--sym-files") == 0) { + unsigned nfiles = getint(argv[++i]); + unsigned nbytes = getint(argv[++i]); + + while(nfiles-- > 0) { + push_obj(&b, "file", nbytes, nbytes); + push_obj(&b, "file-stat", sizeof(struct stat64), sizeof(struct stat64)); + } + + push_obj(&b, "stdin", nbytes, nbytes); + push_obj(&b, "stdin-stat", sizeof(struct stat64), sizeof(struct stat64)); + } else { + fprintf(stderr, "unexpected option <%s>\n", argv[i]); + assert(0); + } + } + + if (!bOut_toFile(&b, "file.bout")) + assert(0); + return 0; +} diff --git a/tools/kleaver/Makefile b/tools/kleaver/Makefile new file mode 100644 index 00000000..283b5bef --- /dev/null +++ b/tools/kleaver/Makefile @@ -0,0 +1,19 @@ +#===-- tools/kleaver/Makefile ------------------------------*- Makefile -*--===# +# +# The KLEE Symbolic Virtual Machine +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +LEVEL=../.. +TOOLNAME = kleaver +# FIXME: Ideally we wouldn't have any LLVM dependencies here, which +# means kicking out klee's Support. +USEDLIBS = kleaverExpr.a kleaverSolver.a kleeSupport.a kleeBasic.a +LINK_COMPONENTS = support + +include $(LEVEL)/Makefile.common + +LIBS += -lstp diff --git a/tools/kleaver/main.cpp b/tools/kleaver/main.cpp new file mode 100644 index 00000000..621776ee --- /dev/null +++ b/tools/kleaver/main.cpp @@ -0,0 +1,120 @@ +#include "expr/Lexer.h" +#include "expr/Parser.h" + +#include "klee/Expr.h" +#include "klee/Solver.h" +#include "klee/util/ExprPPrinter.h" +#include "klee/util/ExprVisitor.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Streams.h" +#include "llvm/System/Signals.h" + +#include <iomanip> +#include <sstream> + +using namespace llvm; +using namespace klee; +using namespace klee::expr; + +namespace { + llvm::cl::opt<std::string> + InputFile(llvm::cl::desc("<input query log>"), llvm::cl::Positional, + llvm::cl::init("-")); + + enum ToolActions { + PrintTokens, + PrintAST + }; + + static llvm::cl::opt<ToolActions> + ToolAction(llvm::cl::desc("Tool actions:"), + llvm::cl::init(PrintTokens), + llvm::cl::values( + clEnumValN(PrintTokens, "print-tokens", + "Print tokens from the input file."), + clEnumValN(PrintAST, "print-ast", + "Print parsed AST nodes from the input file."), + clEnumValEnd)); +} + +static std::string escapedString(const char *start, unsigned length) { + std::stringstream s; + for (unsigned i=0; i<length; ++i) { + char c = start[i]; + if (isprint(c)) { + s << c; + } else if (c == '\n') { + s << "\\n"; + } else { + s << "\\x" << hexdigit(((unsigned char) c >> 4) & 0xF) << hexdigit((unsigned char) c & 0xF); + } + } + return s.str(); +} + +static void PrintInputTokens(const MemoryBuffer *MB) { + Lexer L(MB); + Token T; + do { + L.Lex(T); + llvm::cout << "(Token \"" << T.getKindName() << "\" " + << "\"" << escapedString(T.start, T.length) << "\" " + << T.length << " " + << T.line << " " << T.column << ")\n"; + } while (T.kind != Token::EndOfFile); +} + +static bool PrintInputAST(const char *Filename, + const MemoryBuffer *MB) { + Parser *P = Parser::Create(Filename, MB); + P->SetMaxErrors(20); + while (Decl *D = P->ParseTopLevelDecl()) { + if (!P->GetNumErrors()) + D->dump(); + delete D; + } + + bool success = true; + if (unsigned N = P->GetNumErrors()) { + llvm::cerr << Filename << ": parse failure: " + << N << " errors.\n"; + success = false; + } + delete P; + + return success; +} + +int main(int argc, char **argv) { + bool success = true; + + llvm::sys::PrintStackTraceOnErrorSignal(); + llvm::cl::ParseCommandLineOptions(argc, argv); + + std::string ErrorStr; + MemoryBuffer *MB = MemoryBuffer::getFileOrSTDIN(InputFile.c_str(), &ErrorStr); + if (!MB) { + llvm::cerr << argv[0] << ": ERROR: " << ErrorStr << "\n"; + return 1; + } + + switch (ToolAction) { + case PrintTokens: + PrintInputTokens(MB); + break; + case PrintAST: + success = PrintInputAST(InputFile=="-" ? "<stdin>" : InputFile.c_str(), MB); + break; + default: + llvm::cerr << argv[0] << ": ERROR: Unknown program action!\n"; + } + + delete MB; + + llvm::llvm_shutdown(); + return success ? 0 : 1; +} diff --git a/tools/klee-bout-tool/Makefile b/tools/klee-bout-tool/Makefile new file mode 100644 index 00000000..28e80841 --- /dev/null +++ b/tools/klee-bout-tool/Makefile @@ -0,0 +1,46 @@ +#===-- tools/klee-bout-tool/Makefile -----------------------*- Makefile -*--===# +# +# The KLEE Symbolic Virtual Machine +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +LEVEL = ../.. + +TOOLSCRIPTNAME := klee-bout-tool + +include $(LEVEL)/Makefile.common + +# FIXME: Move this stuff (to "build" a script) into Makefile.rules. + +ToolBuildPath := $(ToolDir)/$(TOOLSCRIPTNAME) + +all-local:: $(ToolBuildPath) + +$(ToolBuildPath): $(ToolDir)/.dir + +$(ToolBuildPath): $(TOOLSCRIPTNAME) + $(Echo) Copying $(BuildMode) script $(TOOLSCRIPTNAME) + $(Verb) $(CP) -f $(TOOLSCRIPTNAME) "$@" + $(Verb) chmod 0755 "$@" + +ifdef NO_INSTALL +install-local:: + $(Echo) Install circumvented with NO_INSTALL +uninstall-local:: + $(Echo) Uninstall circumvented with NO_INSTALL +else +DestTool = $(PROJ_bindir)/$(TOOLSCRIPTNAME) + +install-local:: $(DestTool) + +$(DestTool): $(ToolBuildPath) $(PROJ_bindir) + $(Echo) Installing $(BuildMode) $(DestTool) + $(Verb) $(ProgInstall) $(ToolBuildPath) $(DestTool) + +uninstall-local:: + $(Echo) Uninstalling $(BuildMode) $(DestTool) + -$(Verb) $(RM) -f $(DestTool) +endif diff --git a/tools/klee-bout-tool/klee-bout-tool b/tools/klee-bout-tool/klee-bout-tool new file mode 100755 index 00000000..19c9e893 --- /dev/null +++ b/tools/klee-bout-tool/klee-bout-tool @@ -0,0 +1,106 @@ +#!/usr/bin/python + +import os +import struct +import sys + +version_no=2 + +class BOutError(Exception): + pass + +class BOut: + @staticmethod + def fromfile(path): + if not os.path.exists(path): + print "ERROR: file %s not found" % (path) + sys.exit(1) + + f = open(path,'rb') + hdr = f.read(5) + if len(hdr)!=5 or hdr!='BOUT\n': + raise BOutError,'unrecognized file' + version, = struct.unpack('>i', f.read(4)) + if version > version_no: + raise BOutError,'unrecognized version' + numArgs, = struct.unpack('>i', f.read(4)) + args = [] + for i in range(numArgs): + size, = struct.unpack('>i', f.read(4)) + args.append(f.read(size)) + + if version >= 2: + symArgvs, = struct.unpack('>i', f.read(4)) + symArgvLen, = struct.unpack('>i', f.read(4)) + else: + symArgvs = 0 + symArgvLen = 0 + + numObjects, = struct.unpack('>i', f.read(4)) + objects = [] + for i in range(numObjects): + size, = struct.unpack('>i', f.read(4)) + name = f.read(size) + size, = struct.unpack('>i', f.read(4)) + bytes = f.read(size) + objects.append( (name,bytes) ) + + # Create an instance + b = BOut(version, args, symArgvs, symArgvLen, objects) + # Augment with extra filename field + b.filename = path + return b + + def __init__(self, version, args, symArgvs, symArgvLen, objects): + self.version = version + self.symArgvs = symArgvs + self.symArgvLen = symArgvLen + self.args = args + self.objects = objects + + # add a field that represents the name of the program used to + # generate this .bout file: + program_full_path = self.args[0] + program_name = os.path.basename(program_full_path) + # sometimes program names end in .bc, so strip them + if program_name.endswith('.bc'): + program_name = program_name[:-3] + self.programName = program_name + +def trimZeros(str): + for i in range(len(str))[::-1]: + if str[i] != '\x00': + return str[:i+1] + return '' + +def main(args): + from optparse import OptionParser + op = OptionParser("usage: %prog [options] files") + op.add_option('','--trim-zeros', dest='trimZeros', action='store_true', + default=False, + help='trim trailing zeros') + + opts,args = op.parse_args() + if not args: + op.error("incorrect number of arguments") + + for file in args: + b = BOut.fromfile(file) + pos = 0 + print 'bout file : %r' % file + print 'args : %r' % b.args + print 'num objects: %r' % len(b.objects) + for i,(name,data) in enumerate(b.objects): + if opts.trimZeros: + str = trimZeros(data) + else: + str = data + + print 'object %4d: name: %r' % (i, name) + print 'object %4d: size: %r' % (i, len(data)) + print 'object %4d: data: %r' % (i, str) + if file != args[-1]: + print + +if __name__=='__main__': + main(sys.argv) diff --git a/tools/klee/Debug.cpp b/tools/klee/Debug.cpp new file mode 100644 index 00000000..d11a79a7 --- /dev/null +++ b/tools/klee/Debug.cpp @@ -0,0 +1,12 @@ +#include <klee/Expr.h> +#include <iostream> + +void kdb_printExpr(klee::Expr *e) { + llvm::cerr << "expr: " << e << " -- "; + if (e) { + llvm::cerr << *e; + } else { + llvm::cerr << "(null)"; + } + llvm::cerr << "\n"; +} diff --git a/tools/klee/Makefile b/tools/klee/Makefile new file mode 100644 index 00000000..b556175d --- /dev/null +++ b/tools/klee/Makefile @@ -0,0 +1,22 @@ +#===-- tools/klee/Makefile ---------------------------------*- Makefile -*--===# +# +# The KLEE Symbolic Virtual Machine +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +LEVEL=../.. +TOOLNAME = klee +USEDLIBS = kleeCore.a kleeModule.a kleaverSolver.a kleaverExpr.a kleeSupport.a kleeBasic.a +LINK_COMPONENTS = bitreader bitwriter ipo linker engine + +include $(LEVEL)/Makefile.common + +ifeq ($(ENABLE_STPLOG), 1) + LIBS += -lstplog +endif + +LIBS += -lstp +#-ltcmalloc diff --git a/tools/klee/main.cpp b/tools/klee/main.cpp new file mode 100644 index 00000000..39b208ec --- /dev/null +++ b/tools/klee/main.cpp @@ -0,0 +1,1422 @@ +/* -*- mode: c++; c-basic-offset: 2; -*- */ + +// FIXME: This does not belong here. +#include "../lib/Core/Common.h" + +#include "klee/ExecutionState.h" +#include "klee/Expr.h" +#include "klee/Interpreter.h" +#include "klee/Statistics.h" +#include "klee/Config/config.h" +#include "klee/Internal/ADT/BOut.h" +#include "klee/Internal/ADT/TreeStream.h" +#include "klee/Internal/Support/ModuleUtil.h" +#include "klee/Internal/System/Time.h" + +#include "llvm/Constants.h" +#include "llvm/Module.h" +#include "llvm/ModuleProvider.h" +#include "llvm/Type.h" +#include "llvm/InstrTypes.h" +#include "llvm/Instruction.h" +#include "llvm/Instructions.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/System/Signals.h" +#include <iostream> +#include <fstream> +#include <cerrno> +#include <dirent.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <signal.h> + +#include <iostream> +#include <iterator> +#include <fstream> +#include <sstream> + +using namespace llvm; +using namespace klee; + +namespace { + cl::opt<std::string> + InputFile(cl::desc("<input bytecode>"), cl::Positional, cl::init("-")); + + cl::opt<std::string> + RunInDir("run-in", cl::desc("Change to the given directory prior to executing")); + + cl::opt<std::string> + Environ("environ", cl::desc("Parse environ from given file (in \"env\" format)")); + + cl::list<std::string> + InputArgv(cl::ConsumeAfter, + cl::desc("<program arguments>...")); + + cl::opt<bool> + NoOutput("no-output", + cl::desc("Don't generate test files")); + + cl::opt<bool> + WarnAllExternals("warn-all-externals", + cl::desc("Give initial warning for all externals.")); + + cl::opt<bool> + WriteCVCs("write-cvcs", + cl::desc("Write .cvc files for each test case")); + + cl::opt<bool> + WritePCs("write-pcs", + cl::desc("Write .pc files for each test case")); + + cl::opt<bool> + WriteCov("write-cov", + cl::desc("Write coverage information for each test case")); + + cl::opt<bool> + WriteTestInfo("write-test-info", + cl::desc("Write additional test case information")); + + cl::opt<bool> + WritePaths("write-paths", + cl::desc("Write .path files for each test case")); + + cl::opt<bool> + WriteSymPaths("write-sym-paths", + cl::desc("Write .sym.path files for each test case")); + + cl::opt<bool> + ExitOnError("exit-on-error", + cl::desc("Exit if errors occur")); + + + enum LibcType { + NoLibc, KleeLibc, UcLibc + }; + + cl::opt<LibcType> + Libc("libc", + cl::desc("Choose libc version (none by default)."), + cl::values(clEnumValN(NoLibc, "none", "Don't link in a libc"), + clEnumValN(KleeLibc, "klee", "Link in klee libc"), + clEnumValN(UcLibc, "uclibc", "Link in uclibc (adapted for klee)"), + clEnumValEnd), + cl::init(NoLibc)); + + + cl::opt<bool> + WithPOSIXRuntime("posix-runtime", + cl::desc("Link with POSIX runtime"), + cl::init(false)); + + cl::opt<bool> + OptimizeModule("optimize", + cl::desc("Optimize before execution")); + + cl::opt<bool> + CheckDivZero("check-div-zero", + cl::desc("Inject checks for division-by-zero"), + cl::init(true)); + + cl::opt<std::string> + OutputDir("output-dir", + cl::desc("Directory to write results in (defaults to klee-out-N)"), + cl::init("")); + + // this is a fake entry, its automagically handled + cl::list<std::string> + ReadArgsFilesFake("read-args", + cl::desc("File to read arguments from (one arg per line)")); + + cl::opt<bool> + ReplayKeepSymbolic("replay-keep-symbolic", + cl::desc("Replay the test cases only by asserting" + "the bytes, not necessarily making them concrete.")); + + cl::list<std::string> + ReplayOutFile("replay-out", + cl::desc("Specify an out file to replay"), + cl::value_desc("out file")); + + cl::list<std::string> + ReplayOutDir("replay-out-dir", + cl::desc("Specify a directory to replay .out files from"), + cl::value_desc("output directory")); + + cl::opt<std::string> + ReplayPathFile("replay-path", + cl::desc("Specify a path file to replay"), + cl::value_desc("path file")); + + cl::list<std::string> + SeedOutFile("seed-out"); + + cl::list<std::string> + SeedOutDir("seed-out-dir"); + + cl::opt<unsigned> + MakeConcreteSymbolic("make-concrete-symbolic", + cl::desc("Rate at which to make concrete reads symbolic (0=off)"), + cl::init(0)); + + cl::opt<bool> + InitEnv("init-env", + cl::desc("Create custom environment. Options that can be passed as arguments to the programs are: --sym-argv <max-len> --sym-argvs <min-argvs> <max-argvs> <max-len> + file model options")); + + cl::opt<unsigned> + StopAfterNTests("stop-after-n-tests", + cl::desc("Stop execution after generating the given number of tests. Extra tests corresponding to partially explored paths will also be dumped."), + cl::init(0)); + + cl::opt<bool> + Watchdog("watchdog", + cl::desc("Use a watchdog process to enforce --max-time."), + cl::init(0)); +} + +extern bool WriteTraces; +extern cl::opt<double> MaxTime; + +/***/ + +class KleeHandler : public InterpreterHandler { +private: + Interpreter *m_interpreter; + TreeStreamWriter *m_pathWriter, *m_symPathWriter; + std::ostream *m_infoFile; + + char m_outputDirectory[1024]; + unsigned m_testIndex; // number of tests written so far + unsigned m_pathsExplored; // number of paths explored so far + + // used for writing .bout files + int m_argc; + char **m_argv; + +public: + KleeHandler(int argc, char **argv); + ~KleeHandler(); + + std::ostream &getInfoStream() const { return *m_infoFile; } + unsigned getNumTestCases() { return m_testIndex; } + unsigned getNumPathsExplored() { return m_pathsExplored; } + void incPathsExplored() { m_pathsExplored++; } + + void setInterpreter(Interpreter *i); + + void processTestCase(const ExecutionState &state, + const char *errorMessage, + const char *errorSuffix); + + std::string getOutputFilename(const std::string &filename); + std::ostream *openOutputFile(const std::string &filename); + std::string getTestFilename(const std::string &suffix, unsigned id); + std::ostream *openTestFile(const std::string &suffix, unsigned id); + + // load a .out file + static void loadOutFile(std::string name, + std::vector<unsigned char> &buffer); + + // load a .path file + static void loadPathFile(std::string name, + std::vector<bool> &buffer); + + static void getOutFiles(std::string path, + std::vector<std::string> &results); +}; + +KleeHandler::KleeHandler(int argc, char **argv) + : m_interpreter(0), + m_pathWriter(0), + m_symPathWriter(0), + m_infoFile(0), + m_testIndex(0), + m_pathsExplored(0), + m_argc(argc), + m_argv(argv) { + std::string theDir; + + if (OutputDir=="") { + llvm::sys::Path directory(InputFile); + std::string dirname = ""; + directory.eraseComponent(); + + if (directory.isEmpty()) + directory.set("."); + + for (int i = 0; ; i++) { + char buf[256], tmp[64]; + sprintf(tmp, "klee-out-%d", i); + dirname = tmp; + sprintf(buf, "%s/%s", directory.c_str(), tmp); + theDir = buf; + + if (DIR *dir = opendir(theDir.c_str())) { + closedir(dir); + } else { + break; + } + } + + llvm::cerr << "KLEE: output directory = \"" << dirname << "\"\n"; + + llvm::sys::Path klee_last(directory); + klee_last.appendComponent("klee-last"); + + if ((unlink(klee_last.c_str()) < 0) && (errno != ENOENT)) { + perror("Cannot unlink klee-last"); + assert(0 && "exiting."); + } + + if (symlink(dirname.c_str(), klee_last.c_str()) < 0) { + perror("Cannot make symlink"); + assert(0 && "exiting."); + } + } else { + theDir = OutputDir; + } + + sys::Path p(theDir); + if (!p.isAbsolute()) { + sys::Path cwd = sys::Path::GetCurrentDirectory(); + cwd.appendComponent(theDir); + p = cwd; + } + strcpy(m_outputDirectory, p.c_str()); + + if (mkdir(m_outputDirectory, 0775) < 0) { + llvm::cerr << "KLEE: ERROR: Unable to make output directory: \"" + << m_outputDirectory + << "\", refusing to overwrite.\n"; + exit(1); + } + + char fname[1024]; + snprintf(fname, sizeof(fname), "%s/warnings.txt", m_outputDirectory); + klee_warning_file = fopen(fname, "w"); + assert(klee_warning_file); + + snprintf(fname, sizeof(fname), "%s/messages.txt", m_outputDirectory); + klee_message_file = fopen(fname, "w"); + assert(klee_message_file); + + m_infoFile = openOutputFile("info"); +} + +KleeHandler::~KleeHandler() { + if (m_pathWriter) delete m_pathWriter; + if (m_symPathWriter) delete m_symPathWriter; + delete m_infoFile; +} + +void KleeHandler::setInterpreter(Interpreter *i) { + m_interpreter = i; + + if (WritePaths) { + m_pathWriter = new TreeStreamWriter(getOutputFilename("paths.ts")); + assert(m_pathWriter->good()); + m_interpreter->setPathWriter(m_pathWriter); + } + + if (WriteSymPaths) { + m_symPathWriter = new TreeStreamWriter(getOutputFilename("symPaths.ts")); + assert(m_symPathWriter->good()); + m_interpreter->setSymbolicPathWriter(m_symPathWriter); + } +} + +std::string KleeHandler::getOutputFilename(const std::string &filename) { + char outfile[1024]; + sprintf(outfile, "%s/%s", m_outputDirectory, filename.c_str()); + return outfile; +} + +std::ostream *KleeHandler::openOutputFile(const std::string &filename) { + std::ios::openmode io_mode = std::ios::out | std::ios::trunc + | std::ios::binary; + std::ostream *f; + std::string path = getOutputFilename(filename); + f = new std::ofstream(path.c_str(), io_mode); + if (!f) { + klee_warning("out of memory"); + } else if (!f->good()) { + klee_warning("error opening: %s", filename.c_str()); + delete f; + f = NULL; + } + + return f; +} + +std::string KleeHandler::getTestFilename(const std::string &suffix, unsigned id) { + char filename[1024]; + sprintf(filename, "test%06d.%s", id, suffix.c_str()); + return getOutputFilename(filename); +} + +std::ostream *KleeHandler::openTestFile(const std::string &suffix, unsigned id) { + char filename[1024]; + sprintf(filename, "test%06d.%s", id, suffix.c_str()); + return openOutputFile(filename); +} + + +/* Outputs all files (.bout, .pc, .cov etc.) describing a test case */ +void KleeHandler::processTestCase(const ExecutionState &state, + const char *errorMessage, + const char *errorSuffix) { + if (errorMessage && ExitOnError) { + llvm::cerr << "EXITING ON ERROR: " << errorMessage << "\n"; + abort(); + } + + if (!NoOutput) { + std::vector< std::pair<std::string, std::vector<unsigned char> > > out; + bool success = m_interpreter->getSymbolicSolution(state, out); + + if (!success) + klee_warning("unable to get symbolic solution, losing test case"); + + double start_time = util::getWallTime(); + + unsigned id = ++m_testIndex; + + if (success) { + BOut b; + b.numArgs = m_argc; + b.args = m_argv; + b.symArgvs = 0; + b.symArgvLen = 0; + b.numObjects = out.size(); + b.objects = new BOutObject[b.numObjects]; + assert(b.objects); + for (unsigned i=0; i<b.numObjects; i++) { + BOutObject *o = &b.objects[i]; + o->name = const_cast<char*>(out[i].first.c_str()); + o->numBytes = out[i].second.size(); + o->bytes = new unsigned char[o->numBytes]; + assert(o->bytes); + std::copy(out[i].second.begin(), out[i].second.end(), o->bytes); + } + + if (!bOut_toFile(&b, getTestFilename("bout", id).c_str())) { + klee_warning("unable to write output test case, losing it"); + } + + for (unsigned i=0; i<b.numObjects; i++) + delete[] b.objects[i].bytes; + delete[] b.objects; + } + + if (errorMessage) { + std::ostream *f = openTestFile(errorSuffix, id); + *f << errorMessage; + delete f; + } + + if (m_pathWriter) { + std::vector<unsigned char> concreteBranches; + m_pathWriter->readStream(m_interpreter->getPathStreamID(state), + concreteBranches); + std::ostream *f = openTestFile("path", id); + std::copy(concreteBranches.begin(), concreteBranches.end(), + std::ostream_iterator<unsigned char>(*f, "\n")); + delete f; + } + + if (errorMessage || WritePCs) { + std::string constraints; + m_interpreter->getConstraintLog(state, constraints); + std::ostream *f = openTestFile("pc", id); + *f << constraints; + delete f; + } + + if (WriteCVCs) { + std::string constraints; + m_interpreter->getConstraintLog(state, constraints, true); + std::ostream *f = openTestFile("cvc", id); + *f << constraints; + delete f; + } + + if (m_symPathWriter) { + std::vector<unsigned char> symbolicBranches; + m_symPathWriter->readStream(m_interpreter->getSymbolicPathStreamID(state), + symbolicBranches); + std::ostream *f = openTestFile("sym.path", id); + std::copy(symbolicBranches.begin(), symbolicBranches.end(), + std::ostream_iterator<unsigned char>(*f, "\n")); + delete f; + } + + if (WriteCov) { + std::map<const std::string*, std::set<unsigned> > cov; + m_interpreter->getCoveredLines(state, cov); + std::ostream *f = openTestFile("cov", id); + for (std::map<const std::string*, std::set<unsigned> >::iterator + it = cov.begin(), ie = cov.end(); + it != ie; ++it) { + for (std::set<unsigned>::iterator + it2 = it->second.begin(), ie = it->second.end(); + it2 != ie; ++it2) + *f << *it->first << ":" << *it2 << "\n"; + } + delete f; + } + + if (WriteTraces) { + std::ostream *f = openTestFile("trace", id); + state.exeTraceMgr.printAllEvents(*f); + delete f; + } + + if (m_testIndex == StopAfterNTests) + m_interpreter->setHaltExecution(true); + + if (WriteTestInfo) { + double elapsed_time = util::getWallTime() - start_time; + std::ostream *f = openTestFile("info", id); + *f << "Time to generate test case: " + << elapsed_time << "s\n"; + delete f; + } + } +} + + // load a .path file +void KleeHandler::loadPathFile(std::string name, + std::vector<bool> &buffer) { + std::ifstream f(name.c_str(), std::ios::in | std::ios::binary); + + if (!f.good()) + assert(0 && "unable to open path file"); + + while (f.good()) { + unsigned value; + f >> value; + buffer.push_back(!!value); + f.get(); + } +} + +void KleeHandler::getOutFiles(std::string path, + std::vector<std::string> &results) { + llvm::sys::Path p(path); + std::set<llvm::sys::Path> contents; + std::string error; + if (p.getDirectoryContents(contents, &error)) { + llvm::cerr << "ERROR: unable to read output directory: " << path << ": " << error << "\n"; + exit(1); + } + for (std::set<llvm::sys::Path>::iterator it = contents.begin(), + ie = contents.end(); it != ie; ++it) { + std::string f = it->toString(); + if (f.substr(f.size()-5,f.size()) == ".bout") { + results.push_back(f); + } + } +} + +//===----------------------------------------------------------------------===// +// main Driver function +// +#if ENABLE_STPLOG == 1 +extern "C" void STPLOG_init(const char *); +#endif + +static std::string strip(std::string &in) { + unsigned len = in.size(); + unsigned lead = 0, trail = len; + while (lead<len && isspace(in[lead])) + ++lead; + while (trail>lead && isspace(in[trail-1])) + --trail; + return in.substr(lead, trail-lead); +} + +static void readArgumentsFromFile(char *file, std::vector<std::string> &results) { + std::ifstream f(file); + assert(f.is_open() && "unable to open input for reading arguments"); + while (!f.eof()) { + std::string line; + std::getline(f, line); + line = strip(line); + if (!line.empty()) + results.push_back(line); + } + f.close(); +} + +static void parseArguments(int argc, char **argv) { + std::vector<std::string> arguments; + + for (int i=1; i<argc; i++) { + if (!strcmp(argv[i],"--read-args") && i+1<argc) { + readArgumentsFromFile(argv[++i], arguments); + } else { + arguments.push_back(argv[i]); + } + } + + int numArgs = arguments.size() + 1; + const char **argArray = new const char*[numArgs+1]; + argArray[0] = argv[0]; + argArray[numArgs] = 0; + for (int i=1; i<numArgs; i++) { + argArray[i] = arguments[i-1].c_str(); + } + + cl::ParseCommandLineOptions(numArgs, (char**) argArray, " klee\n"); + delete[] argArray; +} + + + +static int initEnv(Module *mainModule) { + + /* + nArgcP = alloc oldArgc->getType() + nArgvV = alloc oldArgv->getType() + store oldArgc nArgcP + store oldArgv nArgvP + klee_init_environment(nArgcP, nArgvP) + nArgc = load nArgcP + nArgv = load nArgvP + oldArgc->replaceAllUsesWith(nArgc) + oldArgv->replaceAllUsesWith(nArgv) + */ + + Function *mainFn = mainModule->getFunction("main"); + + if (mainFn->arg_size() < 2) { + llvm::cerr << "Cannot handle ""-init-env"" when main() has less than two arguments.\n"; + return -1; + } + + Instruction* firstInst = mainFn->begin()->begin(); + + Value* oldArgc = mainFn->arg_begin(); + Value* oldArgv = ++mainFn->arg_begin(); + + AllocaInst* argcPtr = new AllocaInst(oldArgc->getType(), "argcPtr", firstInst); + AllocaInst* argvPtr = new AllocaInst(oldArgv->getType(), "argvPtr", firstInst); + + /* Insert void klee_init_env(int* argc, char*** argv) */ + std::vector<const Type*> params; + params.push_back(Type::Int32Ty); + params.push_back(Type::Int32Ty); + Function* initEnvFn = + cast<Function>(mainModule->getOrInsertFunction("klee_init_env", + Type::VoidTy, + argcPtr->getType(), + argvPtr->getType(), + NULL)); + assert(initEnvFn); + std::vector<Value*> args; + args.push_back(argcPtr); + args.push_back(argvPtr); + Instruction* initEnvCall = CallInst::Create(initEnvFn, args.begin(), args.end(), + "", firstInst); + Value *argc = new LoadInst(argcPtr, "newArgc", firstInst); + Value *argv = new LoadInst(argvPtr, "newArgv", firstInst); + + oldArgc->replaceAllUsesWith(argc); + oldArgv->replaceAllUsesWith(argv); + + new StoreInst(oldArgc, argcPtr, initEnvCall); + new StoreInst(oldArgv, argvPtr, initEnvCall); + + return 0; +} + + +// This is a terrible hack until we get some real modelling of the +// system. All we do is check the undefined symbols and m and warn about +// any "unrecognized" externals and about any obviously unsafe ones. + +// Symbols we explicitly support +static const char *modelledExternals[] = { + "_ZTVN10__cxxabiv117__class_type_infoE", + "_ZTVN10__cxxabiv120__si_class_type_infoE", + "_ZTVN10__cxxabiv121__vmi_class_type_infoE", + + // special functions + "_assert", + "__assert_fail", + "__assert_rtn", + "calloc", + "_exit", + "exit", + "free", + "abort", + "klee_abort", + "klee_assume", + "klee_check_memory_access", + "klee_define_fixed_object", + "klee_get_errno", + "klee_get_value", + "klee_get_obj_size", + "klee_is_symbolic", + "klee_make_symbolic_name", + "klee_mark_global", + "klee_malloc_n", + "klee_merge", + "klee_prefer_cex", + "klee_print_expr", + "klee_print_range", + "klee_report_error", + "klee_revirt_objects", + "klee_set_forking", + "klee_silent_exit", + "klee_under_constrained", + "klee_warning", + "klee_warning_once", + "klee_alias_function", + "llvm.dbg.stoppoint", + "llvm.va_start", + "llvm.va_end", + "malloc", + "realloc", + "_ZdaPv", + "_ZdlPv", + "_Znaj", + "_Znwj", + "_Znam", + "_Znwm", +}; +// Symbols we aren't going to warn about +static const char *dontCareExternals[] = { +#if 0 + // stdio + "fprintf", + "fflush", + "fopen", + "fclose", + "fputs_unlocked", + "putchar_unlocked", + "vfprintf", + "fwrite", + "puts", + "printf", + "stdin", + "stdout", + "stderr", + "_stdio_term", + "__errno_location", + "fstat", +#endif + + // static information, pretty ok to return + "getegid", + "geteuid", + "getgid", + "getuid", + "getpid", + "gethostname", + "getpgrp", + "getppid", + "getpagesize", + "getpriority", + "getgroups", + "getdtablesize", + "getrlimit", + "getrlimit64", + "getcwd", + "getwd", + "gettimeofday", + "uname", + + // fp stuff we just don't worry about yet + "frexp", + "ldexp", + "__isnan", + "__signbit", +}; +// Extra symbols we aren't going to warn about with klee-libc +static const char *dontCareKlee[] = { + "__ctype_b_loc", + "__ctype_get_mb_cur_max", + + // io system calls + "open", + "write", + "read", + "close", +}; +// Extra symbols we aren't going to warn about with uclibc +static const char *dontCareUclibc[] = { + "__dso_handle", + + // Don't warn about these since we explicitly commented them out of + // uclibc. + "printf", + "vprintf" +}; +// Symbols we consider unsafe +static const char *unsafeExternals[] = { + "fork", // oh lord + "exec", // heaven help us + "error", // calls _exit + "raise", // yeah + "kill", // mmmhmmm +}; +#define NELEMS(array) (sizeof(array)/sizeof(array[0])) +void externalsAndGlobalsCheck(const Module *m) { + std::map<std::string, bool> externals; + std::set<std::string> modelled(modelledExternals, + modelledExternals+NELEMS(modelledExternals)); + std::set<std::string> dontCare(dontCareExternals, + dontCareExternals+NELEMS(dontCareExternals)); + std::set<std::string> unsafe(unsafeExternals, + unsafeExternals+NELEMS(unsafeExternals)); + + switch (Libc) { + case KleeLibc: + dontCare.insert(dontCareKlee, dontCareKlee+NELEMS(dontCareKlee)); + break; + case UcLibc: + dontCare.insert(dontCareUclibc, + dontCareUclibc+NELEMS(dontCareUclibc)); + break; + case NoLibc: /* silence compiler warning */ + break; + } + + + if (WithPOSIXRuntime) + dontCare.insert("syscall"); + + for (Module::const_iterator fnIt = m->begin(), fn_ie = m->end(); + fnIt != fn_ie; ++fnIt) { + if (fnIt->isDeclaration() && !fnIt->use_empty()) + externals.insert(std::make_pair(fnIt->getName(), false)); + for (Function::const_iterator bbIt = fnIt->begin(), bb_ie = fnIt->end(); + bbIt != bb_ie; ++bbIt) { + for (BasicBlock::const_iterator it = bbIt->begin(), ie = bbIt->end(); + it != it; ++it) { + if (const CallInst *ci = dyn_cast<CallInst>(it)) { + if (isa<InlineAsm>(ci->getCalledValue())) { + klee_warning_once(&*fnIt, + "function \"%s\" has inline asm", + fnIt->getName().c_str()); + } + } + } + } + } + for (Module::const_global_iterator + it = m->global_begin(), ie = m->global_end(); + it != ie; ++it) + if (it->isDeclaration() && !it->use_empty()) + externals.insert(std::make_pair(it->getName(), true)); + // and remove aliases (they define the symbol after global + // initialization) + for (Module::const_alias_iterator + it = m->alias_begin(), ie = m->alias_end(); + it != ie; ++it) { + std::map<std::string, bool>::iterator it2 = + externals.find(it->getName()); + if (it2!=externals.end()) + externals.erase(it2); + } + + std::map<std::string, bool> foundUnsafe; + for (std::map<std::string, bool>::iterator + it = externals.begin(), ie = externals.end(); + it != ie; ++it) { + const std::string &ext = it->first; + if (!modelled.count(ext) && (WarnAllExternals || + !dontCare.count(ext))) { + if (unsafe.count(ext)) { + foundUnsafe.insert(*it); + } else { + klee_warning("undefined reference to %s: %s", + it->second ? "variable" : "function", + ext.c_str()); + } + } + } + + for (std::map<std::string, bool>::iterator + it = foundUnsafe.begin(), ie = foundUnsafe.end(); + it != ie; ++it) { + const std::string &ext = it->first; + klee_warning("undefined reference to %s: %s (UNSAFE)!", + it->second ? "variable" : "function", + ext.c_str()); + } +} + +static Interpreter *theInterpreter = 0; + +static bool interrupted = false; + +// Pulled out so it can be easily called from a debugger. +extern "C" +void halt_execution() { + theInterpreter->setHaltExecution(true); +} + +extern "C" +void stop_forking() { + theInterpreter->setInhibitForking(true); +} + +static void interrupt_handle() { + if (!interrupted && theInterpreter) { + llvm::cerr << "KLEE: ctrl-c detected, requesting interpreter to halt.\n"; + halt_execution(); + sys::SetInterruptFunction(interrupt_handle); + } else { + llvm::cerr << "KLEE: ctrl-c detected, exiting.\n"; + exit(1); + } + interrupted = true; +} + +// This is a temporary hack. If the running process has access to +// externals then it can disable interrupts, which screws up the +// normal "nice" watchdog termination process. We try to request the +// interpreter to halt using this mechanism as a last resort to save +// the state data before going ahead and killing it. +static void halt_via_gdb(int pid) { + char buffer[256]; + sprintf(buffer, + "gdb --batch --eval-command=\"p halt_execution()\" " + "--eval-command=detach --pid=%d &> /dev/null", + pid); + // fprintf(stderr, "KLEE: WATCHDOG: running: %s\n", buffer); + if (system(buffer)==-1) + perror("system"); +} + +// returns the end of the string put in buf +static char *format_tdiff(char *buf, long seconds) +{ + assert(seconds >= 0); + + long minutes = seconds / 60; seconds %= 60; + long hours = minutes / 60; minutes %= 60; + long days = hours / 24; hours %= 24; + + buf = strrchr(buf, '\0'); + if (days > 0) buf += sprintf(buf, "%ld days, ", days); + buf += sprintf(buf, "%02ld:%02ld:%02ld", hours, minutes, seconds); + return buf; +} + +#ifndef KLEE_UCLIBC +static llvm::Module *linkWithUclibc(llvm::Module *mainModule) { + fprintf(stderr, "error: invalid libc, no uclibc support!\n"); + exit(1); + return 0; +} +#else +static llvm::Module *linkWithUclibc(llvm::Module *mainModule) { + Function *f; + // force import of __uClibc_main + mainModule->getOrInsertFunction("__uClibc_main", + FunctionType::get(Type::VoidTy, + std::vector<const Type*>(), + true)); + + // force various imports + if (WithPOSIXRuntime) { + mainModule->getOrInsertFunction("realpath", + PointerType::getUnqual(Type::Int8Ty), + PointerType::getUnqual(Type::Int8Ty), + PointerType::getUnqual(Type::Int8Ty), + NULL); + mainModule->getOrInsertFunction("getutent", + PointerType::getUnqual(Type::Int8Ty), + NULL); + mainModule->getOrInsertFunction("__fgetc_unlocked", + Type::Int32Ty, + PointerType::getUnqual(Type::Int8Ty), + NULL); + mainModule->getOrInsertFunction("__fputc_unlocked", + Type::Int32Ty, + Type::Int32Ty, + PointerType::getUnqual(Type::Int8Ty), + NULL); + } + + f = mainModule->getFunction("__ctype_get_mb_cur_max"); + if (f) f->setName("_stdlib_mb_cur_max"); + + // Strip of asm prefixes for 64 bit versions because they are not + // present in uclibc and we want to make sure stuff will get + // linked. In the off chance that both prefixed and unprefixed + // versions are present in the module, make sure we don't create a + // naming conflict. + for (Module::iterator fi = mainModule->begin(), fe = mainModule->end(); + fi != fe;) { + Function *f = fi; + ++fi; + const std::string &name = f->getName(); + if (name[0]=='\01') { + unsigned size = name.size(); + if (name[size-2]=='6' && name[size-1]=='4') { + std::string unprefixed = name.substr(1); + + // See if the unprefixed version exists. + if (Function *f2 = mainModule->getFunction(unprefixed)) { + f->replaceAllUsesWith(f2); + f->eraseFromParent(); + } else { + f->setName(unprefixed); + } + } + } + } + + mainModule = klee::linkWithLibrary(mainModule, + KLEE_UCLIBC "/lib/libc.a"); + assert(mainModule && "unable to link with uclibc"); + + // more sighs, this is horrible but just a temp hack + // f = mainModule->getFunction("__fputc_unlocked"); + // if (f) f->setName("fputc_unlocked"); + // f = mainModule->getFunction("__fgetc_unlocked"); + // if (f) f->setName("fgetc_unlocked"); + + Function *f2; + f = mainModule->getFunction("open"); + f2 = mainModule->getFunction("__libc_open"); + if (f2) { + if (f) { + f2->replaceAllUsesWith(f); + f2->eraseFromParent(); + } else { + f2->setName("open"); + assert(f2->getName() == "open"); + } + } + + f = mainModule->getFunction("fcntl"); + f2 = mainModule->getFunction("__libc_fcntl"); + if (f2) { + if (f) { + f2->replaceAllUsesWith(f); + f2->eraseFromParent(); + } else { + f2->setName("fcntl"); + assert(f2->getName() == "fcntl"); + } + } + + // XXX we need to rearchitect so this can also be used with + // programs externally linked with uclibc. + + // We now need to swap things so that __uClibc_main is the entry + // point, in such a way that the arguments are passed to + // __uClibc_main correctly. We do this by renaming the user main + // and generating a stub function to call __uClibc_main. There is + // also an implicit cooperation in that runFunctionAsMain sets up + // the environment arguments to what uclibc expects (following + // argv), since it does not explicitly take an envp argument. + Function *userMainFn = mainModule->getFunction("main"); + assert(userMainFn && "unable to get user main"); + Function *uclibcMainFn = mainModule->getFunction("__uClibc_main"); + assert(uclibcMainFn && "unable to get uclibc main"); + userMainFn->setName("__user_main"); + + const FunctionType *ft = uclibcMainFn->getFunctionType(); + assert(ft->getNumParams() == 7); + + std::vector<const Type*> fArgs; + fArgs.push_back(ft->getParamType(1)); // argc + fArgs.push_back(ft->getParamType(2)); // argv + Function *stub = Function::Create(FunctionType::get(Type::Int32Ty, fArgs, false), + GlobalVariable::ExternalLinkage, + "main", + mainModule); + BasicBlock *bb = BasicBlock::Create("entry", stub); + + std::vector<llvm::Value*> args; + args.push_back(llvm::ConstantExpr::getBitCast(userMainFn, + ft->getParamType(0))); + args.push_back(stub->arg_begin()); // argc + args.push_back(++stub->arg_begin()); // argv + args.push_back(Constant::getNullValue(ft->getParamType(3))); // app_init + args.push_back(Constant::getNullValue(ft->getParamType(4))); // app_fini + args.push_back(Constant::getNullValue(ft->getParamType(5))); // rtld_fini + args.push_back(Constant::getNullValue(ft->getParamType(6))); // stack_end + CallInst::Create(uclibcMainFn, args.begin(), args.end(), "", bb); + + new UnreachableInst(bb); + + return mainModule; +} +#endif + +int main(int argc, char **argv, char **envp) { +#if ENABLE_STPLOG == 1 + STPLOG_init("stplog.c"); +#endif + + atexit(llvm_shutdown); // Call llvm_shutdown() on exit. + parseArguments(argc, argv); + sys::PrintStackTraceOnErrorSignal(); + + if (Watchdog) { + if (MaxTime==0) { + klee_error("--watchdog used without --max-time"); + } + + int pid = fork(); + if (pid<0) { + klee_error("unable to fork watchdog"); + } else if (pid) { + fprintf(stderr, "KLEE: WATCHDOG: watching %d\n", pid); + fflush(stderr); + + double nextStep = util::getWallTime() + MaxTime*1.1; + int level = 0; + + // Simple stupid code... + while (1) { + sleep(1); + + int status, res = waitpid(pid, &status, WNOHANG); + + if (res < 0) { + if (errno==ECHILD) { // No child, no need to watch but + // return error since we didn't catch + // the exit. + fprintf(stderr, "KLEE: watchdog exiting (no child)\n"); + return 1; + } else if (errno!=EINTR) { + perror("watchdog waitpid"); + exit(1); + } + } else if (res==pid && WIFEXITED(status)) { + return WEXITSTATUS(status); + } else { + double time = util::getWallTime(); + + if (time > nextStep) { + ++level; + + if (level==1) { + fprintf(stderr, "KLEE: WATCHDOG: time expired, attempting halt via INT\n"); + kill(pid, SIGINT); + } else if (level==2) { + fprintf(stderr, "KLEE: WATCHDOG: time expired, attempting halt via gdb\n"); + halt_via_gdb(pid); + } else { + fprintf(stderr, "KLEE: WATCHDOG: kill(9)ing child (I tried to be nice)\n"); + kill(pid, SIGKILL); + return 1; // what more can we do + } + + // Ideally this triggers a dump, which may take a while, + // so try and give the process extra time to clean up. + nextStep = util::getWallTime() + std::max(15., MaxTime*.1); + } + } + } + + return 0; + } + } + + sys::SetInterruptFunction(interrupt_handle); + + // Load the bytecode... + std::string ErrorMsg; + ModuleProvider *MP = 0; + if (MemoryBuffer *Buffer = MemoryBuffer::getFileOrSTDIN(InputFile, &ErrorMsg)) { + MP = getBitcodeModuleProvider(Buffer, &ErrorMsg); + if (!MP) delete Buffer; + } + + if (!MP) + klee_error("error loading program '%s': %s", InputFile.c_str(), ErrorMsg.c_str()); + + Module *mainModule = MP->materializeModule(); + MP->releaseModule(); + delete MP; + + assert(mainModule && "unable to materialize"); + + if (WithPOSIXRuntime) + InitEnv = true; + + if (InitEnv) { + int r = initEnv(mainModule); + if (r != 0) + return r; + } + + llvm::sys::Path LibraryDir(KLEE_DIR "/" RUNTIME_CONFIGURATION "/lib"); + Interpreter::ModuleOptions Opts(LibraryDir.c_str(), + /*Optimize=*/OptimizeModule, + /*CheckDivZero=*/CheckDivZero); + + switch (Libc) { + case NoLibc: /* silence compiler warning */ + break; + + case KleeLibc: { + // FIXME: Find a reasonable solution for this. + llvm::sys::Path Path(Opts.LibraryDir); + Path.appendComponent("libklee-libc.bca"); + mainModule = klee::linkWithLibrary(mainModule, Path.c_str()); + assert(mainModule && "unable to link with klee-libc"); + break; + } + + case UcLibc: + mainModule = linkWithUclibc(mainModule); + break; + } + + if (WithPOSIXRuntime) { + llvm::sys::Path Path(Opts.LibraryDir); + Path.appendComponent("libkleeRuntimePOSIX.bca"); + klee_message("NOTE: Using model: %s", Path.c_str()); + mainModule = klee::linkWithLibrary(mainModule, Path.c_str()); + assert(mainModule && "unable to link with simple model"); + } + + // Get the desired main function. klee_main initializes uClibc + // locale and other data and then calls main. + Function *mainFn = mainModule->getFunction("main"); + if (!mainFn) { + llvm::cerr << "'main' function not found in module.\n"; + return -1; + } + + // FIXME: Change me to std types. + int pArgc; + char **pArgv; + char **pEnvp; + if (Environ != "") { + std::vector<std::string> items; + std::ifstream f(Environ.c_str()); + if (!f.good()) + klee_error("unable to open --environ file: %s", Environ.c_str()); + while (!f.eof()) { + std::string line; + std::getline(f, line); + line = strip(line); + if (!line.empty()) + items.push_back(line); + } + f.close(); + pEnvp = new char *[items.size()+1]; + unsigned i=0; + for (; i != items.size(); ++i) + pEnvp[i] = strdup(items[i].c_str()); + pEnvp[i] = 0; + } else { + pEnvp = envp; + } + + pArgc = InputArgv.size() + 1; + pArgv = new char *[pArgc]; + for (unsigned i=0; i<InputArgv.size()+1; i++) { + std::string &arg = (i==0 ? InputFile : InputArgv[i-1]); + unsigned size = arg.size() + 1; + char *pArg = new char[size]; + + std::copy(arg.begin(), arg.end(), pArg); + pArg[size - 1] = 0; + + pArgv[i] = pArg; + } + + std::vector<bool> replayPath; + + if (ReplayPathFile != "") { + KleeHandler::loadPathFile(ReplayPathFile, replayPath); + } + + Interpreter::InterpreterOptions IOpts; + IOpts.MakeConcreteSymbolic = MakeConcreteSymbolic; + KleeHandler *handler = new KleeHandler(pArgc, pArgv); + Interpreter *interpreter = + theInterpreter = Interpreter::create(IOpts, handler); + handler->setInterpreter(interpreter); + + std::ostream &infoFile = handler->getInfoStream(); + for (int i=0; i<argc; i++) { + infoFile << argv[i] << (i+1<argc ? " ":"\n"); + } + infoFile << "PID: " << getpid() << "\n"; + + const Module *finalModule = + interpreter->setModule(mainModule, Opts); + externalsAndGlobalsCheck(finalModule); + + if (ReplayPathFile != "") { + interpreter->setReplayPath(&replayPath); + } + + char buf[256]; + time_t t[2]; + t[0] = time(NULL); + strftime(buf, sizeof(buf), "Started: %Y-%m-%d %H:%M:%S\n", localtime(&t[0])); + infoFile << buf; + infoFile.flush(); + + if (!ReplayOutDir.empty() || !ReplayOutFile.empty()) { + assert(SeedOutFile.empty()); + assert(SeedOutDir.empty()); + + std::vector<std::string> outFiles = ReplayOutFile; + for (std::vector<std::string>::iterator + it = ReplayOutDir.begin(), ie = ReplayOutDir.end(); + it != ie; ++it) + KleeHandler::getOutFiles(*it, outFiles); + std::vector<BOut*> bOuts; + for (std::vector<std::string>::iterator + it = outFiles.begin(), ie = outFiles.end(); + it != ie; ++it) { + BOut *out = bOut_fromFile(it->c_str()); + if (out) { + bOuts.push_back(out); + } else { + llvm::cerr << "KLEE: unable to open: " << *it << "\n"; + } + } + + if (RunInDir != "") { + int res = chdir(RunInDir.c_str()); + if (res < 0) { + klee_error("Unable to change directory to: %s", RunInDir.c_str()); + } + } + + unsigned i=0; + for (std::vector<BOut*>::iterator + it = bOuts.begin(), ie = bOuts.end(); + it != ie; ++it) { + BOut *out = *it; + interpreter->setReplayOut(out); + llvm::cerr << "KLEE: replaying: " << *it << " (" << bOut_numBytes(out) << " bytes)" + << " (" << ++i << "/" << outFiles.size() << ")\n"; + // XXX should put envp in .bout ? + interpreter->runFunctionAsMain(mainFn, out->numArgs, out->args, pEnvp); + if (interrupted) break; + } + interpreter->setReplayOut(0); + while (!bOuts.empty()) { + bOut_free(bOuts.back()); + bOuts.pop_back(); + } + } else { + std::vector<BOut *> seeds; + for (std::vector<std::string>::iterator + it = SeedOutFile.begin(), ie = SeedOutFile.end(); + it != ie; ++it) { + BOut *out = bOut_fromFile(it->c_str()); + if (!out) { + llvm::cerr << "KLEE: unable to open: " << *it << "\n"; + exit(1); + } + seeds.push_back(out); + } + for (std::vector<std::string>::iterator + it = SeedOutDir.begin(), ie = SeedOutDir.end(); + it != ie; ++it) { + std::vector<std::string> outFiles; + KleeHandler::getOutFiles(*it, outFiles); + for (std::vector<std::string>::iterator + it2 = outFiles.begin(), ie = outFiles.end(); + it2 != ie; ++it2) { + BOut *out = bOut_fromFile(it2->c_str()); + if (!out) { + llvm::cerr << "KLEE: unable to open: " << *it2 << "\n"; + exit(1); + } + seeds.push_back(out); + } + if (outFiles.empty()) { + llvm::cerr << "KLEE: seeds directory is empty: " << *it << "\n"; + exit(1); + } + } + + if (!seeds.empty()) { + llvm::cerr << "KLEE: using " << seeds.size() << " seeds\n"; + interpreter->useSeeds(&seeds); + } + if (RunInDir != "") { + int res = chdir(RunInDir.c_str()); + if (res < 0) { + klee_error("Unable to change directory to: %s", RunInDir.c_str()); + } + } + interpreter->runFunctionAsMain(mainFn, pArgc, pArgv, pEnvp); + + while (!seeds.empty()) { + bOut_free(seeds.back()); + seeds.pop_back(); + } + } + + t[1] = time(NULL); + strftime(buf, sizeof(buf), "Finished: %Y-%m-%d %H:%M:%S\n", localtime(&t[1])); + infoFile << buf; + + strcpy(buf, "Elapsed: "); + strcpy(format_tdiff(buf, t[1] - t[0]), "\n"); + infoFile << buf; + + // Free all the args. + for (unsigned i=0; i<InputArgv.size()+1; i++) + delete[] pArgv[i]; + delete[] pArgv; + + delete interpreter; + + uint64_t queries = + *theStatisticManager->getStatisticByName("Queries"); + uint64_t queriesValid = + *theStatisticManager->getStatisticByName("QueriesValid"); + uint64_t queriesInvalid = + *theStatisticManager->getStatisticByName("QueriesInvalid"); + uint64_t queryCounterexamples = + *theStatisticManager->getStatisticByName("QueriesCEX"); + uint64_t queryConstructs = + *theStatisticManager->getStatisticByName("QueriesConstructs"); + uint64_t instructions = + *theStatisticManager->getStatisticByName("Instructions"); + uint64_t forks = + *theStatisticManager->getStatisticByName("Forks"); + + handler->getInfoStream() + << "KLEE: done: explored paths = " << 1 + forks << "\n"; + + // Write some extra information in the info file which users won't + // necessarily care about or understand. + if (queries) + handler->getInfoStream() + << "KLEE: done: avg. constructs per query = " + << queryConstructs / queries << "\n"; + handler->getInfoStream() + << "KLEE: done: total queries = " << queries << "\n" + << "KLEE: done: valid queries = " << queriesValid << "\n" + << "KLEE: done: invalid queriers = " << queriesInvalid << "\n" + << "KLEE: done: query cex = " << queryCounterexamples << "\n"; + + std::stringstream stats; + stats << "KLEE: done: total instructions = " + << instructions << "\n"; + stats << "KLEE: done: completed paths = " + << handler->getNumPathsExplored() << "\n"; + stats << "KLEE: done: generated tests = " + << handler->getNumTestCases() << "\n"; + llvm::cerr << stats.str(); + handler->getInfoStream() << stats.str(); + + delete handler; + + return 0; +} diff --git a/unittests/Expr/ExprTest.cpp b/unittests/Expr/ExprTest.cpp new file mode 100644 index 00000000..9220c53e --- /dev/null +++ b/unittests/Expr/ExprTest.cpp @@ -0,0 +1,112 @@ +//===-- ExprTest.cpp ------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "klee/Expr.h" + +using namespace klee; + +namespace { + +ref<Expr> getConstant(int value, Expr::Width width) { + int64_t ext = value; + uint64_t trunc = ext & (((uint64_t) -1LL) >> (64 - width)); + return ConstantExpr::create(trunc, width); +} + +TEST(ExprTest, BasicConstruction) { + EXPECT_EQ(ref<Expr>(0, 32), + SubExpr::create(ref<Expr>(10, 32), + ref<Expr>(10, 32))); +} + +TEST(ExprTest, ConcatExtract) { + Array *array = new Array(0, 1, 256); + ref<Expr> read8 = Expr::createTempRead(array, 8); + Array *array2 = new Array(0, 2, 256); + ref<Expr> read8_2 = Expr::createTempRead(array2, 8); + ref<Expr> c100 = getConstant(100, 8); + + ref<Expr> concat1 = ConcatExpr::create4(read8, read8, c100, read8_2); + EXPECT_EQ(2U, concat1.getNumKids()); + EXPECT_EQ(2U, concat1.getKid(1).getNumKids()); + EXPECT_EQ(2U, concat1.getKid(1).getKid(1).getNumKids()); + + ref<Expr> extract1 = ExtractExpr::create(concat1, 8, 16); + EXPECT_EQ(Expr::Concat, extract1.getKind()); + EXPECT_EQ(read8, extract1.getKid(0)); + EXPECT_EQ(c100, extract1.getKid(1)); + + ref<Expr> extract2 = ExtractExpr::create(concat1, 6, 26); + EXPECT_EQ( Expr::Concat, extract2.getKind()); + EXPECT_EQ( read8, extract2.getKid(0)); + EXPECT_EQ( Expr::Concat, extract2.getKid(1).getKind()); + EXPECT_EQ( read8, extract2.getKid(1).getKid(0)); + EXPECT_EQ( Expr::Concat, extract2.getKid(1).getKid(1).getKind()); + EXPECT_EQ( c100, extract2.getKid(1).getKid(1).getKid(0)); + EXPECT_EQ( Expr::Extract, extract2.getKid(1).getKid(1).getKid(1).getKind()); + + ref<Expr> extract3 = ExtractExpr::create(concat1, 24, 1); + EXPECT_EQ(Expr::Extract, extract3.getKind()); + + ref<Expr> extract4 = ExtractExpr::create(concat1, 27, 2); + EXPECT_EQ(Expr::Extract, extract4.getKind()); + const ExtractExpr* tmp = dyn_ref_cast<ExtractExpr>(extract4); + EXPECT_EQ(3U, tmp->offset); + EXPECT_EQ(2U, tmp->getWidth()); + + ref<Expr> extract5 = ExtractExpr::create(concat1, 17, 5); + EXPECT_EQ(Expr::Extract, extract5.getKind()); + + ref<Expr> extract6 = ExtractExpr::create(concat1, 3, 26); + EXPECT_EQ(Expr::Concat, extract6.getKind()); + EXPECT_EQ(Expr::Extract, extract6.getKid(0).getKind()); + EXPECT_EQ(Expr::Concat, extract6.getKid(1).getKind()); + EXPECT_EQ(read8, extract6.getKid(1).getKid(0)); + EXPECT_EQ(Expr::Concat, extract6.getKid(1).getKid(1).getKind()); + EXPECT_EQ(c100, extract6.getKid(1).getKid(1).getKid(0)); + EXPECT_EQ(Expr::Extract, extract6.getKid(1).getKid(1).getKid(1).getKind()); + + ref<Expr> concat10 = ConcatExpr::create4(read8, c100, c100, read8); + ref<Expr> extract10 = ExtractExpr::create(concat10, 8, 16); + EXPECT_EQ(Expr::Constant, extract10.getKind()); +} + +TEST(ExprTest, ExtractConcat) { + Array *array = new Array(0, 3, 256); + ref<Expr> read64 = Expr::createTempRead(array, 64); + + Array *array2 = new Array(0, 4, 256); + ref<Expr> read8_2 = Expr::createTempRead(array2, 8); + + ref<Expr> extract1 = ExtractExpr::create(read64, 36, 4); + ref<Expr> extract2 = ExtractExpr::create(read64, 32, 4); + + ref<Expr> extract3 = ExtractExpr::create(read64, 12, 3); + ref<Expr> extract4 = ExtractExpr::create(read64, 10, 2); + ref<Expr> extract5 = ExtractExpr::create(read64, 2, 8); + + ref<Expr> kids1[6] = { extract1, extract2, + read8_2, + extract3, extract4, extract5 }; + ref<Expr> concat1 = ConcatExpr::createN(6, kids1); + EXPECT_EQ(29U, concat1.getWidth()); + + ref<Expr> extract6 = ExtractExpr::create(read8_2, 2, 5); + ref<Expr> extract7 = ExtractExpr::create(read8_2, 1, 1); + + ref<Expr> kids2[3] = { extract1, extract6, extract7 }; + ref<Expr> concat2 = ConcatExpr::createN(3, kids2); + EXPECT_EQ(10U, concat2.getWidth()); + EXPECT_EQ(Expr::Extract, concat2.getKid(0).getKind()); + EXPECT_EQ(Expr::Extract, concat2.getKid(1).getKind()); +} + +} diff --git a/unittests/Expr/Makefile b/unittests/Expr/Makefile new file mode 100644 index 00000000..14945b1a --- /dev/null +++ b/unittests/Expr/Makefile @@ -0,0 +1,11 @@ +##===- unittests/Expr/Makefile -----------------------------*- Makefile -*-===## + +LEVEL := ../.. +TESTNAME := Expr +USEDLIBS := kleaverExpr.a kleeBasic.a +LINK_COMPONENTS := support + +include $(LEVEL)/Makefile.config +include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest + +LIBS += -lstp diff --git a/unittests/Makefile b/unittests/Makefile new file mode 100755 index 00000000..545d7fa7 --- /dev/null +++ b/unittests/Makefile @@ -0,0 +1,18 @@ +##===- unittests/Makefile ----------------------------------*- Makefile -*-===## + +LEVEL = .. + +include $(LEVEL)/Makefile.config + +LIBRARYNAME = UnitTestMain +BUILD_ARCHIVE = 1 +CPP.Flags += -I$(LLVM_SRC_ROOT)/utils/unittest/googletest/include/ +CPP.Flags += -Wno-variadic-macros + +# FIXME: Parallel dirs is broken? +DIRS = Expr Solver + +include $(LEVEL)/Makefile.common + +clean:: + $(Verb) $(RM) -f *Tests diff --git a/unittests/Solver/Makefile b/unittests/Solver/Makefile new file mode 100644 index 00000000..583025fb --- /dev/null +++ b/unittests/Solver/Makefile @@ -0,0 +1,11 @@ +##===- unittests/Solver/Makefile ---------------------------*- Makefile -*-===## + +LEVEL := ../.. +TESTNAME := Solver +USEDLIBS := kleaverSolver.a kleaverExpr.a kleeSupport.a kleeBasic.a +LINK_COMPONENTS := support + +include $(LEVEL)/Makefile.config +include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest + +LIBS += -lstp diff --git a/unittests/Solver/SolverTest.cpp b/unittests/Solver/SolverTest.cpp new file mode 100644 index 00000000..1b1e0f3f --- /dev/null +++ b/unittests/Solver/SolverTest.cpp @@ -0,0 +1,164 @@ +//===-- SolverTest.cpp ----------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "klee/Constraints.h" +#include "klee/Expr.h" +#include "klee/Solver.h" + +using namespace klee; + +namespace { + +const int g_constants[] = { -1, 1, 4, 17, 0 }; +const Expr::Width g_types[] = { Expr::Bool, + Expr::Int8, + Expr::Int16, + Expr::Int32, + Expr::Int64 }; + +ref<Expr> getConstant(int value, Expr::Width width) { + int64_t ext = value; + uint64_t trunc = ext & (((uint64_t) -1LL) >> (64 - width)); + return ConstantExpr::create(trunc, width); +} + + +template<class T> +void testOperation(Solver &solver, + int value, + Expr::Width operandWidth, + Expr::Width resultWidth) { + std::vector<Expr::CreateArg> symbolicArgs; + + for (unsigned i = 0; i < T::numKids; i++) { + if (!T::isValidKidWidth(i, operandWidth)) + return; + + unsigned size = Expr::getMinBytesForWidth(operandWidth); + static unsigned id = 0; + Array *array = new Array(0, ++id, size); + symbolicArgs.push_back(Expr::CreateArg(Expr::createTempRead(array, + operandWidth))); + } + + if (T::needsResultType()) + symbolicArgs.push_back(Expr::CreateArg(resultWidth)); + + ref<Expr> fullySymbolicExpr = Expr::createFromKind(T::kind, symbolicArgs); + + // For each kid, replace the kid with a constant value and verify + // that the fully symbolic expression is equivalent to it when the + // replaced value is appropriated constrained. + for (unsigned kid = 0; kid < T::numKids; kid++) { + std::vector<Expr::CreateArg> partiallyConstantArgs(symbolicArgs); + for (unsigned i = 0; i < T::numKids; i++) + if (i==kid) + partiallyConstantArgs[i] = getConstant(value, operandWidth); + + ref<Expr> expr = + NotOptimizedExpr::create(EqExpr::create(partiallyConstantArgs[kid].expr, + symbolicArgs[kid].expr)); + + ref<Expr> partiallyConstantExpr = + Expr::createFromKind(T::kind, partiallyConstantArgs); + + ref<Expr> queryExpr = EqExpr::create(fullySymbolicExpr, + partiallyConstantExpr); + + ConstraintManager constraints; + constraints.addConstraint(expr); + bool res; + bool success = solver.mustBeTrue(Query(constraints, queryExpr), res); + EXPECT_EQ(true, success) << "Constraint solving failed"; + + if (success) { + EXPECT_EQ(true, res) << "Evaluation failed!\n" + << "query " << queryExpr + << " with " << expr; + } + } +} + +template<class T> +void testOpcode(Solver &solver, bool tryBool = true, bool tryZero = true, + unsigned maxWidth = 64) { + for (unsigned j=0; j<sizeof(g_types)/sizeof(g_types[0]); j++) { + Expr::Width type = g_types[j]; + + if (type > maxWidth) continue; + + for (unsigned i=0; i<sizeof(g_constants)/sizeof(g_constants[0]); i++) { + int value = g_constants[i]; + if (!tryZero && !value) continue; + if (type == Expr::Bool && !tryBool) continue; + + if (!T::needsResultType()) { + testOperation<T>(solver, value, type, type); + continue; + } + + for (unsigned k=0; k<sizeof(g_types)/sizeof(g_types[0]); k++) { + Expr::Width resultType = g_types[k]; + + // nasty hack to give only Trunc/ZExt/SExt the right types + if (T::kind == Expr::SExt || T::kind == Expr::ZExt) { + if (Expr::getMinBytesForWidth(type) >= + Expr::getMinBytesForWidth(resultType)) + continue; + } + + testOperation<T>(solver, value, type, resultType); + } + } + } +} + +TEST(SolverTest, Evaluation) { + STPSolver *stpSolver = new STPSolver(true); + Solver *solver = stpSolver; + + solver = createCexCachingSolver(solver); + solver = createCachingSolver(solver); + solver = createIndependentSolver(solver); + + testOpcode<SelectExpr>(*solver); + testOpcode<ZExtExpr>(*solver); + testOpcode<SExtExpr>(*solver); + + testOpcode<AddExpr>(*solver); + testOpcode<SubExpr>(*solver); + testOpcode<MulExpr>(*solver, false, true, 8); + testOpcode<SDivExpr>(*solver, false, false, 8); + testOpcode<UDivExpr>(*solver, false, false, 8); + testOpcode<SRemExpr>(*solver, false, false, 8); + testOpcode<URemExpr>(*solver, false, false, 8); + testOpcode<ShlExpr>(*solver, false); + testOpcode<LShrExpr>(*solver, false); + testOpcode<AShrExpr>(*solver, false); + testOpcode<AndExpr>(*solver); + testOpcode<OrExpr>(*solver); + testOpcode<XorExpr>(*solver); + + testOpcode<EqExpr>(*solver); + testOpcode<NeExpr>(*solver); + testOpcode<UltExpr>(*solver); + testOpcode<UleExpr>(*solver); + testOpcode<UgtExpr>(*solver); + testOpcode<UgeExpr>(*solver); + testOpcode<SltExpr>(*solver); + testOpcode<SleExpr>(*solver); + testOpcode<SgtExpr>(*solver); + testOpcode<SgeExpr>(*solver); + + delete solver; +} + +} diff --git a/unittests/TestMain.cpp b/unittests/TestMain.cpp new file mode 100644 index 00000000..095076b2 --- /dev/null +++ b/unittests/TestMain.cpp @@ -0,0 +1,15 @@ +//===--- unittests/TestMain.cpp - unittest driver -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +int main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/utils/emacs/klee-pc-mode.el b/utils/emacs/klee-pc-mode.el new file mode 100644 index 00000000..a398adbf --- /dev/null +++ b/utils/emacs/klee-pc-mode.el @@ -0,0 +1,64 @@ +(provide 'klee-pc-mode) + +(require 'font-lock) + +(defvar klee-pc-mode-syntax-table nil + "Syntax table used while in ft-bt mode.") + +;;; -------------------------------------------------- +;;;setup the syntax table +;;; -------------------------------------------------- +(unless klee-pc-mode-syntax-table + (setq klee-pc-mode-syntax-table (make-syntax-table)) + ;;misc + (modify-syntax-entry ?\_ "w" klee-pc-mode-syntax-table) + (modify-syntax-entry ?\. "." klee-pc-mode-syntax-table)) + +;;;------------------------------------------------------------ +;;; local keymap +;;;------------------------------------------------------------ + +(defvar klee-pc-local-keymap nil) + +;;; -------------------------------------------------- +;;; set keywords +;;; -------------------------------------------------- + +(defvar klee-pc-mode-font-lock-keywords + (list + ;; Comments + '("#.*" . font-lock-comment-face) + ;; Identifiers + '("%[_a-zA-Z][a-zA-Z_.0-9]*" . font-lock-variable-name-face) + ;; Numbers + '("[+-]?0b[01_]+" . font-lock-preprocessor-face) + '("[+-]?0o[0-7_]+" . font-lock-preprocessor-face) + '("[+-]?0x[a-zA-Z0-9_]+" . font-lock-preprocessor-face) + '("[+-]?[0-9]+" . font-lock-preprocessor-face) + ;; Keywords + '("\\bdef\\b\\|\\bvar\\b\\|\\btrue\\b\\|\\barray\\b\\|\\bfalse\\b\\|\\bquery\\b\\|\\bdefine\\b\\|\\bdeclare\\b" . font-lock-keyword-face) + ) + "klee-PC mode keywords") + +;;; -------------------------------------------------- +;;; major mode function +;;; -------------------------------------------------- +(defun klee-pc-mode () + "Switch to the klee-pc major mode" + (interactive) + (kill-all-local-variables) + (setq major-mode 'klee-pc-mode) + (setq mode-name "klee-pc") + + ;; handle keymap + (use-local-map klee-pc-local-keymap) + + ;;handle syntax table + (set-syntax-table klee-pc-mode-syntax-table) + ;;handle fontlock + (make-local-variable 'font-lock-defaults) + (setq font-lock-defaults '(klee-pc-mode-font-lock-keywords + nil t)) + + ;;handle hook + (run-hooks 'klee-pc-mode-hook)) diff --git a/utils/valgrind/README.txt b/utils/valgrind/README.txt new file mode 100644 index 00000000..9f7cb318 --- /dev/null +++ b/utils/valgrind/README.txt @@ -0,0 +1,2 @@ +A few valgrind suppression files for known leaks. The LLVM ones may be +fixed by now. diff --git a/utils/valgrind/valgrind-llvm.supp b/utils/valgrind/valgrind-llvm.supp new file mode 100644 index 00000000..43bc5389 --- /dev/null +++ b/utils/valgrind/valgrind-llvm.supp @@ -0,0 +1,24 @@ +{ + LLVM:Tmp1 + Memcheck:Leak + fun:_vgrZU_libstdcZpZpZa__Znwj + fun:_ZN4llvm* +} +{ + LLVM:Tmp2 + Memcheck:Leak + fun:_vgrZU_libcZdsoZa_malloc + fun:_ZN4llvm* +} +{ + LLVM:Tmp3 + Memcheck:Leak + fun:*nwj* + fun:*llvm* +} +{ + LLVM:Tmp4 + Memcheck:Leak + fun:malloc + fun:*llvm* +} diff --git a/utils/valgrind/valgrind-stp.supp b/utils/valgrind/valgrind-stp.supp new file mode 100644 index 00000000..9dba58f1 --- /dev/null +++ b/utils/valgrind/valgrind-stp.supp @@ -0,0 +1,32 @@ +{ + STP:BeevMgr + Memcheck:Leak + fun:_vgrZU_libstdcZpZpZa__Znwj + fun:_ZN4BEEV7BeevMgr* +} +{ + STP:BeevMgr:strdup + Memcheck:Leak + fun:_vgrZU_libcZdsoZa_malloc + fun:strdup + fun:_ZN4BEEV7BeevMgr* +} +{ + STP:c_interface + Memcheck:Leak + fun:_vgrZU_libstdcZpZpZa__Znwj + fun:vc_* +} +{ + STP:BeevMgr:vector + Memcheck:Leak + fun:_vgrZU_libstdcZpZpZa__Znwj + fun:_ZNSt6vector* + fun:_ZN4BEEV7BeevMgr* +} +{ + LLVM:Tmp1 + Memcheck:Leak + fun:_vgrZU_libstdcZpZpZa__Znwj + fun:_ZN4llvm* +} diff --git a/www/Examples.html b/www/Examples.html new file mode 100644 index 00000000..23584053 --- /dev/null +++ b/www/Examples.html @@ -0,0 +1,34 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> +<head> + <META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> + <title>KLEE - Examples</title> + <link type="text/css" rel="stylesheet" href="menu.css" /> + <link type="text/css" rel="stylesheet" href="content.css" /> +</head> +<body> + +<!--#include virtual="menu.html.incl"--> + +<div id="content"> + +<h1>KLEE Examples</h1> + +<p>FIXME: Intro.</p> + +<h2>Basic Sort Example</h2> + +<p>FIXME: Write.</p> + +<h2>FIXME: More complicated example</h2> + +<p>FIXME: Write: show the important klee.h functions.</p> + +<p>FIXME: Write: show the important klee tools.</p> + +<p>FIXME: Write: show the important klee options.</p> + +</div> +</body> +</html> diff --git a/www/GetInvolved.html b/www/GetInvolved.html new file mode 100644 index 00000000..b8afcbb3 --- /dev/null +++ b/www/GetInvolved.html @@ -0,0 +1,34 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> +<head> + <META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> + <title>klee - Get Involved</title> + <link type="text/css" rel="stylesheet" href="menu.css" /> + <link type="text/css" rel="stylesheet" href="content.css" /> +</head> +<body> + +<!--#include virtual="menu.html.incl"--> + +<div id="content"> + +<h1>Getting Involved with the klee Project</h1> + +<p>FIXME: Intro.</p> + +<h2>Mailing Lists</h2> + +<p>klee-dev</p> + +<p>klee-commits</p> + +<h2>Working with the Code</h2> + +<p>FIXME: Point at pertinent LLVM docs.</p> + +<p>FIXME: Point at doxygen.</p> + +</div> +</body> +</html> diff --git a/www/GetStarted.html b/www/GetStarted.html new file mode 100644 index 00000000..07083d4f --- /dev/null +++ b/www/GetStarted.html @@ -0,0 +1,101 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> +<head> + <META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> + <title>KLEE - Getting Started</title> + <link type="text/css" rel="stylesheet" href="menu.css" /> + <link type="text/css" rel="stylesheet" href="content.css" /> +</head> +<body> + +<!--#include virtual="menu.html.incl"--> + +<div id="content"> + +<h1>Getting Started: Building and Running KLEE</h1> + +<!-- <p>FIXME: Intro and disclaimer.</p> --> + +<h2 id="build">Building KLEE and Working with the Code</h2> + +<p>If you would like to check out and build KLEE, the current procedure is as +follows:</p> + +KLEE is built on LLVM; the first steps are to get a working LLVM +installation. See <a href="http://llvm.org/docs/GettingStarted.html">Getting +Started with the LLVM System</a> for more information. + +<ol> +<li>Install llvm-gcc:</li> +<ul> +<li>Download and install the LLVM 2.5 release of <tt>llvm-gcc</tt> + from <a href="http://llvm.org/releases/download.html">here</a>. It + is important to do this first so that it is found in + subsequent <tt>configure</tt> steps. <tt>llvm-gcc</tt> will be used + later to compile programs that KLEE can execute.</li> +</ul> + +<li><a href="http://www.llvm.org/docs/GettingStarted.html#checkout">Checkout + and build LLVM</a> from SVN head: + + <code class="instr"> <code> + svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm + cd llvm + ./configure --enable-optimized + make + </code></code> + +(the <tt>--enable-optimized</tt> configure argument is not +necessary, but KLEE runs very slowly in Debug mode). + </li> + + <li>Checkout KLEE (to any path you like): + <div class="instr"> + svn co http://llvm.org/svn/llvm-project/klee/trunk klee + </div> + </li> + + <li>Configure KLEE (from the KLEE source directory): + <div class="instr"> + ./configure --with-llvm=<i>path/to/llvm</i> + </div> + + <p>This assumes that you compiled LLVM in-place. If you used a + different directory for the object files then use: + <div class="instr"> + ./configure --with-llvmsrc=<i>path/to/llvm/src</i>--with-llvmobj=<i>path/to/llvm/obj</i> + </div> + </li> + + <li>Build KLEE (from the KLEE source directory): + <div class="instr"> + make + </div> + </li> + + <li>Run DejaGNU and unit tests to verify your build: + <div class="instr"> + make check<br> + make unittests<br> + </div> + </li> + + <li>You're ready to go! Go to the <a href="tutorials.html">Tutorials</a> page + to try KLEE.</li> +</ol> + +<!-- <h2> Full Installation </h2> --> + +<!-- If you need uCLibc and/or POSIX support add <i>-with-uclibc</i> --> +<!-- and <i>-enable-posix-runtime</i> to configure. Thus, to enable --> +<!-- both, replace step 3 above with: --> + +<!-- <div class="instr"> --> +<!-- ./configure -with-llvm=<i>path/to/llvm</i> -with-uclibc -enable-posix-runtime ENABLE_OPTIMIZED=1 --> +<!-- </div> --> +<!-- However, note that... --> + +</div> +</body> +</html> diff --git a/www/bugs.html b/www/bugs.html index bd5dd86c..fca13033 100644 --- a/www/bugs.html +++ b/www/bugs.html @@ -9,7 +9,7 @@ <link type="text/css" rel="stylesheet" href="content.css"> </head> <body> -<include virtual="menu.html.incl"> +<!--#include virtual="menu.html.incl"--> <div id="content"> <!--*********************************************************************--> <h1>KLEE Bug Reports</h1> diff --git a/www/content.css b/www/content.css index caee1f00..9571114d 100644 --- a/www/content.css +++ b/www/content.css @@ -1,6 +1,8 @@ html, body { padding:0px; - font-size:small; font-family:"Lucida Grande", "Lucida Sans Unicode", Arial, Verdana, Helvetica, sans-serif; background-color: #fff; color: #222; + font-size:small; + font-family:"Lucida Grande", "Lucida Sans Unicode", Arial, Verdana, Helvetica, sans-serif; + background-color: #fff; color: #222; line-height:1.5; } @@ -24,11 +26,23 @@ IMG.img_slide { /* Tables */ tr { vertical-align:top } -/* Code */ -div.code{ +/* Instructions */ +div.instr{ border: 1px solid LightSteelBlue ; font-family: Courier New; background-color: #E3E3E3; padding: 7px; margin: 7px; } + +/* Code */ +pre.code{ + display:table; + text-align: left; + border: 1px solid LightSteelBlue ; + font-family: Courier New; + background-color: #E3E3E3; + margin: 10px; + padding: 10px; +} + diff --git a/www/index.html b/www/index.html index ea60335a..4b9e5f3b 100644 --- a/www/index.html +++ b/www/index.html @@ -27,7 +27,9 @@ the <a href="http://llvm.org/pubs/2008-12-OSDI-KLEE.html">OSDI 2008</a> paper.</p> +<!-- <p>FIXME: Somewhere need to describe what KLEE can do well and what + is more "experimental" or research level. This should also address + how KLEE could be used by outside groups (i.e. kleaver).</p> --> </div> - </body> </html> diff --git a/www/install.html b/www/install.html index b01f2105..79aeb4b1 100644 --- a/www/install.html +++ b/www/install.html @@ -9,7 +9,7 @@ <link type="text/css" rel="stylesheet" href="content.css"> </head> <body> -<include virtual="menu.html.incl"> +<!--#include virtual="menu.html.incl"--> <div id="content"> <!--*********************************************************************--> <h1>KLEE Installation Guide</h1> diff --git a/www/menu.html.incl b/www/menu.html.incl index 2fbffaef..ef450ca7 100644 --- a/www/menu.html.incl +++ b/www/menu.html.incl @@ -6,7 +6,10 @@ <div class="submenu"> <label>klee Info</label> <a href="index.html">About</a> - <a href="install.html">Download & Install</a> + <a href="GetStarted.html">Get Started</a> + <a href="GetInvolved.html">Get Involved</a> + <a href="tutorials.html">Tutorials</a> + <a href="Examples.html">Examples</a> </div> <div class="submenu"> @@ -14,5 +17,7 @@ <a href="http://keeda.stanford.edu/mailman/listinfo/klee-dev">klee-dev</a> <a href="http://llvm.org/svn/llvm-project/klee/trunk/">Browse SVN</a> <a href="http://llvm.org/viewvc/llvm-project/klee/trunk/">Browse ViewVC</a> + <a href="http://t1.minormatter.com/~ddunbar/klee-doxygen/index.html">doxygen</a> + <a href="http://t1.minormatter.com/~ddunbar/klee-cov/index.html">Testing Coverage</a> </div> </div> diff --git a/www/tutorials.html b/www/tutorials.html index db5220e4..08e9e4f7 100644 --- a/www/tutorials.html +++ b/www/tutorials.html @@ -4,12 +4,12 @@ <html> <head> <META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> - <title>The klee Symbolic Virtual Machine</title> + <title>The KLEE Symbolic Virtual Machine</title> <link type="text/css" rel="stylesheet" href="menu.css"> <link type="text/css" rel="stylesheet" href="content.css"> </head> <body> -<include virtual="menu.html.incl"> +<!--#include virtual="menu.html.incl"--> <div id="content"> <!--*********************************************************************--> <h1>KLEE Tutorials</h1> |