Goose  Check-in [98a85945f6]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:FuncValidator: more work on the implementation.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 98a85945f6fe4a920a83d743037b0ebaebc66bcfd9c5cf3e383aed23be8fa0dd
User & Date: achavasse 2019-10-02 20:09:26.523
Context
2019-10-02
22:11
z3 builder: instead of directly adding negated assertions to the solver, gather them into a conjunction and add that. check-in: 2734029a31 user: achavasse tags: trunk
20:09
FuncValidator: more work on the implementation. check-in: 98a85945f6 user: achavasse tags: trunk
19:30
Implemented a helper class allowing to wrap each z3 expression with a predicate indicating that the execution flow went through a certain basic block. check-in: 1d340c945f user: achavasse tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to bs/analyze/analyze.h.
9
10
11
12
13
14
15

16
17
18
19
20
21
22
    using namespace ir;
    using namespace llr;

    class Builder;

    extern z3::context& GetZ3Context();
    extern optional< z3::expr > BuildZ3ExprFromValue( Builder& b, const Value& val );

}

#include "builder.h"
#include "verifstmtvalidator.h"
#include "funcvalidator.h"

#endif







>







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    using namespace ir;
    using namespace llr;

    class Builder;

    extern z3::context& GetZ3Context();
    extern optional< z3::expr > BuildZ3ExprFromValue( Builder& b, const Value& val );
    extern optional< z3::expr > BuildZ3Op( Builder& b, const llr::Instruction& instr );
}

#include "builder.h"
#include "verifstmtvalidator.h"
#include "funcvalidator.h"

#endif
Changes to bs/analyze/builder.cpp.
14
15
16
17
18
19
20


21
22
23
24
25
26
27
    }

    m_solver.add( z3::implies( *m_currentPredicate, e ) );
}

void Builder::addAssertion( const z3::expr e )
{


    if( !m_currentPredicate )
    {
        m_solver.add( !e );
        return;
    }

    m_solver.add( z3::implies( *m_currentPredicate, !e ) );







>
>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    }

    m_solver.add( z3::implies( *m_currentPredicate, e ) );
}

void Builder::addAssertion( const z3::expr e )
{
    m_haveAnyAssertion = true;

    if( !m_currentPredicate )
    {
        m_solver.add( !e );
        return;
    }

    m_solver.add( z3::implies( *m_currentPredicate, !e ) );
Changes to bs/analyze/builder.h.
16
17
18
19
20
21
22


23
24
25
26
27
28
29
30
31




32
33
34
35
            Builder( z3::solver& solver ) :
                m_solver( solver )
            {}

            void addAssumption( const z3::expr e );
            void addAssertion( const z3::expr e );



        private:
            z3::solver& m_solver;

            // All emitted assumptions and assertions are wrapped
            // with implication from this predicate.
            // This is used to model the control flow, where each
            // basic block gets a predicate that indicates that
            // the execution flow is going through that block.
            optional< z3::expr > m_currentPredicate;




    };
}

#endif







>
>









>
>
>
>




16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
            Builder( z3::solver& solver ) :
                m_solver( solver )
            {}

            void addAssumption( const z3::expr e );
            void addAssertion( const z3::expr e );

            bool haveAnyAssertion() const { return m_haveAnyAssertion; }

        private:
            z3::solver& m_solver;

            // All emitted assumptions and assertions are wrapped
            // with implication from this predicate.
            // This is used to model the control flow, where each
            // basic block gets a predicate that indicates that
            // the execution flow is going through that block.
            optional< z3::expr > m_currentPredicate;

            // Check if we actually emitted any assertion. If we didn't,
            // then we have no condition to prove, so just skip the checking.
            bool m_haveAnyAssertion = false;
    };
}

#endif
Changes to bs/analyze/funcvalidator.cpp.
1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16



17
18
19
20
21
22
23
#include "analyze.h"
#include "builtins/builtins.h"
#include "diagnostics/diagnostics.h"

using namespace goose::analyze;
using namespace goose::diagnostics;

FuncValidator::FuncValidator( const builtins::Func& func ) :
    m_solver( GetZ3Context() ),
    m_builder( m_solver )

{
    buildZ3Formula( func );
}

bool FuncValidator::validate()
{



    switch( m_solver.check() )
    {
        case z3::check_result::unsat:
            // We are using the solver to try and find a counter example,
            // so if it's unsat, it's good.
            return true;










|
>






>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include "analyze.h"
#include "builtins/builtins.h"
#include "diagnostics/diagnostics.h"

using namespace goose::analyze;
using namespace goose::diagnostics;

FuncValidator::FuncValidator( const builtins::Func& func ) :
    m_solver( GetZ3Context() ),
    m_builder( m_solver ),
    m_func( func )
{
    buildZ3Formula( func );
}

bool FuncValidator::validate()
{
    if( !m_builder.haveAnyAssertion() )
        return true;

    switch( m_solver.check() )
    {
        case z3::check_result::unsat:
            // We are using the solver to try and find a counter example,
            // so if it's unsat, it's good.
            return true;

52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

69


70





































        return true;
    } );

    const auto& llr = func.llr();
    const auto& cfg = llr->body();

    // For now, we assume that the BBs are already ordered so that each BB's dominating BBs precedes it,
    // which should be the case due to the way if, while and shortcut eval operators are implemented.
    cfg->ForEachBB( [&]( auto&& bb )
    {
        buildFormulasForBB( bb );
    } );
}

void FuncValidator::buildFormulasForBB( const ptr< llr::BasicBlock >& bb )
{




}











































|









>

>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113

        return true;
    } );

    const auto& llr = func.llr();
    const auto& cfg = llr->body();

    // For now, we assume that the BBs are already ordered so that each BB's dominating BB precedes it,
    // which should be the case due to the way if, while and shortcut eval operators are implemented.
    cfg->ForEachBB( [&]( auto&& bb )
    {
        buildFormulasForBB( bb );
    } );
}

