Skip to content

Commit d9a5b44

Browse files
committed
saw-core-what4: Build muxes of symbolic indexes in big-endian order
SAWCore assumes the invariant that when indexing into an infinite stream using a symbolic index, the mux tree constructed over the index will test each bit in big-endian order. This is the responsibility of the `selectV` function found in `saw-core`, `saw-core-sbv`, and `saw-core-what4`. All of these functions save for `saw-core-what4`'s `selectV` were upholding this invariant, as `saw-core-what4`'s was testing each bit in little-endian order, resulting in the oddities observed in #1703. This corrects the mistake in `saw-core-what4'`s implementation, fixing #1703. It also leaves some more documentation to make the fact that each `selectV` should be proceeding in big-endian order more apparent.
1 parent 279e6d3 commit d9a5b44

File tree

8 files changed

+60
-6
lines changed

8 files changed

+60
-6
lines changed

intTests/test1703/Makefile

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

intTests/test1703/test.bc

2.92 KB
Binary file not shown.

intTests/test1703/test.c

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#include <stdint.h>
2+
3+
uint32_t weird(uint32_t cv[8], uint8_t i) {
4+
return cv[i % 8];
5+
}

intTests/test1703/test.cry

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
weird : [8][32] -> [8] -> [32]
2+
weird cv i = k
3+
where
4+
k = cvs @ i
5+
cvs = cv # cvs

intTests/test1703/test.saw

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import "test.cry";
2+
3+
let weird_spec = do {
4+
cv <- llvm_fresh_var "cv" (llvm_array 8 (llvm_int 32));
5+
cv_p <- llvm_alloc (llvm_array 8 (llvm_int 32));
6+
llvm_points_to cv_p (llvm_term {{ cv }});
7+
i <- llvm_fresh_var "i" (llvm_int 8);
8+
9+
llvm_execute_func [cv_p, llvm_term i];
10+
11+
llvm_return (llvm_term {{ weird cv i }});
12+
};
13+
14+
m <- llvm_load_module "test.bc";
15+
llvm_verify m "weird" [] false weird_spec (w4_unint_z3 []);

intTests/test1703/test.sh

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
set -e
2+
3+
$SAW test.saw

saw-core-sbv/src/Verifier/SAW/Simulator/SBV.hs

+11-3
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,10 @@ lazyMux muxFn c tm fm =
314314
f <- fm
315315
muxFn c t f
316316

317-
-- selectV merger maxValue valueFn index returns valueFn v when index has value v
318-
-- if index is greater than maxValue, it returns valueFn maxValue. Use the ite op from merger.
317+
-- @selectV merger maxValue valueFn vx@ treats @vx@ as an index, represented
318+
-- as a big-endian list of bits. It does a binary lookup, using @merger@ as an
319+
-- if-then-else operator. If the index is greater than @maxValue@, then it
320+
-- returns @valueFn maxValue@.
319321
selectV :: (SBool -> b -> b -> b) -> Natural -> (Natural -> b) -> SWord -> b
320322
selectV merger maxValue valueFn vx =
321323
case svAsInteger vx of
@@ -326,7 +328,13 @@ selectV merger maxValue valueFn vx =
326328
where
327329
impl _ x | x > maxValue || x < 0 = valueFn maxValue
328330
impl 0 y = valueFn y
329-
impl i y = merger (svTestBit vx j) (impl j (y `setBit` j)) (impl j y) where j = i - 1
331+
impl i y =
332+
-- NB: `i` counts down in each iteration, so we use svTestBit (a
333+
-- little-endian indexing function) to ensure that the bits are processed
334+
-- in big-endian order. Alternatively, we could have `i` count up and use
335+
-- svAt (a big-endian indexing function), but we use svTestBit as it is
336+
-- slightly cheaper to compute.
337+
merger (svTestBit vx j) (impl j (y `setBit` j)) (impl j y) where j = i - 1
330338

331339
-- Big-endian version of svTestBit
332340
svAt :: SWord -> Int -> SBool

saw-core-what4/src/Verifier/SAW/Simulator/What4.hs

+10-3
Original file line numberDiff line numberDiff line change
@@ -580,8 +580,10 @@ lazyMux muxFn c tm fm =
580580
f <- fm
581581
muxFn c t f
582582

583-
-- selectV merger maxValue valueFn index returns valueFn v when index has value v
584-
-- if index is greater than maxValue, it returns valueFn maxValue. Use the ite op from merger.
583+
-- @selectV sym merger maxValue valueFn vx@ treats @vx@ as an index, represented
584+
-- as a big-endian list of bits. It does a binary lookup, using @merger@ as an
585+
-- if-then-else operator. If the index is greater than @maxValue@, then it
586+
-- returns @valueFn maxValue@.
585587
selectV :: forall sym b.
586588
Sym sym =>
587589
sym ->
@@ -595,7 +597,12 @@ selectV sym merger maxValue valueFn vx =
595597
impl _ x | x > maxValue || x < 0 = valueFn maxValue
596598
impl 0 y = valueFn y
597599
impl i y = do
598-
p <- SW.bvAtBE sym vx (toInteger j)
600+
-- NB: `i` counts down in each iteration, so we use bvAtLE (a
601+
-- little-endian indexing function) to ensure that the bits are processed
602+
-- in big-endian order. Alternatively, we could have `i` count up and use
603+
-- bvAtBE (a big-endian indexing function), but we use bvAtLE as it is
604+
-- slightly cheaper to compute.
605+
p <- SW.bvAtLE sym vx (toInteger j)
599606
merger p (impl j (y `setBit` j)) (impl j y) where j = i - 1
600607

601608
instance Show (SArray sym) where

0 commit comments

Comments
 (0)