Skip to content

Commit 9a390f3

Browse files
authored
Merge pull request #1943 from GaloisInc/T1938
`w4_abc_{aiger,verilog}`: Handle variable-less properties correctly
2 parents 9b71965 + 8f73198 commit 9a390f3

File tree

6 files changed

+61
-19
lines changed

6 files changed

+61
-19
lines changed

intTests/test1938/Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CC = clang
2+
CFLAGS = -g -frecord-command-line -O0
3+
4+
all: test.bc
5+
6+
test.bc: test.c
7+
$(CC) $(CFLAGS) -c -emit-llvm $< -o $@
8+
9+
.PHONY: clean
10+
clean:
11+
rm -f test.bc

intTests/test1938/test.bc

2.46 KB
Binary file not shown.

intTests/test1938/test.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
void test(int *x) {
2+
*x = 2;
3+
}

intTests/test1938/test.saw

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Test that the `abc` and `w4_abc_verilog` proof scripts will report invalid
2+
// goals that do not contain any variables. We test this both directly (with the
3+
// trivial `False` property) as well as via an LLVM verification involving a
4+
// proof goal that evaluates to `False`.
5+
6+
fails (prove_print abc {{ False }});
7+
fails (prove_print w4_abc_verilog {{ False }});
8+
9+
m <- llvm_load_module "test.bc";
10+
11+
// This will generate a failing proof goal about `x` being read-only.
12+
let setup = do {
13+
x <- llvm_alloc_readonly (llvm_int 32);
14+
llvm_execute_func [x];
15+
};
16+
17+
fails (llvm_verify m "test" [] true setup abc);
18+
fails (llvm_verify m "test" [] true setup w4_abc_verilog);

intTests/test1938/test.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
set -e
2+
3+
$SAW test.saw

src/SAWScript/Prover/ABC.hs

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@ module SAWScript.Prover.ABC
88
import Control.Monad (unless)
99
import Control.Monad.IO.Class
1010
import qualified Data.ByteString.Char8 as C8
11-
import Data.Char (isSpace)
1211
import Data.List (isPrefixOf)
1312
import qualified Data.Map as Map
1413
import Data.Maybe
1514
import qualified Data.Text as Text
1615
import Text.Read (readMaybe)
1716

1817
import System.Directory
18+
import System.FilePath ((</>))
1919
import System.IO
20-
import System.IO.Temp (emptySystemTempFile)
20+
import System.IO.Temp (createTempDirectory, getCanonicalTemporaryDirectory)
2121
import System.Process (readProcessWithExitCode)
2222

2323
import qualified Data.AIG as AIG
@@ -105,30 +105,33 @@ w4AbcExternal ::
105105
SATQuery ->
106106
TopLevel (Maybe CEX, String)
107107
w4AbcExternal exporter argFn _hashcons satq =
108-
-- Create temporary files
109-
do let tpl = "abc_verilog.v"
110-
tplCex = "abc_verilog.cex"
111-
tmp <- liftIO $ emptySystemTempFile tpl
112-
tmpCex <- liftIO $ emptySystemTempFile tplCex
108+
-- Create a temporary directory to put input and output files
109+
do canonTmpDir <- liftIO getCanonicalTemporaryDirectory
110+
tmpDir <- liftIO $ createTempDirectory canonTmpDir "saw_abc_external"
111+
let tmp = tmpDir </> "abc_verilog.v"
112+
tmpCex = tmpDir </> "abc_verilog.cex"
113113

114114
(argNames, argTys) <- unzip <$> exporter tmp satq
115115

116116
-- Run ABC and remove temporaries
117117
let execName = "abc"
118118
args = ["-q", argFn tmp tmpCex]
119119
(_out, _err) <- liftIO $ readProcessExitIfFailure execName args
120-
cexText <- liftIO $ C8.unpack <$> C8.readFile tmpCex
121-
liftIO $ removeFile tmp
122-
liftIO $ removeFile tmpCex
123-
124-
-- Parse and report results
125-
res <- if all isSpace cexText
126-
then return Nothing
127-
else do cex <- liftIO $ parseAigerCex cexText argTys
128-
case cex of
129-
Left parseErr -> fail parseErr
130-
Right vs -> return $ Just model
131-
where model = zip argNames (map toFirstOrderValue vs)
120+
cexExists <- liftIO $ doesFileExist tmpCex
121+
res <-
122+
if cexExists
123+
then do
124+
-- If a counterexample file exists, parse and report the results.
125+
cexText <- liftIO $ C8.unpack <$> C8.readFile tmpCex
126+
cex <- liftIO $ parseAigerCex cexText argTys
127+
case cex of
128+
Left parseErr -> fail parseErr
129+
Right vs -> return $ Just model
130+
where model = zip argNames (map toFirstOrderValue vs)
131+
else
132+
-- Otherwise, there is no counterexample.
133+
return Nothing
134+
liftIO $ removeDirectoryRecursive tmpDir
132135
return (res, "abc_verilog")
133136

134137
parseAigerCex :: String -> [FiniteType] -> IO (Either String [FiniteValue])
@@ -137,11 +140,15 @@ parseAigerCex text tys =
137140
-- Output from `write_cex`
138141
[cex] ->
139142
case words cex of
143+
-- Queries with no variables will result in a blank line of output.
144+
[] -> pure $ liftCexBB tys []
140145
[bits] -> liftCexBB tys <$> mapM bitToBool bits
141146
_ -> fail $ "invalid counterexample line: " ++ cex
142147
-- Output from `write_aiger_cex`
143148
[_, cex] ->
144149
case words cex of
150+
-- Queries with no variables will result in an empty vector of output.
151+
[_] -> pure $ liftCexBB tys []
145152
[bits, _] -> liftLECexBB tys <$> mapM bitToBool bits
146153
_ -> fail $ "invalid counterexample line: " ++ cex
147154
_ -> fail $ "invalid counterexample text: " ++ text

0 commit comments

Comments
 (0)