void FuncValidator::buildFormulasForBB( const ptr< llr::BasicBlock >& bb )
{
    // TODO: setup the predicate for this basic block

    for( auto&& x : *bb )
        BuildZ3Op( m_builder, x );

    handleTerminator( *bb->terminator() );
}

void FuncValidator::handleTerminator( const llr::Terminator& t )
{
    return visit( [&]( auto&& t )
    {
        return handleTerminator( t );
    }, t.content() );
}

void FuncValidator::handleTerminator( const llr::Ret& t )
{
    // Emit the "ensures" expressions as assertions.
    const auto& reqs = m_func.type().verifInfos()->postAssertion();
    ForEachInVectorTerm( reqs, [&]( auto&& t )
    {
        // Don't do any error handling here, it should already have been taken care of
        // by VerifStmtValidator.
        if( auto z3expr = BuildZ3ExprFromValue( m_builder, *ValueFromIRExpr( t ) ) )
            m_builder.addAssertion( *z3expr );

        return true;
    } );
}

void FuncValidator::handleTerminator( const llr::Branch& t )
{
    // TODO - set the predicate of the target BB to be the same as the current BB's
}

void FuncValidator::handleTerminator( const llr::CondBranch& t )
{
    // TODO - set the predicate of the true BB to a z3expr generated from the condition,
    // and the predicate for the false BB to its negation
}
Changes to bs/analyze/funcvalidator.h.
16
17
18
19
20
21
22










23
24


25
26
27
28
29
30
31
            FuncValidator( const builtins::Func& func );
            bool validate();

        private:
            void buildZ3Formula( const builtins::Func& func );
            void buildFormulasForBB( const ptr< llr::BasicBlock >& bb );











            z3::solver m_solver;
            Builder m_builder;



            // For now we don't attempt to handle loops. We only keep track
            // of which basic blocks we already converted to formulas to avoid
            // doing them again.
            unordered_set< uint32_t > m_convertedBBIndices;
    };
}







>
>
>
>
>
>
>
>
>
>


>
>







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
            FuncValidator( const builtins::Func& func );
            bool validate();

        private:
            void buildZ3Formula( const builtins::Func& func );
            void buildFormulasForBB( const ptr< llr::BasicBlock >& bb );

            void handleTerminator( const llr::Terminator& t );

            template< typename T >
            void handleTerminator( const T& t )
            {}

            void handleTerminator( const llr::Ret& t );
            void handleTerminator( const llr::Branch& t );
            void handleTerminator( const llr::CondBranch& t );

            z3::solver m_solver;
            Builder m_builder;

            const builtins::Func& m_func;

            // For now we don't attempt to handle loops. We only keep track
            // of which basic blocks we already converted to formulas to avoid
            // doing them again.
            unordered_set< uint32_t > m_convertedBBIndices;
    };
}
Changes to bs/analyze/meson.build.
1
2
3

4
5
6
7
8
9
goose_analyze = library( 'goose-analyze',
    'analyze.cpp',
    'value.cpp',

    'verifstmtvalidator.cpp',
    'funcvalidator.cpp',

    include_directories: bsinc,
    dependencies: [fmt_dep, z3_dep]
)



>






1
2
3
4
5
6
7
8
9
10
goose_analyze = library( 'goose-analyze',
    'analyze.cpp',
    'value.cpp',
    'builder.cpp',
    'verifstmtvalidator.cpp',
    'funcvalidator.cpp',

    include_directories: bsinc,
    dependencies: [fmt_dep, z3_dep]
)
Changes to bs/builtins/types/func/func.cpp.
332
333
334
335
336
337
338


339
340
341
342
343
344
345
346
            // calls to DestroyValue() may also have requirements to enforce, so we'll need to emit
            // the eventual implicit return first.
            p.destroyAllLiveValues();
            p.emitTerminator( llr::Ret() );
        }

        pFuncLLR->body() = cfg;


        return true;
    }

    Term BuildArgListForCall( const FuncType& ft, const Term& unifiedArgs )
    {
        auto av = make_shared< Vector >();
        av->reserve( VecSize( ft.params() ) );








>
>
|







332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
            // calls to DestroyValue() may also have requirements to enforce, so we'll need to emit
            // the eventual implicit return first.
            p.destroyAllLiveValues();
            p.emitTerminator( llr::Ret() );
        }

        pFuncLLR->body() = cfg;

        analyze::FuncValidator fv( f );
        return fv.validate();
    }

    Term BuildArgListForCall( const FuncType& ft, const Term& unifiedArgs )
    {
        auto av = make_shared< Vector >();
        av->reserve( VecSize( ft.params() ) );