Goose  Changes On Branch cir-stack-language

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

Changes In Branch cir-stack-language Excluding Merge-Ins

This is equivalent to a diff from b8a2990900 to 9814f18b04

2022-06-29
21:47
The CIR is no longer made out of instructions that are basically expression trees, but is now instead a stack language to make it possible to insert verification instructions before and after the evaluation of individual function arguments check-in: 1f87fbda15 user: zlodo tags: trunk
21:39
  • Verifier: consider verification failed if we encountered anything funny that prevented us from translating the entire function cfg to z3, rather than silently ignoring it
  • Verifier: fixed a case where the verifier would improperly bail out and do the above
  • Verifier: fixed a nasty use-after-free caused by forgetting an ampersand. fucking c++
Closed-Leaf check-in: 9814f18b04 user: zlodo tags: cir-stack-language
2022-06-28
22:51
Removed an unusued file and a left over debugging profanity check-in: 6c5b747f5c user: zlodo tags: cir-stack-language
2022-05-26
22:48
Merge mac build fixes from trunk check-in: 536a94f712 user: zlodo tags: cir-stack-language
22:42
Mac build fixes check-in: b8a2990900 user: zlodo tags: trunk
2022-05-15
19:25
cir: implemented a helper object to decorate operations such as calls with a prologue and an epilogue, not used yet. check-in: 2503738366 user: zlodo tags: trunk

Changes to bs/builtins/builders/codebuilder.cpp.
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
    auto result = InvokeOverloadSet( c, c.env()->extDestroyValue(),
        MakeTuple( v ) );

    if( result.isPoison() )
        return false;

    if( !result.isConstant() )
        cfg()->currentBB()->emplace_back( *result.cir() );

    return true;
}

void CodeBuilder::extendValueLifetime( uint32_t index )
{
    // Find the live value.







|







119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
    auto result = InvokeOverloadSet( c, c.env()->extDestroyValue(),
        MakeTuple( v ) );

    if( result.isPoison() )
        return false;

    if( !result.isConstant() )
        cfg()->currentBB()->append( result );

    return true;
}

void CodeBuilder::extendValueLifetime( uint32_t index )
{
    // Find the live value.
Changes to bs/builtins/exprhelpers.h.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
#ifndef GOOSE_BUILTINS_EXPRHELPERS_H
#define GOOSE_BUILTINS_EXPRHELPERS_H

#include "parse/parse.h"

namespace goose::builtins::exprhelpers
{
    template< typename V >
    Value Not( V&& val )
    {

        return BuildComputedValue( GetValueType< bool >(),
            cir::Not( forward< V >( val ) ) ).setLocationId( val.locationId() );
    }

    template< typename L, typename R >
    Value Or( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::Or( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value And( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::And( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value Eq( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::Eq( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value Neq( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::Neq( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value UGT( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::UGT( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value UGE( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::UGE( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value ULT( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::ULT( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value ULE( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::ULE( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value SGT( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::SGT( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value SGE( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::SGE( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value SLT( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::SLT( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value SLE( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::SLE( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }
}

#endif










>

|







|







|







|







|







|







|







|







|







|







|







|







|




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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
#ifndef GOOSE_BUILTINS_EXPRHELPERS_H
#define GOOSE_BUILTINS_EXPRHELPERS_H

#include "parse/parse.h"

namespace goose::builtins::exprhelpers
{
    template< typename V >
    Value Not( V&& val )
    {
        auto locId = val.locationId();
        return BuildComputedValue( GetValueType< bool >(),
            forward< V >( val ), cir::Not( locId ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value Or( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            forward< L >( lhs ), forward< R >( rhs ), cir::Or( locId ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value And( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            forward< L >( lhs ), forward< R >( rhs ), cir::And( locId ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value Eq( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            forward< L >( lhs ), forward< R >( rhs ), cir::Eq( locId ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value Neq( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            forward< L >( lhs ), forward< R >( rhs ), cir::Neq( locId ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value UGT( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            forward< L >( lhs ), forward< R >( rhs ), cir::UGT( locId ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value UGE( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            forward< L >( lhs ), forward< R >( rhs ), cir::UGE( locId ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value ULT( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            forward< L >( lhs ), forward< R >( rhs ), cir::ULT( locId ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value ULE( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            forward< L >( lhs ), forward< R >( rhs ), cir::ULE( locId ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value SGT( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            forward< L >( lhs ), forward< R >( rhs ), cir::SGT( locId ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value SGE( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            forward< L >( lhs ), forward< R >( rhs ), cir::SGE( locId ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value SLT( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            forward< L >( lhs ), forward< R >( rhs ), cir::SLT( locId ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value SLE( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            forward< L >( lhs ), forward< R >( rhs ), cir::SLE( locId ) ).setLocationId( locId );
    }
}

#endif
Changes to bs/builtins/meson.build.
38
39
40
41
42
43
44
45
46
47








48
49
50
51
52
53
54

    'types/func/bfunc.cpp',
    'types/func/bintrinsic.cpp',
    'types/func/functype.cpp',
    'types/func/func.cpp',
    'types/func/build.cpp',
    'types/func/compile.cpp',
    'types/func/invoke.cpp',
    'types/func/typecheck.cpp',
    'types/func/lower.cpp',









    'types/template/tvec.cpp',
    'types/template/tvar.cpp',
    'types/template/ttvar.cpp',
    'types/template/tdecl.cpp',
    'types/template/tnameddecl.cpp',
    'types/template/tfunctype.cpp',







<


>
>
>
>
>
>
>
>







38
39
40
41
42
43
44

45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

    'types/func/bfunc.cpp',
    'types/func/bintrinsic.cpp',
    'types/func/functype.cpp',
    'types/func/func.cpp',
    'types/func/build.cpp',
    'types/func/compile.cpp',

    'types/func/typecheck.cpp',
    'types/func/lower.cpp',

    'types/func/invocation/beagerfunc.cpp',
    'types/func/invocation/bfunc.cpp',
    'types/func/invocation/bintrinsic.cpp',
    'types/func/invocation/common.cpp',
    'types/func/invocation/func.cpp',
    'types/func/invocation/ghostfunc.cpp',
    'types/func/invocation/intrinsic.cpp',

    'types/template/tvec.cpp',
    'types/template/tvar.cpp',
    'types/template/ttvar.cpp',
    'types/template/tdecl.cpp',
    'types/template/tnameddecl.cpp',
    'types/template/tfunctype.cpp',
Changes to bs/builtins/operators/arith.cpp.
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
            LeftAssInfixOp( "operator_add"_sid, precedence::AddSubOp,
                BuildGenericTupleOperator(),
                ForType< BigInt, Add >(),
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( lhs.type(),
                        Add( lhs, rhs ) );
                } )
            )
        );

        BuildParseRule( e, "-"_sid,
            PrefixOp( "operator_unary_minus"_sid, precedence::UnaryOps,
                BuildGenericTupleOperator(),
                ForType< BigInt >( []( auto&& c, auto&& operand ) -> Value
                {
                    return BuildComputedValue( GetValueType< BigInt >(),
                        Sub( ToValue( BigInt() ), operand ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                []( auto&& c, auto&& operand ) -> Value
                {
                    auto opTypeVal = *EIRToValue( operand.type() );
                    auto opType = *FromValue< IntegerType >( opTypeVal );
                    return BuildComputedValue( operand.type(),
                        Sub( Value( operand.type(), APSInt( opType.m_numBits, false ) ),
                            operand ) );
                } )
            ),
            LeftAssInfixOp( "operator_sub"_sid, precedence::AddSubOp,
                BuildGenericTupleOperator(),
                ForType< BigInt, Sub >(),
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( lhs.type(),
                        Sub( lhs, rhs ) );
                } )
            )
        );

        BuildParseRule( e, "*"_sid,
            LeftAssInfixOp( "operator_multiply"_sid, precedence::MulDivOp,
                BuildGenericTupleOperator(),
                ForType< BigInt, Mul >(),
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( lhs.type(),
                        Mul( lhs, rhs ) );
                } )
            )
        );

        BuildParseRule( e, "/"_sid,
            LeftAssInfixOp( "operator_divide"_sid, precedence::MulDivOp,
                BuildGenericTupleOperator(),







|










|







|
|









|












|







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
            LeftAssInfixOp( "operator_add"_sid, precedence::AddSubOp,
                BuildGenericTupleOperator(),
                ForType< BigInt, Add >(),
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, Add( c.locationId() ) );
                } )
            )
        );

        BuildParseRule( e, "-"_sid,
            PrefixOp( "operator_unary_minus"_sid, precedence::UnaryOps,
                BuildGenericTupleOperator(),
                ForType< BigInt >( []( auto&& c, auto&& operand ) -> Value
                {
                    return BuildComputedValue( GetValueType< BigInt >(),
                        ToValue( BigInt() ), operand, Sub( c.locationId() ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                []( auto&& c, auto&& operand ) -> Value
                {
                    auto opTypeVal = *EIRToValue( operand.type() );
                    auto opType = *FromValue< IntegerType >( opTypeVal );
                    return BuildComputedValue( operand.type(),
                        Value( operand.type(), APSInt( opType.m_numBits, false ) ),
                        operand, Sub( c.locationId() ) );
                } )
            ),
            LeftAssInfixOp( "operator_sub"_sid, precedence::AddSubOp,
                BuildGenericTupleOperator(),
                ForType< BigInt, Sub >(),
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, Sub( c.locationId() ) );
                } )
            )
        );

        BuildParseRule( e, "*"_sid,
            LeftAssInfixOp( "operator_multiply"_sid, precedence::MulDivOp,
                BuildGenericTupleOperator(),
                ForType< BigInt, Mul >(),
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, Mul( c.locationId() ) );
                } )
            )
        );

        BuildParseRule( e, "/"_sid,
            LeftAssInfixOp( "operator_divide"_sid, precedence::MulDivOp,
                BuildGenericTupleOperator(),
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
                    // to construct the assertion expression.
                    auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
                    auto cond = Neq( rhs, zeroValue );

                    DiagnosticsManager::GetInstance().defineCustomDiagnostic(
                        cond.locationId(), "assert"_sid, "the divisor may be 0." );

                    cfg->currentBB()->emplace_back(
                        cir::Assert( move( cond ) )
                    );

                    return BuildComputedValue( lhs.type(),
                        SDiv( lhs, rhs ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    using namespace goose::builtins::exprhelpers;

                    auto cfg = GetCFG( c );
                    assert( cfg );

                    // Build a zero constant of the same type as the denominator
                    // to construct the assertion expression.
                    auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
                    auto cond = Neq( rhs, zeroValue );

                    DiagnosticsManager::GetInstance().defineCustomDiagnostic(
                        cond.locationId(), "assert"_sid, "the divisor may be 0." );

                    cfg->currentBB()->emplace_back(
                        cir::Assert( move( cond ) )
                    );

                    return BuildComputedValue( lhs.type(),
                        UDiv( lhs, rhs ) );
                } )
            )
        );

        BuildParseRule( e, "%"_sid,
            LeftAssInfixOp( "operator_modulo"_sid, precedence::MulDivOp,
                BuildGenericTupleOperator(),







|
|



|

















|
|



|







96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
                    // to construct the assertion expression.
                    auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
                    auto cond = Neq( rhs, zeroValue );

                    DiagnosticsManager::GetInstance().defineCustomDiagnostic(
                        cond.locationId(), "assert"_sid, "the divisor may be 0." );

                    cfg->currentBB()->append(
                        move( cond ), cir::Assert( rhs.locationId() )
                    );

                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, SDiv( c.locationId() ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    using namespace goose::builtins::exprhelpers;

                    auto cfg = GetCFG( c );
                    assert( cfg );

                    // Build a zero constant of the same type as the denominator
                    // to construct the assertion expression.
                    auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
                    auto cond = Neq( rhs, zeroValue );

                    DiagnosticsManager::GetInstance().defineCustomDiagnostic(
                        cond.locationId(), "assert"_sid, "the divisor may be 0." );

                    cfg->currentBB()->append(
                        move( cond ), cir::Assert( rhs.locationId() )
                    );

                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, UDiv( c.locationId() ) );
                } )
            )
        );

        BuildParseRule( e, "%"_sid,
            LeftAssInfixOp( "operator_modulo"_sid, precedence::MulDivOp,
                BuildGenericTupleOperator(),
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
                    // to construct the assertion expression.
                    auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
                    auto cond = Neq( rhs, zeroValue );

                    DiagnosticsManager::GetInstance().defineCustomDiagnostic(
                        cond.locationId(), "assert"_sid, "the divisor may be 0." );

                    cfg->currentBB()->emplace_back(
                        cir::Assert( move( cond ) )
                    );

                    return BuildComputedValue( lhs.type(),
                        SRem( lhs, rhs ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    using namespace goose::builtins::exprhelpers;

                    auto cfg = GetCFG( c );
                    assert( cfg );

                    // Build a zero constant of the same type as the denominator
                    // to construct the assertion expression.
                    auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
                    auto cond = Neq( rhs, zeroValue );

                    DiagnosticsManager::GetInstance().defineCustomDiagnostic(
                        cond.locationId(), "assert"_sid, "the divisor may be 0." );

                    cfg->currentBB()->emplace_back(
                        cir::Assert( move( cond ) )
                    );

                    return BuildComputedValue( lhs.type(),
                        URem( lhs, rhs ) );
                } )
            )
        );
    }
}







|
|



|

















|
|



|





149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
                    // to construct the assertion expression.
                    auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
                    auto cond = Neq( rhs, zeroValue );

                    DiagnosticsManager::GetInstance().defineCustomDiagnostic(
                        cond.locationId(), "assert"_sid, "the divisor may be 0." );

                    cfg->currentBB()->append(
                        move( cond ), cir::Assert( rhs.locationId() )
                    );

                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, SRem( c.locationId() ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    using namespace goose::builtins::exprhelpers;

                    auto cfg = GetCFG( c );
                    assert( cfg );

                    // Build a zero constant of the same type as the denominator
                    // to construct the assertion expression.
                    auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
                    auto cond = Neq( rhs, zeroValue );

                    DiagnosticsManager::GetInstance().defineCustomDiagnostic(
                        cond.locationId(), "assert"_sid, "the divisor may be 0." );

                    cfg->currentBB()->append(
                        move( cond ), cir::Assert( rhs.locationId() )
                    );

                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, URem( c.locationId() ) );
                } )
            )
        );
    }
}
Changes to bs/builtins/operators/assignment.cpp.
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
            auto cfg = GetCFG( c );
            if( !cfg )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( 0, "assignments are not allowed here." );
                return PoisonValue();
            }
            else if( auto bb = cfg->currentBB() )
                bb->emplace_back( Store( lhs.cir(), refType.type(), rhs, lhs.locationId() ) );

            // Return the ref to support chained assignments, like in c++.
            return Value( lhs );
        };

        // Generic function to assign a value to a decl (local variable declaration)
        auto DeclAssignment = []( auto&& c, auto&& lhs, auto&& rhs ) -> Value







|







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
            auto cfg = GetCFG( c );
            if( !cfg )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( 0, "assignments are not allowed here." );
                return PoisonValue();
            }
            else if( auto bb = cfg->currentBB() )
                bb->append( lhs, rhs, Store( refType.type(), rhs.locationId(), lhs.locationId() ) );

            // Return the ref to support chained assignments, like in c++.
            return Value( lhs );
        };

        // Generic function to assign a value to a decl (local variable declaration)
        auto DeclAssignment = []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
135
136
137
138
139
140
141




142
143
144
145
146
147
148
149
150
151
152






153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
                return PoisonValue();

            return result;
        };

        auto RTTupleAssignment = [assOp]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
        {




            auto tupSize = TupleSize( lhs );

            auto tupRefType = *FromValue< ReferenceType >( *EIRToValue( rhs.type() ) );
            auto rhsTupType = *EIRToValue( tupRefType.type() );

            if( tupSize != TupleTypeSize( rhsTupType ) )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage( 0, "incompatible tuple sizes." );
                return PoisonValue();
            }







            // Load the values from the rhs tuple into temporary vars.
            vector< Value > tempVars;
            tempVars.reserve( tupSize );

            uint32_t index = 0;

            bool success = true;
            ForEachInTupleType( rhsTupType, [&]( auto&& srcType )
            {
                auto rt = ValueToEIR( ToValue( ReferenceType( srcType, ConstAccessSpecifer() ) ) );
                auto srcVal = BuildComputedValue( rt, Select( rhs.cir(), index++ ) );

                // We go at very high level to construct the temporary variables by resolving an invocation
                // of the assignment of the rhs value to a TNamedDecl with an empty name.
                // The reason we need to work at such high level is so that the initializer value gets
                // resolved exactly like if it was a regular assignment. In particular, if the value in question
                // is a locvar, we want it to go through the type checking process to be replaced with its content.
                // This is the simplest and most robust way to achieve this, which should honor every relevant







>
>
>
>











>
>
>
>
>
>










|







135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
                return PoisonValue();

            return result;
        };

        auto RTTupleAssignment = [assOp]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
        {
            auto cfg = GetCFG( c );
            if( !cfg )
                return PoisonValue();

            auto tupSize = TupleSize( lhs );

            auto tupRefType = *FromValue< ReferenceType >( *EIRToValue( rhs.type() ) );
            auto rhsTupType = *EIRToValue( tupRefType.type() );

            if( tupSize != TupleTypeSize( rhsTupType ) )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage( 0, "incompatible tuple sizes." );
                return PoisonValue();
            }

            // Store the source tuple as a temporary, otherwise we're going to recompute rhs
            // for each member assigned (which is bad)
            auto rhsIndex = cfg->getNewTemporaryIndex();
            cfg->currentBB()->append( rhs, CreateTemporary( rhsIndex, rhs.locationId() ) );
            auto srcTup = BuildComputedValue( rhs.type(), GetTemporary( rhs.type(), rhsIndex, rhs.locationId() ) );

            // Load the values from the rhs tuple into temporary vars.
            vector< Value > tempVars;
            tempVars.reserve( tupSize );

            uint32_t index = 0;

            bool success = true;
            ForEachInTupleType( rhsTupType, [&]( auto&& srcType )
            {
                auto rt = ValueToEIR( ToValue( ReferenceType( srcType, ConstAccessSpecifer() ) ) );
                auto srcVal = BuildComputedValue( rt, srcTup, Select( index++, ( c.locationId() ) ) );

                // We go at very high level to construct the temporary variables by resolving an invocation
                // of the assignment of the rhs value to a TNamedDecl with an empty name.
                // The reason we need to work at such high level is so that the initializer value gets
                // resolved exactly like if it was a regular assignment. In particular, if the value in question
                // is a locvar, we want it to go through the type checking process to be replaced with its content.
                // This is the simplest and most robust way to achieve this, which should honor every relevant
Changes to bs/builtins/operators/comparison.cpp.
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
114
115
116
117
118
119
                ForType< BigInt, Eq, bool >(),
                ForType< char32_t, Eq, bool >(),
                ForType< string, Eq, bool >(),
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        Eq( lhs, rhs ) );
                } )
            )
        );

        BuildParseRule( e, "!="_sid,
            LeftAssInfixOp( "operator_not_equals"_sid, precedence::EqualityOp,
                ForType< bool, Neq, bool >(),
                ForType< BigInt, Neq, bool >(),
                ForType< char32_t, Neq, bool >(),
                ForType< string, Neq, bool >(),
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        Neq( lhs, rhs ) );
                } )
            )
        );

        BuildParseRule( e, ">"_sid,
            LeftAssInfixOp( "operator_greater"_sid, precedence::GreaterLesserOp,
                ForType< BigInt, SGT, bool >(),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        SGT( lhs, rhs ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        UGT( lhs, rhs ) );
                } )
            )
        );

        BuildParseRule( e, ">="_sid,
            LeftAssInfixOp( "operator_greater_or_equals"_sid, precedence::GreaterLesserOp,
                ForType< BigInt, SGE, bool >(),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        SGE( lhs, rhs ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        UGE( lhs, rhs ) );
                } )
            )
        );

        BuildParseRule( e, "<"_sid,
            LeftAssInfixOp( "operator_lesser"_sid, precedence::GreaterLesserOp,
                ForType< BigInt, SLT, bool >(),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        SLT( lhs, rhs ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        ULT( lhs, rhs ) );
                } )
            )
        );

        BuildParseRule( e, "<="_sid,
            LeftAssInfixOp( "operator_lesser_or_equals"_sid, precedence::GreaterLesserOp,
                ForType< BigInt, SLE, bool >(),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        SLE( lhs, rhs ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        ULE( lhs, rhs ) );
                } )
            )
        );
    }
}







|














|











|





|











|





|











|





|











|





|





20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
114
115
116
117
118
119
                ForType< BigInt, Eq, bool >(),
                ForType< char32_t, Eq, bool >(),
                ForType< string, Eq, bool >(),
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        lhs, rhs, Eq( c.locationId() ) );
                } )
            )
        );

        BuildParseRule( e, "!="_sid,
            LeftAssInfixOp( "operator_not_equals"_sid, precedence::EqualityOp,
                ForType< bool, Neq, bool >(),
                ForType< BigInt, Neq, bool >(),
                ForType< char32_t, Neq, bool >(),
                ForType< string, Neq, bool >(),
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        lhs, rhs, Neq( c.locationId() ) );
                } )
            )
        );

        BuildParseRule( e, ">"_sid,
            LeftAssInfixOp( "operator_greater"_sid, precedence::GreaterLesserOp,
                ForType< BigInt, SGT, bool >(),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        lhs, rhs, SGT( c.locationId() ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        lhs, rhs, UGT( c.locationId() ) );
                } )
            )
        );

        BuildParseRule( e, ">="_sid,
            LeftAssInfixOp( "operator_greater_or_equals"_sid, precedence::GreaterLesserOp,
                ForType< BigInt, SGE, bool >(),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        lhs, rhs, SGE( c.locationId() ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        lhs, rhs, UGE( c.locationId() ) );
                } )
            )
        );

        BuildParseRule( e, "<"_sid,
            LeftAssInfixOp( "operator_lesser"_sid, precedence::GreaterLesserOp,
                ForType< BigInt, SLT, bool >(),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        lhs, rhs, SLT( c.locationId() ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        lhs, rhs, ULT( c.locationId() ) );
                } )
            )
        );

        BuildParseRule( e, "<="_sid,
            LeftAssInfixOp( "operator_lesser_or_equals"_sid, precedence::GreaterLesserOp,
                ForType< BigInt, SLE, bool >(),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        lhs, rhs, SLE( c.locationId() ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        lhs, rhs, ULE( c.locationId() ) );
                } )
            )
        );
    }
}
Changes to bs/builtins/operators/dot.cpp.
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
                    if( index >= TupleTypeSize( tupType ) )
                    {
                        DiagnosticsManager::GetInstance().emitErrorMessage( rhs.locationId(),
                            "the index is out of range." );
                        return PoisonValue();
                    }

                    auto loc = Location::CreateSpanningLocation( lhs.locationId(), rhs.locationId() );
                    auto rt = ValueToEIR( ToValue( ReferenceType( GetTupleTypeElement( tupType, index ), refType.behavior() ) ) );
                    return BuildComputedValue( rt, Select( lhs.cir(), index ) ).setLocationId( loc );
                } )
            )
        );
    }
}







<

|





36
37
38
39
40
41
42

43
44
45
46
47
48
49
                    if( index >= TupleTypeSize( tupType ) )
                    {
                        DiagnosticsManager::GetInstance().emitErrorMessage( rhs.locationId(),
                            "the index is out of range." );
                        return PoisonValue();
                    }


                    auto rt = ValueToEIR( ToValue( ReferenceType( GetTupleTypeElement( tupType, index ), refType.behavior() ) ) );
                    return BuildComputedValue( rt, lhs, Select( index, c.locationId() ) );
                } )
            )
        );
    }
}
Changes to bs/builtins/operators/helpers.h.
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
114
115
    auto ForType()
    {
        return []< typename tag >( auto&& e, auto&& pOvlSet, tag t )
        {
            using intrinsicType = Intrinsic< Value ( T, T ) >;
            auto intrinsicFunc = []( const Context& c, const Value& lhs, const Value& rhs )
            {
                return BuildComputedValue( GetValueType< RT >(), I( lhs, rhs ) );
            };

            pOvlSet->add( e, ToValue< intrinsicType >( move( intrinsicFunc ) ), GetFuncInvocationRule() );
        };
    }

    template< typename T, typename F >
    auto ForType( F&& func )
    {
        return [&]< typename tag >( auto&& e, auto&& pOvlSet, tag t )
        {
            if constexpr( is_same_v< tag, UnaryOpTag > )
            {
                using intrinsicType = Intrinsic< Value ( T ) >;
                pOvlSet->add( e, ToValue< intrinsicType >( forward< F >( func ) ), GetFuncInvocationRule() );
            }
            else
            {
                using intrinsicType = Intrinsic< Value ( T, T ) >;
                pOvlSet->add( e, ToValue< intrinsicType >( forward< F >( func ) ), GetFuncInvocationRule() );
            }
        };
    }

    template< typename T1, typename T2, typename F >
    auto ForTypes( F&& func )
    {
        return [=]< typename tag >( auto&& e, auto&& pOvlSet, tag t )
        {
            using intrinsicType = Intrinsic< Value ( T1, T2 ) >;
            pOvlSet->add( e, ToValue< intrinsicType >( func ), GetFuncInvocationRule() );
        };
    }
}

#endif







|


|











|




|










|





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
114
115
    auto ForType()
    {
        return []< typename tag >( auto&& e, auto&& pOvlSet, tag t )
        {
            using intrinsicType = Intrinsic< Value ( T, T ) >;
            auto intrinsicFunc = []( const Context& c, const Value& lhs, const Value& rhs )
            {
                return BuildComputedValue( GetValueType< RT >(), lhs, rhs, I( ( c.locationId() ) ) );
            };

            pOvlSet->add( e, ToValue< intrinsicType >( move( intrinsicFunc ) ), GetBuiltinIntrinsicFuncInvocationRule() );
        };
    }

    template< typename T, typename F >
    auto ForType( F&& func )
    {
        return [&]< typename tag >( auto&& e, auto&& pOvlSet, tag t )
        {
            if constexpr( is_same_v< tag, UnaryOpTag > )
            {
                using intrinsicType = Intrinsic< Value ( T ) >;
                pOvlSet->add( e, ToValue< intrinsicType >( forward< F >( func ) ), GetBuiltinIntrinsicFuncInvocationRule() );
            }
            else
            {
                using intrinsicType = Intrinsic< Value ( T, T ) >;
                pOvlSet->add( e, ToValue< intrinsicType >( forward< F >( func ) ), GetBuiltinIntrinsicFuncInvocationRule() );
            }
        };
    }

    template< typename T1, typename T2, typename F >
    auto ForTypes( F&& func )
    {
        return [=]< typename tag >( auto&& e, auto&& pOvlSet, tag t )
        {
            using intrinsicType = Intrinsic< Value ( T1, T2 ) >;
            pOvlSet->add( e, ToValue< intrinsicType >( func ), GetBuiltinIntrinsicFuncInvocationRule() );
        };
    }
}

#endif
Changes to bs/builtins/operators/logic.cpp.
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
44
45
46
47
48
49
50
51
52
53
54
55
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
        BuildParseRule( e, "!"_sid,
            PrefixOp( "operator_logical_not"_sid, precedence::UnaryOps,
                BuildGenericTupleOperator(),

                ForType< bool >( []( auto&& c, auto&& operand ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        cir::Not( operand ) );
                } )
            )
        );

        BuildParseRule( e, "~"_sid,
            PrefixOp( "operator_bitwise_not"_sid, precedence::UnaryOps,
                BuildGenericTupleOperator(),

                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >( []( auto&& c, auto&& operand ) -> Value
                {
                    auto opTypeVal = *EIRToValue( operand.type() );
                    auto opType = *FromValue< IntegerType >( opTypeVal );
                    return BuildComputedValue( operand.type(),
                        Xor( operand,
                            Value( operand.type(), APSInt::getMaxValue( opType.m_numBits, true ) )

                        ) );
                } )
            )
        );

        BuildParseRule( e, "^"_sid,
            LeftAssInfixOp( "operator_xor"_sid, precedence::OrOp,
                BuildGenericTupleOperator(),

                // Logical xor
                ForType< bool, Xor >(),

                // ct_int xor
                ForType< BigInt >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    if( !lhs.isConstant() || !rhs.isConstant() )
                    {
                        auto loc = Location::CreateSpanningLocation( lhs.locationId(), rhs.locationId() );
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( loc, "bitwise operations between ct_int values are only allowed on constants." );
                        return PoisonValue();
                    }

                    return BuildComputedValue( lhs.type(),
                        Xor( lhs, rhs ) );
                } ),

                // runtime integer xor, defined to work for any two integers of same
                // bit size and signedness.
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( lhs.type(),
                        Xor( lhs, rhs ) );
                } )
            )
        );

        BuildParseRule( e, "|"_sid,
            LeftAssInfixOp( "operator_or"_sid, precedence::OrOp,
                BuildGenericTupleOperator(),

                // ct_int or
                ForType< BigInt >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    if( !lhs.isConstant() || !rhs.isConstant() )
                    {
                        auto loc = Location::CreateSpanningLocation( lhs.locationId(), rhs.locationId() );
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( loc, "bitwise operations between ct_int values are only allowed on constants." );
                        return PoisonValue();
                    }

                    return BuildComputedValue( lhs.type(),
                        Or( lhs, rhs ) );
                } ),

                // runtime integer or, defined to work for any two integers of same
                // bit size and signedness.
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( lhs.type(),
                        Or( lhs, rhs ) );
                } ),

                // bool or
                ForType< bool >( [orOp]< typename L, typename R >( auto&& c, L&& lhs, R&& rhs ) -> Value
                {
                    // Handle the case where lhs is constant, so that
                    // the result gets properly eagerly evaluated (in case







|













<
|
>
|

















<
|




|








|




















|








|







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
44
45
46
47
48
49
50
51
52
53
54
55
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
        BuildParseRule( e, "!"_sid,
            PrefixOp( "operator_logical_not"_sid, precedence::UnaryOps,
                BuildGenericTupleOperator(),

                ForType< bool >( []( auto&& c, auto&& operand ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        operand, cir::Not( c.locationId() ) );
                } )
            )
        );

        BuildParseRule( e, "~"_sid,
            PrefixOp( "operator_bitwise_not"_sid, precedence::UnaryOps,
                BuildGenericTupleOperator(),

                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >( []( auto&& c, auto&& operand ) -> Value
                {
                    auto opTypeVal = *EIRToValue( operand.type() );
                    auto opType = *FromValue< IntegerType >( opTypeVal );
                    return BuildComputedValue( operand.type(),

                        operand, Value( operand.type(), APSInt::getMaxValue( opType.m_numBits, true ) ),
                        Xor( c.locationId() )
                    );
                } )
            )
        );

        BuildParseRule( e, "^"_sid,
            LeftAssInfixOp( "operator_xor"_sid, precedence::OrOp,
                BuildGenericTupleOperator(),

                // Logical xor
                ForType< bool, Xor >(),

                // ct_int xor
                ForType< BigInt >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    if( !lhs.isConstant() || !rhs.isConstant() )
                    {

                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( c.locationId(), "bitwise operations between ct_int values are only allowed on constants." );
                        return PoisonValue();
                    }

                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, Xor( c.locationId() ) );
                } ),

                // runtime integer xor, defined to work for any two integers of same
                // bit size and signedness.
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, Xor( c.locationId() ) );
                } )
            )
        );

        BuildParseRule( e, "|"_sid,
            LeftAssInfixOp( "operator_or"_sid, precedence::OrOp,
                BuildGenericTupleOperator(),

                // ct_int or
                ForType< BigInt >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    if( !lhs.isConstant() || !rhs.isConstant() )
                    {
                        auto loc = Location::CreateSpanningLocation( lhs.locationId(), rhs.locationId() );
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( loc, "bitwise operations between ct_int values are only allowed on constants." );
                        return PoisonValue();
                    }

                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, Or( c.locationId() ) );
                } ),

                // runtime integer or, defined to work for any two integers of same
                // bit size and signedness.
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, Or( c.locationId() ) );
                } ),

                // bool or
                ForType< bool >( [orOp]< typename L, typename R >( auto&& c, L&& lhs, R&& rhs ) -> Value
                {
                    // Handle the case where lhs is constant, so that
                    // the result gets properly eagerly evaluated (in case
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151

152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
                } )
            )
        );

        RegisterBuiltinFunc< Intrinsic< bool ( Value, bool, bool ) > >( e, orOp,
            []( auto&& c, auto&& b, auto&& lhs, auto&& rhs )
            {
                return BuildComputedValue( GetValueType< bool >(), Or( lhs, rhs ) );
            } );

        RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< CodeBuilder > >, bool, bool ) > >( e, orOp,
            []( auto&& c, auto&& b, auto&& lhs, auto&& rhs )
            {
                auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( b );
                const auto& cfg = cb->cfg();

                // Build the control flow for shortcut evaluation.
                const auto& predBB = cfg->currentBB();

                auto pRhsBB = cfg->createBB();
                auto pSuccBB = cfg->createBB();

                // If the lhs is true, skip to the end directly.
                // Otherwise, jump to the BB that computes rhs.

                predBB->setTerminator( CondBranch( lhs, pSuccBB, pRhsBB ) );

                auto rhsIndex = cfg->getNewTemporaryIndex();
                pRhsBB->emplace_back( CreateTemporary( rhsIndex, rhs ) );
                pRhsBB->setTerminator( Branch( pSuccBB ) );

                auto resultIndex = cfg->getNewTemporaryIndex();

                // Build the Phi instruction that will collect the final result.
                auto phi = Phi( *EIRToValue( GetValueType< bool >() ), 2,
                    resultIndex );

                // If coming directly from the lhs BB, we know the result is true.
                phi.setIncoming( predBB, ToValue( true ) );

                // Otherwise, the result is whatever was computed by the rhs block.
                phi.setIncoming( pRhsBB, BuildComputedValue( GetValueType< bool >(),
                    GetTemporary( GetValueType< bool >(), rhsIndex ) ) );

                pSuccBB->emplace_back( move( phi ) );
                cfg->setCurrentBB( pSuccBB );

                // Build the result val which pulls the temporary created above.
                return BuildComputedValue( GetValueType< bool >(),
                    GetTemporary( GetValueType< bool >(), resultIndex ) );
            } );

        BuildParseRule( e, "&"_sid,
            LeftAssInfixOp( "operator_and"_sid, precedence::AndOp,
                BuildGenericTupleOperator(),

                // ct_int and
                ForType< BigInt >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    if( !lhs.isConstant() || !rhs.isConstant() )
                    {
                        auto loc = Location::CreateSpanningLocation( lhs.locationId(), rhs.locationId() );
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( loc, "bitwise operations between ct_int values are only allowed on constants." );
                        return PoisonValue();
                    }

                    return BuildComputedValue( lhs.type(),
                        And( lhs, rhs ) );
                } ),

                // runtime integer and, defined to work for any two integers of same
                // bit size and signedness.
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( lhs.type(),
                        And( lhs, rhs ) );
                } ),

                // bool and
                ForType< bool >( [andOp]< typename L, typename R >( auto&& c, L&& lhs, R&& rhs ) -> Value
                {
                    // Handle the case where lhs is constant, so that
                    // the result gets properly eagerly evaluated (in case







|
















>
|


|






|






|

|




|












<
|




|








|







127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188

189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
                } )
            )
        );

        RegisterBuiltinFunc< Intrinsic< bool ( Value, bool, bool ) > >( e, orOp,
            []( auto&& c, auto&& b, auto&& lhs, auto&& rhs )
            {
                return BuildComputedValue( GetValueType< bool >(), lhs, rhs, Or( c.locationId() ) );
            } );

        RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< CodeBuilder > >, bool, bool ) > >( e, orOp,
            []( auto&& c, auto&& b, auto&& lhs, auto&& rhs )
            {
                auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( b );
                const auto& cfg = cb->cfg();

                // Build the control flow for shortcut evaluation.
                const auto& predBB = cfg->currentBB();

                auto pRhsBB = cfg->createBB();
                auto pSuccBB = cfg->createBB();

                // If the lhs is true, skip to the end directly.
                // Otherwise, jump to the BB that computes rhs.
                predBB->append( lhs );
                predBB->setTerminator( CondBranch( pSuccBB, pRhsBB ) );

                auto rhsIndex = cfg->getNewTemporaryIndex();
                pRhsBB->append( rhs, CreateTemporary( rhsIndex, c.locationId() ) );
                pRhsBB->setTerminator( Branch( pSuccBB ) );

                auto resultIndex = cfg->getNewTemporaryIndex();

                // Build the Phi instruction that will collect the final result.
                auto phi = Phi( *EIRToValue( GetValueType< bool >() ), 2,
                    resultIndex, c.locationId() );

                // If coming directly from the lhs BB, we know the result is true.
                phi.setIncoming( predBB, ToValue( true ) );

                // Otherwise, the result is whatever was computed by the rhs block.
                phi.setIncoming( pRhsBB, BuildComputedValue( GetValueType< bool >(),
                    GetTemporary( GetValueType< bool >(), rhsIndex, c.locationId() ) ) );

                pSuccBB->append( move( phi ) );
                cfg->setCurrentBB( pSuccBB );

                // Build the result val which pulls the temporary created above.
                return BuildComputedValue( GetValueType< bool >(),
                    GetTemporary( GetValueType< bool >(), resultIndex, c.locationId() ) );
            } );

        BuildParseRule( e, "&"_sid,
            LeftAssInfixOp( "operator_and"_sid, precedence::AndOp,
                BuildGenericTupleOperator(),

                // ct_int and
                ForType< BigInt >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    if( !lhs.isConstant() || !rhs.isConstant() )
                    {

                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( c.locationId(), "bitwise operations between ct_int values are only allowed on constants." );
                        return PoisonValue();
                    }

                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, And( c.locationId() ) );
                } ),

                // runtime integer and, defined to work for any two integers of same
                // bit size and signedness.
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, And( c.locationId() ) );
                } ),

                // bool and
                ForType< bool >( [andOp]< typename L, typename R >( auto&& c, L&& lhs, R&& rhs ) -> Value
                {
                    // Handle the case where lhs is constant, so that
                    // the result gets properly eagerly evaluated (in case
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251

252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
                } )
            )
        );

        RegisterBuiltinFunc< Intrinsic< bool ( Value, bool, bool ) > >( e, andOp,
            []( auto&& c, auto&& b, auto&& lhs, auto&& rhs )
            {
                return BuildComputedValue( GetValueType< bool >(), And( lhs, rhs ) );
            } );

        RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< CodeBuilder > >, bool, bool ) > >( e, andOp,
            []( auto&& c, auto&& b, auto&& lhs, auto&& rhs )
            {
                auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( b );
                const auto& cfg = cb->cfg();

                // Build the control flow for shortcut evaluation.
                const auto& predBB = cfg->currentBB();

                auto pRhsBB = cfg->createBB();
                auto pSuccBB = cfg->createBB();

                // If the lhs is false, skip to the end directly.
                // Otherwise, jump to the BB that computes rhs.

                predBB->setTerminator( CondBranch( lhs, pRhsBB, pSuccBB ) );

                auto rhsIndex = cfg->getNewTemporaryIndex();
                pRhsBB->emplace_back( CreateTemporary( rhsIndex, rhs ) );
                pRhsBB->setTerminator( Branch( pSuccBB ) );

                auto resultIndex = cfg->getNewTemporaryIndex();

                // Build the Phi instruction that will collect the final result.
                auto phi = Phi( *EIRToValue( GetValueType< bool >() ), 2,
                    resultIndex );

                // If coming directly from the lhs BB, we know the result is false.
                phi.setIncoming( predBB, ToValue( false ) );

                // Otherwise, the result is whatever was computed by the rhs block.
                phi.setIncoming( pRhsBB, BuildComputedValue( GetValueType< bool >(),
                    GetTemporary( GetValueType< bool >(), rhsIndex ) ) );

                pSuccBB->emplace_back( move( phi ) );
                cfg->setCurrentBB( pSuccBB );

                // Build the result val which pulls the temporary created above.
                return BuildComputedValue( GetValueType< bool >(),
                    GetTemporary( GetValueType< bool >(), resultIndex ) );
            } );

        BuildParseRule( e, "<<"_sid,
            LeftAssInfixOp( "operator_shift_left"_sid, precedence::BitShiftOp,
                BuildGenericTupleOperator(),

                // ct_int left shift
                ForTypes< BigInt, uint32_t >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    if( !lhs.isConstant() || !rhs.isConstant() )
                    {
                        auto loc = Location::CreateSpanningLocation( lhs.locationId(), rhs.locationId() );
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( loc, "bitwise operations between ct_int values are only allowed on constants." );
                        return PoisonValue();
                    }

                    return BuildComputedValue( lhs.type(),
                        Shl( lhs, rhs ) );
                } ),

                // runtime integer left shift.
                ForTypes< CustomPattern< IntegerType, IntegerType::Pattern >,
                    CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    using namespace goose::builtins::exprhelpers;

                    auto cfg = GetCFG( c );
                    assert( cfg );

                    // Shifting for a number of bits equal or larger than the bitsize
                    // of lhs is an undefined behavior, so we require verification that
                    // it won't happen.
                    // Extract the integer type of lhs to retreieve its bit size.
                    auto lhsType = *FromValue< IntegerType >( *EIRToValue( lhs.type() ) );
                    auto bitSizeValue = Value( rhs.type(), APSInt::get( lhsType.m_numBits) );

                    auto cond = ULT( rhs, bitSizeValue );

                    DiagnosticsManager::GetInstance().defineCustomDiagnostic(
                        cond.locationId(), "assert"_sid, "the shift amount may be equal or greater than the bitsize." );

                    cfg->currentBB()->emplace_back(
                        cir::Assert( move( cond ) )
                    );

                    return BuildComputedValue( lhs.type(),
                        Shl( lhs, rhs ) );
                } )
            )
        );

        BuildParseRule( e, ">>"_sid,
            LeftAssInfixOp( "operator_shift_right"_sid, precedence::BitShiftOp,
                BuildGenericTupleOperator(),

                // ct_int right shift
                ForTypes< BigInt, uint32_t >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    if( !lhs.isConstant() || !rhs.isConstant() )
                    {
                        auto loc = Location::CreateSpanningLocation( lhs.locationId(), rhs.locationId() );
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( loc, "bitwise operations between ct_int values are only allowed on constants." );
                        return PoisonValue();
                    }

                    return BuildComputedValue( lhs.type(),
                        AShr( lhs, rhs ) );
                } ),

                // runtime signed integer right shift, defined to work for any two integers of same
                // bit size.
                ForTypes< CustomPattern< IntegerType, IntegerType::PatternSigned >,
                    CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value







|
















>
|


|






|






|

|




|


















|















|

|






|
|



|














<
|




|







227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339

340
341
342
343
344
345
346
347
348
349
350
351
352
                } )
            )
        );

        RegisterBuiltinFunc< Intrinsic< bool ( Value, bool, bool ) > >( e, andOp,
            []( auto&& c, auto&& b, auto&& lhs, auto&& rhs )
            {
                return BuildComputedValue( GetValueType< bool >(), lhs, rhs, And( c.locationId() ) );
            } );

        RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< CodeBuilder > >, bool, bool ) > >( e, andOp,
            []( auto&& c, auto&& b, auto&& lhs, auto&& rhs )
            {
                auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( b );
                const auto& cfg = cb->cfg();

                // Build the control flow for shortcut evaluation.
                const auto& predBB = cfg->currentBB();

                auto pRhsBB = cfg->createBB();
                auto pSuccBB = cfg->createBB();

                // If the lhs is false, skip to the end directly.
                // Otherwise, jump to the BB that computes rhs.
                predBB->append( lhs );
                predBB->setTerminator( CondBranch( pRhsBB, pSuccBB ) );

                auto rhsIndex = cfg->getNewTemporaryIndex();
                pRhsBB->append( rhs, CreateTemporary( rhsIndex, c.locationId() ) );
                pRhsBB->setTerminator( Branch( pSuccBB ) );

                auto resultIndex = cfg->getNewTemporaryIndex();

                // Build the Phi instruction that will collect the final result.
                auto phi = Phi( *EIRToValue( GetValueType< bool >() ), 2,
                    resultIndex, c.locationId() );

                // If coming directly from the lhs BB, we know the result is false.
                phi.setIncoming( predBB, ToValue( false ) );

                // Otherwise, the result is whatever was computed by the rhs block.
                phi.setIncoming( pRhsBB, BuildComputedValue( GetValueType< bool >(),
                    GetTemporary( GetValueType< bool >(), rhsIndex, c.locationId() ) ) );

                pSuccBB->append( move( phi ) );
                cfg->setCurrentBB( pSuccBB );

                // Build the result val which pulls the temporary created above.
                return BuildComputedValue( GetValueType< bool >(),
                    GetTemporary( GetValueType< bool >(), resultIndex, c.locationId() ) );
            } );

        BuildParseRule( e, "<<"_sid,
            LeftAssInfixOp( "operator_shift_left"_sid, precedence::BitShiftOp,
                BuildGenericTupleOperator(),

                // ct_int left shift
                ForTypes< BigInt, uint32_t >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    if( !lhs.isConstant() || !rhs.isConstant() )
                    {
                        auto loc = Location::CreateSpanningLocation( lhs.locationId(), rhs.locationId() );
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( loc, "bitwise operations between ct_int values are only allowed on constants." );
                        return PoisonValue();
                    }

                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, Shl( c.locationId() ) );
                } ),

                // runtime integer left shift.
                ForTypes< CustomPattern< IntegerType, IntegerType::Pattern >,
                    CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    using namespace goose::builtins::exprhelpers;

                    auto cfg = GetCFG( c );
                    assert( cfg );

                    // Shifting for a number of bits equal or larger than the bitsize
                    // of lhs is an undefined behavior, so we require verification that
                    // it won't happen.
                    // Extract the integer type of lhs to retrieve its bit size.
                    auto lhsType = *FromValue< IntegerType >( *EIRToValue( lhs.type() ) );
                    auto bitSizeValue = Value( rhs.type(), APSInt::get( lhsType.m_numBits ) );

                    auto cond = ULT( rhs, bitSizeValue );

                    DiagnosticsManager::GetInstance().defineCustomDiagnostic(
                        cond.locationId(), "assert"_sid, "the shift amount may be equal or greater than the bitsize." );

                    cfg->currentBB()->append(
                        move( cond ), cir::Assert( rhs.locationId() )
                    );

                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, Shl( c.locationId() ) );
                } )
            )
        );

        BuildParseRule( e, ">>"_sid,
            LeftAssInfixOp( "operator_shift_right"_sid, precedence::BitShiftOp,
                BuildGenericTupleOperator(),

                // ct_int right shift
                ForTypes< BigInt, uint32_t >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    if( !lhs.isConstant() || !rhs.isConstant() )
                    {

                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( c.locationId(), "bitwise operations between ct_int values are only allowed on constants." );
                        return PoisonValue();
                    }

                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, AShr( c.locationId() ) );
                } ),

                // runtime signed integer right shift, defined to work for any two integers of same
                // bit size.
                ForTypes< CustomPattern< IntegerType, IntegerType::PatternSigned >,
                    CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
                    auto bitSizeValue = Value( rhs.type(), APSInt::get( lhsType.m_numBits) );

                    auto cond = ULT( rhs, bitSizeValue );

                    DiagnosticsManager::GetInstance().defineCustomDiagnostic(
                        cond.locationId(), "assert"_sid, "the shift amount may be equal or greater than the bitsize." );

                    cfg->currentBB()->emplace_back(
                        cir::Assert( move( cond ) )
                    );

                    return BuildComputedValue( lhs.type(),
                        AShr( lhs, rhs ) );
                } ),

                // runtime unsigned integer right shift, defined to work for any two integers of same
                // bit size.
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    using namespace goose::builtins::exprhelpers;

                    auto cfg = GetCFG( c );
                    assert( cfg );

                    // Shifting for a number of bits equal or larger than the bitsize
                    // of lhs is an undefined behavior, so we require verification that
                    // it won't happen.
                    // Extract the integer type of lhs to retreieve its bit size.
                    auto lhsType = *FromValue< IntegerType >( *EIRToValue( lhs.type() ) );
                    auto bitSizeValue = Value( rhs.type(), APSInt::get( lhsType.m_numBits) );

                    auto cond = ULT( rhs, bitSizeValue );

                    DiagnosticsManager::GetInstance().defineCustomDiagnostic(
                        cond.locationId(), "assert"_sid, "the shift amount may be equal or greater than the bitsize." );

                    cfg->currentBB()->emplace_back(
                        cir::Assert( move( cond ) )
                    );

                    return BuildComputedValue( lhs.type(),
                        LShr( lhs, rhs ) );
                } )
            )
        );
    }
}







|
|



|

















|






|
|



|





364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
                    auto bitSizeValue = Value( rhs.type(), APSInt::get( lhsType.m_numBits) );

                    auto cond = ULT( rhs, bitSizeValue );

                    DiagnosticsManager::GetInstance().defineCustomDiagnostic(
                        cond.locationId(), "assert"_sid, "the shift amount may be equal or greater than the bitsize." );

                    cfg->currentBB()->append(
                        move( cond ), cir::Assert( rhs.locationId() )
                    );

                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, AShr( c.locationId() ) );
                } ),

                // runtime unsigned integer right shift, defined to work for any two integers of same
                // bit size.
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    using namespace goose::builtins::exprhelpers;

                    auto cfg = GetCFG( c );
                    assert( cfg );

                    // Shifting for a number of bits equal or larger than the bitsize
                    // of lhs is an undefined behavior, so we require verification that
                    // it won't happen.
                    // Extract the integer type of lhs to retreieve its bit size.
                    auto lhsType = *FromValue< IntegerType >( *EIRToValue( lhs.type() ) );
                    auto bitSizeValue = Value( rhs.type(), APSInt::get( lhsType.m_numBits ) );

                    auto cond = ULT( rhs, bitSizeValue );

                    DiagnosticsManager::GetInstance().defineCustomDiagnostic(
                        cond.locationId(), "assert"_sid, "the shift amount may be equal or greater than the bitsize." );

                    cfg->currentBB()->append(
                        move( cond ), cir::Assert( rhs.locationId() )
                    );

                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, LShr( c.locationId() ) );
                } )
            )
        );
    }
}
Changes to bs/builtins/operators/tuple.h.
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
                        result = AppendToTuple( result, r );
                        return true;
                    } );

                    return result;
                };

                pOvlSet->add( e, ToValue< intrinsicType >( move( intrinsicFunc ) ), GetFuncInvocationRule() );
            }
            else
            {
                using intrinsicType = Intrinsic< Value ( CustomPattern< Value, TuplePattern >, CustomPattern< Value, TuplePattern > ) >;
                auto intrinsicFunc = [pOvlSet]( const Context& c, const Value& lhs, const Value& rhs )
                {
                    if( TupleSize( lhs ) != TupleSize( rhs ) )







|







23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
                        result = AppendToTuple( result, r );
                        return true;
                    } );

                    return result;
                };

                pOvlSet->add( e, ToValue< intrinsicType >( move( intrinsicFunc ) ), GetBuiltinIntrinsicFuncInvocationRule() );
            }
            else
            {
                using intrinsicType = Intrinsic< Value ( CustomPattern< Value, TuplePattern >, CustomPattern< Value, TuplePattern > ) >;
                auto intrinsicFunc = [pOvlSet]( const Context& c, const Value& lhs, const Value& rhs )
                {
                    if( TupleSize( lhs ) != TupleSize( rhs ) )
54
55
56
57
58
59
60
61
62
63
64
65
66
67
                        result = AppendToTuple( result, r );
                        return true;
                    } );

                    return result;
                };

                pOvlSet->add( e, ToValue< intrinsicType >( move( intrinsicFunc ) ), GetFuncInvocationRule() );
            }
        };
    }
}

#endif







|






54
55
56
57
58
59
60
61
62
63
64
65
66
67
                        result = AppendToTuple( result, r );
                        return true;
                    } );

                    return result;
                };

                pOvlSet->add( e, ToValue< intrinsicType >( move( intrinsicFunc ) ), GetBuiltinIntrinsicFuncInvocationRule() );
            }
        };
    }
}

#endif
Changes to bs/builtins/statements/if.cpp.
112
113
114
115
116
117
118

119
120
121
122
123
124
125
126
127

            // If both the then and the else blocks successors exist and are terminated,
            // we don't need a successor block.
            if( !pElseBB || ( pThenSuccBB && !pThenSuccBB->terminator() )
                || ( pElseSuccBB && !pElseSuccBB->terminator() ) )
                pSuccBB = cfg->createBB();


            pPrecBB->setTerminator( cir::CondBranch(
                get< Value >( converted ),
                pThenBB,
                pElseBB ? pElseBB : pSuccBB ) );

            if( pThenSuccBB && !pThenSuccBB->terminator() )
                pThenSuccBB->setTerminator( cir::Branch( pSuccBB ) );

            if( pElseSuccBB && !pElseSuccBB->terminator() )







>

<







112
113
114
115
116
117
118
119
120

121
122
123
124
125
126
127

            // If both the then and the else blocks successors exist and are terminated,
            // we don't need a successor block.
            if( !pElseBB || ( pThenSuccBB && !pThenSuccBB->terminator() )
                || ( pElseSuccBB && !pElseSuccBB->terminator() ) )
                pSuccBB = cfg->createBB();

            pPrecBB->append( get< Value >( converted ) );
            pPrecBB->setTerminator( cir::CondBranch(

                pThenBB,
                pElseBB ? pElseBB : pSuccBB ) );

            if( pThenSuccBB && !pThenSuccBB->terminator() )
                pThenSuccBB->setTerminator( cir::Branch( pSuccBB ) );

            if( pElseSuccBB && !pElseSuccBB->terminator() )
Changes to bs/builtins/statements/return.cpp.
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

48
49
50
51
52
53
54
55

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
            // Emit cleanups (destructor calls) for all currently live values in the function.
            DestroyAllLiveValues( p.context() );

            const auto& context = p.context();

            if( context.returnType() == GetValueType< void >() )
            {
                cfg->emitTerminator( p.resolver()->currentLocation(), cir::Ret() );
                return true;
            }

            auto np = p.makeNestedParser();
            if( !np.parseExpression( precedence::ReturnStmt + 1 ) )
            {
                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the return statement.", 0 );

                cfg->emitTerminator( p.resolver()->currentLocation(), cir::Ret( PoisonValue() ) );
                return false;
            }

            auto retVal = np.popValue();
            if( !retVal )
            {
                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the return statement.", 0 );

                cfg->emitTerminator( p.resolver()->currentLocation(), cir::Ret( PoisonValue() ) );
                return false;
            }

            auto converted = ConvertValueToType( context, *retVal, *context.returnType() );
            if( holds_alternative< ValUnifyError >( converted ) )
            {
                switch( get< ValUnifyError >( converted ) )
                {
                    case ValUnifyError::NoSolution:
                        dm.emitErrorMessage( retVal->locationId(), "return value type mismatch." );
                        break;

                    case ValUnifyError::Ambiguous:
                        dm.emitErrorMessage( retVal->locationId(), "ambiguous return value conversion." );
                        break;
                }

                // Emit a terminator with a poison value to avoid the function compilation
                // code to complain about a missing return.

                cfg->emitTerminator( p.resolver()->currentLocation(), cir::Ret( PoisonValue() ) );
                PoisonBuilder( p.context() );
                return true;
            }

            cfg->emitTerminator( p.resolver()->currentLocation(), cir::Ret( get< Value >( converted ) ) );

            return true;
        };

        RegisterRule( e, "return"_sid, Rule( handleReturn ) );
    }
}







|







>
|







>
|



















>
|




|
>






33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
            // Emit cleanups (destructor calls) for all currently live values in the function.
            DestroyAllLiveValues( p.context() );

            const auto& context = p.context();

            if( context.returnType() == GetValueType< void >() )
            {
                cfg->emitTerminator( locationId, cir::RetVoid( locationId ) );
                return true;
            }

            auto np = p.makeNestedParser();
            if( !np.parseExpression( precedence::ReturnStmt + 1 ) )
            {
                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the return statement.", 0 );
                cfg->currentBB()->append( PoisonValue() );
                cfg->emitTerminator( locationId, cir::Ret( locationId ) );
                return false;
            }

            auto retVal = np.popValue();
            if( !retVal )
            {
                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the return statement.", 0 );
                cfg->currentBB()->append( PoisonValue() );
                cfg->emitTerminator( locationId, cir::Ret( locationId ) );
                return false;
            }

            auto converted = ConvertValueToType( context, *retVal, *context.returnType() );
            if( holds_alternative< ValUnifyError >( converted ) )
            {
                switch( get< ValUnifyError >( converted ) )
                {
                    case ValUnifyError::NoSolution:
                        dm.emitErrorMessage( retVal->locationId(), "return value type mismatch." );
                        break;

                    case ValUnifyError::Ambiguous:
                        dm.emitErrorMessage( retVal->locationId(), "ambiguous return value conversion." );
                        break;
                }

                // Emit a terminator with a poison value to avoid the function compilation
                // code to complain about a missing return.
                cfg->currentBB()->append( PoisonValue() );
                cfg->emitTerminator( locationId, cir::Ret( locationId ) );
                PoisonBuilder( p.context() );
                return true;
            }

            cfg->currentBB()->append( get< Value >( converted ) );
            cfg->emitTerminator( locationId, cir::Ret( locationId ) );
            return true;
        };

        RegisterRule( e, "return"_sid, Rule( handleReturn ) );
    }
}
Changes to bs/builtins/statements/while.cpp.
128
129
130
131
132
133
134

135
136
137
138
139
140
141
142
143
                    if( pCont->level() == continueLevel )
                        bb->setTerminator( cir::Branch( pHeaderBB ) );
                    return;
                }
            } );

            // Emit the conditional branch that will either run an iteration of the loop or exit to the succ bb.

            pHeaderBB->setTerminator( cir::CondBranch(
                get< Value >( converted ),
                pBodyBB, pSuccBB ) );

            cfg->setCurrentBB( pSuccBB );
            return true;
        };

        RegisterRule( e, "while"_sid, Rule( handleWhile ) );







>

<







128
129
130
131
132
133
134
135
136

137
138
139
140
141
142
143
                    if( pCont->level() == continueLevel )
                        bb->setTerminator( cir::Branch( pHeaderBB ) );
                    return;
                }
            } );

            // Emit the conditional branch that will either run an iteration of the loop or exit to the succ bb.
            pHeaderBB->append( get< Value >( converted ) );
            pHeaderBB->setTerminator( cir::CondBranch(

                pBodyBB, pSuccBB ) );

            cfg->setCurrentBB( pSuccBB );
            return true;
        };

        RegisterRule( e, "while"_sid, Rule( handleWhile ) );
Changes to bs/builtins/types/drop.cpp.
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
        // is appended to the current BB of the current parser.
        RegisterBuiltinFunc< Intrinsic< void ( Value, Value ) > >( e, e.extDropValue(),
            []( const Context& c, const Value& b, const Value& v )
            {
                if( v.isConstant() )
                    return;

                // Reference use a load instruction to store their address,
                // so don't emit them. There is never any point in emitting
                // a load whose result isn't used anyway.
                if( holds_alternative< Load >( v.cir()->content() ) )
                    return;

                auto cfg = GetCFG( c );
                if( !cfg )
                    return;

                auto bb = cfg->currentBB();
                bb->emplace_back( move( *v.cir() ) );
            } );

        using AnyDeclType = CustomPattern< Decl, Decl::Pattern >;

        // DropValue for Decls: declare a local variable with default initialization.
        // TODO: if the invocation to InitializeValue fails, we should have a way to
        // replace the generic "function arguments mismatch" error message with something







<
<
<
|







|







13
14
15
16
17
18
19



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
        // is appended to the current BB of the current parser.
        RegisterBuiltinFunc< Intrinsic< void ( Value, Value ) > >( e, e.extDropValue(),
            []( const Context& c, const Value& b, const Value& v )
            {
                if( v.isConstant() )
                    return;




                if( !DoesInstrSeqHaveSideEffects( *v.cir() ) )
                    return;

                auto cfg = GetCFG( c );
                if( !cfg )
                    return;

                auto bb = cfg->currentBB();
                bb->append( move( *v.cir() ) );
            } );

        using AnyDeclType = CustomPattern< Decl, Decl::Pattern >;

        // DropValue for Decls: declare a local variable with default initialization.
        // TODO: if the invocation to InitializeValue fails, we should have a way to
        // replace the generic "function arguments mismatch" error message with something
Changes to bs/builtins/types/func/bfunc.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#ifndef GOOSE_BUILTINS_TYPES_BFUNC_H
#define GOOSE_BUILTINS_TYPES_BFUNC_H

namespace goose::builtins
{
    class FuncVerificationInfos;

    using BuiltinFuncWrapper = function< Value ( const Term& argVec ) >;

    template< typename F >
    ptr< FuncVerificationInfos > RegisterBuiltinFunc( Env& env, StringId name, F&& func );

    template< typename FT, typename F >
    ptr< FuncVerificationInfos > RegisterBuiltinFunc( Env& env, ptr< OverloadSet > pOvlSet, F&& func );

    extern bool IsBuiltinFunc( const Value& func );
    extern bool IsEagerBuiltinFunc( const Value& func );









|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#ifndef GOOSE_BUILTINS_TYPES_BFUNC_H
#define GOOSE_BUILTINS_TYPES_BFUNC_H

namespace goose::builtins
{
    class FuncVerificationInfos;

    using BuiltinFuncWrapper = function< Value ( const Term& argVec ) >;

    template< typename FT, typename F >
    ptr< FuncVerificationInfos > RegisterBuiltinFunc( Env& env, StringId name, F&& func );

    template< typename FT, typename F >
    ptr< FuncVerificationInfos > RegisterBuiltinFunc( Env& env, ptr< OverloadSet > pOvlSet, F&& func );

    extern bool IsBuiltinFunc( const Value& func );
    extern bool IsEagerBuiltinFunc( const Value& func );
Changes to bs/builtins/types/func/bfunc.inl.
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    }

    template< typename FT, typename F >
    ptr< FuncVerificationInfos > RegisterBuiltinFunc( Env& env, ptr< OverloadSet > pOvlSet, F&& func )
    {
        auto fvi = make_shared< builtins::FuncVerificationInfos >( RootG0Identity() );

        if( !pOvlSet->add( env, ToValue< FT >( forward< F >( func ), fvi ), GetFuncInvocationRule() ) )
            G_ERROR( "duplicate overload registered for builtin func." );

        return fvi;
    }
}

namespace goose::eir







|







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    }

    template< typename FT, typename F >
    ptr< FuncVerificationInfos > RegisterBuiltinFunc( Env& env, ptr< OverloadSet > pOvlSet, F&& func )
    {
        auto fvi = make_shared< builtins::FuncVerificationInfos >( RootG0Identity() );

        if( !pOvlSet->add( env, ToValue< FT >( forward< F >( func ), fvi ), GetInvocationRule< FT >() ) )
            G_ERROR( "duplicate overload registered for builtin func." );

        return fvi;
    }
}

namespace goose::eir
Changes to bs/builtins/types/func/build.cpp.
39
40
41
42
43
44
45
46

47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74

                // Bind a stand-in value with the parameters name to be used inside of verification expressions.
                auto paramVerificationIdentity = AppendToVectorTerm( verificationIdentity,
                    TERM( decl.name() ) );

                c.env()->storeValue( paramVerificationIdentity, ANYTERM( _ ),
                    ValueToEIR( BuildComputedValue( decl.type(),
                    cir::Load( make_shared< cir::Instruction >( cir::VarAddr( varId++, decl.type() ) ), decl.type() ) ) ) );

            }
            else if( param.isConstant() )
                tv->append( ValueToEIR( param ) );

            return true;
        } );

        if( failed )
            return nullopt;

        // If the return type is non-void, expose @result under the verification identity as a computed value whose type
        // is the function's return type, and the value is a placeholder cir instruction. This will allow verification
        // expressions to refer to the current function's return value as a value of the correct type.
        auto rtTerm = ValueToEIR( returnType );
        if( rtTerm != GetValueType< void >() )
        {
            auto name = "@result"_sid;
            auto retValVerificationIdentity = AppendToVectorTerm( verificationIdentity, TERM( name ) );

            c.env()->storeValue( retValVerificationIdentity, ANYTERM( _ ),
                ValueToEIR( BuildComputedValue( rtTerm, cir::Placeholder( rtTerm, name ) ) ) );
        }

        auto pVerifInfos = make_shared< FuncVerificationInfos >( move( verificationIdentity ) );
        return FuncType( rtTerm, tv, move( pVerifInfos ) );
    }

    Func BuildExternalFunc( FuncType funcType, const string& symbol, bool varArg )







|
>




















|







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

                // Bind a stand-in value with the parameters name to be used inside of verification expressions.
                auto paramVerificationIdentity = AppendToVectorTerm( verificationIdentity,
                    TERM( decl.name() ) );

                c.env()->storeValue( paramVerificationIdentity, ANYTERM( _ ),
                    ValueToEIR( BuildComputedValue( decl.type(),
                        cir::VarAddr( varId++, decl.type(), param.locationId() ),
                        cir::Load( decl.type(), param.locationId() ) ) ) );
            }
            else if( param.isConstant() )
                tv->append( ValueToEIR( param ) );

            return true;
        } );

        if( failed )
            return nullopt;

        // If the return type is non-void, expose @result under the verification identity as a computed value whose type
        // is the function's return type, and the value is a placeholder cir instruction. This will allow verification
        // expressions to refer to the current function's return value as a value of the correct type.
        auto rtTerm = ValueToEIR( returnType );
        if( rtTerm != GetValueType< void >() )
        {
            auto name = "@result"_sid;
            auto retValVerificationIdentity = AppendToVectorTerm( verificationIdentity, TERM( name ) );

            c.env()->storeValue( retValVerificationIdentity, ANYTERM( _ ),
                ValueToEIR( BuildComputedValue( rtTerm, cir::Placeholder( rtTerm, name, returnType.locationId() ) ) ) );
        }

        auto pVerifInfos = make_shared< FuncVerificationInfos >( move( verificationIdentity ) );
        return FuncType( rtTerm, tv, move( pVerifInfos ) );
    }

    Func BuildExternalFunc( FuncType funcType, const string& symbol, bool varArg )
Changes to bs/builtins/types/func/compile.cpp.
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
            // TODO: at some point we'll want to check for reachability in the static verifier,
            // and either emit the implicit return or declare the code unreachable depending on the result.
            // The reachability analysis will have to be done before contract validation, as the
            // calls to DestroyValue() may also have requirements to enforce, so we'll need to emit
            // the eventual implicit return first.
            p.flushValue();
            cb->destroyAllLiveValues( localContext );
            cfg->emitTerminator( r->currentLocation(), cir::Ret() );
        }

        pFuncCIR->body() = cfg;
        verify::Func fv( localContext, f );
        return fv.verify();
    }
}







|







194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
            // TODO: at some point we'll want to check for reachability in the static verifier,
            // and either emit the implicit return or declare the code unreachable depending on the result.
            // The reachability analysis will have to be done before contract validation, as the
            // calls to DestroyValue() may also have requirements to enforce, so we'll need to emit
            // the eventual implicit return first.
            p.flushValue();
            cb->destroyAllLiveValues( localContext );
            cfg->emitTerminator( r->currentLocation(), cir::RetVoid( r->currentLocation() ) );
        }

        pFuncCIR->body() = cfg;
        verify::Func fv( localContext, f );
        return fv.verify();
    }
}
Changes to bs/builtins/types/func/func.cpp.
1
2
3
4
5
6
7
8
9
10













11
12
13
14
15
16
17
#include "builtins/builtins.h"
#include "lex/lex.h"
#include "parse/parse.h"
#include "verify/verify.h"

using namespace goose::builtins;
using namespace goose::parse;

namespace goose::builtins
{













    const Term& FuncPattern::GetPattern()
    {
        static auto pattern = ValueToEIR(
            Value( TypeType(), VEC( TSID( func ), HOLE( "llvmType"_sid ),
            HOLE( "_"_sid ), HOLE( "_"_sid ),
            HOLE( "_"_sid ), HOLE( "_"_sid ) ) ) );











>
>
>
>
>
>
>
>
>
>
>
>
>







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
28
29
30
#include "builtins/builtins.h"
#include "lex/lex.h"
#include "parse/parse.h"
#include "verify/verify.h"

using namespace goose::builtins;
using namespace goose::parse;

namespace goose::builtins
{
    cir::InstrSeq BuildArgsInstrSeq( const Term& args )
    {
        cir::InstrSeq result;

        ForEachInVectorTerm( args, [&]( auto&& arg )
        {
            cir::AppendToInstrSeq( result, *EIRToValue( arg ) );
            return true;
        } );

        return result;
    }

    const Term& FuncPattern::GetPattern()
    {
        static auto pattern = ValueToEIR(
            Value( TypeType(), VEC( TSID( func ), HOLE( "llvmType"_sid ),
            HOLE( "_"_sid ), HOLE( "_"_sid ),
            HOLE( "_"_sid ), HOLE( "_"_sid ) ) ) );

52
53
54
55
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
    Term GetFuncSig( const Value& func )
    {
        auto funcType = EIRToValue( func.type() );
        assert( funcType );
        return GetFuncSigFromType( *funcType );
    }

    Term GetFuncRType( const Value& func )
    {
        auto funcType = EIRToValue( func.type() );
        assert( funcType );


        auto typeDecomp = Decompose( funcType->val(),
            Vec(
                Lit( "func"_sid ),
                SubTerm(),  // kind
                SubTerm(),  // return type
                SubTerm(),  // param types
                SubTerm(),  // verif infos
                SubTerm()   // kind
            )
        );

        assert( typeDecomp );
        auto&& [kind, rtype, ptypes, vinf, varArg] = *typeDecomp;



        return rtype;
    }

    const cir::Func* GetFuncCIR( const Value& f )
    {
        if( !f.isConstant() )
            return nullptr;







|


|
>








|


>
|
<
>

>







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
    Term GetFuncSig( const Value& func )
    {
        auto funcType = EIRToValue( func.type() );
        assert( funcType );
        return GetFuncSigFromType( *funcType );
    }

    optional< Term > GetFuncRType( const Value& func )
    {
        auto funcType = EIRToValue( func.type() );
        if( !funcType )
            return nullopt;

        auto typeDecomp = Decompose( funcType->val(),
            Vec(
                Lit( "func"_sid ),
                SubTerm(),  // kind
                SubTerm(),  // return type
                SubTerm(),  // param types
                SubTerm(),  // verif infos
                SubTerm()   // varArg
            )
        );

        if( !typeDecomp )

            return nullopt;

        auto&& [kind, rtype, ptypes, vinf, varArg] = *typeDecomp;
        return rtype;
    }

    const cir::Func* GetFuncCIR( const Value& f )
    {
        if( !f.isConstant() )
            return nullptr;
Changes to bs/builtins/types/func/func.h.
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
#ifndef GOOSE_BUILTINS_TYPES_FUNC_H
#define GOOSE_BUILTINS_TYPES_FUNC_H

namespace goose::builtins
{
    extern ptr< InvocationRule >& GetFuncInvocationRule();
    extern void SetupFunctionInvocationRule( Env& e );
    extern void SetupFunctionTypeChecking( Env& e );
    extern void SetupFunctionLowering( Env& e );

    // Helper to provide generic param patterns for functions.
    struct FuncPattern
    {
        static const Term& GetPattern();
    };

    extern Term GetFuncSig( const Value& func );
    extern Term GetFuncRType( const Value& func );
    extern const cir::Func* GetFuncCIR( const Value& func );

    template< typename F >
    void ForEachDeclInTuple( const Value& tup, F&& func );

    extern bool IsExternalFunc( const Value& f );
    extern bool IsIntrinsicFunc( const Value& f );





<
<










|







1
2
3
4
5


6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef GOOSE_BUILTINS_TYPES_FUNC_H
#define GOOSE_BUILTINS_TYPES_FUNC_H

namespace goose::builtins
{


    extern void SetupFunctionTypeChecking( Env& e );
    extern void SetupFunctionLowering( Env& e );

    // Helper to provide generic param patterns for functions.
    struct FuncPattern
    {
        static const Term& GetPattern();
    };

    extern Term GetFuncSig( const Value& func );
    extern optional< Term > GetFuncRType( const Value& func );
    extern const cir::Func* GetFuncCIR( const Value& func );

    template< typename F >
    void ForEachDeclInTuple( const Value& tup, F&& func );

    extern bool IsExternalFunc( const Value& f );
    extern bool IsIntrinsicFunc( const Value& f );
81
82
83
84
85
86
87


88
89
90
91
92
93
94
    };

    extern Value CompileFunc( const Context& c, const Value& f );

    extern bool ParseFuncConstraints( const Context& c, const FuncType& funcType );
    extern bool ParseFuncBody( const Context& c, const Value& f );
    extern bool ParseFuncBody( const Context& c, const Func& f );


}

namespace goose::eir
{
    template<>
    struct Bridge< builtins::Func >
    {







>
>







79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
    };

    extern Value CompileFunc( const Context& c, const Value& f );

    extern bool ParseFuncConstraints( const Context& c, const FuncType& funcType );
    extern bool ParseFuncBody( const Context& c, const Value& f );
    extern bool ParseFuncBody( const Context& c, const Func& f );

    extern cir::InstrSeq BuildArgsInstrSeq( const Term& args );
}

namespace goose::eir
{
    template<>
    struct Bridge< builtins::Func >
    {
Changes to bs/builtins/types/func/functype.cpp.
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
        auto result = Decompose( t.val(),
            Vec(
                Lit( "func"_sid ),
                Lit( "intrinsic"_sid ),
                SubTerm(),  // return type
                SubTerm(),  // param types
                SubTerm(),  // verif infos
                SubTerm()   // kind
            )
        );

        return !!result;
    }

    Term GetFuncSigFromType( const Value& funcType )
    {
        auto typeDecomp = Decompose( funcType.val(),
            Vec(
                Lit( "func"_sid ),
                SubTerm(),  // kind
                SubTerm(),  // return type
                SubTerm(),  // param types
                SubTerm(),  // verif infos
                SubTerm()   // kind
            )
        );
        assert( typeDecomp );
        auto&& [kind, rtype, ptypes, vinf, varArg] = *typeDecomp;

        return PrependToVectorTerm( *Unquote( ptypes ), rtype );
    }







|















|







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
        auto result = Decompose( t.val(),
            Vec(
                Lit( "func"_sid ),
                Lit( "intrinsic"_sid ),
                SubTerm(),  // return type
                SubTerm(),  // param types
                SubTerm(),  // verif infos
                SubTerm()   // varArg
            )
        );

        return !!result;
    }

    Term GetFuncSigFromType( const Value& funcType )
    {
        auto typeDecomp = Decompose( funcType.val(),
            Vec(
                Lit( "func"_sid ),
                SubTerm(),  // kind
                SubTerm(),  // return type
                SubTerm(),  // param types
                SubTerm(),  // verif infos
                SubTerm()   // varArg
            )
        );
        assert( typeDecomp );
        auto&& [kind, rtype, ptypes, vinf, varArg] = *typeDecomp;

        return PrependToVectorTerm( *Unquote( ptypes ), rtype );
    }
95
96
97
98
99
100
101

102
103
104
105
106
107
108
109

110
111
112
113
114
115
116

        bool failed = false;
        ForEachInVectorTerms( ft.params(), unifiedArgs, [&]( auto&& p, auto&& a )
        {
            auto vp = ValuePatternFromEIR( p );
            if( vp->val() == HOLE( "_"_sid ) )
            {

                auto convertedArg = InvokeOverloadSet( c, c.env()->extConvertFuncArg(),
                    MakeTuple( *EIRToValue( a ), *EIRToValue( vp->type() ) ) );
                if( convertedArg.isPoison() )
                {
                    failed = true;
                    return false;
                }


                av->append( ValueToEIR( convertedArg ) );
            }

            return true;
        } );

        if( failed )







>

|






>







95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

        bool failed = false;
        ForEachInVectorTerms( ft.params(), unifiedArgs, [&]( auto&& p, auto&& a )
        {
            auto vp = ValuePatternFromEIR( p );
            if( vp->val() == HOLE( "_"_sid ) )
            {
                auto arg = *EIRToValue( a );
                auto convertedArg = InvokeOverloadSet( c, c.env()->extConvertFuncArg(),
                    MakeTuple( arg, *EIRToValue( vp->type() ) ) );
                if( convertedArg.isPoison() )
                {
                    failed = true;
                    return false;
                }

                convertedArg.setLocationId( arg.locationId() );
                av->append( ValueToEIR( convertedArg ) );
            }

            return true;
        } );

        if( failed )
Added bs/builtins/types/func/invocation/beagerfunc.cpp.


















































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include "builtins/builtins.h"
#include "common.h"

using namespace goose::sema;

namespace goose::builtins
{
    class BuiltinEagerFuncInvocationRule : public BaseFuncInvocationRule
    {
        public:
            Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
            {
                auto callDecomp = Decompose( typeCheckedCallPat,
                    Val< pvec >()
                );

                const auto& typeCheckedRType = callDecomp->get()->terms().front();
                auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
                auto argCount = VecSize( typeCheckedArgs );
                auto argsInstrSeq = BuildArgsInstrSeq( typeCheckedArgs );

                auto call = BuildComputedValue( typeCheckedRType, argsInstrSeq, callee,
                    cir::Call( argCount, loc ) );

                if( !cir::CanValueBeEagerlyEvaluated( call ) )
                    return call;

                execute::VM vm;
                return execute::Evaluate( call, vm );
            }

            Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
            {
                return callee;
            }
    };

    const ptr< InvocationRule >& GetBuiltinEagerFuncInvocationRule()
    {
        static ptr< InvocationRule > pRule = make_shared< BuiltinEagerFuncInvocationRule >();
        return pRule;
    }

    void SetupBuiltinEagerFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( Value( TypeType(), VEC( TSID( func ),
                TSID( builtin_eager ),
                ANYTERM( _ ), ANYTERM( _ ),
                ANYTERM( _ ), ANYTERM( _ ) ) ) ),

                ANYTERM( _ ) ) ),
            GetBuiltinEagerFuncInvocationRule() );
    }
}
Added bs/builtins/types/func/invocation/bfunc.cpp.




































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include "builtins/builtins.h"
#include "common.h"

using namespace goose::sema;

namespace goose::builtins
{
    class BuiltinFuncInvocationRule : public BaseFuncInvocationRule
    {
        public:
            Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
            {
                auto callDecomp = Decompose( typeCheckedCallPat,
                    Val< pvec >()
                );

                const auto& typeCheckedRType = callDecomp->get()->terms().front();
                auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
                auto argCount = VecSize( typeCheckedArgs );
                auto argsInstrSeq = BuildArgsInstrSeq( typeCheckedArgs );

                return BuildComputedValue( typeCheckedRType, argsInstrSeq, callee, cir::Call( argCount, loc ) );
            }

            Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
            {
                return callee;
            }
    };

    const ptr< InvocationRule >& GetBuiltinFuncInvocationRule()
    {
        static ptr< InvocationRule > pRule = make_shared< BuiltinFuncInvocationRule >();
        return pRule;
    }

    void SetupBuiltinFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( Value( TypeType(), VEC( TSID( func ),
                TSID( builtin ),
                ANYTERM( _ ), ANYTERM( _ ),
                ANYTERM( _ ), ANYTERM( _ ) ) ) ),

                ANYTERM( _ ) ) ),
            GetBuiltinFuncInvocationRule() );
    }
}
Added bs/builtins/types/func/invocation/bintrinsic.cpp.




















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include "builtins/builtins.h"
#include "common.h"

using namespace goose::sema;

namespace goose::builtins
{
    class BuiltinIntrinsicFuncInvocationRule : public BaseFuncInvocationRule
    {
        public:
            Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
            {
                auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
                return GetBuiltinIntrinsicFuncWrapper( callee )( c, move( typeCheckedArgs ) );
            }

            Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
            {
                return callee;
            }
    };

    const ptr< InvocationRule >& GetBuiltinIntrinsicFuncInvocationRule()
    {
        static ptr< InvocationRule > pRule = make_shared< BuiltinIntrinsicFuncInvocationRule >();
        return pRule;
    }

    void SetupBuiltinIntrinsicFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( Value( TypeType(), VEC( TSID( func ),
                TSID( intrinsic_builtin ),
                ANYTERM( _ ), ANYTERM( _ ),
                ANYTERM( _ ), ANYTERM( _ ) ) ) ),

                ANYTERM( _ ) ) ),
            GetBuiltinIntrinsicFuncInvocationRule() );
    }
}
Added bs/builtins/types/func/invocation/common.cpp.






















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include "builtins/builtins.h"
#include "common.h"

using namespace goose::sema;

namespace goose::builtins
{
    Value BaseFuncInvocationRule::resolveInvocation( Context& c, LocationId loc, const Value& callee, const Term& args ) const
    {
        optional< TypeCheckingContext > bestTCC;
        optional< Term > bestSol;

        auto sig = GetFuncSig( callee );
        auto callPat = PrependToVectorTerm( args, HOLE( "_"_sid ) );

        auto us = FindBestTyping( sig, callPat, c );

        if( holds_alternative< NoUnification >( us ) )
        {
            // TODO display details
            DiagnosticsManager::GetInstance().emitErrorMessage( loc,
                "function arguments mismatch." );
            return PoisonValue();
        }

        if( holds_alternative< AmbiguousTypeCheck >( us ) )
        {
            // TODO display details
            DiagnosticsManager::GetInstance().emitErrorMessage( loc,
                "ambiguous function call." );
            return PoisonValue();
        }

        auto&& [s,tcc] = get< TCSol >( us );

        return invoke( c, loc, callee, args, s, tcc );
    }

    optional< Term > BaseFuncInvocationRule::getSignature( const Value& callee ) const
    {
        return GetFuncSig( callee );
    }
}
Added bs/builtins/types/func/invocation/common.h.




























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef GOOSE_BUILTINS_TYPES_FUNC_INVOCATION_COMMON_H
#define GOOSE_BUILTINS_TYPES_FUNC_INVOCATION_COMMON_H

namespace goose::builtins
{
    class BaseFuncInvocationRule : public InvocationRule
    {
        public:
            Value resolveInvocation( Context& c, LocationId loc, const Value& callee, const Term& args ) const final;
            optional< Term > getSignature( const Value& callee ) const final;
    };
}

#endif
Added bs/builtins/types/func/invocation/func.cpp.




























































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include "builtins/builtins.h"
#include "common.h"

using namespace goose::sema;

namespace goose::builtins
{
    class FuncInvocationRule : public BaseFuncInvocationRule
    {
        public:
            Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
            {
                auto preparedCallee = prepareFunc( c, 0, callee, typeCheckedCallPat, tcc );
                if( preparedCallee.isPoison() )
                    return PoisonValue();

                auto callDecomp = Decompose( typeCheckedCallPat,
                    Val< pvec >()
                );

                const auto& typeCheckedRType = callDecomp->get()->terms().front();
                auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );

                preparedCallee.setLocationId( loc );

                auto ft = *FromValue< FuncType >( *EIRToValue( preparedCallee.type() ) );

                auto argList = BuildArgListForCall( c, ft, typeCheckedArgs );
                if( !argList )
                    return PoisonValue();
                auto argsInstrSeq = BuildArgsInstrSeq( *argList );
                auto argCount = VecSize( *argList );

                auto result = BuildComputedValue( typeCheckedRType, argsInstrSeq, preparedCallee,
                    cir::Call( argCount, loc ) );

                if( result.type() != GetValueType< void >() && !IsExternalFunc( callee ) )
                {
                    if( cir::CanValueBeEagerlyEvaluated( result ) )
                    {
                        if( !verify::VerifyInstrSeq( c, *result.cir() ) )
                            return PoisonValue();

                        execute::VM vm;
                        result = execute::Evaluate( result, vm );
                    }

                    // Register the result for destruction.
                    if( auto cfg = GetCFG( c ) )
                        DeclareValue( c, result, cfg->getNewTemporaryIndex() );
                }

                return result;
            }

            Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
            {
                // TODO better description with the function's name if possible (we may need to explicitely store it in the func)
                DiagnosticsContext dc( 0, true );
                VerbosityContext vc( Verbosity::Normal, true );

                return CompileFunc( c, callee );
            }
    };

    void SetupFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( Value( TypeType(), VEC( TSID( func ),
                ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ),
                ANYTERM( _ ), ANYTERM( _ ) ) ) ),

                ANYTERM( _ ) ) ),
            make_shared< FuncInvocationRule >() );
    }
}
Added bs/builtins/types/func/invocation/ghostfunc.cpp.
























































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include "builtins/builtins.h"
#include "common.h"

using namespace goose::sema;

namespace goose::builtins
{
    class GhostFuncInvocationRule : public BaseFuncInvocationRule
    {
        public:
            Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
            {
                if( !CanInvokeGhostFuncs( c ) )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( loc,
                        "ghost functions can't be called in this context." );
                    return PoisonValue();
                }

                auto callDecomp = Decompose( typeCheckedCallPat,
                    Val< pvec >()
                );

                const auto& typeCheckedRType = callDecomp->get()->terms().front();
                auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
                auto argCount = VecSize( typeCheckedArgs );

                auto ft = *FromValue< FuncType >( *EIRToValue( callee.type() ) );

                auto argList = BuildArgListForCall( c, ft, typeCheckedArgs );
                if( !argList )
                    return PoisonValue();
                auto argsInstrSeq = BuildArgsInstrSeq( *argList );

                // A ghost call is a mutable reference to a ghost closure, using the special "GhostCall"
                // as the instruction to compute its "address"
                auto rt = ValueToEIR( ToValue( ReferenceType{ typeCheckedRType, MutAccessSpecifer() } ) );
                return BuildComputedValue( rt, argsInstrSeq, callee, cir::GhostCall( argCount, loc ) );
            }

            Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
            {
                return callee;
            }
    };

    void SetupGhostFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( Value( TypeType(), VEC( TSID( func ),
                ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ),
                ANYTERM( _ ),
                TERM( static_cast< uint64_t >( FuncType::Kind::Ghost ) ) ) ) ),

                ANYTERM( _ ) ) ),
            make_shared< GhostFuncInvocationRule >() );
    }
}
Added bs/builtins/types/func/invocation/intrinsic.cpp.












































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include "builtins/builtins.h"
#include "common.h"

using namespace goose::sema;

namespace goose::builtins
{
    class IntrinsicFuncInvocationRule : public BaseFuncInvocationRule
    {
        public:
            Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
            {
                auto preparedCallee = prepareFunc( c, 0, callee, typeCheckedCallPat, tcc );
                if( preparedCallee.isPoison() )
                    return PoisonValue();

                auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
                preparedCallee.setLocationId( loc );

                auto ft = *FromValue< FuncType >( *EIRToValue( preparedCallee.type() ) );

                // Intrinsic call: we insert the context wrapper as first param,
                // wrap all args with TypeWrapper< Value >, and execute the function directly.
                auto argList = BuildArgListForIntrinsicCall( c, ft, typeCheckedArgs );
                if( !argList )
                    return PoisonValue();
                auto argsInstrSeq = BuildArgsInstrSeq( *argList );
                auto argCount = VecSize( *argList );

                execute::VM vm;
                cir::InstrSeq is;
                AppendToInstrSeq( is, argsInstrSeq, preparedCallee, cir::Call( argCount, loc ) );
                if( !vm.execute( is ) )
                    return PoisonValue();
                if( ft.returnType() == GetValueType< void >() )
                    return Value( GetValueType< void >(), 0U );

                auto result = vm.pop();
                if( !result )
                    return PoisonValue();

                // Unwrap the returned value
                auto unwrapped = FromValue< TypeWrapper< Value > >( *result );
                return unwrapped ? *unwrapped : PoisonValue();
            }

            Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
            {
                // TODO better description with the function's name if possible (we may need to explicitely store it in the func)
                DiagnosticsContext dc( 0, true );
                VerbosityContext vc( Verbosity::Normal, true );

                return CompileFunc( c, callee );
            }
    };

    void SetupIntrinsicFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( Value( TypeType(), VEC( TSID( func ),
                ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ),
                ANYTERM( _ ),
                TERM( static_cast< uint64_t >( FuncType::Kind::Intrinsic ) ) ) ) ),

                ANYTERM( _ ) ) ),
            make_shared< IntrinsicFuncInvocationRule >() );
    }
}
Added bs/builtins/types/func/invocation/invocation.h.








































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#ifndef GOOSE_BUILTINS_TYPES_FUNC_INVOCATION_H
#define GOOSE_BUILTINS_TYPES_FUNC_INVOCATION_H

namespace goose::builtins
{
    extern void SetupBuiltinEagerFuncInvocationRule( Env& e );
    extern void SetupBuiltinFuncInvocationRule( Env& e );
    extern void SetupBuiltinIntrinsicFuncInvocationRule( Env& e );
    extern void SetupFuncInvocationRule( Env& e );
    extern void SetupGhostFuncInvocationRule( Env& e );
    extern void SetupIntrinsicFuncInvocationRule( Env& e );

    extern const ptr< InvocationRule >& GetBuiltinFuncInvocationRule();
    extern const ptr< InvocationRule >& GetBuiltinEagerFuncInvocationRule();
    extern const ptr< InvocationRule >& GetBuiltinIntrinsicFuncInvocationRule();

    template< typename F >
    struct InvocationRuleSelector
    {
        static const auto& Get() { return GetBuiltinFuncInvocationRule(); }
    };

    template< typename R, typename... T >
    struct InvocationRuleSelector< Eager< R >( T... ) >
    {
        static const auto& Get() { return GetBuiltinEagerFuncInvocationRule(); }
    };

    template< typename F >
    struct InvocationRuleSelector< Intrinsic< F > >
    {
        static const auto& Get() { return GetBuiltinIntrinsicFuncInvocationRule(); }
    };

    template< typename F >
    const ptr< InvocationRule >& GetInvocationRule()
    {
        return InvocationRuleSelector< F >::Get();
    }

    static inline void SetupFuncInvocationRules( Env& e )
    {
        SetupBuiltinEagerFuncInvocationRule( e );
        SetupBuiltinFuncInvocationRule( e );
        SetupBuiltinIntrinsicFuncInvocationRule( e );
        SetupFuncInvocationRule( e );
        SetupGhostFuncInvocationRule( e );
        SetupIntrinsicFuncInvocationRule( e );
    }
}

#endif
Deleted bs/builtins/types/func/invoke.cpp.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include "builtins/builtins.h"

using namespace goose::sema;

namespace goose::builtins
{
    class FunctionInvocationRule : public InvocationRule
    {
        public:
            Value resolveInvocation( Context& c, LocationId loc, const Value& callee, const Term& args ) const final
            {
                optional< TypeCheckingContext > bestTCC;
                optional< Term > bestSol;

                auto sig = GetFuncSig( callee );
                auto callPat = PrependToVectorTerm( args, HOLE( "_"_sid ) );

                auto us = FindBestTyping( sig, callPat, c );

                if( holds_alternative< NoUnification >( us ) )
                {
                    // TODO display details
                    DiagnosticsManager::GetInstance().emitErrorMessage( loc,
                        "function arguments mismatch." );
                    return PoisonValue();
                }

                if( holds_alternative< AmbiguousTypeCheck >( us ) )
                {
                    // TODO display details
                    DiagnosticsManager::GetInstance().emitErrorMessage( loc,
                        "ambiguous function call." );
                    return PoisonValue();
                }

                auto&& [s,tcc] = get< TCSol >( us );

                return invoke( c, loc, callee, args, s, tcc );
            }

            Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
            {
                auto preparedCallee = prepareFunc( c, 0, callee, typeCheckedCallPat, tcc );
                if( preparedCallee.isPoison() )
                    return PoisonValue();

                auto callDecomp = Decompose( typeCheckedCallPat,
                    Val< pvec >()
                );

                const auto& typeCheckedRType = callDecomp->get()->terms().front();
                auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );

                preparedCallee.setLocationId( loc );

                if( IsBuiltinFunc( preparedCallee ) )
                    return BuildComputedValue( typeCheckedRType, cir::Call( preparedCallee, move( typeCheckedArgs ) ) );

                if( IsBuiltinIntrinsicFunc( preparedCallee ) )
                    return GetBuiltinIntrinsicFuncWrapper( preparedCallee )( c, move( typeCheckedArgs ) );

                auto ft = *FromValue< FuncType >( *EIRToValue( preparedCallee.type() ) );

                if( ft.kind() == FuncType::Kind::Intrinsic )
                {
                    // Intrinsic call: we insert the context wrapper as first param,
                    // wrap all args with TypeWrapper< Value >, and execute the function directly.
                    auto argList = BuildArgListForIntrinsicCall( c, ft, typeCheckedArgs );
                    if( !argList )
                        return PoisonValue();

                    execute::VM vm;
                    auto result = vm.execute( cir::Call( preparedCallee, move( *argList ) ) );
                    if( ft.returnType() == GetValueType< void >() )
                        return Value( GetValueType< void >(), 0U );

                    if( !result )
                        return PoisonValue();

                    // Unwrap the returned value
                    auto unwrapped = FromValue< TypeWrapper< Value > >( *result );
                    return unwrapped ? *unwrapped : PoisonValue();
                }

                auto argList = BuildArgListForCall( c, ft, typeCheckedArgs );
                if( !argList )
                    return PoisonValue();

                if( ft.kind() == FuncType::Kind::Ghost )
                {
                    if( !CanInvokeGhostFuncs( c ) )
                    {
                        DiagnosticsManager::GetInstance().emitErrorMessage( loc,
                            "ghost functions can't be called in this context." );
                        return PoisonValue();
                    }
                    // A ghost call is a mutable reference to a ghost closure, using the special "GhostCall"
                    // as the instruction to compute its "address"
                    auto rt = ValueToEIR( ToValue( ReferenceType{ typeCheckedRType, MutAccessSpecifer() } ) );
                    return BuildComputedValue( rt, cir::GhostCall( preparedCallee, move( *argList ) ) );
                }
                else
                {
                    auto result = BuildComputedValue( typeCheckedRType, cir::Call( preparedCallee, move( *argList ) ) );

                    // If the result is non-void, register it for destruction.
                    if( result.type() != GetValueType< void >() )
                    {
                        if( auto cfg = GetCFG( c ) )
                            DeclareValue( c, result, cfg->getNewTemporaryIndex() );
                    }

                    return result;
                }
            }

            optional< Term > getSignature( const Value& callee ) const final
            {
                return GetFuncSig( callee );
            }

            Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
            {
                if( IsBuiltinFunc( callee ) || IsBuiltinIntrinsicFunc( callee ) || IsGhostFunc( callee ) )
                    return callee;

                // TODO better description with the function's name if possible (we may need to explicitely store it in the func)
                DiagnosticsContext dc( 0, true );
                VerbosityContext vc( Verbosity::Normal, true );

                return CompileFunc( c, callee );
            }
    };

    ptr< InvocationRule >& GetFuncInvocationRule()
    {
        static ptr< InvocationRule > pRule = make_shared< FunctionInvocationRule >();
        return pRule;
    }

    void SetupFunctionInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( Value( TypeType(), VEC( TSID( func ),
                ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ),
                ANYTERM( _ ), ANYTERM( _ ) ) ) ),

                ANYTERM( _ ) ) ),
            GetFuncInvocationRule() );
    }
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































































































































































































































































































Changes to bs/builtins/types/ghostcode/drop.cpp.
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    {
        // When a boolean is dropped into a ghost code block, it is appended as an assertion check.
        RegisterBuiltinFunc< Intrinsic< void ( TypeWrapper< ptr< GhostCode > >, bool ) > >( e, e.extDropValue(),
            []( const Context& c, const Value& gcv, const Value& b )
            {
                auto gc = *FromValue< TypeWrapper< ptr< GhostCode > > >( gcv );

                gc->cfg()->currentBB()->emplace_back(
                    cir::Assert( b )
                );
            } );

        // When a ghost code block is dropped into a code builder, we append it using a GhostBranch terminator.
        RegisterBuiltinFunc< Intrinsic< void ( TypeWrapper< ptr< CodeBuilder > >, TypeWrapper< ptr< GhostCode > > ) > >( e, e.extDropValue(),
            []( const Context& c, const Value& cbv, const Value& gcv )
            {







|
|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    {
        // When a boolean is dropped into a ghost code block, it is appended as an assertion check.
        RegisterBuiltinFunc< Intrinsic< void ( TypeWrapper< ptr< GhostCode > >, bool ) > >( e, e.extDropValue(),
            []( const Context& c, const Value& gcv, const Value& b )
            {
                auto gc = *FromValue< TypeWrapper< ptr< GhostCode > > >( gcv );

                gc->cfg()->currentBB()->append(
                    b, cir::Assert( b.locationId() )
                );
            } );

        // When a ghost code block is dropped into a code builder, we append it using a GhostBranch terminator.
        RegisterBuiltinFunc< Intrinsic< void ( TypeWrapper< ptr< CodeBuilder > >, TypeWrapper< ptr< GhostCode > > ) > >( e, e.extDropValue(),
            []( const Context& c, const Value& cbv, const Value& gcv )
            {
Changes to bs/builtins/types/init.cpp.
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
        // binary runtime code.
        RegisterBuiltinFunc< Intrinsic< Value ( MutRefOfTypeT, ValueOfTypeT ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r, const Value& initVal )
            {
                G_VAL_ASSERT( r, !r.isConstant() );
                auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
                return BuildComputedValue( GetValueType< void >(),
                    Store( r.cir(), refType.type(), initVal, r.locationId() ) );
            } );

        // Default initialization for ct_int vars
        RegisterBuiltinFunc< Intrinsic< Value ( CTIntMutRefType ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r )
            {
                G_VAL_ASSERT( r, !r.isConstant() );
                auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
                return BuildComputedValue( GetValueType< void >(),
                    Store( r.cir(), refType.type(), ToValue( BigInt() ), r.locationId() ) );
            } );

        // Default initialization for ct_string vars
        RegisterBuiltinFunc< Intrinsic< Value ( CTStringMutRefType ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r )
            {
                G_VAL_ASSERT( r, !r.isConstant() );
                auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
                return BuildComputedValue( GetValueType< void >(),
                    Store( r.cir(), refType.type(), ToValue( ""s ), r.locationId() ) );
            } );
    }
}







|









|









|



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
        // binary runtime code.
        RegisterBuiltinFunc< Intrinsic< Value ( MutRefOfTypeT, ValueOfTypeT ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r, const Value& initVal )
            {
                G_VAL_ASSERT( r, !r.isConstant() );
                auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
                return BuildComputedValue( GetValueType< void >(),
                    r, initVal, Store( refType.type(), initVal.locationId(), r.locationId() ) );
            } );

        // Default initialization for ct_int vars
        RegisterBuiltinFunc< Intrinsic< Value ( CTIntMutRefType ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r )
            {
                G_VAL_ASSERT( r, !r.isConstant() );
                auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
                return BuildComputedValue( GetValueType< void >(),
                    r, ToValue( BigInt() ), Store( refType.type(), {}, r.locationId() ) );
            } );

        // Default initialization for ct_string vars
        RegisterBuiltinFunc< Intrinsic< Value ( CTStringMutRefType ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r )
            {
                G_VAL_ASSERT( r, !r.isConstant() );
                auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
                return BuildComputedValue( GetValueType< void >(),
                    r, ToValue( ""s ), Store( refType.type(), {}, r.locationId() ) );
            } );
    }
}
Changes to bs/builtins/types/localvar/invoke.cpp.
8
9
10
11
12
13
14
15

16
17
18
19
20
21
22
23
24
25

26
27
28
29
30
31
32
    {
        public:
            bool canBeInvoked( const Context& c, const Value& callee ) const final
            {
                auto lv = *FromValue< LocalVar >( callee );

                auto val = BuildComputedValue( lv.type(),
                    cir::Load( GetAddrFromLocalVar( lv ), lv.type() ) );


                return sema::CanBeInvoked( c, val );
            }

            Value resolveInvocation( Context& c, LocationId locationId, const Value& callee, const Term& args ) const final
            {
                auto lv = *FromValue< LocalVar >( callee );

                auto val = BuildComputedValue( lv.type(),
                    cir::Load( GetAddrFromLocalVar( lv ), lv.type() ) );


                return sema::GetInvocationRule( *c.env(), val )->resolveInvocation( c, locationId, val, args );
            }
    };

    void SetupLocalVarInvocationRule( Env& e )
    {







|
>









|
>







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
    {
        public:
            bool canBeInvoked( const Context& c, const Value& callee ) const final
            {
                auto lv = *FromValue< LocalVar >( callee );

                auto val = BuildComputedValue( lv.type(),
                    GetAddrFromLocalVar( lv ),
                    cir::Load( lv.type(), {} ) );

                return sema::CanBeInvoked( c, val );
            }

            Value resolveInvocation( Context& c, LocationId locationId, const Value& callee, const Term& args ) const final
            {
                auto lv = *FromValue< LocalVar >( callee );

                auto val = BuildComputedValue( lv.type(),
                    GetAddrFromLocalVar( lv ),
                    cir::Load( lv.type(), locationId ) );

                return sema::GetInvocationRule( *c.env(), val )->resolveInvocation( c, locationId, val, args );
            }
    };

    void SetupLocalVarInvocationRule( Env& e )
    {
Changes to bs/builtins/types/localvar/localvar.cpp.
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
        if( !typeVal.isType() )
            typeVal = ToType( c, typeVal );

        if( !ParseTypePredicates( c, typeVal ) )
            return PoisonValue();

        LocalVar lv( name, ValueToEIR( typeVal ), index );
        bb->emplace_back( AllocVar( typeVal, index ) );

        Value initResult;

        {
            DiagnosticsContext dc( 0, "When invoking Initialize." );

            if( initializer )







|







35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
        if( !typeVal.isType() )
            typeVal = ToType( c, typeVal );

        if( !ParseTypePredicates( c, typeVal ) )
            return PoisonValue();

        LocalVar lv( name, ValueToEIR( typeVal ), index );
        bb->append( AllocVar( typeVal, index, locId ) );

        Value initResult;

        {
            DiagnosticsContext dc( 0, "When invoking Initialize." );

            if( initializer )
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194

        // Retrieve the texpr's location and set it on the inferred type. This way if an
        // error occurs later with it, for instance when calling LowerTypeForRuntime on it during codegen,
        // it will have a meaningful location for the error message to attach itself on.
        auto typeLoc = EIRToValue( typeTExpr )->locationId();
        LocalVar lv( name, type, index );

        bb->emplace_back( AllocVar( EIRToValue( lv.type() )->setLocationId( typeLoc ), index ) );

        DiagnosticsContext dc( 0, "When invoking Initialize." );

        auto initResult = InvokeOverloadSet( c,
            c.env()->extInitialize(),
            MakeTuple( ToValue( lv ).setLocationId( locId ), initVal ) );








|







180
181
182
183
184
185
186
187
188
189
190
191
192
193
194

        // Retrieve the texpr's location and set it on the inferred type. This way if an
        // error occurs later with it, for instance when calling LowerTypeForRuntime on it during codegen,
        // it will have a meaningful location for the error message to attach itself on.
        auto typeLoc = EIRToValue( typeTExpr )->locationId();
        LocalVar lv( name, type, index );

        bb->append( AllocVar( EIRToValue( lv.type() )->setLocationId( typeLoc ), index, locId ) );

        DiagnosticsContext dc( 0, "When invoking Initialize." );

        auto initResult = InvokeOverloadSet( c,
            c.env()->extInitialize(),
            MakeTuple( ToValue( lv ).setLocationId( locId ), initVal ) );

Changes to bs/builtins/types/lower.cpp.
17
18
19
20
21
22
23








24
25
26
27
            } );

        // Default implementation of LowerConstantForVerification():
        // Do nothing.
        RegisterBuiltinFunc< Intrinsic< Value ( Value ) > >( e, e.extLowerConstantForVerification(),
            []( const Context& c, const Value& v )
            {








                return v;
            } );
    }
}







>
>
>
>
>
>
>
>




17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
            } );

        // Default implementation of LowerConstantForVerification():
        // Do nothing.
        RegisterBuiltinFunc< Intrinsic< Value ( Value ) > >( e, e.extLowerConstantForVerification(),
            []( const Context& c, const Value& v )
            {
                return v;
            } );

        // Default implementation of LowerConstantForRuntime():
        // Do nothing.
        RegisterBuiltinFunc< Intrinsic< Value ( Value ) > >( e, e.extLowerConstantForRuntime(),
            []( const Context& c, const Value& v )
            {
                return v;
            } );
    }
}
Changes to bs/builtins/types/pretty.cpp.
40
41
42
43
44
45
46

47




48
49
50
51
52
53
54
            {
                auto v = *EIRToValue( t );
                auto ty = EIRToValue( v.type() );
                out << "value[ ";
                pp.print( out, ty ? ty->val() : v.type() );
                out << " ]";
                if( v.cir() )

                    out << "{ " << *v.cir() << " }";




                return true;
            } );

        pp.addRule( ValueToEIR( ValuePattern( TSID( constant ),
            ValueToEIR( Value( TypeType(), VEC( TSID( decl ), ANYTERM( _ ) ) ) ),
                ANYTERM( _ ) ) ),
            [&]( auto&& out, auto&& t )







>
|
>
>
>
>







40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
            {
                auto v = *EIRToValue( t );
                auto ty = EIRToValue( v.type() );
                out << "value[ ";
                pp.print( out, ty ? ty->val() : v.type() );
                out << " ]";
                if( v.cir() )
                {
                    out << "{ ";
                    for( const auto& instr : *v.cir() )
                        out << instr << ' ';
                    out << "}";
                }
                return true;
            } );

        pp.addRule( ValueToEIR( ValuePattern( TSID( constant ),
            ValueToEIR( Value( TypeType(), VEC( TSID( decl ), ANYTERM( _ ) ) ) ),
                ANYTERM( _ ) ) ),
            [&]( auto&& out, auto&& t )
Changes to bs/builtins/types/propositions/propositions.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "builtins/builtins.h"

namespace goose::builtins
{
    size_t Propositions::hash() const
    {
        if( !m_hash )
        {
            auto g1 = goose::eir::ContainerHashGenerator( m_propositions );
            m_hash = llvm::hash_combine_range( g1.begin(), g1.end() );
        }
        return *m_hash;
    }

    bool Propositions::parse( const Context& c )
    {








|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "builtins/builtins.h"

namespace goose::builtins
{
    size_t Propositions::hash() const
    {
        if( !m_hash )
        {
            auto g1 = ContainerHashGenerator( m_propositions );
            m_hash = llvm::hash_combine_range( g1.begin(), g1.end() );
        }
        return *m_hash;
    }

    bool Propositions::parse( const Context& c )
    {
Changes to bs/builtins/types/reference/init.cpp.
12
13
14
15
16
17
18
19
20
21
22
            ReferenceType::PatternMutableOf< ReferenceType::PatternXOfTypeT > >,
            CustomPattern< Value, ReferenceType::PatternXOfTypeT > ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r, const Value& initVal )
            {
                G_VAL_ASSERT( r, !r.isConstant() );
                auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
                return BuildComputedValue( GetValueType< void >(),
                    Store( r.cir(), refType.type(), initVal, r.locationId() ) );
            } );
    }
}







|



12
13
14
15
16
17
18
19
20
21
22
            ReferenceType::PatternMutableOf< ReferenceType::PatternXOfTypeT > >,
            CustomPattern< Value, ReferenceType::PatternXOfTypeT > ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r, const Value& initVal )
            {
                G_VAL_ASSERT( r, !r.isConstant() );
                auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
                return BuildComputedValue( GetValueType< void >(),
                    r, initVal, Store( refType.type(), initVal.locationId(), r.locationId() ) );
            } );
    }
}
Changes to bs/builtins/types/reference/reference.cpp.
48
49
50
51
52
53
54
55
56

57
58
59
60
61

62
63
64
65
66
67
68
69
70
    const Term& ReferenceType::PatternXOfTypeT::GetPattern()
    {
        static auto pattern = ValueToEIR( ToValue( ReferenceType( HOLE( "T"_sid, TSID( ttvar ) ), HOLE( "X"_sid ) ) ) );
        return pattern;
    }

    // Returns an instruction that computes the address of whatever's contained in the locvar.
    ptr< cir::Instruction > GetAddrFromLocalVar( const LocalVar& lv )
    {

        return make_shared< cir::Instruction >( cir::VarAddr( lv.index(), lv.type() ) );
    }

    Value BuildLocalVarMutRef( const LocalVar& lv )
    {

        auto rt = ValueToEIR( ToValue( ReferenceType{ lv.type(), MutAccessSpecifer() } ) );
        return BuildComputedValue( move( rt ), cir::VarAddr( lv.index(), lv.type() ) );
    }

    const Term& MutAccessSpecifer()
    {
        static auto al = ValueToEIR( ToValue( AccessSpecifier( "mut"_sid ) ) );
        return al;
    }







|

>
|




>

|







48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
    const Term& ReferenceType::PatternXOfTypeT::GetPattern()
    {
        static auto pattern = ValueToEIR( ToValue( ReferenceType( HOLE( "T"_sid, TSID( ttvar ) ), HOLE( "X"_sid ) ) ) );
        return pattern;
    }

    // Returns an instruction that computes the address of whatever's contained in the locvar.
    cir::Instruction GetAddrFromLocalVar( const LocalVar& lv )
    {
        // TODO LOC: may need loc in LocalVar
        return cir::VarAddr( lv.index(), lv.type(), {} );
    }

    Value BuildLocalVarMutRef( const LocalVar& lv )
    {
        // TODO LOC: may need loc in LocalVar
        auto rt = ValueToEIR( ToValue( ReferenceType{ lv.type(), MutAccessSpecifer() } ) );
        return BuildComputedValue( move( rt ), cir::VarAddr( lv.index(), lv.type(), {} ) );
    }

    const Term& MutAccessSpecifer()
    {
        static auto al = ValueToEIR( ToValue( AccessSpecifier( "mut"_sid ) ) );
        return al;
    }
Changes to bs/builtins/types/reference/reference.h.
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146

            // Not an enum because we want to be able
            // to have holes there and it's a pain in
            // the ass
            Term m_behavior;
    };

    extern ptr< cir::Instruction > GetAddrFromLocalVar( const LocalVar& lv );
    extern Value BuildLocalVarMutRef( const LocalVar& lv );
}

namespace goose::eir
{
    template<>
    struct Bridge< builtins::ReferenceType >







|







132
133
134
135
136
137
138
139
140
141
142
143
144
145
146

            // Not an enum because we want to be able
            // to have holes there and it's a pain in
            // the ass
            Term m_behavior;
    };

    extern cir::Instruction GetAddrFromLocalVar( const LocalVar& lv );
    extern Value BuildLocalVarMutRef( const LocalVar& lv );
}

namespace goose::eir
{
    template<>
    struct Bridge< builtins::ReferenceType >
Changes to bs/builtins/types/reference/typecheck.cpp.
17
18
19
20
21
22
23

24
25
26
27
28
29
30
31
32
33
        if( !refval )
            co_return;

        auto refType = FromValue< ReferenceType >( *EIRToValue( refval->type() ) );
        if( !refType )
            co_return;


        auto content = ValueToEIR( BuildComputedValue( refType->type(),
            cir::Load( refval->cir(), refType->type() ) )
            .setLocationId( refval->locationId() ) );

        // TypeCheck the param with the ref's content
        co_yield TypeCheck( lhs, content, tcc );
    }

    TCGen TypeCheckingBuildTempRef( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc )
    {







>

|
|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
        if( !refval )
            co_return;

        auto refType = FromValue< ReferenceType >( *EIRToValue( refval->type() ) );
        if( !refType )
            co_return;

        auto loc = refval->locationId() ;
        auto content = ValueToEIR( BuildComputedValue( refType->type(),
            *refval, cir::Load( refType->type(), loc ) )
            .setLocationId( loc ) );

        // TypeCheck the param with the ref's content
        co_yield TypeCheck( lhs, content, tcc );
    }

    TCGen TypeCheckingBuildTempRef( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc )
    {
59
60
61
62
63
64
65

66
67
68
69
70
71
72
73
74
75
                auto rhsVal = *EIRToValue( rhs );

                auto cfg = GetCFG( tcc.context() );
                if( !cfg )
                    return nullopt;

                // TODO create an ext point for this

                auto tempIndex = cfg->getNewTemporaryIndex();
                return ValueToEIR( BuildComputedValue( ValueToEIR( ToValue( rt ) ), TempAddr( tempIndex, rhsVal ) )
                    .setLocationId( rhsVal.locationId() ) );
            } );

            // Override the weight because we don't want
            // this solution to count more than directly using
            // the value without wrapping it into a tempref
            SetWeight( wrapped, GetWeight( rhs ) - 1 );
            co_yield { move( wrapped ), tcc };







>

|
|







60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
                auto rhsVal = *EIRToValue( rhs );

                auto cfg = GetCFG( tcc.context() );
                if( !cfg )
                    return nullopt;

                // TODO create an ext point for this
                auto loc = rhsVal.locationId();
                auto tempIndex = cfg->getNewTemporaryIndex();
                return ValueToEIR( BuildComputedValue( ValueToEIR( ToValue( rt ) ), rhsVal,
                    TempAddr( tempIndex, loc ) ).setLocationId( loc ) );
            } );

            // Override the weight because we don't want
            // this solution to count more than directly using
            // the value without wrapping it into a tempref
            SetWeight( wrapped, GetWeight( rhs ) - 1 );
            co_yield { move( wrapped ), tcc };
257
258
259
260
261
262
263

264
265
266
267
268
269
270
271
272
            auto ltype = ValuePatternFromEIR( lhs )->type();

            auto rrefVal = *EIRToValue( rhs );
            auto rrType = FromValue< ReferenceType >( *EIRToValue( rrefVal.type() ) );
            if( !rrType )
                co_return;


            auto content = ValueToEIR( BuildComputedValue( rrType->type(),
                cir::Load( rrefVal.cir(), rrType->type() ) ).setLocationId( rrefVal.locationId() ) );

            co_yield TypeCheck( lhs, content, tcc );
        } );

        // Implicit referencing of non-variables against a non mutable ref: build a tempref
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,








>

|







259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
            auto ltype = ValuePatternFromEIR( lhs )->type();

            auto rrefVal = *EIRToValue( rhs );
            auto rrType = FromValue< ReferenceType >( *EIRToValue( rrefVal.type() ) );
            if( !rrType )
                co_return;

            auto loc = rrefVal.locationId();
            auto content = ValueToEIR( BuildComputedValue( rrType->type(),
                rrefVal, cir::Load( rrType->type(), loc ) ).setLocationId( loc ) );

            co_yield TypeCheck( lhs, content, tcc );
        } );

        // Implicit referencing of non-variables against a non mutable ref: build a tempref
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

Changes to bs/builtins/types/runtime/init.cpp.
16
17
18
19
20
21
22
23
24
25
26
        // Initialization for integer vars
        RegisterBuiltinFunc< Intrinsic< Value ( IntegerMutRefType, IntegerType ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r, const Value& initVal )
            {
                G_VAL_ASSERT( r, !r.isConstant() );
                auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
                return BuildComputedValue( GetValueType< void >(),
                    Store( r.cir(), refType.type(), initVal, r.locationId() ) );
            } );
    }
}







|



16
17
18
19
20
21
22
23
24
25
26
        // Initialization for integer vars
        RegisterBuiltinFunc< Intrinsic< Value ( IntegerMutRefType, IntegerType ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r, const Value& initVal )
            {
                G_VAL_ASSERT( r, !r.isConstant() );
                auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
                return BuildComputedValue( GetValueType< void >(),
                    r, initVal, Store( refType.type(), initVal.locationId(), r.locationId() ) );
            } );
    }
}
Changes to bs/builtins/types/template/invoke.cpp.
45
46
47
48
49
50
51




52
53
54
55
56
57
58
59
            {
                DiagnosticsContext dc( loc, loc.invalid() ? "" : "Called here.", false );

                auto instanceFunc = InstantiateTFunc( c, callee, checkedCallPat, tcc );
                if( instanceFunc.isPoison() )
                    return PoisonValue();





                return GetFuncInvocationRule()->resolveInvocation( c, loc, instanceFunc, args );
            }

            optional< Term > getSignature( const Value& callee ) const final
            {
                auto tfuncVal = FromValue< TFunc >( callee );
                assert( tfuncVal );
                return tfuncVal->signature();







>
>
>
>
|







45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
            {
                DiagnosticsContext dc( loc, loc.invalid() ? "" : "Called here.", false );

                auto instanceFunc = InstantiateTFunc( c, callee, checkedCallPat, tcc );
                if( instanceFunc.isPoison() )
                    return PoisonValue();

                auto pIR = GetInvocationRule( *c.env(), instanceFunc );
                G_VAL_ASSERT( callee, pIR );
                G_VAL_ASSERT( callee, pIR->canBeInvoked( c, instanceFunc ) );

                return pIR->resolveInvocation( c, loc, instanceFunc, args );
            }

            optional< Term > getSignature( const Value& callee ) const final
            {
                auto tfuncVal = FromValue< TFunc >( callee );
                assert( tfuncVal );
                return tfuncVal->signature();
Changes to bs/builtins/types/tuple/init.cpp.
20
21
22
23
24
25
26

27
28
29
30
31
32
33
34
35
36
37
38
            DiagnosticsManager::GetInstance().emitErrorMessage( 0, "Incompatible tuple sizes." );
            return;
        }

        ForEachInTupleType( tupType, [&]( auto&& t )
        {
            auto elemType = *EIRToValue( t );


            // Create a mutable reference to the element to initialize
            ReferenceType rt( t, MutAccessSpecifer() );
            auto elemRef = BuildComputedValue( ValueToEIR( ToValue( rt ) ),
                Select( tupRef.cir(), index ) ).setLocationId( elemType.locationId() );

            auto elemInit = *EIRToValue( GetTupleElement( initTup, index++ ) );

            DiagnosticsContext dc( elemType.locationId(), "When invoking Initialize." );
            auto init = InvokeOverloadSet( c, c.env()->extInitialize(),
                MakeTuple( elemRef, move( elemInit ) ) );








>




|







20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
            DiagnosticsManager::GetInstance().emitErrorMessage( 0, "Incompatible tuple sizes." );
            return;
        }

        ForEachInTupleType( tupType, [&]( auto&& t )
        {
            auto elemType = *EIRToValue( t );
            auto loc = elemType.locationId();

            // Create a mutable reference to the element to initialize
            ReferenceType rt( t, MutAccessSpecifer() );
            auto elemRef = BuildComputedValue( ValueToEIR( ToValue( rt ) ),
                tupRef, Select( index, loc ) ).setLocationId( loc );

            auto elemInit = *EIRToValue( GetTupleElement( initTup, index++ ) );

            DiagnosticsContext dc( elemType.locationId(), "When invoking Initialize." );
            auto init = InvokeOverloadSet( c, c.env()->extInitialize(),
                MakeTuple( elemRef, move( elemInit ) ) );

57
58
59
60
61
62
63

64
65
66
67
68
69
70
71
72
73
74
75

                uint32_t index = 0;
                auto tupType = *EIRToValue( refType.type() );

                ForEachInTupleType( tupType, [&]( auto&& t )
                {
                    auto elemType = *EIRToValue( t );


                    // Create a mutable reference to the element to initialize
                    ReferenceType rt( t, MutAccessSpecifer() );
                    auto elemRef = BuildComputedValue( ValueToEIR( ToValue( rt ) ),
                        Select( tupRef.cir(), index++ ) ).setLocationId( elemType.locationId() );

                    DiagnosticsContext dc( elemType.locationId(), "When invoking Initialize." );
                    auto init = InvokeOverloadSet( c, c.env()->extInitialize(),
                        MakeTuple( elemRef ) );

                    DiagnosticsContext dc2( elemType.locationId(), "When invoking DropValue." );
                     InvokeOverloadSet( c, c.env()->extDropValue(),







>




|







58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

                uint32_t index = 0;
                auto tupType = *EIRToValue( refType.type() );

                ForEachInTupleType( tupType, [&]( auto&& t )
                {
                    auto elemType = *EIRToValue( t );
                    auto loc = elemType.locationId();

                    // Create a mutable reference to the element to initialize
                    ReferenceType rt( t, MutAccessSpecifer() );
                    auto elemRef = BuildComputedValue( ValueToEIR( ToValue( rt ) ),
                        tupRef, Select( index++, loc ) ).setLocationId( loc );

                    DiagnosticsContext dc( elemType.locationId(), "When invoking Initialize." );
                    auto init = InvokeOverloadSet( c, c.env()->extInitialize(),
                        MakeTuple( elemRef ) );

                    DiagnosticsContext dc2( elemType.locationId(), "When invoking DropValue." );
                     InvokeOverloadSet( c, c.env()->extDropValue(),
Changes to bs/builtins/types/tuple/typecheck.cpp.
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
    {
        auto param = ParamPat( GetTupleTypeElement( tupType, index ) );

        auto argType = GetTupleElementType( tupArg, index );
        ReferenceType rt( argType, ConstAccessSpecifer() );

        G_VAL_ASSERT( tupArgRef, !tupArgRef.isConstant() );
        auto argAddr = cir::Select( tupArgRef.cir(), index );

        auto argRef = ValueToEIR( BuildComputedValue( ValueToEIR( ToValue( rt ) ),
            move( argAddr ) ) );

        auto tupSize = TupleTypeSize( tupType );

        for( auto&& [s,tcc] : TypeCheck( param, argRef, tcc ) )
        {
            auto val = ValuePatternFromEIR( s );
            assert( val );







<


|







31
32
33
34
35
36
37

38
39
40
41
42
43
44
45
46
47
    {
        auto param = ParamPat( GetTupleTypeElement( tupType, index ) );

        auto argType = GetTupleElementType( tupArg, index );
        ReferenceType rt( argType, ConstAccessSpecifer() );

        G_VAL_ASSERT( tupArgRef, !tupArgRef.isConstant() );


        auto argRef = ValueToEIR( BuildComputedValue( ValueToEIR( ToValue( rt ) ),
            tupArgRef, cir::Select( index, tupArg.locationId() ) ) );

        auto tupSize = TupleTypeSize( tupType );

        for( auto&& [s,tcc] : TypeCheck( param, argRef, tcc ) )
        {
            auto val = ValuePatternFromEIR( s );
            assert( val );
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
            auto tupType = *EIRToValue( ltup->type() );
            assert( TupleTypeSize( tupType ) == TupleTypeSize( *EIRToValue( rtup->type() ) ) );

            // TODO create an ext point for this instead of going directly thru cfg
            auto tempIndex = cfg->getNewTemporaryIndex();

            auto rtupref = BuildComputedValue( ValueToEIR( ToValue( ReferenceType{ rtup->type(), ConstAccessSpecifer() } ) ),
                cir::TempAddr( tempIndex, *rtup ) );

            co_yield TypeCheckComputedTuple( tcc, tupType, *rtup, rtupref, 0, EmptyTuple() );
        } );

        // Single element tuple unwrapping rules: if we encounter such a tuple, attempt to typecheck
        // its contained value with whatever's on the other side.
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,







|







109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
            auto tupType = *EIRToValue( ltup->type() );
            assert( TupleTypeSize( tupType ) == TupleTypeSize( *EIRToValue( rtup->type() ) ) );

            // TODO create an ext point for this instead of going directly thru cfg
            auto tempIndex = cfg->getNewTemporaryIndex();

            auto rtupref = BuildComputedValue( ValueToEIR( ToValue( ReferenceType{ rtup->type(), ConstAccessSpecifer() } ) ),
                *rtup, cir::TempAddr( tempIndex, rtup->locationId() ) );

            co_yield TypeCheckComputedTuple( tcc, tupType, *rtup, rtupref, 0, EmptyTuple() );
        } );

        // Single element tuple unwrapping rules: if we encounter such a tuple, attempt to typecheck
        // its contained value with whatever's on the other side.
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
Changes to bs/builtins/types/types.cpp.
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
        SetupPredicatesTypeChecking( e );

        SetupBasicTypes( e );
        SetupBasicTypesPrettyPrinting();

        SetupTupleTypeChecking( e );

        SetupFunctionInvocationRule( e );
        SetupFunctionTypeChecking( e );
        SetupFunctionLowering( e );

        SetupTemplateRules( e );
        SetupTemplateFunctionInvocationRule( e );
        SetupTemplateFunctionTypeChecking( e );
        SetupTDeclTypeChecking( e );







|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
        SetupPredicatesTypeChecking( e );

        SetupBasicTypes( e );
        SetupBasicTypesPrettyPrinting();

        SetupTupleTypeChecking( e );

        SetupFuncInvocationRules( e );
        SetupFunctionTypeChecking( e );
        SetupFunctionLowering( e );

        SetupTemplateRules( e );
        SetupTemplateFunctionInvocationRule( e );
        SetupTemplateFunctionTypeChecking( e );
        SetupTDeclTypeChecking( e );
Changes to bs/builtins/types/types.h.
10
11
12
13
14
15
16

17
18
19
20
21
22
23
#include "ghostcode/ghostcode.h"

#include "func/bfunc.h"
#include "func/bintrinsic.h"
#include "func/functype.h"
#include "func/func.h"
#include "func/build.h"


#include "basic.h"
#include "decl.h"
#include "runtime/runtime.h"
#include "wrapper.h"

#include "template/tvar.h"







>







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include "ghostcode/ghostcode.h"

#include "func/bfunc.h"
#include "func/bintrinsic.h"
#include "func/functype.h"
#include "func/func.h"
#include "func/build.h"
#include "func/invocation/invocation.h"

#include "basic.h"
#include "decl.h"
#include "runtime/runtime.h"
#include "wrapper.h"

#include "template/tvar.h"
Changes to bs/cir/allocvar.h.
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
#ifndef GOOSE_CIR_ALLOCVAR_H
#define GOOSE_CIR_ALLOCVAR_H

namespace goose::cir
{
    class AllocVar
    {
        public:
            template< typename T >
            AllocVar( T&& type, uint32_t index ) :

                m_type( forward< T >( type ) ),
                m_index( index )
            {}

            const auto& type() const { return m_type; }
            const auto& index() const { return m_index; }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return false; }


            bool operator<( const AllocVar& rhs ) const
            {
                if( m_index != rhs.m_index )
                    return m_index < rhs.m_index;
                return m_type < rhs.m_type;
            }





|



|
>









>







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
28
#ifndef GOOSE_CIR_ALLOCVAR_H
#define GOOSE_CIR_ALLOCVAR_H

namespace goose::cir
{
    class AllocVar : public BaseInstr< 0, false >
    {
        public:
            template< typename T >
            AllocVar( T&& type, uint32_t index, LocationId loc ) :
                BaseInstr( loc ),
                m_type( forward< T >( type ) ),
                m_index( index )
            {}

            const auto& type() const { return m_type; }
            const auto& index() const { return m_index; }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return true; }

            bool operator<( const AllocVar& rhs ) const
            {
                if( m_index != rhs.m_index )
                    return m_index < rhs.m_index;
                return m_type < rhs.m_type;
            }
Changes to bs/cir/arith.h.
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
28
29
30
31
32
33
34

35
36
37
38
39
40
41
42
43
44
45
46

47
48
49
50
51
52
53
54
55
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
#ifndef GOOSE_CIR_ARITH_H
#define GOOSE_CIR_ARITH_H

namespace goose::cir
{
    class Add : public BinaryOp
    {
        public:
            template< typename L, typename R >
            Add( L&& lhs, R&& rhs ) : BinaryOp( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const Add& ins )
            {
                return ins.print( out, "ADD" );
            }
    };

    class Sub : public BinaryOp
    {
        public:
            template< typename L, typename R >
            Sub( L&& lhs, R&& rhs ) : BinaryOp( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const Sub& ins )
            {
                return ins.print( out, "SUB" );
            }
    };

    class Mul : public BinaryOp
    {
        public:
            template< typename L, typename R >
            Mul( L&& lhs, R&& rhs ) : BinaryOp( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const Mul& ins )
            {
                return ins.print( out, "MUL" );
            }
    };

    class UDiv : public BinaryOp
    {
        public:
            template< typename L, typename R >
            UDiv( L&& lhs, R&& rhs ) : BinaryOp( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const UDiv& ins )
            {
                return ins.print( out, "UDIV" );
            }
    };

    class SDiv : public BinaryOp
    {
        public:
            template< typename L, typename R >
            SDiv( L&& lhs, R&& rhs ) : BinaryOp( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const SDiv& ins )
            {
                return ins.print( out, "SDIV" );
            }
    };

    class URem : public BinaryOp
    {
        public:
            template< typename L, typename R >
            URem( L&& lhs, R&& rhs ) : BinaryOp( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const URem& ins )
            {
                return ins.print( out, "UREM" );
            }
    };

    class SRem : public BinaryOp
    {
        public:
            template< typename L, typename R >
            SRem( L&& lhs, R&& rhs ) : BinaryOp( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const SRem& ins )
            {
                return ins.print( out, "SREM" );
            }
    };
}

#endif








|
|
>



|






|
|
>



|






|
|
>



|






|
|
>



|






|
|
>



|






|
|
>



|






|
|
>



|





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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
#ifndef GOOSE_CIR_ARITH_H
#define GOOSE_CIR_ARITH_H

namespace goose::cir
{
    class Add : public BinaryOp
    {
        public:
            Add( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Add& ins )
            {
                return out << "ADD";
            }
    };

    class Sub : public BinaryOp
    {
        public:
            Sub( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Sub& ins )
            {
                return out << "SUB";
            }
    };

    class Mul : public BinaryOp
    {
        public:
            Mul( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Mul& ins )
            {
                return out << "MUL";
            }
    };

    class UDiv : public BinaryOp
    {
        public:
            UDiv( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const UDiv& ins )
            {
                return out << "UDIV";
            }
    };

    class SDiv : public BinaryOp
    {
        public:
            SDiv( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const SDiv& ins )
            {
                return out << "SDIV";
            }
    };

    class URem : public BinaryOp
    {
        public:
            URem( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const URem& ins )
            {
                return out << "UREM";
            }
    };

    class SRem : public BinaryOp
    {
        public:
            SRem( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const SRem& ins )
            {
                return out << "SREM";
            }
    };
}

#endif
Changes to bs/cir/ass.h.
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
28
29
30
31
32
33
34
35
36
37
38
#ifndef GOOSE_CIR_ASS_H
#define GOOSE_CIR_ASS_H

// This file is not called "assert.h" because that fucks the cassert header up
// when it tries to include assert.h.
// "ass" is both a good shorthand and an accurate description of the problem

namespace goose::cir
{
    class Assert
    {
        public:
            template< typename V >
            Assert( V&& cond ) :
                m_cond( forward< V >( cond ) )

            {}

            const auto& cond() const { return m_cond; }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return false; }


            bool operator<( const Assert& rhs ) const
            {
                return m_cond < rhs.m_cond;
            }

            friend ostream& operator<<( ostream& out, const Assert& ins )
            {
                return out << "ASSERT(" << ins.m_cond << ')';
            }

        private:
            eir::Value m_cond;
    };
}

#endif









|


<
|
<
>


<
<


>



|




|

<
<
<




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
28
29



30
31
32
33
#ifndef GOOSE_CIR_ASS_H
#define GOOSE_CIR_ASS_H

// This file is not called "assert.h" because that fucks the cassert header up
// when it tries to include assert.h.
// "ass" is both a good shorthand and an accurate description of the problem

namespace goose::cir
{
    class Assert : public BaseInstr< 1, false >
    {
        public:

            Assert( LocationId loc ) :

                BaseInstr( loc )
            {}



            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return true; }

            bool operator<( const Assert& rhs ) const
            {
                return false;
            }

            friend ostream& operator<<( ostream& out, const Assert& ins )
            {
                return out << "ASSERT";
            }



    };
}

#endif
Changes to bs/cir/basicblock.h.
42
43
44
45
46
47
48
49
50
51
52
53
54
55

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
            const auto& loopEdges() const { return m_loopEdges; }

            uint32_t loopId() const { return m_loopId; }
            bool isLoopHeader() const { return m_isLoopHeader; }
            void setLoopId( uint32_t id ) { m_loopId = id; }
            void setLoopHeader() { m_isLoopHeader = true; }

            const auto& operator[]( size_t i ) const { return m_instructions[i]; }
            auto& operator[]( size_t i ) { return m_instructions[i]; }

            auto* llvmBB() { return m_llvmBB; }
            void setLLVMBB( llvm::BasicBlock* pBB ) { m_llvmBB = pBB; }

            auto begin() const { return m_instructions.begin(); }

            auto end() const { return m_instructions.end(); }







            const auto& front() const { return m_instructions.front(); }
            const auto& back() const { return m_instructions.back(); }
            auto& front() { return m_instructions.front(); }
            auto& back() { return m_instructions.back(); }

            auto empty() const { return m_instructions.empty(); }
            uint32_t size() const { return m_instructions.size(); }

            void reserve( size_t size ) { m_instructions.reserve( size ); }

            void clear()
            {
                m_instructions.clear();

            }

            template< typename... T >
            void emplace_back( T&&... args );


            template< typename... T >
            void push_back_from_var( const variant< T... >& instr );


            template< typename T >
            void setTerminator( T&& terminator );

            const auto& terminator() const { return m_terminator; }

            bool canBeExecuted() const







<
<
<



|
>
|

>
>
>
>
>
>
|
|
|
|
|
|
|
|
<




>


|
|
|
>
|
<
>







42
43
44
45
46
47
48



49
50
51
52
53
54
55
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
            const auto& loopEdges() const { return m_loopEdges; }

            uint32_t loopId() const { return m_loopId; }
            bool isLoopHeader() const { return m_isLoopHeader; }
            void setLoopId( uint32_t id ) { m_loopId = id; }
            void setLoopHeader() { m_isLoopHeader = true; }




            auto* llvmBB() { return m_llvmBB; }
            void setLLVMBB( llvm::BasicBlock* pBB ) { m_llvmBB = pBB; }

            auto empty() const { return m_instructions.empty(); }

            const auto& instructions() const { return m_instructions; }

            using RunnableInstructions = llvm::SmallVector< Instruction, 16 >;
            const RunnableInstructions* runnableInstructions() const
            {
                if( m_dirty )
                {
                    m_dirty = false;

                    m_runnableInstructions.clear();
                    if( !FilterVerificationInstructions( m_instructions, m_runnableInstructions ) )
                        return nullptr;
                }

                return &m_runnableInstructions;
            }


            void clear()
            {
                m_instructions.clear();
                m_dirty = true;
            }

            template< typename... I >
            void append( I&&... instrs )
            {
                AppendToInstrSeq( m_instructions, forward< I >( instrs )... );
                m_dirty = true;

            }

            template< typename T >
            void setTerminator( T&& terminator );

            const auto& terminator() const { return m_terminator; }

            bool canBeExecuted() const
96
97
98
99
100
101
102
103

104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123


124
125
126
127
128
            bool codeGenStarted() const { return m_codeGenStarted; }
            void setCodeGenStarted( bool b = true ) { m_codeGenStarted = b; }

            template< typename F >
            void forEachSuccessor( F&& func ) const;

        private:
            vector< Instruction >   m_instructions;

            optional< Terminator >  m_terminator;

            weak_ptr< CFG >         m_owner;

            llvm::SmallVector< uint32_t, 8 >    m_backEdges;
            llvm::SmallVector< uint32_t, 8 >    m_loopEdges;

            llvm::BasicBlock*       m_llvmBB = nullptr;

            uint32_t                m_index = 0;

            uint32_t                m_loopId = 0;           // Id of the header of the loop that this block belongs to, or 0.

            eir::LocationId         m_locId = 0;            // An optional location id. Used by the verifier on loop headers
                                                            // to indicate the location of a loop that it failed to verify.

            bool                    m_isLoopHeader = false;

            bool                    m_canBeExecuted = true;
            bool                    m_canBeEagerlyEvaluated = true;


            mutable bool            m_codeGenStarted = false;
    };
}

#endif







|
>




















>
>





101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
            bool codeGenStarted() const { return m_codeGenStarted; }
            void setCodeGenStarted( bool b = true ) { m_codeGenStarted = b; }

            template< typename F >
            void forEachSuccessor( F&& func ) const;

        private:
            InstrSeq                m_instructions;
            mutable RunnableInstructions m_runnableInstructions;
            optional< Terminator >  m_terminator;

            weak_ptr< CFG >         m_owner;

            llvm::SmallVector< uint32_t, 8 >    m_backEdges;
            llvm::SmallVector< uint32_t, 8 >    m_loopEdges;

            llvm::BasicBlock*       m_llvmBB = nullptr;

            uint32_t                m_index = 0;

            uint32_t                m_loopId = 0;           // Id of the header of the loop that this block belongs to, or 0.

            eir::LocationId         m_locId = 0;            // An optional location id. Used by the verifier on loop headers
                                                            // to indicate the location of a loop that it failed to verify.

            bool                    m_isLoopHeader = false;

            bool                    m_canBeExecuted = true;
            bool                    m_canBeEagerlyEvaluated = true;

            mutable bool            m_dirty = false;
            mutable bool            m_codeGenStarted = false;
    };
}

#endif
Changes to bs/cir/basicblock.inl.
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
28
29
30
31
#ifndef GOOSE_CIR_BASICBLOCK_INL
#define GOOSE_CIR_BASICBLOCK_INL

namespace goose::cir
{
    template< typename... T >
    void BasicBlock::emplace_back( T&&... args )
    {
        m_instructions.emplace_back( forward< T >( args )... );
        m_canBeExecuted = m_canBeExecuted && m_instructions.back().canBeExecuted();
        m_canBeEagerlyEvaluated = m_canBeEagerlyEvaluated && m_instructions.back().canBeEagerlyEvaluated();
    }

    template< typename... T >
    void BasicBlock::push_back_from_var( const variant< T... >& instr )
    {
        visit( [&]( auto&& e )
        {
            m_canBeExecuted = m_canBeExecuted && e.canBeExecuted();
            m_canBeEagerlyEvaluated = m_canBeEagerlyEvaluated && e.canBeEagerlyEvaluated();
            m_instructions.emplace_back( e );
        }, instr );
    }

    template< typename T >
    void BasicBlock::setTerminator( T&& terminator )
    {
        m_terminator = forward< T >( terminator );
        m_terminator->addCFGEdges( m_owner.lock(), index() );
    }






<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







1
2
3
4
5



















6
7
8
9
10
11
12
#ifndef GOOSE_CIR_BASICBLOCK_INL
#define GOOSE_CIR_BASICBLOCK_INL

namespace goose::cir
{



















    template< typename T >
    void BasicBlock::setTerminator( T&& terminator )
    {
        m_terminator = forward< T >( terminator );
        m_terminator->addCFGEdges( m_owner.lock(), index() );
    }

Deleted bs/cir/binaryop.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "cir.h"

namespace goose::cir
{
    bool BinaryOp::canBeExecuted() const
    {
        return IsValueConstantOrExecutable( m_lhs )
            && IsValueConstantOrExecutable( m_rhs );
    }

    bool BinaryOp::canBeEagerlyEvaluated() const
    {
        return CanValueBeEagerlyEvaluated( m_lhs )
            && CanValueBeEagerlyEvaluated( m_rhs );
    }
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
































Changes to bs/cir/binaryop.h.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#ifndef GOOSE_CIR_BINARYINSTR_H
#define GOOSE_CIR_BINARYINSTR_H

namespace goose::cir
{
    class BinaryOp
    {
        public:
            template< typename L, typename R >
            BinaryOp( L&& lhs, R&& rhs ) :
                m_lhs( forward< L >( lhs ) ),
                m_rhs( forward< R >( rhs ) )
            {}

            const auto& lhs() const { return m_lhs; }
            const auto& rhs() const { return m_rhs; }

            bool canBeExecuted() const;
            bool canBeEagerlyEvaluated() const;


            bool operator<( const BinaryOp& rhs ) const
            {
                if( m_lhs != rhs.m_lhs )
                    return m_lhs < rhs.m_lhs;
                return m_rhs < rhs.m_rhs;
            }

        protected:
            ostream& print( ostream& out, const char* name ) const
            {
                return out << name << '(' << m_lhs << ", " << m_rhs << ')';
            }

        private:
            eir::Value m_lhs;
            eir::Value m_rhs;
    };

    class BinaryOpSameTypes : public BinaryOp
    {
        public:
            template< typename L, typename R >
            BinaryOpSameTypes( L&& l, R&& r ) :
                BinaryOp( forward< L >( l ), forward< R >( r ) )
            {
                assert( lhs().type() == rhs().type() );
            }
    };
}

#endif





|


<
|
|
<


<
<
<
|
|
>



<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<





1
2
3
4
5
6
7
8

9
10

11
12



13
14
15
16
17
18

19






















20
21
22
23
24
#ifndef GOOSE_CIR_BINARYINSTR_H
#define GOOSE_CIR_BINARYINSTR_H

namespace goose::cir
{
    class BinaryOp : public BaseInstr< 2, true >
    {
        public:

            BinaryOp( LocationId loc ) :
                BaseInstr( loc )

            {}




            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return true; }
            bool haveSideEffects() const { return false; }

            bool operator<( const BinaryOp& rhs ) const
            {

                return false;






















            }
    };
}

#endif
Changes to bs/cir/bitwise.h.
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
28
29
30
31
32
33
34

35
36
37
38
39
40
41
42
43
#ifndef GOOSE_CIR_BITWISE_H
#define GOOSE_CIR_BITWISE_H

namespace goose::cir
{
    class Shl : public BinaryOp
    {
        public:
            template< typename L, typename R >
            Shl( L&& lhs, R&& rhs ) : BinaryOp( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const Shl& ins )
            {
                return ins.print( out, "SHL" );
            }
    };

    class LShr : public BinaryOp
    {
        public:
            template< typename L, typename R >
            LShr( L&& lhs, R&& rhs ) : BinaryOp( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const LShr& ins )
            {
                return ins.print( out, "LSHR" );
            }
    };

    class AShr : public BinaryOp
    {
        public:
            template< typename L, typename R >
            AShr( L&& lhs, R&& rhs ) : BinaryOp( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const AShr& ins )
            {
                return ins.print( out, "ASHR" );
            }
    };
}

#endif








|
|
>



|






|
|
>



|






|
|
>



|





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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#ifndef GOOSE_CIR_BITWISE_H
#define GOOSE_CIR_BITWISE_H

namespace goose::cir
{
    class Shl : public BinaryOp
    {
        public:
            Shl( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Shl& ins )
            {
                return out << "SHL";
            }
    };

    class LShr : public BinaryOp
    {
        public:
            LShr( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const LShr& ins )
            {
                return out << "LSHR";
            }
    };

    class AShr : public BinaryOp
    {
        public:
            AShr( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const AShr& ins )
            {
                return out << "ASHR";
            }
    };
}

#endif
Changes to bs/cir/branch.h.
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
        private:
            wptr< BasicBlock > m_dest;
    };

    class CondBranch
    {
        public:
            template< typename V, typename BT, typename BF >
            CondBranch( V&& cond, BT&& trueDest, BF&& falseDest ) :
                m_cond( forward< V >( cond ) ),
                m_trueDest( forward< BT >( trueDest ) ),
                m_falseDest( forward< BF >( falseDest ) )
            {}

            const auto& cond() const { return m_cond; }
            const auto& trueDest() const { return m_trueDest; }
            const auto& falseDest() const { return m_falseDest; }

            bool canBeExecuted() const
            {
                return IsValueConstantOrExecutable( m_cond );
            }

            bool canBeEagerlyEvaluated() const
            {
                return CanValueBeEagerlyEvaluated( m_cond );
            }

            void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex );

        private:
            eir::Value m_cond;
            wptr< BasicBlock > m_trueDest;
            wptr< BasicBlock > m_falseDest;
    };

    // A special terminator that points to a basic block to use only
    // during verification (containing "ghost code") and to a continuation
    // basic block







|
|
<




<



|
<
<
<
<
|
<
<
<




<







23
24
25
26
27
28
29
30
31

32
33
34
35

36
37
38
39




40



41
42
43
44

45
46
47
48
49
50
51
        private:
            wptr< BasicBlock > m_dest;
    };

    class CondBranch
    {
        public:
            template< typename BT, typename BF >
            CondBranch( BT&& trueDest, BF&& falseDest ) :

                m_trueDest( forward< BT >( trueDest ) ),
                m_falseDest( forward< BF >( falseDest ) )
            {}


            const auto& trueDest() const { return m_trueDest; }
            const auto& falseDest() const { return m_falseDest; }

            bool canBeExecuted() const { return true; }




            bool canBeEagerlyEvaluated() const { return true; }




            void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex );

        private:

            wptr< BasicBlock > m_trueDest;
            wptr< BasicBlock > m_falseDest;
    };

    // A special terminator that points to a basic block to use only
    // during verification (containing "ghost code") and to a continuation
    // basic block
Deleted bs/cir/call.cpp.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
#include "cir/cir.h"
#include "builtins/builtins.h"

using namespace goose::builtins;

namespace goose::cir
{
    bool Call::canBeExecuted() const
    {
        if( !IsValueConstantOrExecutable( m_func ) )
            return false;

        if( IsExternalFunc( m_func ) )
            return false;

        bool argsCanBeExecuted = true;
        ForEachInVectorTerm( m_args, [&]( auto&& arg )
        {
            if( !IsValueConstantOrExecutable( *EIRToValue( arg ) ) )
            {
                argsCanBeExecuted = false;
                return false;
            }

            return true;
        } );

        if( !argsCanBeExecuted )
            return false;

        if( IsBuiltinFunc( m_func ) )
            return true;

        const auto* pFunc = GetFuncCIR( m_func );

        // If the func is passed as a value, we might not have been able to evaluate
        // it yet, so we assume it's going to be executable. If it isn't, we'll fail
        // at execution time, which isn't a big deal - we should be able to avoid trying
        // executing it in the first place in most cases.
        return !pFunc || pFunc->canBeExecuted();
    }

    bool Call::canBeEagerlyEvaluated() const
    {
        // Functions with void return type are assumed to have side effects
        // and therefore that they should never be eagerly evaluated,
        // It's not like they would need to be anyway.
        // An exception is builtin functions that have been explicitely marked to be eagerly evaluated.
        if( GetFuncRType( m_func ) == GetValueType< void >() )
            return IsEagerBuiltinFunc( m_func );

        if( IsNonEagerBuiltinFunc( m_func ) )
            return false;

        if( IsExternalFunc( m_func ) )
            return false;

        bool argsAreConstant = true;
        ForEachInVectorTerm( m_args, [&]( auto&& arg )
        {
            if( !CanValueBeEagerlyEvaluated( *EIRToValue( arg ) ) )
            {
                argsAreConstant = false;
                return false;
            }

            return true;
        } );

        if( !argsAreConstant )
            return false;

        if( IsEagerBuiltinFunc( m_func ) )
            return true;

        const auto* pFuncCIR = GetFuncCIR( m_func );

        if( !pFuncCIR )
            return false;

        return pFuncCIR->canBeExecuted();
    }

    ostream& operator<<( ostream& out, const Call& ins )
    {
        return out << "CALL(" << ins.m_func << ", " << ins.m_args << ')';
    }
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
















































































































































































Changes to bs/cir/call.h.
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
28
29



30
31
32
33
34
35
36
#ifndef GOOSE_CIR_CALL_H
#define GOOSE_CIR_CALL_H

namespace goose::cir
{
    class Call
    {
        public:
            template< typename F, typename A >
            Call( F&& func, A&& args ) :
                m_func( forward< F >( func ) ),
                m_args( forward< A >( args ) )
            {}

            const auto& func() const { return m_func; }
            const auto& args() const { return m_args; }

            bool canBeExecuted() const;
            bool canBeEagerlyEvaluated() const;


            bool operator<( const Call& rhs ) const
            {
                if( m_func != rhs.m_func )
                    return m_func < rhs.m_func;
                return m_args < rhs.m_args;
            }

            friend ostream& operator<<( ostream& out, const Call& ins );




        private:
            eir::Value m_func;
            eir::Term m_args;
    };
}

#endif





|


<
|
|
|


<
|

|
|
>



<
<
|


|
|
>
>
>

|
<




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
28
29
30
31

32
33
34
35
#ifndef GOOSE_CIR_CALL_H
#define GOOSE_CIR_CALL_H

namespace goose::cir
{
    class Call : public BaseInstr< 1, true >   // Can pop more depending on arg count
    {
        public:

            Call( uint32_t numArgs, LocationId loc ) :
                BaseInstr( loc ),
                m_numArgs( numArgs )
            {}


            const auto& numArgs() const { return m_numArgs; }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return true; }
            bool haveSideEffects() const { return true; }

            bool operator<( const Call& rhs ) const
            {


                return m_numArgs < rhs.m_numArgs;
            }

            friend ostream& operator<<( ostream& out, const Call& ins )
            {
                return out << "CALL";
            }

        private:
            uint32_t m_numArgs = 0;

    };
}

#endif
Changes to bs/cir/cfg.h.
9
10
11
12
13
14
15
16



17
18
19
20
21
22
23
    {
        public:
            CFG( uint32_t numParams ) :
                m_temporariesCount( numParams )
            {}

            bool isPoisoned() const { return m_poisoned; }
            void poison() { m_poisoned = true; }




            const auto& entryBB() const { return m_basicBlocks.front(); }
            const auto& lastBB() const { return m_basicBlocks.back(); }

            const auto& currentBB() const { return m_currentBB; }
            template< typename T >
            void setCurrentBB( T&& pBB )







|
>
>
>







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    {
        public:
            CFG( uint32_t numParams ) :
                m_temporariesCount( numParams )
            {}

            bool isPoisoned() const { return m_poisoned; }
            void poison()
            {
                m_poisoned = true;
            }

            const auto& entryBB() const { return m_basicBlocks.front(); }
            const auto& lastBB() const { return m_basicBlocks.back(); }

            const auto& currentBB() const { return m_currentBB; }
            template< typename T >
            void setCurrentBB( T&& pBB )
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
            const auto& getBB( uint32_t index ) const { return m_basicBlocks[index - 1]; }

            auto count() const { return m_basicBlocks.size(); }

            const ptr< BasicBlock >& createBB();

            auto getNewTemporaryIndex() { return m_temporariesCount++; }


            // Clear the llvm basic block pointers from the entire cfg.
            void unbindFromLLVM();

            bool canBeExecuted() const;
            bool canBeEagerlyEvaluated() const;

            template< typename F >
            void forEachBB( F&& func )
            {
                for( auto&& bb : m_basicBlocks )
                    func( bb );
            }

            void setAddressModifiedByLoop( uint32_t loopId, const eir::Term& type, const Instruction& addrCalculation )
            {
                m_loopModifiedAddresses.emplace( make_pair( loopId, addrCalculation ), type );
            }

            template< typename F >
            void forEachAddressModifiedByLoop( uint32_t loopId, F&& func ) const
            {
                auto begin = m_loopModifiedAddresses.lower_bound( { loopId, {} } );
                auto end = m_loopModifiedAddresses.upper_bound( { loopId, Placeholder( TERM( 0U ), ""_sid ) } );

                for( auto it = begin; it != end; ++it )
                    func( it->second, it->first.second );
            }

            template< typename F >
            void forEachReachableBlock( const ptr< BasicBlock >& bb, F&& func ) const







>














|

|



|

|
|







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
            const auto& getBB( uint32_t index ) const { return m_basicBlocks[index - 1]; }

            auto count() const { return m_basicBlocks.size(); }

            const ptr< BasicBlock >& createBB();

            auto getNewTemporaryIndex() { return m_temporariesCount++; }
            auto temporariesCount() const { return m_temporariesCount; }

            // Clear the llvm basic block pointers from the entire cfg.
            void unbindFromLLVM();

            bool canBeExecuted() const;
            bool canBeEagerlyEvaluated() const;

            template< typename F >
            void forEachBB( F&& func )
            {
                for( auto&& bb : m_basicBlocks )
                    func( bb );
            }

            void setStorageLocationModifiedByLoop( uint32_t loopId, const eir::Term& type, const StorageLocation& sloc )
            {
                m_loopModifiedStorageLocations.emplace( make_pair( loopId, sloc ), type );
            }

            template< typename F >
            void forEachStorageLocationModifiedByLoop( uint32_t loopId, F&& func ) const
            {
                auto begin = m_loopModifiedStorageLocations.lower_bound( { loopId, Address{ Address::Origin::Stack, 0, 0 } } );
                auto end = m_loopModifiedStorageLocations.upper_bound( { loopId, monostate() } );

                for( auto it = begin; it != end; ++it )
                    func( it->second, it->first.second );
            }

            template< typename F >
            void forEachReachableBlock( const ptr< BasicBlock >& bb, F&& func ) const
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
            // All the edges of the CFG in SrcBBIndex, DestBBIndex form
            unordered_multimap< uint32_t, uint32_t > m_edges;

            // For each BB, store the index of its immediate dominator.
            // May be be empty if it has not (yet) been computed.
            vector< uint32_t > m_idoms;

            // For each loop, store all of the adresses modified
            // during that loop.
            // May be be empty if it has not (yet) been computed.
            multimap< pair< uint32_t, Instruction >, eir::Term > m_loopModifiedAddresses;

            // The number of temporary indices used by this CFG.
            uint32_t m_temporariesCount = 0;

            uint32_t m_loopCount = 0;

            bool m_poisoned = false;







|


|







144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
            // All the edges of the CFG in SrcBBIndex, DestBBIndex form
            unordered_multimap< uint32_t, uint32_t > m_edges;

            // For each BB, store the index of its immediate dominator.
            // May be be empty if it has not (yet) been computed.
            vector< uint32_t > m_idoms;

            // For each loop, store all of the storage locations modified
            // during that loop.
            // May be be empty if it has not (yet) been computed.
            multimap< pair< uint32_t, StorageLocation >, eir::Term > m_loopModifiedStorageLocations;

            // The number of temporary indices used by this CFG.
            uint32_t m_temporariesCount = 0;

            uint32_t m_loopCount = 0;

            bool m_poisoned = false;
Changes to bs/cir/cfgviz.cpp.
1
2
3
4
5
6
7






8
9
10
11
12
13
14
#include "cir/cir.h"
#include "builtins/builtins.h"

using namespace goose::builtins;

namespace goose::cir
{






    void CfgViz( GraphVizBuilder& builder, const CFG& cfg )
    {
        CfgViz( builder, cfg.entryBB() );
    }

    void CfgViz( GraphVizBuilder& builder, const ptr< BasicBlock >& bb )
    {







>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "cir/cir.h"
#include "builtins/builtins.h"

using namespace goose::builtins;

namespace goose::cir
{
    void CfgViz( GraphVizBuilder& builder, const Func& func )
    {
        if( func.body() )
            CfgViz( builder, *func.body() );
    }

    void CfgViz( GraphVizBuilder& builder, const CFG& cfg )
    {
        CfgViz( builder, cfg.entryBB() );
    }

    void CfgViz( GraphVizBuilder& builder, const ptr< BasicBlock >& bb )
    {
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
            sstr << ", L" << bb->loopId();

        if( bb->isLoopHeader() )
            sstr << ", LH";

        GraphVizBuilder::Node n( builder, bb.get(), sstr.str().c_str() );

        for( auto&& instr : *bb )
            CfgViz( builder, instr );

        if( bb->terminator() )
            CfgViz( builder, *bb->terminator() );
    }

    void CfgViz( GraphVizBuilder& builder, const eir::Value& val )







|







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
            sstr << ", L" << bb->loopId();

        if( bb->isLoopHeader() )
            sstr << ", LH";

        GraphVizBuilder::Node n( builder, bb.get(), sstr.str().c_str() );

        for( auto&& instr : bb->instructions() )
            CfgViz( builder, instr );

        if( bb->terminator() )
            CfgViz( builder, *bb->terminator() );
    }

    void CfgViz( GraphVizBuilder& builder, const eir::Value& val )
44
45
46
47
48
49
50






51
52
53
54
55
56
57
            GraphVizBuilder::Color col( builder );
            builder.output() << val;
            return;
        }

        CfgViz( builder, *val.cir() );
    }







    void CfgViz( GraphVizBuilder& builder, const Instruction& instr )
    {
        visit( [&]( auto&& ins )
        {
            CfgViz( builder, ins );
        }, instr.content() );







>
>
>
>
>
>







50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
            GraphVizBuilder::Color col( builder );
            builder.output() << val;
            return;
        }

        CfgViz( builder, *val.cir() );
    }

    void CfgViz( GraphVizBuilder& builder, const InstrSeq& is )
    {
        for( const auto& instr : is )
            CfgViz( builder, instr );
    }

    void CfgViz( GraphVizBuilder& builder, const Instruction& instr )
    {
        visit( [&]( auto&& ins )
        {
            CfgViz( builder, ins );
        }, instr.content() );
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130

    void CfgViz( GraphVizBuilder& builder, const Call& instr )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder );



        // TODO also visualize the func and the args













        builder.output() << "Call";










    }

    void CfgViz( GraphVizBuilder& builder, const VarAddr& instr )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder );

        builder.output() << "VarAddr";
    }

    void CfgViz( GraphVizBuilder& builder, const TempAddr& instr )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder );

        builder.output() << "TempAddr";
    }

    void CfgViz( GraphVizBuilder& builder, const Select& instr )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder );

        builder.output() << "Select";
    }

    void CfgViz( GraphVizBuilder& builder, const CreateTemporary& instr )
    {
        builder.queueWork( [&]
        {
            CfgViz( builder, instr.value() );
        } );
        auto id = builder.getNodeId( &instr.value() );

        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );

        builder.output() << "CreateTemporary " << instr.index() << ", " << id;
        builder.addEdge( builder.currentNodeId(), id );
    }

    void CfgViz( GraphVizBuilder& builder, const GetTemporary& instr )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder );







>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>








|








|








|




<
<
<
<
<
<


|

|
<







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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148






149
150
151
152
153

154
155
156
157
158
159
160

    void CfgViz( GraphVizBuilder& builder, const Call& instr )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder );

        builder.output() << "Call " << instr.numArgs();
    }

    void CfgViz( GraphVizBuilder& builder, const Constant& instr )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder );

        stringstream sstr;
        sstr << instr.value();
        for( auto c : sstr.str() )
        {
            switch( c )
            {
                case '<':
                    builder.output() << "&lt;";
                    break;

                case '>':
                    builder.output() << "&gt;";
                    break;

                default:
                    builder.output() << c;
            }
        }
    }

    void CfgViz( GraphVizBuilder& builder, const VarAddr& instr )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder );

        builder.output() << "VarAddr " << instr.varIndex();
    }

    void CfgViz( GraphVizBuilder& builder, const TempAddr& instr )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder );

        builder.output() << "TempAddr " << instr.tempIndex();
    }

    void CfgViz( GraphVizBuilder& builder, const Select& instr )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder );

        builder.output() << "Select " << instr.memberIndex();
    }

    void CfgViz( GraphVizBuilder& builder, const CreateTemporary& instr )
    {






        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder );

        builder.output() << "CreateTemporary " << instr.index();

    }

    void CfgViz( GraphVizBuilder& builder, const GetTemporary& instr )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder );
173
174
175
176
177
178
179








180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
            auto id = builder.getNodeId( pBB.get() );
            GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );

            builder.output() << "BB #" << id << ", " << index;
            return true;
        } );
    }









    void CfgViz( GraphVizBuilder& builder, const Ret& t )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );

        if( t.value() )
        {
            builder.queueWork( [&]
            {
                CfgViz( builder, *t.value() );
            } );
            auto id = builder.getNodeId( &*t.value() );
            GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );

            builder.output() << "Ret " << id;
            builder.addEdge( builder.currentNodeId(), id );
        }
        else
        {
            GraphVizBuilder::Color col( builder );
            builder.output() << "Ret";
        }
    }

    void CfgViz( GraphVizBuilder& builder, const Not& instr )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder );







>
>
>
>
>
>
>
>





<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|
<







203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222















223
224

225
226
227
228
229
230
231
            auto id = builder.getNodeId( pBB.get() );
            GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );

            builder.output() << "BB #" << id << ", " << index;
            return true;
        } );
    }

    void CfgViz( GraphVizBuilder& builder, const RetVoid& t )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder );
        builder.output() << "RetVoid";
    }

    void CfgViz( GraphVizBuilder& builder, const Ret& t )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );















        GraphVizBuilder::Color col( builder );
        builder.output() << "Ret";

    }

    void CfgViz( GraphVizBuilder& builder, const Not& instr )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder );
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246








247
248
249
250
251
252
253
254
255
256
257
258
        builder.output() << "BinaryOp";
    }

    void CfgViz( GraphVizBuilder& builder, const Assert& instr )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );

        /*builder.queueWork( [&]
        {
            CfgViz( builder, instr.cond() );
        } );*/
        auto id = builder.getNodeId( &instr.cond() );
        GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );

        builder.output() << "Assert " << id;
        //builder.addEdge( builder.currentNodeId(), id );
    }

    void CfgViz( GraphVizBuilder& builder, const Placeholder& instr )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        builder.output() << '@' << instr.name();
    }

    void CfgViz( GraphVizBuilder& builder, const PHOverride& instr )








    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder );
        builder.output() << "PHOverride";
    }

    void CfgViz( GraphVizBuilder& builder, const GhostCall& instr )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder );







<
<
<
<
<
<
|
<
|
<









|
>
>
>
>
>
>
>
>




|







242
243
244
245
246
247
248






249

250

251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
        builder.output() << "BinaryOp";
    }

    void CfgViz( GraphVizBuilder& builder, const Assert& instr )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );






        GraphVizBuilder::Color col( builder );

        builder.output() << "Assert";

    }

    void CfgViz( GraphVizBuilder& builder, const Placeholder& instr )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        builder.output() << '@' << instr.name();
    }

    void CfgViz( GraphVizBuilder& builder, const PHOverrideSet& instr )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder );
        builder.output() << "PHOverrideSet";
    }

    void CfgViz( GraphVizBuilder& builder, const PHOverrideClear& instr )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder );
        builder.output() << "PHOverrideClear";
    }

    void CfgViz( GraphVizBuilder& builder, const GhostCall& instr )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder );
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
            CfgViz( builder, t.trueDest().lock() );
            CfgViz( builder, t.falseDest().lock() );
        } );

        {
            GraphVizBuilder::Row row( builder );
            GraphVizBuilder::Cell cell( builder );
            auto id = builder.getNodeId( &t.cond() );
            GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );

            builder.output() << "CondBranch " << id;
        }

        GraphVizBuilder::Row row( builder );

        {
            GraphVizBuilder::Cell cell( builder );








<
|

|







307
308
309
310
311
312
313

314
315
316
317
318
319
320
321
322
323
            CfgViz( builder, t.trueDest().lock() );
            CfgViz( builder, t.falseDest().lock() );
        } );

        {
            GraphVizBuilder::Row row( builder );
            GraphVizBuilder::Cell cell( builder );

            GraphVizBuilder::Color col( builder );

            builder.output() << "CondBranch";
        }

        GraphVizBuilder::Row row( builder );

        {
            GraphVizBuilder::Cell cell( builder );

325
326
327
328
329
330
331

332
333
334
335
336
337
338
            CfgViz( builder, t.ghostCode().lock() );
            CfgViz( builder, t.continuation().lock() );
        } );

        {
            GraphVizBuilder::Row row( builder );
            GraphVizBuilder::Cell cell( builder );


            builder.output() << "GhostBranch";
        }

        GraphVizBuilder::Row row( builder );

        {







>







346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
            CfgViz( builder, t.ghostCode().lock() );
            CfgViz( builder, t.continuation().lock() );
        } );

        {
            GraphVizBuilder::Row row( builder );
            GraphVizBuilder::Cell cell( builder );
            GraphVizBuilder::Color col( builder );

            builder.output() << "GhostBranch";
        }

        GraphVizBuilder::Row row( builder );

        {
Changes to bs/cir/cfgviz.h.
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
28
29
30
31
32
33
34
35
36
37
38

39
40

41
42
43
44
45
46
47
48
49
#ifndef GOOSE_CIR_CFGVIZ_H
#define GOOSE_CIR_CFGVIZ_H

namespace goose::cir
{
    template< typename T >
    void CfgViz( const char* pFilename, const T& x )
    {
        ofstream file( pFilename );
        GraphVizBuilder builder( file, true );
        CfgViz( builder, x );
    }


    extern void CfgViz( GraphVizBuilder& builder, const CFG& cfg );
    extern void CfgViz( GraphVizBuilder& builder, const ptr< BasicBlock >& bb );
    extern void CfgViz( GraphVizBuilder& builder, const eir::Value& val );


    extern void CfgViz( GraphVizBuilder& builder, const Instruction& instr );
    extern void CfgViz( GraphVizBuilder& builder, const Terminator& t );

    extern void CfgViz( GraphVizBuilder& builder, const monostate& );
    extern void CfgViz( GraphVizBuilder& builder, const Call& instr );


    extern void CfgViz( GraphVizBuilder& builder, const VarAddr& instr );
    extern void CfgViz( GraphVizBuilder& builder, const TempAddr& instr );
    extern void CfgViz( GraphVizBuilder& builder, const Select& instr );
    extern void CfgViz( GraphVizBuilder& builder, const CreateTemporary& instr );
    extern void CfgViz( GraphVizBuilder& builder, const GetTemporary& instr );
    extern void CfgViz( GraphVizBuilder& builder, const AllocVar& instr );
    extern void CfgViz( GraphVizBuilder& builder, const Load& instr );
    extern void CfgViz( GraphVizBuilder& builder, const Store& instr );
    extern void CfgViz( GraphVizBuilder& builder, const Phi& instr );

    extern void CfgViz( GraphVizBuilder& builder, const Not& instr );
    extern void CfgViz( GraphVizBuilder& builder, const BinaryOp& instr );

    extern void CfgViz( GraphVizBuilder& builder, const Assert& instr );
    extern void CfgViz( GraphVizBuilder& builder, const Placeholder& instr );
    extern void CfgViz( GraphVizBuilder& builder, const PHOverride& instr );

    extern void CfgViz( GraphVizBuilder& builder, const GhostCall& instr );


    extern void CfgViz( GraphVizBuilder& builder, const Ret& t );
    extern void CfgViz( GraphVizBuilder& builder, const Branch& t );
    extern void CfgViz( GraphVizBuilder& builder, const CondBranch& t );
    extern void CfgViz( GraphVizBuilder& builder, const GhostBranch& t );
    extern void CfgViz( GraphVizBuilder& builder, const Break& t );
    extern void CfgViz( GraphVizBuilder& builder, const Continue& t );
}

#endif













>




>





>
>















|
>


>









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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#ifndef GOOSE_CIR_CFGVIZ_H
#define GOOSE_CIR_CFGVIZ_H

namespace goose::cir
{
    template< typename T >
    void CfgViz( const char* pFilename, const T& x )
    {
        ofstream file( pFilename );
        GraphVizBuilder builder( file, true );
        CfgViz( builder, x );
    }

    extern void CfgViz( GraphVizBuilder& builder, const Func& func );
    extern void CfgViz( GraphVizBuilder& builder, const CFG& cfg );
    extern void CfgViz( GraphVizBuilder& builder, const ptr< BasicBlock >& bb );
    extern void CfgViz( GraphVizBuilder& builder, const eir::Value& val );

    extern void CfgViz( GraphVizBuilder& builder, const InstrSeq& is );
    extern void CfgViz( GraphVizBuilder& builder, const Instruction& instr );
    extern void CfgViz( GraphVizBuilder& builder, const Terminator& t );

    extern void CfgViz( GraphVizBuilder& builder, const monostate& );
    extern void CfgViz( GraphVizBuilder& builder, const Call& instr );

    extern void CfgViz( GraphVizBuilder& builder, const Constant& instr );
    extern void CfgViz( GraphVizBuilder& builder, const VarAddr& instr );
    extern void CfgViz( GraphVizBuilder& builder, const TempAddr& instr );
    extern void CfgViz( GraphVizBuilder& builder, const Select& instr );
    extern void CfgViz( GraphVizBuilder& builder, const CreateTemporary& instr );
    extern void CfgViz( GraphVizBuilder& builder, const GetTemporary& instr );
    extern void CfgViz( GraphVizBuilder& builder, const AllocVar& instr );
    extern void CfgViz( GraphVizBuilder& builder, const Load& instr );
    extern void CfgViz( GraphVizBuilder& builder, const Store& instr );
    extern void CfgViz( GraphVizBuilder& builder, const Phi& instr );

    extern void CfgViz( GraphVizBuilder& builder, const Not& instr );
    extern void CfgViz( GraphVizBuilder& builder, const BinaryOp& instr );

    extern void CfgViz( GraphVizBuilder& builder, const Assert& instr );
    extern void CfgViz( GraphVizBuilder& builder, const Placeholder& instr );
    extern void CfgViz( GraphVizBuilder& builder, const PHOverrideSet& instr );
    extern void CfgViz( GraphVizBuilder& builder, const PHOverrideClear& instr );
    extern void CfgViz( GraphVizBuilder& builder, const GhostCall& instr );

    extern void CfgViz( GraphVizBuilder& builder, const RetVoid& t );
    extern void CfgViz( GraphVizBuilder& builder, const Ret& t );
    extern void CfgViz( GraphVizBuilder& builder, const Branch& t );
    extern void CfgViz( GraphVizBuilder& builder, const CondBranch& t );
    extern void CfgViz( GraphVizBuilder& builder, const GhostBranch& t );
    extern void CfgViz( GraphVizBuilder& builder, const Break& t );
    extern void CfgViz( GraphVizBuilder& builder, const Continue& t );
}

#endif
Changes to bs/cir/cir.h.
13
14
15
16
17
18
19



20
21
22
23
24
25
26
    static constexpr uint32_t InvalidVarId = numeric_limits< uint32_t >::max();

    class CFG;
}

#include "helpers.h"




#include "varaddr.h"
#include "tempaddr.h"
#include "select.h"
#include "call.h"
#include "createtemporary.h"
#include "gettemporary.h"
#include "allocvar.h"







>
>
>







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    static constexpr uint32_t InvalidVarId = numeric_limits< uint32_t >::max();

    class CFG;
}

#include "helpers.h"

#include "storagelocation.h"

#include "constant.h"
#include "varaddr.h"
#include "tempaddr.h"
#include "select.h"
#include "call.h"
#include "createtemporary.h"
#include "gettemporary.h"
#include "allocvar.h"
40
41
42
43
44
45
46

47
48
49
50
51
52
53
54
55
56
#include "ass.h"
#include "placeholder.h"
#include "phoverride.h"
#include "ghostcall.h"

#include "instruction.h"
#include "terminator.h"

#include "basicblock.h"
#include "cfg.h"
#include "func.h"
#include "hash.h"
#include "decorator.h"
#include "cfgviz.h"

#include "basicblock.inl"

#endif







>










43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include "ass.h"
#include "placeholder.h"
#include "phoverride.h"
#include "ghostcall.h"

#include "instruction.h"
#include "terminator.h"
#include "verifinstrfilter.h"
#include "basicblock.h"
#include "cfg.h"
#include "func.h"
#include "hash.h"
#include "decorator.h"
#include "cfgviz.h"

#include "basicblock.inl"

#endif
Changes to bs/cir/comparison.h.
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
28
29
30
31
32
33
34

35
36
37
38
39
40
41
42
43
44
45
46

47
48
49
50
51
52
53
54
55
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
114
115
116
117
118

119
120
121
122
123
124
125
126
127
#ifndef GOOSE_CIR_COMPARISON_H
#define GOOSE_CIR_COMPARISON_H

namespace goose::cir
{
    class Eq : public BinaryOpSameTypes
    {
        public:
            template< typename L, typename R >
            Eq( L&& lhs, R&& rhs ) : BinaryOpSameTypes( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const Eq& ins )
            {
                return ins.print( out, "EQ" );
            }
    };

    class Neq : public BinaryOpSameTypes
    {
        public:
            template< typename L, typename R >
            Neq( L&& lhs, R&& rhs ) : BinaryOpSameTypes( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const Neq& ins )
            {
                return ins.print( out, "NEQ" );
            }
    };

    class UGT : public BinaryOpSameTypes
    {
        public:
            template< typename L, typename R >
            UGT( L&& lhs, R&& rhs ) : BinaryOpSameTypes( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const UGT& ins )
            {
                return ins.print( out, "UGT" );
            }
    };

    class UGE : public BinaryOpSameTypes
    {
        public:
            template< typename L, typename R >
            UGE( L&& lhs, R&& rhs ) : BinaryOpSameTypes( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const UGE& ins )
            {
                return ins.print( out, "UGE" );
            }
    };

    class ULT : public BinaryOpSameTypes
    {
        public:
            template< typename L, typename R >
            ULT( L&& lhs, R&& rhs ) : BinaryOpSameTypes( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const ULT& ins )
            {
                return ins.print( out, "ULT" );
            }
    };

    class ULE : public BinaryOpSameTypes
    {
        public:
            template< typename L, typename R >
            ULE( L&& lhs, R&& rhs ) : BinaryOpSameTypes( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const ULE& ins )
            {
                return ins.print( out, "ULE" );
            }
    };

    class SGT : public BinaryOpSameTypes
    {
        public:
            template< typename L, typename R >
            SGT( L&& lhs, R&& rhs ) : BinaryOpSameTypes( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const SGT& ins )
            {
                return ins.print( out, "SGT" );
            }
    };

    class SGE : public BinaryOpSameTypes
    {
        public:
            template< typename L, typename R >
            SGE( L&& lhs, R&& rhs ) : BinaryOpSameTypes( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const SGE& ins )
            {
                return ins.print( out, "SGE" );
            }
    };

    class SLT : public BinaryOpSameTypes
    {
        public:
            template< typename L, typename R >
            SLT( L&& lhs, R&& rhs ) : BinaryOpSameTypes( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const SLT& ins )
            {
                return ins.print( out, "SLT" );
            }
    };

    class SLE : public BinaryOpSameTypes
    {
        public:
            template< typename L, typename R >
            SLE( L&& lhs, R&& rhs ) : BinaryOpSameTypes( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const SLE& ins )
            {
                return ins.print( out, "SLE" );
            }
    };
}

#endif





|


|
|
>



|



|


|
|
>



|



|


|
|
>



|



|


|
|
>



|



|


|
|
>



|



|


|
|
>



|



|


|
|
>



|



|


|
|
>



|



|


|
|
>



|



|


|
|
>



|





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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#ifndef GOOSE_CIR_COMPARISON_H
#define GOOSE_CIR_COMPARISON_H

namespace goose::cir
{
    class Eq : public BinaryOp
    {
        public:
            Eq( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Eq& ins )
            {
                return out << "EQ";
            }
    };

    class Neq : public BinaryOp
    {
        public:
            Neq( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Neq& ins )
            {
                return out << "NEQ";
            }
    };

    class UGT : public BinaryOp
    {
        public:
            UGT( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const UGT& ins )
            {
                return out << "UGT";
            }
    };

    class UGE : public BinaryOp
    {
        public:
            UGE( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const UGE& ins )
            {
                return out << "UGE";
            }
    };

    class ULT : public BinaryOp
    {
        public:
            ULT( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const ULT& ins )
            {
                return out << "ULT";
            }
    };

    class ULE : public BinaryOp
    {
        public:
            ULE( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const ULE& ins )
            {
                return out << "ULE";
            }
    };

    class SGT : public BinaryOp
    {
        public:
            SGT( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const SGT& ins )
            {
                return out << "SGT";
            }
    };

    class SGE : public BinaryOp
    {
        public:
            SGE( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const SGE& ins )
            {
                return out << "SGE";
            }
    };

    class SLT : public BinaryOp
    {
        public:
            SLT( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const SLT& ins )
            {
                return out << "SLT";
            }
    };

    class SLE : public BinaryOp
    {
        public:
            SLE( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const SLE& ins )
            {
                return out << "SLE";
            }
    };
}

#endif
Added bs/cir/constant.h.








































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
28
29
30
31
32
33
34
35
36
#ifndef GOOSE_CIR_CONSTANT_H
#define GOOSE_CIR_CONSTANT_H

namespace goose::cir
{
    class Constant : public BaseInstr< 0, true >
    {
        public:
            template< typename V >
            Constant( V&& val ) :
                BaseInstr( val.locationId() ),
                m_value( forward< V >( val ) )
            {}

            const auto& value() const { return m_value; }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return true; }
            bool haveSideEffects() const { return false; }

            bool operator<( const Constant& rhs ) const
            {
                return m_value < rhs.m_value;
            }

            friend ostream& operator<<( ostream& out, const Constant& ins )
            {
                return out << "CONSTANT(" << ins.m_value << ')';
            }

        private:
            eir::Value m_value;
    };
}

#endif
Changes to bs/cir/createtemporary.h.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#ifndef GOOSE_CIR_CREATETEMPORARY_H
#define GOOSE_CIR_CREATETEMPORARY_H

namespace goose::cir
{
    class CreateTemporary
    {
        public:
            template< typename V >
            CreateTemporary( uint32_t index, V&& val ) :

                m_index( index ),
                m_value( forward< V >( val ) )
            {}

            const auto& index() const { return m_index; }
            const auto& value() const { return m_value; }

            bool canBeExecuted() const
            {
                return IsValueConstantOrExecutable( m_value );
            }

            bool canBeEagerlyEvaluated() const
            {
                return CanValueBeEagerlyEvaluated( m_value );
            }

            bool operator<( const CreateTemporary& rhs ) const
            {
                if( m_index != rhs.m_index )
                    return m_index < rhs.m_index;
                return m_value < rhs.m_value;
            }

            friend ostream& operator<<( ostream& out, const CreateTemporary& ins )
            {
                return out << "CREATETEMP(" << ins.m_index << ", " << ins.m_value << ')';
            }

        private:
            uint32_t m_index = 0;
            eir::Value m_value;
    };
}

#endif





|


<
|
>
|
<



<

|
<
<
<
<
|
<
|
<



<
|
<




|




<




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
28
29
30
31

32
33
34
35
#ifndef GOOSE_CIR_CREATETEMPORARY_H
#define GOOSE_CIR_CREATETEMPORARY_H

namespace goose::cir
{
    class CreateTemporary : public BaseInstr< 1, false >
    {
        public:

            CreateTemporary( uint32_t index, LocationId loc ) :
                BaseInstr( loc ),
                m_index( index )

            {}

            const auto& index() const { return m_index; }


            bool canBeExecuted() const { return true; }




            bool canBeEagerlyEvaluated() const { return true; }

            bool haveSideEffects() const { return true; }


            bool operator<( const CreateTemporary& rhs ) const
            {

                return m_index < rhs.m_index;

            }

            friend ostream& operator<<( ostream& out, const CreateTemporary& ins )
            {
                return out << "CREATETEMP(" << ins.m_index << ')';
            }

        private:
            uint32_t m_index = 0;

    };
}

#endif
Changes to bs/cir/decorator.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "cir.h"

using namespace goose::cir;

void Decorator::appendTo( BasicBlock& bb ) &&
{
    bb.reserve( bb.size() + m_prologue.size() + m_operation.size() + m_epilogue.size() );

    for( auto&& x : m_prologue )
        bb.emplace_back( move( x ) );

    for( auto&& x : m_operation )
        bb.emplace_back( move( x ) );

    for( auto&& x : m_epilogue )
        bb.emplace_back( move( x ) );
}






<
<
|
<
<
|
<
<
|
<

1
2
3
4
5
6


7


8


9

10
#include "cir.h"

using namespace goose::cir;

void Decorator::appendTo( BasicBlock& bb ) &&
{


    bb.append( move( m_prologue ) );


    bb.append( move( m_operation ) );


    bb.append( move( m_epilogue) );

}
Changes to bs/cir/decorator.h.
25
26
27
28
29
30
31
32
33
34
35
36
37
38
            {
                m_epilogue.emplace_back( forward< I >( instr ) );
            }

            void appendTo( BasicBlock& bb ) &&;

        private:
            llvm::SmallVector< Instruction, 8 > m_prologue;
            llvm::SmallVector< Instruction, 8 > m_operation;
            llvm::SmallVector< Instruction, 8 > m_epilogue;
    };
}

#endif







|
|
|




25
26
27
28
29
30
31
32
33
34
35
36
37
38
            {
                m_epilogue.emplace_back( forward< I >( instr ) );
            }

            void appendTo( BasicBlock& bb ) &&;

        private:
            InstrSeq m_prologue;
            InstrSeq m_operation;
            InstrSeq m_epilogue;
    };
}

#endif
Changes to bs/cir/gettemporary.h.
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
#ifndef GOOSE_CIR_GETTEMPORARY_H
#define GOOSE_CIR_GETTEMPORARY_H

namespace goose::cir
{
    class GetTemporary
    {
        public:
            template< typename T >
            GetTemporary( T&& type, uint32_t index ) :

                m_type( forward< T >( type ) ),
                m_index( index )
            {}

            const auto& type() const { return m_type; }
            const auto& index() const { return m_index; }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return true; }


            bool operator<( const GetTemporary& rhs ) const
            {
                if( m_index != rhs.m_index )
                    return m_index < rhs.m_index;
                return m_type < rhs.m_type;
            }





|



|
>









>







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
28
#ifndef GOOSE_CIR_GETTEMPORARY_H
#define GOOSE_CIR_GETTEMPORARY_H

namespace goose::cir
{
    class GetTemporary : public BaseInstr< 0, true >
    {
        public:
            template< typename T >
            GetTemporary( T&& type, uint32_t index, LocationId loc ) :
                BaseInstr( loc ),
                m_type( forward< T >( type ) ),
                m_index( index )
            {}

            const auto& type() const { return m_type; }
            const auto& index() const { return m_index; }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return true; }
            bool haveSideEffects() const { return false; }

            bool operator<( const GetTemporary& rhs ) const
            {
                if( m_index != rhs.m_index )
                    return m_index < rhs.m_index;
                return m_type < rhs.m_type;
            }
Changes to bs/cir/ghostcall.h.
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
28
29
30
31
32
33
34
35
36
37
38
39
#ifndef GOOSE_CIR_GHOSTCALL_H
#define GOOSE_CIR_GHOSTCALL_H

namespace goose::cir
{
    class GhostCall
    {
        public:
            template< typename F, typename A >
            GhostCall( F&& func, A&& args ) :
                m_func( forward< F >( func ) ),
                m_args( forward< A >( args ) )
            {}

            const auto& func() const { return m_func; }
            const auto& args() const { return m_args; }

            bool canBeExecuted() const { return false; }
            bool canBeEagerlyEvaluated() const { return false; }


            bool operator<( const GhostCall& rhs ) const
            {
                if( m_func != rhs.m_func )
                    return m_func < rhs.m_func;
                return m_args < rhs.m_args;
            }

            friend ostream& operator<<( ostream& out, const GhostCall& ins )
            {
                return out << "GHOSTCALL(" << ins.m_func << ", " << ins.m_args << ')';
            }

        private:
            eir::Value m_func;
            eir::Term m_args;
    };
}

#endif





|


<
|
|
|


<
|



>



<
<
|




|



|
<




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
28
29
30
31

32
33
34
35
#ifndef GOOSE_CIR_GHOSTCALL_H
#define GOOSE_CIR_GHOSTCALL_H

namespace goose::cir
{
    class GhostCall : public BaseInstr< 1, true >   // Can pop more depending on arg count
    {
        public:

            GhostCall( uint32_t numArgs, LocationId loc ) :
                BaseInstr( loc ),
                m_numArgs( numArgs )
            {}


            const auto& numArgs() const { return m_numArgs; }

            bool canBeExecuted() const { return false; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return true; }

            bool operator<( const GhostCall& rhs ) const
            {


                return m_numArgs < rhs.m_numArgs;
            }

            friend ostream& operator<<( ostream& out, const GhostCall& ins )
            {
                return out << "GHOSTCALL";
            }

        private:
            uint32_t m_numArgs = 0;

    };
}

#endif
Changes to bs/cir/hash.cpp.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199





200
201
202
203
204
205
206
#include "cir/cir.h"

namespace std
{






    size_t hash< goose::cir::Instruction >::operator()( const goose::cir::Instruction& x ) const
    {
        return goose::util::ComputeHash( x.content() );
    }

    size_t hash< goose::cir::Call >::operator()( const goose::cir::Call& x ) const
    {





        return llvm::hash_combine( goose::util::ComputeHash( x.func() ), goose::util::ComputeHash( x.args() ) );
    }

    size_t hash< goose::cir::VarAddr >::operator()( const goose::cir::VarAddr& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.varIndex() ), goose::util::ComputeHash( x.type() ) );
    }

    size_t hash< goose::cir::TempAddr >::operator()( const goose::cir::TempAddr& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.tempIndex() ), goose::util::ComputeHash( x.initValue() ) );
    }

    size_t hash< goose::cir::Select >::operator()( const goose::cir::Select& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.baseAddr() ), goose::util::ComputeHash( x.memberIndex() ) );
    }

    size_t hash< goose::cir::CreateTemporary >::operator()( const goose::cir::CreateTemporary& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.index() ), goose::util::ComputeHash( x.value() ) );
    }

    size_t hash< goose::cir::GetTemporary >::operator()( const goose::cir::GetTemporary& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( x.index() ) );
    }

    size_t hash< goose::cir::AllocVar >::operator()( const goose::cir::AllocVar& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( x.index() ) );
    }

    size_t hash< goose::cir::Load >::operator()( const goose::cir::Load& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( *x.addr() ), goose::util::ComputeHash( x.type() ) );
    }

    size_t hash< goose::cir::Store >::operator()( const goose::cir::Store& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( *x.addr() ), goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( x.val() ) );
    }

    size_t hash< goose::cir::Phi >::operator()( const goose::cir::Phi& x ) const
    {
        // Note: we don't bother hashing the incomings here, and we don't really care because we only really need hashing for the
        // predicates and phi doesn't make sense there
        return llvm::hash_combine( goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( x.numIncomings() ), goose::util::ComputeHash( x.destIndex() ) );
    }

    size_t hash< goose::cir::Not >::operator()( const goose::cir::Not& x ) const
    {
        return goose::util::ComputeHash( x.operand() );
    }

    size_t hash< goose::cir::And >::operator()( const goose::cir::And& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::Or >::operator()( const goose::cir::Or& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::Xor >::operator()( const goose::cir::Xor& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::Implies >::operator()( const goose::cir::Implies& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::Shl >::operator()( const goose::cir::Shl& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::LShr >::operator()( const goose::cir::LShr& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::AShr >::operator()( const goose::cir::AShr& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::Add >::operator()( const goose::cir::Add& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::Sub >::operator()( const goose::cir::Sub& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::Mul >::operator()( const goose::cir::Mul& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::UDiv >::operator()( const goose::cir::UDiv& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::SDiv >::operator()( const goose::cir::SDiv& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::URem >::operator()( const goose::cir::URem& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::SRem >::operator()( const goose::cir::SRem& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::Eq >::operator()( const goose::cir::Eq& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::Neq >::operator()( const goose::cir::Neq& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::UGT >::operator()( const goose::cir::UGT& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::UGE >::operator()( const goose::cir::UGE& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::ULT >::operator()( const goose::cir::ULT& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::ULE >::operator()( const goose::cir::ULE& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::SGT >::operator()( const goose::cir::SGT& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::SGE >::operator()( const goose::cir::SGE& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::SLT >::operator()( const goose::cir::SLT& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::SLE >::operator()( const goose::cir::SLE& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.lhs() ), goose::util::ComputeHash( x.rhs() ) );
    }

    size_t hash< goose::cir::Assert >::operator()( const goose::cir::Assert& x ) const
    {
        return goose::util::ComputeHash( x.cond() );
    }

    size_t hash< goose::cir::Placeholder >::operator()( const goose::cir::Placeholder& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( x.name() ) );
    }

    size_t hash< goose::cir::PHOverride >::operator()( const goose::cir::PHOverride& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.name() ), goose::util::ComputeHash( x.phVal() ), goose::util::ComputeHash( x.val() ) );





    }

    size_t hash< goose::cir::GhostCall >::operator()( const goose::cir::GhostCall& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.func() ), goose::util::ComputeHash( x.args() ) );
    }
}




>
>
>
>
>
>







>
>
>
>
>
|









|




|




|














|




|











|




|




|




|




|




|




|




|




|




|




|




|




|




|




|




|




|




|




|




|




|




|




|




|




|




|







|

|
>
>
>
>
>




|


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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#include "cir/cir.h"

namespace std
{
    size_t hash< goose::cir::InstrSeq >::operator()( const goose::cir::InstrSeq& is ) const
    {
        auto g = goose::util::ContainerHashGenerator( is );
        return llvm::hash_combine_range( g.begin(), g.end() );
    }

    size_t hash< goose::cir::Instruction >::operator()( const goose::cir::Instruction& x ) const
    {
        return goose::util::ComputeHash( x.content() );
    }

    size_t hash< goose::cir::Call >::operator()( const goose::cir::Call& x ) const
    {
        return goose::util::ComputeHash( x.numArgs() );
    }

    size_t hash< goose::cir::Constant >::operator()( const goose::cir::Constant& x ) const
    {
        return goose::util::ComputeHash( x.value() );
    }

    size_t hash< goose::cir::VarAddr >::operator()( const goose::cir::VarAddr& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.varIndex() ), goose::util::ComputeHash( x.type() ) );
    }

    size_t hash< goose::cir::TempAddr >::operator()( const goose::cir::TempAddr& x ) const
    {
        return goose::util::ComputeHash( x.tempIndex() );
    }

    size_t hash< goose::cir::Select >::operator()( const goose::cir::Select& x ) const
    {
        return goose::util::ComputeHash( x.memberIndex() );
    }

    size_t hash< goose::cir::CreateTemporary >::operator()( const goose::cir::CreateTemporary& x ) const
    {
        return goose::util::ComputeHash( x.index() );
    }

    size_t hash< goose::cir::GetTemporary >::operator()( const goose::cir::GetTemporary& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( x.index() ) );
    }

    size_t hash< goose::cir::AllocVar >::operator()( const goose::cir::AllocVar& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( x.index() ) );
    }

    size_t hash< goose::cir::Load >::operator()( const goose::cir::Load& x ) const
    {
        return goose::util::ComputeHash( x.type() );
    }

    size_t hash< goose::cir::Store >::operator()( const goose::cir::Store& x ) const
    {
        return goose::util::ComputeHash( x.type() );
    }

    size_t hash< goose::cir::Phi >::operator()( const goose::cir::Phi& x ) const
    {
        // Note: we don't bother hashing the incomings here, and we don't really care because we only really need hashing for the
        // predicates and phi doesn't make sense there
        return llvm::hash_combine( goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( x.numIncomings() ), goose::util::ComputeHash( x.destIndex() ) );
    }

    size_t hash< goose::cir::Not >::operator()( const goose::cir::Not& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::And >::operator()( const goose::cir::And& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::Or >::operator()( const goose::cir::Or& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::Xor >::operator()( const goose::cir::Xor& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::Implies >::operator()( const goose::cir::Implies& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::Shl >::operator()( const goose::cir::Shl& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::LShr >::operator()( const goose::cir::LShr& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::AShr >::operator()( const goose::cir::AShr& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::Add >::operator()( const goose::cir::Add& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::Sub >::operator()( const goose::cir::Sub& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::Mul >::operator()( const goose::cir::Mul& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::UDiv >::operator()( const goose::cir::UDiv& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::SDiv >::operator()( const goose::cir::SDiv& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::URem >::operator()( const goose::cir::URem& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::SRem >::operator()( const goose::cir::SRem& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::Eq >::operator()( const goose::cir::Eq& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::Neq >::operator()( const goose::cir::Neq& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::UGT >::operator()( const goose::cir::UGT& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::UGE >::operator()( const goose::cir::UGE& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::ULT >::operator()( const goose::cir::ULT& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::ULE >::operator()( const goose::cir::ULE& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::SGT >::operator()( const goose::cir::SGT& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::SGE >::operator()( const goose::cir::SGE& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::SLT >::operator()( const goose::cir::SLT& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::SLE >::operator()( const goose::cir::SLE& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::Assert >::operator()( const goose::cir::Assert& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::Placeholder >::operator()( const goose::cir::Placeholder& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( x.name() ) );
    }

    size_t hash< goose::cir::PHOverrideSet >::operator()( const goose::cir::PHOverrideSet& x ) const
    {
        return goose::util::ComputeHash( x.name() );
    }

    size_t hash< goose::cir::PHOverrideClear >::operator()( const goose::cir::PHOverrideClear& x ) const
    {
        return goose::util::ComputeHash( x.name() );
    }

    size_t hash< goose::cir::GhostCall >::operator()( const goose::cir::GhostCall& x ) const
    {
        return goose::util::ComputeHash( x.numArgs() );
    }
}
Changes to bs/cir/hash.h.
1
2
3
4
5





6
7
8
9
10
11
12
13
14





15
16
17
18
19
20
21
#ifndef GOOSE_CIR_HASH_H
#define GOOSE_CIR_HASH_H

namespace std
{





    template<> struct hash< goose::cir::Instruction >
    {
        size_t operator()( const goose::cir::Instruction& x ) const;
    };

    template<> struct hash< goose::cir::Call >
    {
        size_t operator()( const goose::cir::Call& x ) const;
    };






    template<> struct hash< goose::cir::VarAddr >
    {
        size_t operator()( const goose::cir::VarAddr& x ) const;
    };

    template<> struct hash< goose::cir::TempAddr >





>
>
>
>
>









>
>
>
>
>







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
28
29
30
31
#ifndef GOOSE_CIR_HASH_H
#define GOOSE_CIR_HASH_H

namespace std
{
    template<> struct hash< goose::cir::InstrSeq >
    {
        size_t operator()( const goose::cir::InstrSeq& is ) const;
    };

    template<> struct hash< goose::cir::Instruction >
    {
        size_t operator()( const goose::cir::Instruction& x ) const;
    };

    template<> struct hash< goose::cir::Call >
    {
        size_t operator()( const goose::cir::Call& x ) const;
    };

    template<> struct hash< goose::cir::Constant >
    {
        size_t operator()( const goose::cir::Constant& x ) const;
    };

    template<> struct hash< goose::cir::VarAddr >
    {
        size_t operator()( const goose::cir::VarAddr& x ) const;
    };

    template<> struct hash< goose::cir::TempAddr >
189
190
191
192
193
194
195
196
197
198





199
200
201
202
203
204
205
206
207
    };

    template<> struct hash< goose::cir::Placeholder >
    {
        size_t operator()( const goose::cir::Placeholder& x ) const;
    };

    template<> struct hash< goose::cir::PHOverride >
    {
        size_t operator()( const goose::cir::PHOverride& x ) const;





    };

    template<> struct hash< goose::cir::GhostCall >
    {
        size_t operator()( const goose::cir::GhostCall& x ) const;
    };
}

#endif







|

|
>
>
>
>
>









199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
    };

    template<> struct hash< goose::cir::Placeholder >
    {
        size_t operator()( const goose::cir::Placeholder& x ) const;
    };

    template<> struct hash< goose::cir::PHOverrideSet >
    {
        size_t operator()( const goose::cir::PHOverrideSet& x ) const;
    };

    template<> struct hash< goose::cir::PHOverrideClear >
    {
        size_t operator()( const goose::cir::PHOverrideClear& x ) const;
    };

    template<> struct hash< goose::cir::GhostCall >
    {
        size_t operator()( const goose::cir::GhostCall& x ) const;
    };
}

#endif
Changes to bs/cir/helpers.cpp.
1
2
3
4





























5
6
7
8
9


10




11
12
13
14
15
16
17


18















19
20
#include "cir/cir.h"

namespace goose::cir
{





























    bool IsValueConstantOrExecutable( const eir::Value& val )
    {
        if( val.isConstant() )
            return true;



        return val.cir()->canBeExecuted();




    }

    bool CanValueBeEagerlyEvaluated( const eir::Value& val )
    {
        if( val.isConstant() )
            return true;



        return val.cir()->canBeEagerlyEvaluated();















    }
}




>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>





>
>
|
>
>
>
>







>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include "cir/cir.h"

namespace goose::cir
{
    void AppendInstrSeq( InstrSeq& is, InstrSeq&& isToAppend )
    {
        is.splice( is.end(), isToAppend );

        // TODO update canBeExecuted, store it in InstrSeq?
    }

    void AppendInstrSeq( InstrSeq& is, const InstrSeq& isToAppend )
    {
        for( auto&& instr : isToAppend )
            AppendToInstrSeq( is, instr );
    }

    void AppendValue( InstrSeq& is, Value&& v )
    {
        if( v.cir() )
            AppendToInstrSeq( is, move( *v.cir() ) );
        else
            is.emplace_back( Constant( move( v ) ) );
    }

    void AppendValue( InstrSeq& is, const Value& v )
    {
        if( v.cir() )
            AppendToInstrSeq( is, *v.cir() );
        else
            is.emplace_back( Constant( v ) );
    }

    bool IsValueConstantOrExecutable( const eir::Value& val )
    {
        if( val.isConstant() )
            return true;

        for( const auto& instr : *val.cir() )
        {
            if( !instr.canBeExecuted() )
                return false;
        }

        return true;
    }

    bool CanValueBeEagerlyEvaluated( const eir::Value& val )
    {
        if( val.isConstant() )
            return true;

        for( const auto& instr : *val.cir() )
        {
            if( !instr.canBeEagerlyEvaluated() )
                return false;
        }

        return true;
    }

    bool DoesInstrSeqHaveSideEffects( const InstrSeq& is )
    {
        for( auto&& instr : is )
        {
            if( instr.haveSideEffects() )
                return true;
        }

        return false;
    }
}
Changes to bs/cir/helpers.h.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#ifndef GOOSE_CIR_HELPERS_H
#define GOOSE_CIR_HELPERS_H

namespace goose::cir
{
    class Instruction;


    bool IsValueConstantOrExecutable( const eir::Value& val );
    bool CanValueBeEagerlyEvaluated( const eir::Value& val );













































    template< typename T, typename I >
    auto BuildComputedValue( T&& type, I&& instr )
    {



        return eir::Value( forward< T >( type ),

            make_shared< Instruction >( forward< I >( instr ) ) );







    }











    template< typename T >
    class TempStorage
    {
        public:




            template< typename TT >
            auto& set( uint32_t index, TT&& x )
            {
                auto [it, inserted] = m_storage.try_emplace( index );

                it->second = forward< TT >( x );
                return it->second;
            }

            const T* get( uint32_t index ) const
            {
                auto it = m_storage.find( index );
                if( it == m_storage.end() )
                    return nullptr;

                return &it->second;
            }

            T* get( uint32_t index )
            {
                auto it = m_storage.find( index );
                if( it == m_storage.end() )
                    return nullptr;

                return &it->second;
            }

        private:
            unordered_map< uint32_t, T > m_storage;
    };
}

#endif






>

|
|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|

>
>
>
|
>
|
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>





>
>
>
>

|

|
>
|
|




|
|
|
<
<




|
|
|
<
<



|




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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
114
115
116
117
118
119
120
#ifndef GOOSE_CIR_HELPERS_H
#define GOOSE_CIR_HELPERS_H

namespace goose::cir
{
    class Instruction;
    using InstrSeq = list< Instruction >;

    extern bool IsValueConstantOrExecutable( const eir::Value& val );
    extern bool CanValueBeEagerlyEvaluated( const eir::Value& val );

    extern bool DoesInstrSeqHaveSideEffects( const InstrSeq& is );

    extern void AppendInstrSeq( InstrSeq& is, InstrSeq&& isToAppend );
    extern void AppendInstrSeq( InstrSeq& is, const InstrSeq& isToAppend );

    extern void AppendValue( InstrSeq& is, Value&& v );
    extern void AppendValue( InstrSeq& is, const Value& v );

    template< typename I >
    void AppendToInstrSeq( InstrSeq& is, I&& instr )
    {
        using II = remove_cvref_t< I >;

        if constexpr( is_same_v< II, InstrSeq > )
        {
            AppendInstrSeq( is, forward< I >( instr ) );
        }
        else if constexpr( is_same_v< II, Value > )
        {
            AppendValue( is, forward< I >( instr ) );
        }
        else
        {
            is.emplace_back( Instruction( forward< I >( instr ) ) );
        }
    }

    static inline void AppendToInstrSeq( InstrSeq& is, const ptr< InstrSeq >& instr )
    {
        AppendToInstrSeq( is, *instr );
    }

    static inline void AppendToInstrSeq( InstrSeq& is, ptr< InstrSeq >&& instr )
    {
        AppendToInstrSeq( is, move( *instr ) );
    }

    template< typename HI, typename... TI >
    void AppendToInstrSeq( InstrSeq& is, HI&& headInstr, TI&&... tailInstrs )
    {
        AppendToInstrSeq( is, forward< HI >( headInstr ) );
        AppendToInstrSeq( is, forward< TI >( tailInstrs )... );
    }

    template< typename T, typename... I >
    auto BuildComputedValue( T&& type, I&&... instrs )
    {
        auto is = make_shared< InstrSeq >();
        AppendToInstrSeq( *is, forward< I >( instrs )... );

        return eir::Value( forward< T >( type ), move( is ) );
    }

    template< size_t popCount, bool pushesResult >
    class BaseInstr
    {
        public:
            BaseInstr( LocationId loc ) :
                m_loc( loc )
            {}

            void setLocationId( LocationId loc ) { m_loc = loc; }

            auto locationId() const { return m_loc; }

            static constexpr size_t PopCount = popCount;
            static constexpr bool PushesResult = pushesResult;

        private:
            LocationId m_loc;
    };

    template< typename T >
    class TempStorage
    {
        public:
            TempStorage( size_t size ) :
                m_storage( size )
            {}

            template< typename TT >
            T& set( uint32_t index, TT&& x )
            {
                if( index >= m_storage.size() )
                    m_storage.resize( index + 1 );
                m_storage[index] = forward< TT >( x );
                return m_storage[index];
            }

            const T* get( uint32_t index ) const
            {
                if( index < m_storage.size() )
                    return &m_storage[index];
                return nullptr;


            }

            T* get( uint32_t index )
            {
                if( index < m_storage.size() )
                    return &m_storage[index];
                return nullptr;


            }

        private:
            llvm::SmallVector< T, 16 > m_storage;
    };
}

#endif
Changes to bs/cir/instruction.cpp.
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
28
29











30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
114
115
#include "cir/cir.h"

namespace goose::cir
{
    bool Instruction::canBeExecuted() const
    {
        return visit( []< typename ET >( const ET& e )
        {
            if constexpr( is_same_v< ET, monostate > )
                return false;
            else if constexpr( is_same_v< ET, ptr< CFG > > )
                return e->canBeExecuted();
            else
                return e.canBeExecuted();
        }, m_content );
    }

    bool Instruction::canBeEagerlyEvaluated() const
    {
        return visit( []< typename ET >( const ET& e )
        {
            if constexpr( is_same_v< ET, monostate > )
                return false;
            else if constexpr( is_same_v< ET, ptr< CFG > > )
                return e->canBeEagerlyEvaluated();
            else
                return e.canBeEagerlyEvaluated();
        }, m_content );
    }












    ostream& operator<<( ostream& out, const Instruction& inst )
    {
        return visit( [&]< typename ET >( const ET& e ) -> ostream&
        {
            if constexpr( is_same_v< ET, monostate > )
                return out << "NIL";
            else if constexpr( is_same_v< ET, ptr< CFG > > )
                return out << "NESTED_CFG";
            else
                return out << e;
        }, inst.m_content );
    }

    ostream& operator<<( ostream& out, const Select& ins )
    {
        out << "SELECT(" << ins.m_memberIndex << ", ";
        if( ins.m_baseAddr )
            out << *ins.m_baseAddr;
        else
            out << "null" ;
         return out << ')';
    }

    ostream& operator<<( ostream& out, const Load& ins )
    {
        out << "LOAD(";
        if( ins.m_addr )
            out << *ins.m_addr;
        else
            out << "null";
         return out << ", " << ins.m_type << ')';
    }

    ostream& operator<<( ostream& out, const Store& ins )
    {
        out << "STORE(";
        if( ins.m_addr )
            out << *ins.m_addr;
        else
            out << "null";
        return out << ", " << ins.m_val << ')';
    }

    ostream& operator<<( ostream& out, const PHOverride& ins )
    {
        return out << "PHOVERRIDE(" << ins.m_name << ", " << ins.m_phVal << ", " << ins.m_val << ')';
    }

    bool Store::canBeEagerlyEvaluated() const
    {
        return m_addr && CanValueBeEagerlyEvaluated( m_val );
    }

    bool Select::operator<( const Select& rhs ) const
    {
        if( m_memberIndex != rhs.m_memberIndex )
            return m_memberIndex < rhs.m_memberIndex;
        return *m_baseAddr < *rhs.m_baseAddr;
    }

    bool Load::operator<( const Load& rhs ) const
    {
        if( m_type != rhs.m_type )
            return m_type < rhs.m_type;
        return *m_addr < *rhs.m_addr;
    }

    bool Store::operator<( const Store& rhs ) const
    {
        if( m_type != rhs.m_type )
            return m_type < rhs.m_type;

        if( m_addr != rhs.m_addr )
            return *m_addr < *rhs.m_addr;

        return m_val < rhs.m_val;
    }

    bool PHOverride::operator<( const PHOverride& rhs ) const
    {
        if( m_name != rhs.m_name )
            return m_name < rhs.m_name;
        if( m_phVal != rhs.m_phVal )
            return m_phVal < rhs.m_phVal;
        return m_val < rhs.m_val;
    }
}










<
<











<
<




>
>
>
>
>
>
>
>
>
>
>







<
<







|
<
<
<
<
|





<
<
<
<
|




<
<
<
<
<
|


|

|


|

|




<
|
<




<
|
<




<
|
>
|
|
>
|


|

<
|
<
<
<


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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43


44
45
46
47
48
49
50
51




52
53
54
55
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
#include "cir/cir.h"

namespace goose::cir
{
    bool Instruction::canBeExecuted() const
    {
        return visit( []< typename ET >( const ET& e )
        {
            if constexpr( is_same_v< ET, monostate > )
                return false;


            else
                return e.canBeExecuted();
        }, m_content );
    }

    bool Instruction::canBeEagerlyEvaluated() const
    {
        return visit( []< typename ET >( const ET& e )
        {
            if constexpr( is_same_v< ET, monostate > )
                return false;


            else
                return e.canBeEagerlyEvaluated();
        }, m_content );
    }

    bool Instruction::haveSideEffects() const
    {
        return visit( []< typename ET >( const ET& e )
        {
            if constexpr( is_same_v< ET, monostate > )
                return false;
            else
                return e.haveSideEffects();
        }, m_content );
    }

    ostream& operator<<( ostream& out, const Instruction& inst )
    {
        return visit( [&]< typename ET >( const ET& e ) -> ostream&
        {
            if constexpr( is_same_v< ET, monostate > )
                return out << "NIL";


            else
                return out << e;
        }, inst.m_content );
    }

    ostream& operator<<( ostream& out, const Select& ins )
    {
        out << "SELECT(" << ins.m_memberIndex;




        return out << ')';
    }

    ostream& operator<<( ostream& out, const Load& ins )
    {
        out << "LOAD(";




        return out << ins.m_type << ')';
    }

    ostream& operator<<( ostream& out, const Store& ins )
    {





        return out << "STORE";
    }

    ostream& operator<<( ostream& out, const PHOverrideSet& ins )
    {
        return out << "PHOVERRIDESET(" << ins.m_name << ')';
    }

    ostream& operator<<( ostream& out, const PHOverrideClear& ins )
    {
        return out << "PHOVERRIDECLEAR(" << ins.m_name << ')';
    }

    bool Select::operator<( const Select& rhs ) const
    {

        return m_memberIndex < rhs.m_memberIndex;

    }

    bool Load::operator<( const Load& rhs ) const
    {

        return m_type < rhs.m_type;

    }

    bool Store::operator<( const Store& rhs ) const
    {

        return m_type < rhs.m_type;
    }

    bool PHOverrideSet::operator<( const PHOverrideSet& rhs ) const
    {
        return m_name < rhs.m_name;
    }

    bool PHOverrideClear::operator<( const PHOverrideClear& rhs ) const
    {

        return m_name < rhs.m_name;



    }
}
Changes to bs/cir/instruction.h.
21
22
23
24
25
26
27




28
29
30
31
32
33
34
            Instruction( TempAddr&& tad ) :
                m_content( move( tad ) )
            {}

            Instruction( Select&& sel ) :
                m_content( move( sel ) )
            {}





            Instruction( CreateTemporary&& ct ) :
                m_content( move( ct ) )
            {}

            Instruction( GetTemporary&& gt ) :
                m_content( move( gt ) )







>
>
>
>







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
            Instruction( TempAddr&& tad ) :
                m_content( move( tad ) )
            {}

            Instruction( Select&& sel ) :
                m_content( move( sel ) )
            {}

            Instruction( Constant&& cst ) :
                m_content( move( cst ) )
            {}

            Instruction( CreateTemporary&& ct ) :
                m_content( move( ct ) )
            {}

            Instruction( GetTemporary&& gt ) :
                m_content( move( gt ) )
154
155
156
157
158
159
160
161




162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178

179
180
181
182
183
184
185
                m_content( move( x ) )
            {}

            Instruction( Placeholder&& x ) :
                m_content( move( x ) )
            {}

            Instruction( PHOverride&& x ) :




                m_content( move( x ) )
            {}

            Instruction( GhostCall&& x ) :
                m_content( move( x ) )
            {}

            using Content = variant
            <
                monostate,

                Call,

                VarAddr,
                TempAddr,
                Select,


                CreateTemporary,
                GetTemporary,
                AllocVar,
                Load,
                Store,
                Phi,








|
>
>
>
>

















>







158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
                m_content( move( x ) )
            {}

            Instruction( Placeholder&& x ) :
                m_content( move( x ) )
            {}

            Instruction( PHOverrideSet&& x ) :
                m_content( move( x ) )
            {}

            Instruction( PHOverrideClear&& x ) :
                m_content( move( x ) )
            {}

            Instruction( GhostCall&& x ) :
                m_content( move( x ) )
            {}

            using Content = variant
            <
                monostate,

                Call,

                VarAddr,
                TempAddr,
                Select,

                Constant,
                CreateTemporary,
                GetTemporary,
                AllocVar,
                Load,
                Store,
                Phi,

210
211
212
213
214
215
216

217
218
219
220
221
222
223
224
225
226
227
228

229
230
231
232
233
234
235
                SGT,
                SGE,
                SLT,
                SLE,

                Assert,
                GhostCall,

                PHOverride,
                Placeholder

                // WARNING AWFUL SHIT:
                // If the last instruction changes, make sure to update the upper bound lookup in
                // CFG::forEachAddressModifiedByLoop accordingly!
            >;

            const auto& content() const { return m_content; }

            bool canBeExecuted() const;
            bool canBeEagerlyEvaluated() const;


            friend ostream& operator<<( ostream& out, const Instruction& inst );

            bool operator<( const Instruction& rhs ) const
            {
                return m_content < rhs.m_content;
            }







>
|

<
<
<
<






>







219
220
221
222
223
224
225
226
227
228




229
230
231
232
233
234
235
236
237
238
239
240
241
242
                SGT,
                SGE,
                SLT,
                SLE,

                Assert,
                GhostCall,
                PHOverrideSet,
                PHOverrideClear,
                Placeholder




            >;

            const auto& content() const { return m_content; }

            bool canBeExecuted() const;
            bool canBeEagerlyEvaluated() const;
            bool haveSideEffects() const;

            friend ostream& operator<<( ostream& out, const Instruction& inst );

            bool operator<( const Instruction& rhs ) const
            {
                return m_content < rhs.m_content;
            }
Changes to bs/cir/load.h.
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
28
29
30
#ifndef GOOSE_CIR_LOAD_H
#define GOOSE_CIR_LOAD_H

namespace goose::cir
{
    class Load
    {
        public:
            template< typename A, typename T >
            Load( A&& addr, T&& type ) :
                m_addr( forward< A >( addr ) ),
                m_type( forward< T >( type ) )
            {}

            const auto& addr() const { return m_addr; }
            const auto& type() const { return m_type; }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return true; }


            bool operator<( const Load& rhs ) const;
            friend ostream& operator<<( ostream& out, const Load& ins );

        private:
            ptr< Instruction > m_addr;
            eir::Term m_type;
    };
}

#endif





|


|
|
|



<




>





<





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
28
29
#ifndef GOOSE_CIR_LOAD_H
#define GOOSE_CIR_LOAD_H

namespace goose::cir
{
    class Load : public BaseInstr< 1, true >
    {
        public:
            template< typename T >
            Load( T&& type, LocationId loc ) :
                BaseInstr( loc ),
                m_type( forward< T >( type ) )
            {}


            const auto& type() const { return m_type; }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return true; }
            bool haveSideEffects() const { return false; }

            bool operator<( const Load& rhs ) const;
            friend ostream& operator<<( ostream& out, const Load& ins );

        private:

            eir::Term m_type;
    };
}

#endif
Changes to bs/cir/logic.h.
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
28
29
30
31
32
33
34

35
36
37
38
39
40
41
42
43
44
45
46
47

48
49
50
51
52
53
54
55
56
#ifndef GOOSE_CIR_LOGIC_H
#define GOOSE_CIR_LOGIC_H

namespace goose::cir
{
    class And : public BinaryOp
    {
        public:
            template< typename L, typename R >
            And( L&& lhs, R&& rhs ) : BinaryOp( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const And& ins )
            {
                return ins.print( out, "AND" );
            }
    };

    class Or : public BinaryOp
    {
        public:
            template< typename L, typename R >
            Or( L&& lhs, R&& rhs ) : BinaryOp( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const Or& ins )
            {
                return ins.print( out, "OR" );
            }
    };

    class Xor : public BinaryOp
    {
        public:
            template< typename L, typename R >
            Xor( L&& lhs, R&& rhs ) : BinaryOp( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const Xor& ins )
            {
                return ins.print( out, "XOR" );
            }
    };

    // Logical implication (to be used only for verification expression)
    class Implies : public BinaryOp
    {
        public:
            template< typename L, typename R >
            Implies( L&& lhs, R&& rhs ) : BinaryOp( forward< L >( lhs ), forward< R >( rhs ) ) {}


            friend ostream& operator<<( ostream& out, const Implies& ins )
            {
                return ins.print( out, "IMPLIES" );
            }
    };
}

#endif








|
|
>



|






|
|
>



|






|
|
>



|







|
|
>



|





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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#ifndef GOOSE_CIR_LOGIC_H
#define GOOSE_CIR_LOGIC_H

namespace goose::cir
{
    class And : public BinaryOp
    {
        public:
            And( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const And& ins )
            {
                return out << "AND";
            }
    };

    class Or : public BinaryOp
    {
        public:
            Or( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Or& ins )
            {
                return out << "OR";
            }
    };

    class Xor : public BinaryOp
    {
        public:
            Xor( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Xor& ins )
            {
                return out << "XOR";
            }
    };

    // Logical implication (to be used only for verification expression)
    class Implies : public BinaryOp
    {
        public:
            Implies( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Implies& ins )
            {
                return out << "IMPLIES";
            }
    };
}

#endif
Changes to bs/cir/loopaddrs.cpp.
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
28
29
30
31
32
33
34
35
36
#include "cir.h"

namespace goose::cir
{



















































































































































































































    void MarkAddrChangedByLoop( const ptr< CFG >& cfg, uint32_t loopId, const eir::Term& type, const Instruction& addrCalculation )
    {
        cfg->setAddressModifiedByLoop( loopId, type, addrCalculation );

        const auto& pHeader = cfg->getBB( loopId );

        if( pHeader->loopId() )
            MarkAddrChangedByLoop( cfg, pHeader->loopId(), type, addrCalculation );
    }

    void ComputeLoopModifiedAddrs( const ptr< CFG >& cfg )
    {
        cfg->forEachBB( [&]( auto&& bb )
        {
            if( !bb->loopId() )
                return;

            for( auto&& instr : *bb )
            {
                auto st = get_if< Store >( &instr.content() );
                if( !st )
                    continue;

                auto cir = st->addr();
                if( !cir )
                    continue;

                MarkAddrChangedByLoop( cfg, bb->loopId(), st->val().type(), *cir );
            }
        } );
    }
}




>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|

|




|









<
<
<
<
<
|
<
<
<
|
|
|



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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232





233



234
235
236
237
238
239
#include "cir.h"

namespace goose::cir
{
    // EvalStores is a tiny CIR InstrSeq evaluator that only considers store instructions
    // to addresses whose path can be calculated entirely at compilation time

    class AddrEvalStack
    {
        public:
            template< typename T >
            void push( T&& x )
            {
                m_stack.push( forward< T >( x ) );
            }

            bool pop()
            {
                if( m_stack.empty() )
                    return false;

                m_stack.pop();
                return true;
            }

            template< typename T >
            optional< T > pop()
            {
                if( m_stack.empty() )
                    return nullopt;

                if( !holds_alternative< T >( m_stack.top() ) )
                    return nullopt;

                optional< T > result = move( get< T >( m_stack.top() ) );
                m_stack.pop();
                return result;
            }

            bool empty() const { return m_stack.empty(); }

        private:
            stack< variant< monostate, StorageLocation, Value > > m_stack;
    };

    template< typename I, typename F >
    bool EvalStores( AddrEvalStack& stack, const I& instr, F&& storeHandler )
    {
        // For all the instructions we don't care about here,
        // push/pop mock values on the stack to keep the stack layout
        // as intended by the instruction sequence.
        for( size_t i = 0; i < I::PopCount; ++i )
        {
            if( !stack.pop() )
                return false;
        }

        if( I::PushesResult )
            stack.push( monostate() );

        return true;
    }

    template< typename F >
    bool EvalStores( AddrEvalStack& stack, const monostate&, F&& storeHandler )
    {
        return true;
    }

    template< typename F >
    bool EvalStores( AddrEvalStack& stack, const Constant& instr, F&& storeHandler )
    {
        stack.push( instr.value() );
        return true;
    }

    template< typename F >
    bool EvalStores( AddrEvalStack& stack, const Call& instr, F&& storeHandler )
    {
        if( !stack.pop() )
            return false;

        for( size_t i = 0; i < instr.numArgs(); ++i )
        {
            if( !stack.pop() )
                return false;
        }

        stack.push( monostate() );
        return true;
    }

    template< typename F >
    bool EvalStores( AddrEvalStack& stack, const GhostCall& instr, F&& storeHandler )
    {
        auto gfunc = stack.pop< Value >();
        if( !gfunc )
            return false;

        auto argCount = instr.numArgs();
        GhostFuncApplication gfa( move( *gfunc ), instr.locationId() );

        auto& args = gfa.args();
        args.resize( argCount );
        for( size_t i = 0; i < argCount; ++i )
        {
            variant< Value, Address > arg;
            auto a = stack.pop< Value >();
            if( a )
                arg = move( *a );
            else
            {
                auto a = stack.pop< StorageLocation >();
                if( !a || holds_alternative< Address >( *a ) )
                    return false;

                arg = move( get< Address >( *a ) );
            }

            args[argCount - i - 1] = move( arg );
        }

        stack.push( StorageLocation { move( gfa ) } );
        return true;
    }

    template< typename F >
    bool EvalStores( AddrEvalStack& stack, const VarAddr& instr, F&& storeHandler )
    {
        stack.push( Address( Address::Origin::Stack, instr.varIndex(), instr.locationId() ) );
        return true;
    }

    template< typename F >
    bool EvalStores( AddrEvalStack& stack, const TempAddr& instr, F&& storeHandler )
    {
        if( !stack.pop() )
            return false;

        stack.push( Address( Address::Origin::Stack, instr.tempIndex(), instr.locationId() ) );
        return true;
    }

    template< typename F >
    bool EvalStores( AddrEvalStack& stack, const Select& instr, F&& storeHandler )
    {
        if( stack.empty() )
            return false;

        auto baseAddr = stack.pop< StorageLocation >();
        if( !baseAddr )
        {
            // Not an error, just means the base address comes from an indrection,
            // or a function call and we don't handle this case here.
            //
            // TODO:
            // Later we might either outright emit an error right here if this occurs
            // inside of a loop because it means we can't verify it and risk a false
            // positive. Or we could try and go the extra mile to represent
            // the indirection in a way that can be verified.
            return true;
        }

        if( !holds_alternative< Address >( *baseAddr ) )
            return false;

        stack.push( StorageLocation{
            Address::Select( move( get< Address >( *baseAddr ) ),
            instr.memberIndex(), instr.locationId() ) } );
        return true;
    }

    template< typename F >
    bool EvalStores( AddrEvalStack& stack, const Store& instr, F&& storeHandler )
    {
        if( !stack.pop() )
            return false;

        if( stack.empty() )
            return false;

        auto sloc = stack.pop< StorageLocation >();
        if( !sloc )
        {
            // Not an error, see comment in the function above
            return true;
        }

        storeHandler( instr.type(), *sloc );
        return true;
    }

    template< typename F >
    bool EvalStores( AddrEvalStack& stack, const Instruction& instr, F&& storeHandler )
    {
        return visit( [&]( auto&& i )
        {
            return EvalStores( stack, i, forward< F >( storeHandler ) );
        }, instr.content() );
    }

    template< typename F >
    bool EvalStores( const BasicBlock& bb, F&& storeHandler )
    {
        AddrEvalStack stack;

        for( auto&& instr : bb.instructions() )
        {
            if( !EvalStores( stack, instr, storeHandler ) )
                return false;
        }

        return true;
    }

    void MarkSLocChangedByLoop( const ptr< CFG >& cfg, uint32_t loopId, const eir::Term& type, const StorageLocation& sloc )
    {
        cfg->setStorageLocationModifiedByLoop( loopId, type, sloc );

        const auto& pHeader = cfg->getBB( loopId );

        if( pHeader->loopId() )
            MarkSLocChangedByLoop( cfg, pHeader->loopId(), type, sloc );
    }

    void ComputeLoopModifiedAddrs( const ptr< CFG >& cfg )
    {
        cfg->forEachBB( [&]( auto&& bb )
        {
            if( !bb->loopId() )
                return;






            EvalStores( *bb, [&]( auto&& type, auto&& sloc )



            {
                MarkSLocChangedByLoop( cfg, bb->loopId(), type, sloc );
            } );
        } );
    }
}
Changes to bs/cir/meson.build.
1
2
3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
goose_cir = library( 'goose-cir',
    'cfg.cpp',
    'dominators.cpp',
    'loops.cpp',
    'loopaddrs.cpp',
    'instruction.cpp',
    'binaryop.cpp',
    'terminator.cpp',
    'call.cpp',
    'func.cpp',
    'helpers.cpp',

    'hash.cpp',
    'decorator.cpp',
    'cfgviz.cpp',

    include_directories: bsinc,
    dependencies: [tracy_dep]
)






<

<


>







1
2
3
4
5
6

7

8
9
10
11
12
13
14
15
16
17
goose_cir = library( 'goose-cir',
    'cfg.cpp',
    'dominators.cpp',
    'loops.cpp',
    'loopaddrs.cpp',
    'instruction.cpp',

    'terminator.cpp',

    'func.cpp',
    'helpers.cpp',
    'verifinstrfilter.cpp',
    'hash.cpp',
    'decorator.cpp',
    'cfgviz.cpp',

    include_directories: bsinc,
    dependencies: [tracy_dep]
)
Changes to bs/cir/not.h.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#ifndef GOOSE_CIR_NOT_H
#define GOOSE_CIR_NOT_H

namespace goose::cir
{
    class Not
    {
        public:
            template< typename T >
            Not( T&& operand ) :
                m_operand( forward< T >( operand ) )

            {}

            const auto& operand() const { return m_operand; }

            bool canBeExecuted() const
            {
                return IsValueConstantOrExecutable( m_operand );
            }

            bool canBeEagerlyEvaluated() const
            {
                return CanValueBeEagerlyEvaluated( m_operand );
            }

            bool operator<( const Not& rhs ) const
            {
                return m_operand < rhs.m_operand;
            }

            friend ostream& operator<<( ostream& out, const Not& ins )
            {
                return out << "NOT("<< ins.m_operand << ')';
            }

        private:
            eir::Value m_operand;
    };
}

#endif





|


<
|
<
>


<
<
|
<
<
<
<
|
<
|
<



|




|

<
<
<




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
28
29
#ifndef GOOSE_CIR_NOT_H
#define GOOSE_CIR_NOT_H

namespace goose::cir
{
    class Not : public BaseInstr< 1, true >
    {
        public:

            Not( LocationId loc ) :

                BaseInstr( loc )
            {}



            bool canBeExecuted() const { return true; }




            bool canBeEagerlyEvaluated() const { return true; }

            bool haveSideEffects() const { return false; }


            bool operator<( const Not& rhs ) const
            {
                return false;
            }

            friend ostream& operator<<( ostream& out, const Not& ins )
            {
                return out << "NOT";
            }



    };
}

#endif
Changes to bs/cir/phi.h.
1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
16
17
18
19
#ifndef GOOSE_CIR_PHI_H
#define GOOSE_CIR_PHI_H

namespace goose::cir
{
    class BasicBlock;

    class Phi
    {
        public:
            template< typename T >
            Phi( T&& type, uint32_t numIncomings, uint32_t destIndex ) :

                m_type( forward< T >( type ) ),
                m_destIndex( destIndex )
            {
                m_incomings.reserve( numIncomings );
            }

            const auto& type() const { return m_type; }







|



|
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef GOOSE_CIR_PHI_H
#define GOOSE_CIR_PHI_H

namespace goose::cir
{
    class BasicBlock;

    class Phi : public BaseInstr< 0, false >
    {
        public:
            template< typename T >
            Phi( T&& type, uint32_t numIncomings, uint32_t destIndex, LocationId loc ) :
                BaseInstr( loc ),
                m_type( forward< T >( type ) ),
                m_destIndex( destIndex )
            {
                m_incomings.reserve( numIncomings );
            }

            const auto& type() const { return m_type; }
33
34
35
36
37
38
39

40
41
42
43
44
45
46
                    if( !func( bb.lock(), val ) )
                        return;
                }
            }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return true; }


            bool operator<( const Phi& rhs ) const
            {
                if( m_type != rhs.m_type )
                    return m_type < rhs.m_type;

                return m_destIndex < rhs.m_destIndex;







>







34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
                    if( !func( bb.lock(), val ) )
                        return;
                }
            }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return true; }
            bool haveSideEffects() const { return true; }

            bool operator<( const Phi& rhs ) const
            {
                if( m_type != rhs.m_type )
                    return m_type < rhs.m_type;

                return m_destIndex < rhs.m_destIndex;
Changes to bs/cir/phoverride.h.
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
28
29
30
31
32
33
34
35
36
37
#ifndef GOOSE_CIR_PHOVERRIDE_H
#define GOOSE_CIR_PHOVERRIDE_H

namespace goose::cir
{
    // A pseudo instruction that defines an override value for a placeholder, to be applied to
    // its contained value
    class PHOverride
    {
        public:
            template< typename S, typename P, typename V >
            PHOverride( S&& name, P&& phVal, V&& val ) :

                m_name( forward< S >( name ) ),
                m_phVal( forward< P >( phVal ) ),
                m_val( forward< V >( val ) )
            {}

            const auto& name() const { return m_name; }



            const auto& phVal() const { return m_phVal; }




















            const auto& val() const { return m_val; }

            // This can't be executed, it's only meant to appear
            // in expressions handled by the verifier.
            bool canBeExecuted() const { return false; }
            bool canBeEagerlyEvaluated() const { return false; }


            bool operator<( const PHOverride& rhs ) const;
            friend ostream& operator<<( ostream& out, const PHOverride& ins );

        private:
            StringId    m_name;
            eir::Value  m_phVal;
            eir::Value  m_val;
    };
}

#endif





|
<
|


|
|
>
|
<
<



>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|





>

|
|



<
<




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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53


54
55
56
57
#ifndef GOOSE_CIR_PHOVERRIDE_H
#define GOOSE_CIR_PHOVERRIDE_H

namespace goose::cir
{
    // A pseudo instruction that defines an override value for a placeholder

    class PHOverrideSet : public BaseInstr< 1, false >
    {
        public:
            template< typename S >
            PHOverrideSet( S&& name, LocationId loc ) :
                BaseInstr( loc ),
                m_name( forward< S >( name ) )


            {}

            const auto& name() const { return m_name; }

            // This can't be executed, it's only meant to appear
            // in expressions handled by the verifier.
            bool canBeExecuted() const { return false; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return false; }

            bool operator<( const PHOverrideSet& rhs ) const;
            friend ostream& operator<<( ostream& out, const PHOverrideSet& ins );

        private:
            StringId    m_name;
    };

    // A pseudo instruction that clears an override value for a placeholder
    class PHOverrideClear : public BaseInstr< 0, 0 >
    {
        public:
            template< typename S >
            PHOverrideClear( S&& name, LocationId loc ) :
                BaseInstr( loc ),
                m_name( forward< S >( name ) )
            {}

            const auto& name() const { return m_name; }

            // This can't be executed, it's only meant to appear
            // in expressions handled by the verifier.
            bool canBeExecuted() const { return false; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return false; }

            bool operator<( const PHOverrideClear& rhs ) const;
            friend ostream& operator<<( ostream& out, const PHOverrideClear& ins );

        private:
            StringId    m_name;


    };
}

#endif
Changes to bs/cir/placeholder.h.
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
28
29
30
31
#ifndef GOOSE_CIR_PLACEHOLDER_H
#define GOOSE_CIR_PLACEHOLDER_H

namespace goose::cir
{
    // A placeholder value with a name, to be replacedd with something else.
    // Intended to represent the @ return value paceholder in functions'
    // post conditions.
    class Placeholder
    {
        public:
            template< typename T, typename S >
            Placeholder( T&& type, S&& name ) :

                m_type( forward< T >( type ) ),
                m_name( forward< S >( name ) )
            {}

            const auto& type() const { return m_type; }
            const auto& name() const { return m_name; }

            // This can't be executed, it's only meant to appear
            // in expressions handled by the verifier.
            bool canBeExecuted() const { return false; }
            bool canBeEagerlyEvaluated() const { return false; }


            bool operator<( const Placeholder& rhs ) const
            {
                if( m_name != rhs.m_name )
                    return m_name < rhs.m_name;
                return m_type < rhs.m_type;
            }





|
|

|



|
>











>







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
28
29
30
31
32
33
#ifndef GOOSE_CIR_PLACEHOLDER_H
#define GOOSE_CIR_PLACEHOLDER_H

namespace goose::cir
{
    // A placeholder value with a name, to be replaced with something else.
    // Intended to represent the @ return value placeholder in functions'
    // post conditions.
    class Placeholder : public BaseInstr< 0, true >
    {
        public:
            template< typename T, typename S >
            Placeholder( T&& type, S&& name, LocationId loc ) :
                BaseInstr( loc ),
                m_type( forward< T >( type ) ),
                m_name( forward< S >( name ) )
            {}

            const auto& type() const { return m_type; }
            const auto& name() const { return m_name; }

            // This can't be executed, it's only meant to appear
            // in expressions handled by the verifier.
            bool canBeExecuted() const { return false; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return false; }

            bool operator<( const Placeholder& rhs ) const
            {
                if( m_name != rhs.m_name )
                    return m_name < rhs.m_name;
                return m_type < rhs.m_type;
            }
Changes to bs/cir/ret.h.
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
28
29


30
31
32
33
34
35
#ifndef GOOSE_CIR_RET_H
#define GOOSE_CIR_RET_H

namespace goose::cir
{
    class Ret
    {
        public:
            Ret() {}

            template< typename V >
            Ret( V&& val ) :
                m_value( forward< V >( val ) )
            {}

            const auto& value() const { return m_value; }


            bool canBeExecuted() const
            {

                return !m_value || IsValueConstantOrExecutable( *m_value );


            }

            bool canBeEagerlyEvaluated() const

            {
                return !m_value || CanValueBeEagerlyEvaluated( *m_value );




            }



            void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex )  {}



        private:
            optional< eir::Value > m_value;
    };
}

#endif





|


|
|
<
<
<


|
>

|
|
>
|
>
>
|

<
>
|
<
>
>
>
>
|
>
>



>
>

|




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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#ifndef GOOSE_CIR_RET_H
#define GOOSE_CIR_RET_H

namespace goose::cir
{
    class RetVoid
    {
        public:
            RetVoid( LocationId loc ) :
                m_loc( loc )



            {}

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return true; }

            void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex )  {}

            const auto& locationId() const { return m_loc; }

        private:
            LocationId m_loc;
    };


    class Ret
    {

        public:
            Ret( LocationId loc ) :
                m_loc( loc )
            {}

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return true; }

            void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex )  {}

            const auto& locationId() const { return m_loc; }

        private:
            LocationId m_loc;
    };
}

#endif
Changes to bs/cir/select.h.
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
28
29
30
31
32
33
34
35
36
37
#ifndef GOOSE_CIR_SELECT_H
#define GOOSE_CIR_SELECT_H

namespace goose::cir
{
    class Select
    {
        public:
            template< typename BA >
            Select( BA&& baseAddr, uint32_t memberIndex ) :
                m_baseAddr( forward< BA >( baseAddr ) ),
                m_memberIndex( memberIndex )
            {}

            const auto& baseAddr() const
            {
                return m_baseAddr;
            }

            uint32_t memberIndex() const
            {
                return m_memberIndex;
            }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return false; }


            bool operator<( const Select& rhs ) const;
            friend ostream& operator<<( ostream& out, const Select& ins );

        private:
            ptr< Instruction > m_baseAddr;
            uint32_t m_memberIndex = 0;
    };
}

#endif





|


<
|
|



<
<
<
<
<







>





<





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
28
29
30
31
#ifndef GOOSE_CIR_SELECT_H
#define GOOSE_CIR_SELECT_H

namespace goose::cir
{
    class Select : public BaseInstr< 1, true >
    {
        public:

            Select( uint32_t memberIndex, LocationId loc ) :
                BaseInstr( loc ),
                m_memberIndex( memberIndex )
            {}






            uint32_t memberIndex() const
            {
                return m_memberIndex;
            }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return false; }

            bool operator<( const Select& rhs ) const;
            friend ostream& operator<<( ostream& out, const Select& ins );

        private:

            uint32_t m_memberIndex = 0;
    };
}

#endif
Added bs/cir/storagelocation.h.
















































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
#ifndef GOOSE_CIR_STORAGELOCATION_H
#define GOOSE_CIR_STORAGELOCATION_H

namespace goose::cir
{
    using SelectPath = llvm::SmallVector< uint32_t, 4 >;

    class Address
    {
        public:
            enum class Origin
            {
                Stack,
                Heap
            };

            Address() {}

            Address( Address&& ) = default;
            Address( const Address& ) = default;

            Address& operator=( Address&& ) = default;
            Address& operator=( const Address& ) = default;

            Address( Origin o, uint32_t originIndex, LocationId loc ) :
                m_origin( o ),
                m_loc( loc )
            {
                m_path.emplace_back( originIndex );
            }

            auto origin() const { return m_origin; }
            const auto& path() const { return m_path; }
            auto locationId() const { return m_loc; }

            static Address Select( Address&& baseAddr, uint32_t index, LocationId loc )
            {
                baseAddr.m_path.emplace_back( index );
                baseAddr.m_loc = loc;
                return baseAddr;
            }

            bool operator<( const Address& rhs ) const
            {
                if( m_origin != rhs.m_origin )
                    return  m_origin < rhs.m_origin;

                return m_path < rhs.m_path;
            }

        private:
            SelectPath m_path;
            Origin m_origin = Origin::Stack;
            LocationId m_loc;
    };

    class GhostFuncApplication
    {
        public:
            GhostFuncApplication( cir::Value&& func, LocationId loc ) :
                m_func( move( func ) ),
                m_loc( loc )
            {}

            const auto& func() const { return m_func; }
            const auto& args() const { return m_args; }
            auto& args() { return m_args; }

            auto locationId() const { return m_loc; }

            bool operator<( const GhostFuncApplication& rhs ) const
            {
                if( m_func != rhs.m_func )
                    return  m_func < rhs.m_func;

                return m_args < rhs.m_args;
            }

        private:
            eir::Value m_func;
            llvm::SmallVector< variant< eir::Value, Address >, 4 > m_args;
            LocationId m_loc;
    };

    using StorageLocation = variant< Address, GhostFuncApplication, monostate >;
}

#endif
Changes to bs/cir/store.h.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
#ifndef GOOSE_CIR_STORE_H
#define GOOSE_CIR_STORE_H

namespace goose::cir
{
    class Store
    {
        public:
            template< typename A, typename T, typename V >
            Store( A&& addr, T&& type, V&& val, eir::LocationId destLocId ) :
                m_addr( forward< A >( addr ) ),
                m_type( forward< T >( type ) ),
                m_val( forward< V >( val ) ),

                m_destLocId( destLocId )
            {}

            const auto& addr() const { return m_addr; }
            const auto& type() const { return m_type; }
            const auto& val() const { return m_val; }
            const auto& destLocId() const { return m_destLocId; }

            bool canBeExecuted() const
            {

                return IsValueConstantOrExecutable( m_val );
            }

            bool canBeEagerlyEvaluated() const;

            bool operator<( const Store& rhs ) const;
            friend ostream& operator<<( ostream& out, const Store& ins );

        private:
            ptr< Instruction > m_addr;
            eir::Term m_type;
            eir::Value m_val;
            eir::LocationId m_destLocId = 0;
    };
}

#endif








|
|
<

<
>



<

|


|
<
>
|
|
|
|





<

|





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
28
29
30

31
32
33
34
35
36
37
#ifndef GOOSE_CIR_STORE_H
#define GOOSE_CIR_STORE_H

namespace goose::cir
{
    class Store
    {
        public:
            template< typename T >
            Store( T&& type, eir::LocationId srcLocId, eir::LocationId destLocId ) :

                m_type( forward< T >( type ) ),

                m_srcLocId( srcLocId ),
                m_destLocId( destLocId )
            {}


            const auto& type() const { return m_type; }
            const auto& srcLocId() const { return m_srcLocId; }
            const auto& destLocId() const { return m_destLocId; }

            bool canBeExecuted() const { return true; }

            bool canBeEagerlyEvaluated() const { return true; }
            bool haveSideEffects() const { return true; }

            static constexpr size_t PopCount = 2;
            static constexpr bool PushesResult = false;

            bool operator<( const Store& rhs ) const;
            friend ostream& operator<<( ostream& out, const Store& ins );

        private:

            eir::Term m_type;
            eir::LocationId m_srcLocId = 0;
            eir::LocationId m_destLocId = 0;
    };
}

#endif
Changes to bs/cir/tempaddr.h.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#ifndef GOOSE_CIR_TEMPADDR_H
#define GOOSE_CIR_TEMPADDR_H

namespace goose::cir
{
    class TempAddr
    {
        public:
            template< typename T >
            TempAddr( uint32_t i, T&& init ) :
                m_initValue( forward< T >( init ) ),

                m_tempIndex( i )
            {}

            uint32_t tempIndex() const
            {
                return m_tempIndex;
            }

            const auto& initValue() const
            {
                return m_initValue;
            }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return false; }


            bool operator<( const TempAddr& rhs ) const
            {
                if( m_tempIndex != rhs.m_tempIndex )
                    return m_tempIndex < rhs.m_tempIndex;
                return m_initValue < rhs.m_initValue;
            }

            friend ostream& operator<<( ostream& out, const TempAddr& ins )
            {
                return out << "TEMPADDR(" << ins.m_tempIndex << ", " << ins.m_initValue << ')';
            }

        private:
            eir::Value m_initValue;
            uint32_t m_tempIndex = 0;
    };
}

#endif





|


<
|
<
>








<
<
<
<
<


>



<
|
<




|



<





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
28
29
30
31
32
33

34
35
36
37
38
#ifndef GOOSE_CIR_TEMPADDR_H
#define GOOSE_CIR_TEMPADDR_H

namespace goose::cir
{
    class TempAddr : public BaseInstr< 1, true >
    {
        public:

            TempAddr( uint32_t i, LocationId loc ) :

                BaseInstr( loc ),
                m_tempIndex( i )
            {}

            uint32_t tempIndex() const
            {
                return m_tempIndex;
            }






            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return false; }

            bool operator<( const TempAddr& rhs ) const
            {

                return m_tempIndex < rhs.m_tempIndex;

            }

            friend ostream& operator<<( ostream& out, const TempAddr& ins )
            {
                return out << "TEMPADDR(" << ins.m_tempIndex << ')';
            }

        private:

            uint32_t m_tempIndex = 0;
    };
}

#endif
Changes to bs/cir/terminator.h.
1
2
3
4
5
6
7
8
9
10
11


12
13
14
15
16
17
18
#ifndef GOOSE_CIR_TERMINATOR_H
#define GOOSE_CIR_TERMINATOR_H

namespace goose::cir
{
    class BasicBlock;

    class Terminator
    {
        public:
            Terminator() {}



            Terminator( Ret&& r ) :
                m_content( move( r ) )
            {}

            Terminator( Branch&& b ) :
                m_content( move( b ) )










|
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef GOOSE_CIR_TERMINATOR_H
#define GOOSE_CIR_TERMINATOR_H

namespace goose::cir
{
    class BasicBlock;

    class Terminator
    {
        public:
            Terminator( RetVoid&& r ) :
                m_content( move( r ) )
            {}

            Terminator( Ret&& r ) :
                m_content( move( r ) )
            {}

            Terminator( Branch&& b ) :
                m_content( move( b ) )
32
33
34
35
36
37
38

39
40
41
42
43
44
45

            Terminator( Continue&& c ) :
                m_content( move( c ) )
            {}

            using Content = variant
            <

                Ret,
                Branch,
                CondBranch,
                GhostBranch,
                Break,
                Continue
            >;







>







34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

            Terminator( Continue&& c ) :
                m_content( move( c ) )
            {}

            using Content = variant
            <
                RetVoid,
                Ret,
                Branch,
                CondBranch,
                GhostBranch,
                Break,
                Continue
            >;
Changes to bs/cir/tests/dom-1.cpp.
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
        auto bb2 = cfg->createBB();
        auto bb3 = cfg->createBB();
        auto bb4 = cfg->createBB();
        auto bb5 = cfg->createBB();
        auto bb6 = cfg->createBB();

        bb1->setTerminator( Branch( bb2 ) );
        bb2->setTerminator( CondBranch( PoisonValue(), bb3, bb4 ) );
        bb3->setTerminator( Branch( bb5 ) );
        bb4->setTerminator( Branch( bb5 ) );
        bb5->setTerminator( CondBranch( PoisonValue(), bb2, bb6 ) );

        ComputeDominators( cfg );

        THEN( "We get the expected result" )
        {
            const auto& idoms = cfg->idoms();








|


|







23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
        auto bb2 = cfg->createBB();
        auto bb3 = cfg->createBB();
        auto bb4 = cfg->createBB();
        auto bb5 = cfg->createBB();
        auto bb6 = cfg->createBB();

        bb1->setTerminator( Branch( bb2 ) );
        bb2->setTerminator( CondBranch( bb3, bb4 ) );
        bb3->setTerminator( Branch( bb5 ) );
        bb4->setTerminator( Branch( bb5 ) );
        bb5->setTerminator( CondBranch( bb2, bb6 ) );

        ComputeDominators( cfg );

        THEN( "We get the expected result" )
        {
            const auto& idoms = cfg->idoms();

Changes to bs/cir/tests/dom-2.cpp.
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
        auto bb8 = cfg->createBB();
        auto bb9 = cfg->createBB();
        auto bba = cfg->createBB();
        auto bbb = cfg->createBB();
        auto bbc = cfg->createBB();
        auto bbd = cfg->createBB();

        bb1->setTerminator( CondBranch( PoisonValue(), bb2, bb3 ) );

        bb2->setTerminator( CondBranch( PoisonValue(), bb4, bb7 ) );
        bb4->setTerminator( Branch( bb5 ) );
        bb5->setTerminator( Branch( bb6 ) );
        bb6->setTerminator( Branch( bb8 ) );
        bb7->setTerminator( Branch( bb8 ) );
        bb8->setTerminator( CondBranch( PoisonValue(), bb7, bbd ) );

        bb3->setTerminator( CondBranch( PoisonValue(), bb9, bba ) );
        bba->setTerminator( CondBranch( PoisonValue(), bbb, bbc ) );
        bb9->setTerminator( Branch( bbc ) );
        bbb->setTerminator( Branch( bbc ) );
        bbc->setTerminator( Branch( bbd ) );

        ComputeDominators( cfg );

        THEN( "We get the expected result" )







|

|




|

|
|







29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
        auto bb8 = cfg->createBB();
        auto bb9 = cfg->createBB();
        auto bba = cfg->createBB();
        auto bbb = cfg->createBB();
        auto bbc = cfg->createBB();
        auto bbd = cfg->createBB();

        bb1->setTerminator( CondBranch( bb2, bb3 ) );

        bb2->setTerminator( CondBranch( bb4, bb7 ) );
        bb4->setTerminator( Branch( bb5 ) );
        bb5->setTerminator( Branch( bb6 ) );
        bb6->setTerminator( Branch( bb8 ) );
        bb7->setTerminator( Branch( bb8 ) );
        bb8->setTerminator( CondBranch( bb7, bbd ) );

        bb3->setTerminator( CondBranch( bb9, bba ) );
        bba->setTerminator( CondBranch( bbb, bbc ) );
        bb9->setTerminator( Branch( bbc ) );
        bbb->setTerminator( Branch( bbc ) );
        bbc->setTerminator( Branch( bbd ) );

        ComputeDominators( cfg );

        THEN( "We get the expected result" )
Changes to bs/cir/tests/meson.build.
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
            goose_builtins,
            goose_sema,
            goose_lex,
            goose_parse,
            goose_diagnostics,
            goose_execute,
            goose_codegen,
            goose_verify,
            goose_compile
        ],
        include_directories: bsinc,
        dependencies: [catch2_dep, llvm_dep, lld_deps, tracy_dep]
    )
    test( 'cir-' + t, exe )
endforeach







<







15
16
17
18
19
20
21

22
23
24
25
26
27
28
            goose_builtins,
            goose_sema,
            goose_lex,
            goose_parse,
            goose_diagnostics,
            goose_execute,
            goose_codegen,

            goose_compile
        ],
        include_directories: bsinc,
        dependencies: [catch2_dep, llvm_dep, lld_deps, tracy_dep]
    )
    test( 'cir-' + t, exe )
endforeach
Changes to bs/cir/varaddr.h.
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
28
29
30
#ifndef GOOSE_CIR_VARADDR_H
#define GOOSE_CIR_VARADDR_H

namespace goose::cir
{
    class VarAddr
    {
        public:
            template< typename T >
            VarAddr( uint32_t varIndex, T&& type ) :

                m_type( forward< T >( type ) ),
                m_varIndex( varIndex )
            {}

            const auto& type() const { return m_type; }

            uint32_t varIndex() const
            {
                return m_varIndex;
            }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return false; }


            bool operator<( const VarAddr& rhs ) const
            {
                if( m_varIndex != rhs.m_varIndex )
                    return m_varIndex < rhs.m_varIndex;

                return m_type < rhs.m_type;





|



|
>













>







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
28
29
30
31
32
#ifndef GOOSE_CIR_VARADDR_H
#define GOOSE_CIR_VARADDR_H

namespace goose::cir
{
    class VarAddr : public BaseInstr< 0, true >
    {
        public:
            template< typename T >
            VarAddr( uint32_t varIndex, T&& type, LocationId loc ) :
                BaseInstr( loc ),
                m_type( forward< T >( type ) ),
                m_varIndex( varIndex )
            {}

            const auto& type() const { return m_type; }

            uint32_t varIndex() const
            {
                return m_varIndex;
            }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return false; }

            bool operator<( const VarAddr& rhs ) const
            {
                if( m_varIndex != rhs.m_varIndex )
                    return m_varIndex < rhs.m_varIndex;

                return m_type < rhs.m_type;
Added bs/cir/verifinstrfilter.cpp.


























































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#include "cir/cir.h"

namespace goose::cir
{
    void VerifInstrFilter::push( int32_t instrCount )
    {
        m_stack.push( instrCount );
    }

    int32_t VerifInstrFilter::pop()
    {
        if( m_stack.empty() )
            return 0;

        auto result = m_stack.top();
        m_stack.pop();
        return result;
    }

    void VerifInstrFilter::retire()
    {
        auto count = pop();

        auto end = m_pendingInstrs.end();
        auto begin = end - abs( count );

        if( count < 0 )
        {
            m_discardCount -= count;
            for( auto it = begin; it != end; ++it )
                m_instrDiscardFlags[*it] = true;
        }

        m_pendingInstrs.erase( begin, end );
    }

    bool VerifInstrFilter::processInstr( const Call& instr )
    {
        size_t count = 1;

        auto c = pop();
        if( c <= 0 )
            return false;

        count += c;

        for( uint32_t i = 0; i < instr.numArgs(); ++i )
        {
            auto c = pop();
            if( c <= 0 )
                return false;

            count += c;
        }

        push( count );
        return true;
    }

    bool VerifInstrFilter::processInstr( const GhostCall& instr )
    {
        size_t count = 1;

        auto c = pop();
        if( c == 0 )
            return false;

        count += abs( c );

        for( uint32_t i = 0; i < instr.numArgs(); ++i )
        {
            auto c = pop();
            if( c == 0 )
                return false;

            count += abs( c );
        }

        push( -count );
        return true;
    }

    bool VerifInstrFilter::processInstr( const Store& instr )
    {
        size_t count = 1;
        bool isVerifCode = false;

        auto c = pop();
        if( c == 0 )
            return false;

        if( c < 0 )
            isVerifCode = true;

        count += abs( c );

        c = pop();
        if( c == 0 )
            return false;

        if( c < 0 )
            isVerifCode = true;

        count += abs( c );

        push( isVerifCode ? -count : count );
        retire();
        return true;
    }

    bool VerifInstrFilter::processInstr( const Assert& instr )
    {
        size_t count = 1;

        auto c = pop();
        if( c == 0 )
            return false;

        count += abs( c );
        push( -count );
        retire();
        return true;
    }

    bool VerifInstrFilter::processInstr( const Placeholder& instr )
    {
        push( -1 );
        retire();
        return true;
    }

    bool VerifInstrFilter::processInstr( const PHOverrideSet& instr )
    {
        size_t count = 1;

        auto c = pop();
        if( c == 0 )
            return false;

        count += abs( c );
        push( -count );
        retire();
        return true;
    }

    bool VerifInstrFilter::processInstr( const PHOverrideClear& instr )
    {
        push( -1 );
        retire();
        return true;
    }

    template< typename T >
    bool VerifInstrFilter::processInstr( const T& instr )
    {
        size_t count = 1;
        bool isVerifCode = false;

        for( uint32_t i = 0; i < T::PopCount; ++i )
        {
            auto c = pop();
            if( !c )
                return false;

            if( c < 0 )
                isVerifCode = true;

            count += abs( c );
        }

        if( T::PushesResult )
            push( isVerifCode ? -count : count );

        return true;
    }

    bool VerifInstrFilter::processInstr( const Instruction& instr )
    {
        m_pendingInstrs.emplace_back( m_instrCount++ );

        return visit( [&]< typename T >( const T& instr )
        {
            if constexpr( is_same_v< T, monostate > )
                return false;
            else
                return VerifInstrFilter::processInstr( instr );
        }, instr.content() );
    }
}
Added bs/cir/verifinstrfilter.h.




















































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#ifndef GOOSE_CIR_VERIFINSTRFILTER_H
#define GOOSE_CIR_VERIFINSTRFILTER_H

namespace goose::cir
{
    struct VerifInstrFilter
    {
        public:
            VerifInstrFilter( size_t inputInstrCount ) :
                m_instrDiscardFlags( inputInstrCount )
            {
                m_pendingInstrs.reserve( inputInstrCount );
            }

            bool processInstr( const Instruction& instr );

            template< typename T >
            void filter( const InstrSeq& input, T& output )
            {
                assert( input.size() == m_instrDiscardFlags.size() );

                output.reserve( input.size() - m_discardCount );
                auto fit = m_instrDiscardFlags.begin();

                for( const auto& instr : input )
                {
                    if( !*fit )
                        output.emplace_back( instr );
                    ++fit;
                }
            }

        private:
            void push( int32_t instrCount );
            int32_t pop();

            void retire();

            bool processInstr( const Call& instr );
            bool processInstr( const GhostCall& instr );
            bool processInstr( const Store& instr );
            bool processInstr( const Assert& instr );
            bool processInstr( const Placeholder& instr );
            bool processInstr( const PHOverrideSet& instr );
            bool processInstr( const PHOverrideClear& instr );

            template< typename T >
            bool processInstr( const T& instr );

            llvm::SmallVector< size_t, 16 > m_pendingInstrs;
            vector< bool > m_instrDiscardFlags;
            stack< int32_t > m_stack;

            size_t m_instrCount = 0;
            size_t m_discardCount = 0;
    };

    template< typename T >
    bool FilterVerificationInstructions( const InstrSeq& is, T& destination )
    {
        VerifInstrFilter f( is.size() );

        for( const auto& instr : is )
        {
            if( !f.processInstr( instr ) )
                return false;
        }

        f.filter( is, destination );
        return true;
    }
}

#endif
Changes to bs/codegen/address.cpp.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71





















































72
73
74
75
76
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;

llvm::Value* Module::buildInstruction( Infos& inf, const VarAddr& va )
{
    auto* ppVal = inf.temporaries->get( va.varIndex() );

    assert( ppVal );
    if( !ppVal )
        return nullptr;

    return *ppVal;
}

llvm::Value* Module::buildInstruction( Infos& inf, const TempAddr& ta )
{
    auto* ppVal = inf.temporaries->get( ta.tempIndex() );

    if( !ppVal )
    {
        auto* pValue = buildValue( inf, ta.initValue() );
        if( !pValue )
            return nullptr;

        inf.temporaries->set( ta.tempIndex(), pValue );
        ppVal = inf.temporaries->get( ta.tempIndex() );
    }

    if( llvm::isa< llvm::AllocaInst >( **ppVal ) )
        return *ppVal;

    auto* pAlloca = buildAlloca( inf, ( *ppVal )->getType() );
    m_llvmBuilder.CreateStore( *ppVal, pAlloca );
    inf.temporaries->set( ta.tempIndex(), pAlloca );
    return pAlloca;
}

llvm::Value* Module::buildInstruction( Infos& inf, const Select& s )
{
    llvm::SmallVector< uint32_t, 8 > path;
    path.emplace_back( s.memberIndex() );

    ptr< Instruction > pBaseAddr = s.baseAddr();
    for(;;)
    {
        const auto* pSel = get_if< cir::Select >( &pBaseAddr->content() );
        if( !pSel )
            break;

        path.emplace_back( pSel->memberIndex() );
        pBaseAddr = pSel->baseAddr();
    }

    auto* baseAddrVal = buildInstruction( inf, *pBaseAddr );
    if( !baseAddrVal )
        return nullptr;

    llvm::SmallVector< llvm::Value*, 8 > idxs;
    idxs.push_back( llvm::ConstantInt::get( llvm::Type::getInt32Ty( GetLLVMContext() ), 0 ) );

    for( auto it = path.rbegin(); it != path.rend(); ++it )
        idxs.push_back( llvm::ConstantInt::get( llvm::Type::getInt32Ty( GetLLVMContext() ), *it ) );

    return m_llvmBuilder.CreateGEP( baseAddrVal->getType()->getScalarType()->getPointerElementType(), baseAddrVal, idxs );
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::GhostCall& gc )





















































{
    // We should never encounter this during codegen, since it's a construct only
    // intended to be used in verification expressions.
    return nullptr;
}







<
<
<
|
<
<
<
|
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<



|


|


|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



|

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;




llvm::Value* codegen::AddressToGEP( llvm::IRBuilder<>& builder, const codegen::Address& addr )



{


    if( addr.path().empty() )















        return addr.originAddr();



























    llvm::SmallVector< llvm::Value*, 8 > idxs;
    idxs.push_back( llvm::ConstantInt::get( llvm::Type::getInt32Ty( GetLLVMContext() ), 0 ) );

    for( auto it = addr.path().rbegin(); it != addr.path().rend(); ++it )
        idxs.push_back( llvm::ConstantInt::get( llvm::Type::getInt32Ty( GetLLVMContext() ), *it ) );

    return builder.CreateGEP( addr.originAddr()->getType()->getScalarType()->getPointerElementType(), addr.originAddr(), idxs );
}

bool Module::buildInstruction( State& st, const VarAddr& va )
{
    auto* ppVal = st.temporaries->get( va.varIndex() );

    assert( ppVal );
    if( !ppVal )
        return false;

    st.stack.push( Address( *ppVal ) );
    return true;
}

bool Module::buildInstruction( State& st, const TempAddr& ta )
{
    auto initVal = st.stack.pop( m_llvmBuilder );
    if( !initVal )
        return false;

    auto* ppVal = st.temporaries->get( ta.tempIndex() );

    if( !ppVal )
    {
        st.temporaries->set( ta.tempIndex(), *initVal );
        ppVal = st.temporaries->get( ta.tempIndex() );
    }

    if( llvm::isa< llvm::AllocaInst >( **ppVal ) )
    {
        st.stack.push( Address( *ppVal ) );
        return true;
    }

    auto* pAlloca = buildAlloca( st, ( *ppVal )->getType() );
    m_llvmBuilder.CreateStore( *ppVal, pAlloca );
    st.temporaries->set( ta.tempIndex(), pAlloca );

    st.stack.push( Address( pAlloca ) );
    return true;
}

bool Module::buildInstruction( State& st, const Select& s )
{
    auto baseAddr = st.stack.pop();
    if( !baseAddr )
        return false;

    if( holds_alternative< Address >( *baseAddr ) )
        st.stack.push( Address::Select( move( get< Address >( *baseAddr ) ), s.memberIndex() ) );
    else
        st.stack.push( Address::Select( Address( get< llvm::Value* >( *baseAddr ) ), s.memberIndex() ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::GhostCall& gc )
{
    // We should never encounter this during codegen, since it's a construct only
    // intended to be used in verification expressions.
    return false;
}
Added bs/codegen/address.h.




























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
28
29
30
#ifndef GOOSE_CODEGEN_ADDRESS_H
#define GOOSE_CODEGEN_ADDRESS_H

namespace goose::codegen
{
    class Address
    {
        public:
            Address( llvm::Value* originAddr ) :
                m_originAddr( originAddr )
            {}

            auto originAddr() const { return m_originAddr; }
            const auto& path() const { return m_path; }

            static Address Select( Address&& baseAddr, uint32_t index )
            {
                baseAddr.m_path.emplace_back( index );
                return baseAddr;
            }

        private:
            SelectPath m_path;
            llvm::Value* m_originAddr = nullptr;
    };

    extern llvm::Value* AddressToGEP( llvm::IRBuilder<>& builder, const codegen::Address& addr );
}

#endif
Changes to bs/codegen/arithops.cpp.
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
28
29
30
31

32
33
34
35
36
37
38
39
40
41
42
43
44

45
46
47
48
49
50
51

52
53
54
55
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
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Add& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;

    return m_llvmBuilder.CreateAdd( lhs, rhs );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Sub& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;

    return m_llvmBuilder.CreateSub( lhs, rhs );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Mul& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;

    return m_llvmBuilder.CreateMul( lhs, rhs );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::UDiv& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;


    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;

    return m_llvmBuilder.CreateUDiv( lhs, rhs );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::SDiv& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;

    return m_llvmBuilder.CreateSDiv( lhs, rhs );

}




llvm::Value* Module::buildInstruction( Infos& inf, const cir::URem& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;

    return m_llvmBuilder.CreateURem( lhs, rhs );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::SRem& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;

    return m_llvmBuilder.CreateSRem( lhs, rhs );

}







|

|
|
|

|
|
|

|
>


|

|
|
|

|
|
|

|
>


|

|
|
|

|
|
|

|
>


|

|
|
|
>
|
<
|
|

|
>


|

|
|
|

|
|
|

|
>

>
>
>
|
<
<
<
|
|

|
|
|

|
>


|

|
|
|

|
|
|

|
>

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;

bool Module::buildInstruction( State& st, const cir::Add& bo )
{
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    st.stack.push( m_llvmBuilder.CreateAdd( *lhs, *rhs ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::Sub& bo )
{
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    st.stack.push( m_llvmBuilder.CreateSub( *lhs, *rhs ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::Mul& bo )
{
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    st.stack.push( m_llvmBuilder.CreateMul( *lhs, *rhs ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::UDiv& bo )
{
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );

    if( !lhs )
        return false;

    st.stack.push( m_llvmBuilder.CreateUDiv( *lhs, *rhs ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::SDiv& bo )
{
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    st.stack.push( m_llvmBuilder.CreateSDiv( *lhs, *rhs ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::URem& bo )
{
    auto rhs = st.stack.pop( m_llvmBuilder );



    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    st.stack.push( m_llvmBuilder.CreateURem( *lhs, *rhs ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::SRem& bo )
{
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    st.stack.push( m_llvmBuilder.CreateSRem( *lhs, *rhs ) );
    return true;
}
Changes to bs/codegen/basicblock.cpp.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43


44
45
46
47
48
49
50
51
52
53

54
55
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
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;

llvm::BasicBlock* Module::buildBasicBlock( Infos& inf, const ptr< BasicBlock >& pBB )
{
    if( pBB->llvmBB()->getTerminator() || pBB->codeGenStarted() )

        return pBB->llvmBB();






    pBB->setCodeGenStarted();


    m_llvmBuilder.SetInsertPoint( pBB->llvmBB() );


    for( auto&& instr : *pBB )

    {

        if( !buildInstruction( inf, instr ) )


            return nullptr;
    }


    if( !buildTerminator( inf, *pBB->terminator() ) )
        return nullptr;



















    return pBB->llvmBB();
}

bool Module::buildTerminator( Infos& inf, const cir::Terminator& terminator )
{
    return visit( [&]( auto&& e )
    {
        return buildTerminator( inf, e );
    }, terminator.content() );
}

bool Module::buildTerminator( Infos& inf, const cir::Ret& r )
{
    if( !r.value() )
    {
        m_llvmBuilder.CreateRetVoid();
        return true;
    }



    auto* pRetVal = buildValue( inf, *r.value() );
    if( !pRetVal )
        return false;

    m_llvmBuilder.CreateRet( pRetVal );
    return true;
}

bool Module::buildTerminator( Infos& inf, const cir::Branch& b )
{

    m_llvmBuilder.CreateBr( b.dest().lock()->llvmBB() );

    llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );

    if( !buildBasicBlock( inf, b.dest().lock() ) )
        return false;

    return true;
}

bool Module::buildTerminator( Infos& inf, const cir::CondBranch& cb )
{
    auto* pCondVal = buildValue( inf, cb.cond() );
    if( !pCondVal )
        return false;



    m_llvmBuilder.CreateCondBr( pCondVal,
        cb.trueDest().lock()->llvmBB(), cb.falseDest().lock()->llvmBB() );

    llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );

    if( !buildBasicBlock( inf, cb.trueDest().lock() ) )
        return false;

    if( !buildBasicBlock( inf, cb.falseDest().lock() ) )
        return false;

    return true;
}

bool Module::buildTerminator( Infos& inf, const cir::GhostBranch& gb )
{

    m_llvmBuilder.CreateBr( gb.continuation().lock()->llvmBB() );

    llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );

    if( !buildBasicBlock( inf, gb.continuation().lock() ) )
        return false;

    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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;

llvm::BasicBlock* Module::GetOrCreateLLVMBB( State& st, const ptr< cir::BasicBlock >& pBB )
{
    if( !pBB->llvmBB() )
        pBB->setLLVMBB( llvm::BasicBlock::Create( GetLLVMContext(), "", st.llvmFunc ) );
    return pBB->llvmBB();
}

llvm::BasicBlock* Module::buildBasicBlock( State& st, const ptr< BasicBlock >& pBB )
{
    auto pLLVMBB = GetOrCreateLLVMBB( st, pBB );

    if( pLLVMBB->getTerminator() || pBB->codeGenStarted() )
        return pLLVMBB;

    m_llvmBuilder.SetInsertPoint( pLLVMBB );

    auto pCurrentBB = pBB;

    for(;;)
    {
        pCurrentBB->setCodeGenStarted();

        const auto* pInstrs = pCurrentBB->runnableInstructions();
        if( !pInstrs )
            return nullptr;

        for( auto&& instr : *pInstrs )
        {
            if( !buildInstruction( st, instr ) )
                return nullptr;
        }

        // If the terminator is a ghostbranch, directly
        // append the content of the continuation block
        // to avoid unecessary basic blocks splits whenever
        // some ghost code is present
        if( !pCurrentBB->terminator() )
            break;

        auto* pGBranch = get_if< GhostBranch >( &pCurrentBB->terminator()->content() );
        if( !pGBranch )
            break;

        pCurrentBB = pGBranch->continuation().lock();
    }

    if( !buildTerminator( st, *pCurrentBB->terminator() ) )
        return nullptr;

    return pLLVMBB;
}

bool Module::buildTerminator( State& st, const cir::Terminator& terminator )
{
    return visit( [&]( auto&& e )
    {
        return buildTerminator( st, e );
    }, terminator.content() );
}

bool Module::buildTerminator( State& st, const cir::RetVoid& r )


{
    m_llvmBuilder.CreateRetVoid();
    return true;
}

bool Module::buildTerminator( State& st, const cir::Ret& r )
{
    auto retVal = st.stack.pop( m_llvmBuilder );
    if( !retVal )
        return false;

    m_llvmBuilder.CreateRet( *retVal );
    return true;
}

bool Module::buildTerminator( State& st, const cir::Branch& b )
{
    auto pLLVMBB = GetOrCreateLLVMBB( st, b.dest().lock() );
    m_llvmBuilder.CreateBr( pLLVMBB );

    llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );

    if( !buildBasicBlock( st, b.dest().lock() ) )
        return false;

    return true;
}

bool Module::buildTerminator( State& st, const cir::CondBranch& cb )
{
    auto condVal = st.stack.pop( m_llvmBuilder );
    if( !condVal )
        return false;

    auto pTrueLLVMBB = GetOrCreateLLVMBB( st, cb.trueDest().lock() );
    auto pFalseLLVMBB = GetOrCreateLLVMBB( st, cb.falseDest().lock() );
    m_llvmBuilder.CreateCondBr( *condVal, pTrueLLVMBB, pFalseLLVMBB );


    llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );

    if( !buildBasicBlock( st, cb.trueDest().lock() ) )
        return false;

    if( !buildBasicBlock( st, cb.falseDest().lock() ) )
        return false;

    return true;
}

bool Module::buildTerminator( State& st, const cir::GhostBranch& gb )
{
    auto pLLVMBB = GetOrCreateLLVMBB( st, gb.continuation().lock() );
    m_llvmBuilder.CreateBr( pLLVMBB );

    llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );

    if( !buildBasicBlock( st, gb.continuation().lock() ) )
        return false;

    return true;
}
Changes to bs/codegen/codegen.h.
23
24
25
26
27
28
29


30
31
32
    using namespace cir;
    using namespace sema;

    extern llvm::LLVMContext& GetLLVMContext();
    extern optional< string > Mangle( const Term& identity );
}



#include "module.h"

#endif







>
>



23
24
25
26
27
28
29
30
31
32
33
34
    using namespace cir;
    using namespace sema;

    extern llvm::LLVMContext& GetLLVMContext();
    extern optional< string > Mangle( const Term& identity );
}

#include "address.h"
#include "stack.h"
#include "module.h"

#endif
Changes to bs/codegen/compareops.cpp.
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
28
29
30
31

32
33
34
35
36
37
38

39
40
41
42
43
44

45
46
47
48
49
50
51
52
53
54
55
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
114
115
116
117
118
119
120
121
122

123
124
125
126
127
128
129
130
131
132
133
134
135

136
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Eq& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;

    return m_llvmBuilder.CreateICmpEQ( lhs, rhs );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Neq& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;

    return m_llvmBuilder.CreateICmpNE( lhs, rhs );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::UGT& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;


    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;

    return m_llvmBuilder.CreateICmpUGT( lhs, rhs );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::UGE& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;

    return m_llvmBuilder.CreateICmpUGE( lhs, rhs );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::ULT& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;

    return m_llvmBuilder.CreateICmpULT( lhs, rhs );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::ULE& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;

    return m_llvmBuilder.CreateICmpULE( lhs, rhs );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::SGT& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;

    return m_llvmBuilder.CreateICmpSGT( lhs, rhs );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::SGE& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;

    return m_llvmBuilder.CreateICmpSGE( lhs, rhs );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::SLT& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;

    return m_llvmBuilder.CreateICmpSLT( lhs, rhs );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::SLE& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;

    return m_llvmBuilder.CreateICmpSLE( lhs, rhs );

}







|

|
|
|

|
|
|

|
>


|

|
|
|

|
|
|

|
>


|

|
|
|
>
|
<
|
|

|
>


|

|
|
|

|
|
|

|
>


|

|
|
|

|
|
|

|
>


|

|
|
|

|
|
|

|
>


|

|
|
|

|
|
|

|
>


|

|
|
|

|
|
|

|
>


|

|
|
|

|
|
|

|
>


|

|
|
|

|
|
|

|
>

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

43
44
45
46
47
48
49
50
51
52
53
54
55
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;

bool Module::buildInstruction( State& st, const cir::Eq& bo )
{
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    st.stack.push( m_llvmBuilder.CreateICmpEQ( *lhs, *rhs ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::Neq& bo )
{
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    st.stack.push( m_llvmBuilder.CreateICmpNE( *lhs, *rhs ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::UGT& bo )
{
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );

    if( !lhs )
        return false;

    st.stack.push( m_llvmBuilder.CreateICmpUGT( *lhs, *rhs ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::UGE& bo )
{
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    st.stack.push( m_llvmBuilder.CreateICmpUGE( *lhs, *rhs ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::ULT& bo )
{
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    st.stack.push( m_llvmBuilder.CreateICmpULT( *lhs, *rhs ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::ULE& bo )
{
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    st.stack.push( m_llvmBuilder.CreateICmpULE( *lhs, *rhs ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::SGT& bo )
{
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    st.stack.push( m_llvmBuilder.CreateICmpSGT( *lhs, *rhs ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::SGE& bo )
{
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    st.stack.push( m_llvmBuilder.CreateICmpSGE( *lhs, *rhs ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::SLT& bo )
{
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    st.stack.push( m_llvmBuilder.CreateICmpSLT( *lhs, *rhs ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::SLE& bo )
{
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    st.stack.push( m_llvmBuilder.CreateICmpSLE( *lhs, *rhs ) );
    return true;
}
Changes to bs/codegen/func.cpp.
45
46
47
48
49
50
51

52

53
54
55
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
    pllvmFunc = buildFuncProto( c, func, name, linkageType );
    if( !pllvmFunc )
        return nullptr;

    if( func.isExternal() )
        return pllvmFunc;


    Infos inf( c );


    // Generate allocas and stores for the args
    auto pEntryBB = func.cir()->body()->entryBB();
    if( !pEntryBB->llvmBB() )
        pEntryBB->setLLVMBB( llvm::BasicBlock::Create( GetLLVMContext(), "", pllvmFunc ) );

    inf.allocaBasicBlock = pEntryBB->llvmBB();
    m_llvmBuilder.SetInsertPoint( pEntryBB->llvmBB() );

    llvm::SmallVector< NullInit< llvm::Value* >, 8 > args;

    for( auto&& arg : pllvmFunc->args() )
    {
        inf.lastEmittedAlloca = m_llvmBuilder.CreateAlloca( arg.getType() );
        args.push_back( inf.lastEmittedAlloca );
    }

    size_t i = 0;
    for( auto&& arg : pllvmFunc->args() )
    {
        m_llvmBuilder.CreateStore( &arg, args[i] );
        inf.temporaries->set( i, args[i] );
        ++i;
    }

    if( !buildCFG( inf, pllvmFunc, func.cir()->body() ) )
        return nullptr;

    llvm::verifyFunction( *pllvmFunc );
    return pllvmFunc;
}

llvm::BasicBlock* Module::buildCFG( Infos& inf, llvm::Function* llvmFunc, const ptr< cir::CFG >& pCFG )
{
    pCFG->forEachBB( [&]( auto&& pBB )
    {
        if( !pBB->llvmBB() )
            pBB->setLLVMBB( llvm::BasicBlock::Create( GetLLVMContext(), "", llvmFunc ) );
    } );

    return buildBasicBlock( inf, pCFG->entryBB() );
}







>
|
>


|



|






|
|






|



|






|

<
<
<
<
<
<
|

45
46
47
48
49
50
51
52
53
54
55
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
    pllvmFunc = buildFuncProto( c, func, name, linkageType );
    if( !pllvmFunc )
        return nullptr;

    if( func.isExternal() )
        return pllvmFunc;

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

    State st( c, cfg->temporariesCount(), pllvmFunc );

    // Generate allocas and stores for the args
    auto pEntryBB = cfg->entryBB();
    if( !pEntryBB->llvmBB() )
        pEntryBB->setLLVMBB( llvm::BasicBlock::Create( GetLLVMContext(), "", pllvmFunc ) );

    st.allocaBasicBlock = pEntryBB->llvmBB();
    m_llvmBuilder.SetInsertPoint( pEntryBB->llvmBB() );

    llvm::SmallVector< NullInit< llvm::Value* >, 8 > args;

    for( auto&& arg : pllvmFunc->args() )
    {
        st.lastEmittedAlloca = m_llvmBuilder.CreateAlloca( arg.getType() );
        args.push_back( st.lastEmittedAlloca );
    }

    size_t i = 0;
    for( auto&& arg : pllvmFunc->args() )
    {
        m_llvmBuilder.CreateStore( &arg, args[i] );
        st.temporaries->set( i, args[i] );
        ++i;
    }

    if( !buildCFG( st, pllvmFunc, func.cir()->body() ) )
        return nullptr;

    llvm::verifyFunction( *pllvmFunc );
    return pllvmFunc;
}

llvm::BasicBlock* Module::buildCFG( State& st, llvm::Function* llvmFunc, const ptr< cir::CFG >& pCFG )
{






    return buildBasicBlock( st, pCFG->entryBB() );
}
Changes to bs/codegen/instructions.cpp.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

43
44
45
46
47
48
49
50



51
52
53
54
55
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
114
115
116




117
118
119
120
121
122
123
124
125

126
127
128
129
130
131
132
133

134

135
136
137
138
139
140
141
142
143
144
145
146
147
148

149
150
151

152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191

192
193
194
195


196

197



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217


218
219
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;












llvm::Value* Module::buildInstruction( Infos& inf, const cir::Instruction& instr )
{
    return visit( [&]< typename T >( T&& inst )
    {
        return buildInstruction( inf, inst );
    }, instr.content() );
}

llvm::Value* Module::buildInstruction( Infos& inf, const monostate& )
{
    return nullptr;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Call& call )
{
    const auto& callee = call.func();
    if( IsBuiltinFunc( callee ) || IsBuiltinIntrinsicFunc( callee ) )

    {
        DiagnosticsManager::GetInstance().emitErrorMessage(
            callee.locationId(), "builtin functions can't be called at runtime." );
    }

    DiagnosticsContext dc( callee.locationId(), "When calling this.", true );

    llvm::FunctionCallee llvmCallee;

    // If the callee is a plain function, we just need to generate or retrieve it.
    if( callee.isConstant() )
    {
        auto f = FromValue< builtins::Func >( callee );
        if( !f )
            return nullptr;

        llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );
        llvmCallee = llvm::FunctionCallee( getOrCreateFunc( inf.context, *f ) );

    }
    else
    {
        // TODO: function pointers
        assert( false );
        return nullptr;
    }




    if( !llvmCallee )
        return nullptr;

    const auto& vec = *get< pvec >( call.args() );
    vector< llvm::Value* > args;
    args.reserve( vec.terms().size() );

    for( auto&& a : vec.terms() )
    {
        auto* parg = buildValue( inf, *EIRToValue( a ) );
        if( !parg )
            return nullptr;

        args.emplace_back( parg );
    }


    return m_llvmBuilder.CreateCall( llvm::FunctionCallee( llvmCallee ), args );
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::CreateTemporary& ct )

{
    return createTemporary( inf, ct.index(), buildValue( inf, ct.value() ) );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::GetTemporary& gt )
{
    auto* ppVal = inf.temporaries->get( gt.index() );
    assert( ppVal );
    if( !ppVal )
        return nullptr;


    return *ppVal;
}

llvm::Value* Module::createTemporary( Infos& inf, uint32_t index, llvm::Value* pValue ) const
{
    inf.temporaries->set( index, pValue );
    return pValue;
}

llvm::Value* Module::buildAlloca( Infos& inf, llvm::Type* type )
{
    llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );

    if( inf.lastEmittedAlloca && inf.lastEmittedAlloca->getNextNode() )
        m_llvmBuilder.SetInsertPoint( inf.lastEmittedAlloca->getNextNode() );
    else
        m_llvmBuilder.SetInsertPoint( inf.allocaBasicBlock, inf.allocaBasicBlock->begin() );

    inf.lastEmittedAlloca = m_llvmBuilder.CreateAlloca( type );

    return inf.lastEmittedAlloca;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::AllocVar& av )
{
    auto type = LowerTypeForRuntime( inf.context, av.type() );
    if( !type )
        return nullptr;

    auto* pAlloca = buildAlloca( inf, GetLLVMType( *type ) );
    createTemporary( inf, av.index(), pAlloca );

    return pAlloca;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Load& load )
{




    auto type = LowerTypeForRuntime( inf.context, *EIRToValue( load.type() ) );
    if( !type )
        return nullptr;

    auto* llvmType = GetLLVMType( *type );
    assert( llvmType );

    auto* ptrVal = buildInstruction( inf, *load.addr() );
    return m_llvmBuilder.CreateLoad( llvmType, ptrVal );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Store& store )
{
    auto* pValue = buildValue( inf, store.val() );
    if( !pValue )
        return nullptr;


    auto* ptrVal = buildInstruction( inf, *store.addr() );


    if( llvm::isa< llvm::ConstantAggregate >( pValue ) )
    {
        stringstream name;
        name << ".constaggr" << hex << m_nextAggregateID++;

        auto pGlob = new llvm::GlobalVariable( m_llvmModule,
            pValue->getType(), true, llvm::GlobalValue::LinkageTypes::PrivateLinkage,
            static_cast< llvm::Constant* >( pValue ), name.str() );

        auto size = m_dataLayout.getTypeAllocSize( pValue->getType() );

        return m_llvmBuilder.CreateMemCpy( ptrVal, llvm::None, pGlob, llvm::None,
            llvm::ConstantInt::get( llvm::Type::getInt32Ty( GetLLVMContext() ), size ) );

    }

    return m_llvmBuilder.CreateStore( pValue, ptrVal );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Phi& p )
{
    auto type = LowerTypeForRuntime( inf.context, p.type() );
    if( !type )
        return nullptr;

    auto* pPHI = m_llvmBuilder.CreatePHI( GetLLVMType( *type ), p.numIncomings() );
    if( !pPHI )
        return nullptr;

    {
        llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );

        p.forAllIncomings( [&]( auto&& bb, auto&& val )
        {
            auto pIncomingBB = buildBasicBlock( inf, bb );
            if( !pIncomingBB )
            {
                pPHI = nullptr;
                return false;
            }

            auto* pVal = buildValue( inf, val );
            if( !pVal )
            {
                pPHI = nullptr;
                return false;
            }

            pPHI->addIncoming( pVal, pIncomingBB );
            return true;
        } );
    }

    if( !pPHI )
        return nullptr;

    return createTemporary( inf, p.destIndex(), pPHI );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Assert& ass )
{


    // We can't just return null as it is considered an error by the rest of the code.

    // Since the assert instruction will only appear in a cfg instruction list, this



    // will not actually be used anyway.
    return llvm::ConstantInt::getTrue( GetLLVMContext() );
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Placeholder& ph )
{
    // We should never encounter this during codegen, since it's a construct only
    // intended to be used in verification expressions.
    return nullptr;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Implies& bo )
{
    // We should never encounter this during codegen, since it's a construct only
    // intended to be used in verification expressions.
    return nullptr;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::PHOverride& pho )
{


    return buildValue( inf, pho.val() );
}







>
>
>
>
>
>
>
>
>
>
>
|



|



|

|


|

|
|
>
|
<
<
<
|
|

<
|
<
<

|
|
|

<
<
>

<
|
|
<
|
|

>
>
>
|
|

<
<
<
|
<
<
<
<
|
|
<
|
>
|
|
<
|
<
>
|
|
>


|

|


|
>
>
|


|

|



|



|
|

|

|

|


|

|

|

|
|
>
|


|

>
>
>
>
|

|




<
|
>


|

|
|
|

>
|
>

|





|
|

|

|

>


|
>


|

|

|



|






|






|












|

|
>


|

>
>
|
>
|
>
>
>
|
|


|



|


|



|


|

>
>
|

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
28
29
30
31
32
33
34
35
36
37



38
39
40

41


42
43
44
45
46


47
48

49
50

51
52
53
54
55
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;

bool Module::buildInstruction( State& st, const cir::InstrSeq& is )
{
    for( auto&& instr : is )
    {
        if( !buildInstruction( st, instr ) )
            return false;
    }

    return true;
}

bool Module::buildInstruction( State& st, const cir::Instruction& instr )
{
    return visit( [&]< typename T >( T&& inst )
    {
        return buildInstruction( st, inst );
    }, instr.content() );
}

bool Module::buildInstruction( State& st, const monostate& )
{
    return false;
}

bool Module::buildInstruction( State& st, const cir::Call& call )
{
    auto callee = st.stack.pop< llvm::Function* >( m_llvmBuilder );
    if( !callee )
        return false;




    llvm::SmallVector< llvm::Value*, 8 > args;
    args.resize( call.numArgs() );


    for( uint32_t i = 0; i < call.numArgs(); ++i )


    {
        auto parg = st.stack.pop( m_llvmBuilder );
        if( !parg )
            return false;



        args[call.numArgs() - i - 1] = *parg;
    }


    st.stack.push( m_llvmBuilder.CreateCall( llvm::FunctionCallee( *callee ), args ) );

    return true;
}

bool Module::buildInstruction( State& st, const cir::Constant& ct )
{
    auto val = buildValue( st, ct.value() );
    if( !val )
        return false;




    st.stack.push( val );




    return true;
}


bool Module::buildInstruction( State& st, const cir::CreateTemporary& ct )
{
    auto initVal = st.stack.pop( m_llvmBuilder );

    if( !initVal )

        return false;

    createTemporary( st, ct.index(), *initVal );
    return true;
}

bool Module::buildInstruction( State& st, const cir::GetTemporary& gt )
{
    auto* ppVal = st.temporaries->get( gt.index() );
    assert( ppVal );
    if( !ppVal )
        return false;

    st.stack.push( *ppVal );
    return true;
}

llvm::Value* Module::createTemporary( State& st, uint32_t index, llvm::Value* pValue ) const
{
    st.temporaries->set( index, pValue );
    return pValue;
}

llvm::Value* Module::buildAlloca( State& st, llvm::Type* type )
{
    llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );

    if( st.lastEmittedAlloca && st.lastEmittedAlloca->getNextNode() )
        m_llvmBuilder.SetInsertPoint( st.lastEmittedAlloca->getNextNode() );
    else
        m_llvmBuilder.SetInsertPoint( st.allocaBasicBlock, st.allocaBasicBlock->begin() );

    st.lastEmittedAlloca = m_llvmBuilder.CreateAlloca( type );

    return st.lastEmittedAlloca;
}

bool Module::buildInstruction( State& st, const cir::AllocVar& av )
{
    auto type = LowerTypeForRuntime( st.context, av.type() );
    if( !type )
        return false;

    auto* pAlloca = buildAlloca( st, GetLLVMType( *type ) );
    createTemporary( st, av.index(), pAlloca );

    return true;
}

bool Module::buildInstruction( State& st, const cir::Load& load )
{
    auto ptrVal = st.stack.pop( m_llvmBuilder );
    if( !ptrVal )
        return false;

    auto type = LowerTypeForRuntime( st.context, *EIRToValue( load.type() ) );
    if( !type )
        return false;

    auto* llvmType = GetLLVMType( *type );
    assert( llvmType );


    st.stack.push( m_llvmBuilder.CreateLoad( llvmType, *ptrVal ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::Store& store )
{
    auto val = st.stack.pop( m_llvmBuilder );
    if( !val )
        return false;

    auto ptrVal = st.stack.pop( m_llvmBuilder );
    if( !ptrVal )
        return false;

    if( llvm::isa< llvm::ConstantAggregate >( *val ) )
    {
        stringstream name;
        name << ".constaggr" << hex << m_nextAggregateID++;

        auto pGlob = new llvm::GlobalVariable( m_llvmModule,
            ( *val )->getType(), true, llvm::GlobalValue::LinkageTypes::PrivateLinkage,
            static_cast< llvm::Constant* >( *val ), name.str() );

        auto size = m_dataLayout.getTypeAllocSize( ( *val )->getType() );

        m_llvmBuilder.CreateMemCpy( *ptrVal, llvm::None, pGlob, llvm::None,
            llvm::ConstantInt::get( llvm::Type::getInt32Ty( GetLLVMContext() ), size ) );
        return true;
    }

    m_llvmBuilder.CreateStore( *val, *ptrVal );
    return true;
}

bool Module::buildInstruction( State& st, const cir::Phi& p )
{
    auto type = LowerTypeForRuntime( st.context, p.type() );
    if( !type )
        return false;

    auto* pPHI = m_llvmBuilder.CreatePHI( GetLLVMType( *type ), p.numIncomings() );
    if( !pPHI )
        return false;

    {
        llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );

        p.forAllIncomings( [&]( auto&& bb, auto&& val )
        {
            auto pIncomingBB = buildBasicBlock( st, bb );
            if( !pIncomingBB )
            {
                pPHI = nullptr;
                return false;
            }

            auto* pVal = buildValue( st, val );
            if( !pVal )
            {
                pPHI = nullptr;
                return false;
            }

            pPHI->addIncoming( pVal, pIncomingBB );
            return true;
        } );
    }

    if( !pPHI )
        return false;

    createTemporary( st, p.destIndex(), pPHI );
    return true;
}

bool Module::buildInstruction( State& st, const cir::Assert& ass )
{
    // We should never encounter this during codegen, since it's a construct only
    // intended to be used in verification expressions.
    return false;
}

bool Module::buildInstruction( State& st, const cir::Placeholder& ph )
{
    // We should never encounter this during codegen, since it's a construct only
    // intended to be used in verification expressions.
    return false;
}

bool Module::buildInstruction( State& st, const cir::Implies& bo )
{
    // We should never encounter this during codegen, since it's a construct only
    // intended to be used in verification expressions.
    return false;
}

bool Module::buildInstruction( State& st, const cir::PHOverrideSet& phos )
{
    // We should never encounter this during codegen, since it's a construct only
    // intended to be used in verification expressions.
    return false;
}

bool Module::buildInstruction( State& st, const cir::PHOverrideClear& phoc )
{
    // We should never encounter this during codegen, since it's a construct only
    // intended to be used in verification expressions.
    return false;
}
Changes to bs/codegen/logicops.cpp.
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

28
29
30
31
32
33
34
35
36
37
38
39
40

41
42
43
44
45
46
47
48
49
50
51
52
53

54
55
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
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Not& uo )
{
    auto operand = buildValue( inf, uo.operand() );
    if( !operand )
        return nullptr;

    return m_llvmBuilder.CreateXor( operand, llvm::ConstantInt::getTrue( GetLLVMContext() ) );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::And& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;

    return m_llvmBuilder.CreateAnd( lhs, rhs );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Or& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;

    return m_llvmBuilder.CreateOr( lhs, rhs );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Xor& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;

    return m_llvmBuilder.CreateXor( lhs, rhs );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Shl& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;

    return m_llvmBuilder.CreateShl( lhs, rhs );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::LShr& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;





    return m_llvmBuilder.CreateLShr( lhs, rhs );

}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::AShr& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;

    return m_llvmBuilder.CreateAShr( lhs, rhs );

}







|

|

|

|
>


|

|
|
|

|
|
|

|
>


|

|
|
|

|
|
|

|
>


|

|
|
|

|
|
|

|
>


|

|
|
|

|
|
|

|
>


|

<
<
<
|
<

|

>
>
>
>
|
>


|

|
|
|

|
|
|

|
>

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;

bool Module::buildInstruction( State& st, const cir::Not& uo )
{
    auto operand = st.stack.pop( m_llvmBuilder );
    if( !operand )
        return false;

    st.stack.push( m_llvmBuilder.CreateXor( *operand, llvm::ConstantInt::getTrue( GetLLVMContext() ) ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::And& bo )
{
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    st.stack.push( m_llvmBuilder.CreateAnd( *lhs, *rhs ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::Or& bo )
{
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    st.stack.push( m_llvmBuilder.CreateOr( *lhs, *rhs ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::Xor& bo )
{
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    st.stack.push( m_llvmBuilder.CreateXor( *lhs, *rhs ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::Shl& bo )
{
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    st.stack.push( m_llvmBuilder.CreateShl( *lhs, *rhs ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::LShr& bo )
{



    auto rhs = st.stack.pop( m_llvmBuilder );

    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    st.stack.push( m_llvmBuilder.CreateLShr( *lhs, *rhs ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::AShr& bo )
{
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    st.stack.push( m_llvmBuilder.CreateAShr( *lhs, *rhs ) );
    return true;
}
Changes to bs/codegen/module.h.
25
26
27
28
29
30
31
32
33

34




35
36

37
38
39
40
41


42


43
44
45
46
47
48
49
50
51
52
53
54
55

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
114
115
116
117
118
119
120
121
122
123
            llvm::Function* getOrCreateFunc( const Context& c, const builtins::Func& func, const string& name,
                llvm::Function::LinkageTypes linkageType );

            void runOptimizationPasses();
            bool emitToFile( const string& filename, llvm::CodeGenFileType type );

        private:
            struct Infos
            {

                Infos( const Context& c ) : context( c ) {}




                const Context& context;


                llvm::BasicBlock* allocaBasicBlock = nullptr;
                llvm::AllocaInst* lastEmittedAlloca = nullptr;

                using storage_type = cir::TempStorage< NullInit< llvm::Value* > >;
                ptr< storage_type > temporaries = make_shared< storage_type >();


            };



            llvm::Function* getCurrentFunction() const
            {
                return m_llvmBuilder.GetInsertBlock()->getParent();
            }

            llvm::BasicBlock* buildCFG( Infos& inf, llvm::Function* llvmFunc, const ptr< cir::CFG >& pCFG );
            llvm::BasicBlock* buildBasicBlock( Infos& inf, const ptr< cir::BasicBlock >& pBB );

            llvm::Value* buildValue( Infos& inf, const Value& val );
            llvm::Constant* buildConstant( Infos& inf, const Value& val );
            llvm::Constant* buildConstantPointer( Infos& inf, const builtins::PointerType& ptType, const Value& v );


            llvm::Value* buildInstruction( Infos& inf, const cir::Instruction& instr );
            llvm::Value* buildInstruction( Infos& inf, const monostate& );
            llvm::Value* buildInstruction( Infos& inf, const cir::Call& call );

            llvm::Value* buildInstruction( Infos& inf, const cir::VarAddr& va );
            llvm::Value* buildInstruction( Infos& inf, const cir::TempAddr& ta );
            llvm::Value* buildInstruction( Infos& inf, const cir::Select& s );
            llvm::Value* buildInstruction( Infos& inf, const cir::CreateTemporary& ct );
            llvm::Value* buildInstruction( Infos& inf, const cir::GetTemporary& gt );
            llvm::Value* buildInstruction( Infos& inf, const cir::AllocVar& av );
            llvm::Value* buildInstruction( Infos& inf, const cir::Load& load );
            llvm::Value* buildInstruction( Infos& inf, const cir::Store& store );
            llvm::Value* buildInstruction( Infos& inf, const cir::Phi& p );

            llvm::Value* buildInstruction( Infos& inf, const cir::Not& uo );
            llvm::Value* buildInstruction( Infos& inf, const cir::And& bo );
            llvm::Value* buildInstruction( Infos& inf, const cir::Or& bo );
            llvm::Value* buildInstruction( Infos& inf, const cir::Xor& bo );
            llvm::Value* buildInstruction( Infos& inf, const cir::Implies& bo );

            llvm::Value* buildInstruction( Infos& inf, const cir::Shl& bo );
            llvm::Value* buildInstruction( Infos& inf, const cir::LShr& bo );
            llvm::Value* buildInstruction( Infos& inf, const cir::AShr& bo );

            llvm::Value* buildInstruction( Infos& inf, const cir::Add& bo );
            llvm::Value* buildInstruction( Infos& inf, const cir::Sub& bo );
            llvm::Value* buildInstruction( Infos& inf, const cir::Mul& bo );
            llvm::Value* buildInstruction( Infos& inf, const cir::UDiv& bo );
            llvm::Value* buildInstruction( Infos& inf, const cir::SDiv& bo );
            llvm::Value* buildInstruction( Infos& inf, const cir::URem& bo );
            llvm::Value* buildInstruction( Infos& inf, const cir::SRem& bo );

            llvm::Value* buildInstruction( Infos& inf, const cir::Eq& bo );
            llvm::Value* buildInstruction( Infos& inf, const cir::Neq& bo );
            llvm::Value* buildInstruction( Infos& inf, const cir::UGT& bo );
            llvm::Value* buildInstruction( Infos& inf, const cir::UGE& bo );
            llvm::Value* buildInstruction( Infos& inf, const cir::ULT& bo );
            llvm::Value* buildInstruction( Infos& inf, const cir::ULE& bo );
            llvm::Value* buildInstruction( Infos& inf, const cir::SGT& bo );
            llvm::Value* buildInstruction( Infos& inf, const cir::SGE& bo );
            llvm::Value* buildInstruction( Infos& inf, const cir::SLT& bo );
            llvm::Value* buildInstruction( Infos& inf, const cir::SLE& bo );

            llvm::Value* buildInstruction( Infos& inf, const cir::Assert& ass );
            llvm::Value* buildInstruction( Infos& inf, const cir::Placeholder& ph );
            llvm::Value* buildInstruction( Infos& inf, const cir::PHOverride& pho );

            llvm::Value* buildInstruction( Infos& inf, const cir::GhostCall& gc );

            bool buildTerminator( Infos& inf, const cir::Terminator& terminator );

            bool buildTerminator( Infos& inf, const cir::Ret& r );
            bool buildTerminator( Infos& inf, const cir::Branch& b );
            bool buildTerminator( Infos& inf, const cir::CondBranch& cb );
            bool buildTerminator( Infos& inf, const cir::GhostBranch& gb );

            template< typename T >
            bool buildTerminator( Infos& inf, const T& t )
            {
                return false;
            }

            llvm::Value* buildAlloca( Infos& inf, llvm::Type* type );
            llvm::Value* createTemporary( Infos& inf, uint32_t index, llvm::Value* pValue ) const;

            llvm::Module m_llvmModule;
            llvm::DataLayout m_dataLayout;
            llvm::IRBuilder<> m_llvmBuilder;
            llvm::TargetMachine* m_targetMachine = nullptr;

            unordered_map< string, llvm::GlobalVariable* > m_strings;







|

>
|
>
>
>
>


>




|
>
>

>
>






|
|

|
|
|

>
|
|
|
>
|
|
|
|
|
|
|
|
|

|
|
|
|
|

|
|
|

|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|

|
|
|
>
|

|
>
|
|
|
|


|




|
|







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
            llvm::Function* getOrCreateFunc( const Context& c, const builtins::Func& func, const string& name,
                llvm::Function::LinkageTypes linkageType );

            void runOptimizationPasses();
            bool emitToFile( const string& filename, llvm::CodeGenFileType type );

        private:
            struct State
            {
                State( const Context& c, size_t numVars, llvm::Function* f ) :
                    context( c ),
                    llvmFunc( f ),
                    temporaries( make_shared< storage_type >( numVars ) )
                {}

                const Context& context;

                llvm::Function* llvmFunc = nullptr;
                llvm::BasicBlock* allocaBasicBlock = nullptr;
                llvm::AllocaInst* lastEmittedAlloca = nullptr;

                using storage_type = cir::TempStorage< NullInit< llvm::Value* > >;
                ptr< storage_type > temporaries;

                Stack stack;
            };

            static llvm::BasicBlock* GetOrCreateLLVMBB( State& st, const ptr< cir::BasicBlock >& pBB );

            llvm::Function* getCurrentFunction() const
            {
                return m_llvmBuilder.GetInsertBlock()->getParent();
            }

            llvm::BasicBlock* buildCFG( State& st, llvm::Function* llvmFunc, const ptr< cir::CFG >& pCFG );
            llvm::BasicBlock* buildBasicBlock( State& st, const ptr< cir::BasicBlock >& pBB );

            llvm::Value* buildValue( State& st, const Value& val );
            llvm::Constant* buildConstant( State& st, const Value& val );
            llvm::Constant* buildConstantPointer( State& st, const builtins::PointerType& ptType, const Value& v );

            bool buildInstruction( State& st, const cir::InstrSeq& is );
            bool buildInstruction( State& st, const cir::Instruction& instr );
            bool buildInstruction( State& st, const monostate& );
            bool buildInstruction( State& st, const cir::Call& call );
            bool buildInstruction( State& st, const cir::Constant& ct );
            bool buildInstruction( State& st, const cir::VarAddr& va );
            bool buildInstruction( State& st, const cir::TempAddr& ta );
            bool buildInstruction( State& st, const cir::Select& s );
            bool buildInstruction( State& st, const cir::CreateTemporary& ct );
            bool buildInstruction( State& st, const cir::GetTemporary& gt );
            bool buildInstruction( State& st, const cir::AllocVar& av );
            bool buildInstruction( State& st, const cir::Load& load );
            bool buildInstruction( State& st, const cir::Store& store );
            bool buildInstruction( State& st, const cir::Phi& p );

            bool buildInstruction( State& st, const cir::Not& uo );
            bool buildInstruction( State& st, const cir::And& bo );
            bool buildInstruction( State& st, const cir::Or& bo );
            bool buildInstruction( State& st, const cir::Xor& bo );
            bool buildInstruction( State& st, const cir::Implies& bo );

            bool buildInstruction( State& st, const cir::Shl& bo );
            bool buildInstruction( State& st, const cir::LShr& bo );
            bool buildInstruction( State& st, const cir::AShr& bo );

            bool buildInstruction( State& st, const cir::Add& bo );
            bool buildInstruction( State& st, const cir::Sub& bo );
            bool buildInstruction( State& st, const cir::Mul& bo );
            bool buildInstruction( State& st, const cir::UDiv& bo );
            bool buildInstruction( State& st, const cir::SDiv& bo );
            bool buildInstruction( State& st, const cir::URem& bo );
            bool buildInstruction( State& st, const cir::SRem& bo );

            bool buildInstruction( State& st, const cir::Eq& bo );
            bool buildInstruction( State& st, const cir::Neq& bo );
            bool buildInstruction( State& st, const cir::UGT& bo );
            bool buildInstruction( State& st, const cir::UGE& bo );
            bool buildInstruction( State& st, const cir::ULT& bo );
            bool buildInstruction( State& st, const cir::ULE& bo );
            bool buildInstruction( State& st, const cir::SGT& bo );
            bool buildInstruction( State& st, const cir::SGE& bo );
            bool buildInstruction( State& st, const cir::SLT& bo );
            bool buildInstruction( State& st, const cir::SLE& bo );

            bool buildInstruction( State& st, const cir::Assert& ass );
            bool buildInstruction( State& st, const cir::Placeholder& ph );
            bool buildInstruction( State& st, const cir::PHOverrideSet& phos );
            bool buildInstruction( State& st, const cir::PHOverrideClear& phoc );
            bool buildInstruction( State& st, const cir::GhostCall& gc );

            bool buildTerminator( State& st, const cir::Terminator& terminator );
            bool buildTerminator( State& st, const cir::RetVoid& r );
            bool buildTerminator( State& st, const cir::Ret& r );
            bool buildTerminator( State& st, const cir::Branch& b );
            bool buildTerminator( State& st, const cir::CondBranch& cb );
            bool buildTerminator( State& st, const cir::GhostBranch& gb );

            template< typename T >
            bool buildTerminator( State& st, const T& t )
            {
                return false;
            }

            llvm::Value* buildAlloca( State& st, llvm::Type* type );
            llvm::Value* createTemporary( State& st, uint32_t index, llvm::Value* pValue ) const;

            llvm::Module m_llvmModule;
            llvm::DataLayout m_dataLayout;
            llvm::IRBuilder<> m_llvmBuilder;
            llvm::TargetMachine* m_targetMachine = nullptr;

            unordered_map< string, llvm::GlobalVariable* > m_strings;
Added bs/codegen/stack.h.
























































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#ifndef GOOSE_CODEGEN_STACK_H
#define GOOSE_CODEGEN_STACK_H

namespace goose::codegen
{
    class Stack
    {
        public:
            using Slot = variant< llvm::Value*, codegen::Address >;

            template< typename T = llvm::Value* >
            optional< T > pop( llvm::IRBuilder<>& builder )
            {
                if( m_stack.empty() )
                    return nullopt;

                auto result = m_stack.top();
                m_stack.pop();

                if constexpr( is_same_v< T, codegen::Address > )
                {
                    if( !holds_alternative< codegen::Address >( result ) )
                        return nullopt;
                    return get< codegen::Address >( result );
                }
                else if constexpr( is_same_v< T, llvm::Value* > )
                {
                    if( holds_alternative< codegen::Address >( result ) )
                        return AddressToGEP( builder, get< codegen::Address >( result ) );
                    else
                        return get< llvm::Value* >( result );
                }
                else if( holds_alternative< llvm::Value* >( result ) )
                    return llvm::dyn_cast_or_null< remove_pointer_t< T > >( get< llvm::Value* >( result ) );

                return nullopt;
            }

            optional< Slot > pop()
            {
                if( m_stack.empty() )
                    return nullopt;

                auto result = m_stack.top();
                m_stack.pop();
                return result;
            }

            template< typename T >
            void push( T&& v )
            {
                m_stack.push( forward< T >( v ) );
            }

        private:
            stack< Slot > m_stack;
    };
}

#endif
Changes to bs/codegen/value.cpp.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;

llvm::Value* Module::buildValue( Infos& inf, const Value& val )
{
    if( val.isPoison() )
        return nullptr;

    if( val.isConstant() )
        return buildConstant( inf, val );

    return buildInstruction( inf, *val.cir() );

}







llvm::Constant* Module::buildConstant( Infos& inf, const Value& v )
{
    assert( v.isConstant() );

    auto val = LowerConstantForRuntime( inf.context, v );
    if( !val )
        return nullptr;

    if( auto b = FromValue< bool >( *val ) )
    {
        return *b ?
            llvm::ConstantInt::getTrue( GetLLVMContext() ) :
            llvm::ConstantInt::getFalse( GetLLVMContext() );
    }
    else if( auto intVal = FromValue< APSInt >( *val ) )
    {
        return llvm::ConstantInt::get( GetLLVMType( *EIRToValue( val->type() ) ),
            *FromValue< APSInt >( *val ) );
    }
    else if( auto ptType = FromValue< PointerType >( *EIRToValue( val->type() ) ) )
    {
        return buildConstantPointer( inf, *ptType, *val );
    }
    else if( auto recType = FromValue< RecordType >( *EIRToValue( val->type() ) ) )
    {
        llvm::SmallVector< llvm::Constant*, 8 > members;
        assert( holds_alternative< pvec >( val->val() ) );

        bool success = true;

        ForEachInVectorTerm( val->val(), [&]( auto&& t )
        {
            auto* pMemberVal = buildConstant( inf, *EIRToValue( t ) );
            if( !pMemberVal )
            {
                success = false;
                return false;
            }

            members.push_back( pMemberVal );
            return true;
        } );

        if( !success )
            return nullptr;

        return llvm::ConstantStruct::get( static_cast< llvm::StructType* >
            ( GetLLVMType( *EIRToValue( val->type() ) ) ), members );
    }




    DiagnosticsManager::GetInstance().emitErrorMessage(























        v.locationId(), "constants with compile time types are not supported.", 0 );
    return nullptr;
}

llvm::Constant* Module::buildConstantPointer( Infos& inf, const PointerType& ptType, const Value& v )
{
    // We currently have two types of pointer constants: nullptr,
    // and pointers to string literals.
    if( auto str = get_if< string >( &v.val() ) )
    {
        llvm::GlobalVariable* pGlob = nullptr;








|





|

|
>
|
>
>
>

>
>
>
|



|
















|










|
















>
|
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|



|







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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
114
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;

llvm::Value* Module::buildValue( State& st, const Value& val )
{
    if( val.isPoison() )
        return nullptr;

    if( val.isConstant() )
        return buildConstant( st, val );

    if( !buildInstruction( st, *val.cir() ) )
        return nullptr;

    auto result = st.stack.pop( m_llvmBuilder );
    if( !result )
        return nullptr;

    return *result;
}

llvm::Constant* Module::buildConstant( State& st, const Value& v )
{
    assert( v.isConstant() );

    auto val = LowerConstantForRuntime( st.context, v );
    if( !val )
        return nullptr;

    if( auto b = FromValue< bool >( *val ) )
    {
        return *b ?
            llvm::ConstantInt::getTrue( GetLLVMContext() ) :
            llvm::ConstantInt::getFalse( GetLLVMContext() );
    }
    else if( auto intVal = FromValue< APSInt >( *val ) )
    {
        return llvm::ConstantInt::get( GetLLVMType( *EIRToValue( val->type() ) ),
            *FromValue< APSInt >( *val ) );
    }
    else if( auto ptType = FromValue< PointerType >( *EIRToValue( val->type() ) ) )
    {
        return buildConstantPointer( st, *ptType, *val );
    }
    else if( auto recType = FromValue< RecordType >( *EIRToValue( val->type() ) ) )
    {
        llvm::SmallVector< llvm::Constant*, 8 > members;
        assert( holds_alternative< pvec >( val->val() ) );

        bool success = true;

        ForEachInVectorTerm( val->val(), [&]( auto&& t )
        {
            auto* pMemberVal = buildConstant( st, *EIRToValue( t ) );
            if( !pMemberVal )
            {
                success = false;
                return false;
            }

            members.push_back( pMemberVal );
            return true;
        } );

        if( !success )
            return nullptr;

        return llvm::ConstantStruct::get( static_cast< llvm::StructType* >
            ( GetLLVMType( *EIRToValue( val->type() ) ) ), members );
    }
    else if( auto funcType = FromValue< FuncType >( *EIRToValue( val->type() ) ) )
    {
        if( IsBuiltinFunc( *val ) || IsBuiltinIntrinsicFunc( *val ) )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage(
                val->locationId(), "builtin functions can't be called at runtime." );
            return nullptr;
        }

         // If it is a plain function, we just need to generate or retrieve it.
        if( val->isConstant() )
        {
            auto f = FromValue< builtins::Func >( *val );
            if( !f )
                return nullptr;

            llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );
            return getOrCreateFunc( st.context, *f );
        }
        else
        {
            // TODO: function pointers
            assert( false );
            return nullptr;
        }
    }

    DiagnosticsManager::GetInstance().emitErrorMessage(
        v.locationId(), "constants with compile-time types are not supported by code generation.", 0 );
    return nullptr;
}

llvm::Constant* Module::buildConstantPointer( State& st, const PointerType& ptType, const Value& v )
{
    // We currently have two types of pointer constants: nullptr,
    // and pointers to string literals.
    if( auto str = get_if< string >( &v.val() ) )
    {
        llvm::GlobalVariable* pGlob = nullptr;

Changes to bs/compile/compiler.cpp.
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
    optional< Value > Compiler::LoadAndExecuteFile( const ptr< Env >& e, const string& filename, const Term& identity,
        const Term& returnType, optional< Value > defRetVal )
    {
        ProfileZoneScoped;
        ProfileZoneName( filename.c_str(), filename.size() );

        auto cfg = LoadAndParseFile( e, filename, identity, returnType, defRetVal );
        if( !cfg )
            return PoisonValue();

        if( !cfg->entryBB()->canBeExecuted() )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( 0,
                format( "{}: can not be executed.", filename ) );
            return PoisonValue();
        }

        execute::VM vm;
        return vm.execute( cfg->entryBB() );
    }

    ptr< cir::CFG > Compiler::LoadAndParseFile( const ptr< Env >& e, const string& filename, const Term& identity,
        const Term& returnType, optional< Value > defRetVal )
    {
        ProfileZoneScoped;
        ProfileZoneName( filename.c_str(), filename.size() );







|










|







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
    optional< Value > Compiler::LoadAndExecuteFile( const ptr< Env >& e, const string& filename, const Term& identity,
        const Term& returnType, optional< Value > defRetVal )
    {
        ProfileZoneScoped;
        ProfileZoneName( filename.c_str(), filename.size() );

        auto cfg = LoadAndParseFile( e, filename, identity, returnType, defRetVal );
        if( !cfg || cfg->isPoisoned() )
            return PoisonValue();

        if( !cfg->entryBB()->canBeExecuted() )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( 0,
                format( "{}: can not be executed.", filename ) );
            return PoisonValue();
        }

        execute::VM vm;
        return vm.execute( *cfg );
    }

    ptr< cir::CFG > Compiler::LoadAndParseFile( const ptr< Env >& e, const string& filename, const Term& identity,
        const Term& returnType, optional< Value > defRetVal )
    {
        ProfileZoneScoped;
        ProfileZoneName( filename.c_str(), filename.size() );
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160

        if( cfg->currentBB() && !cfg->currentBB()->terminator() )
        {
            p.flushValue();
            cb->destroyAllLiveValues( c );

            if( returnType == GetValueType< void >() )
               cfg->emitTerminator( r->currentLocation(), cir::Ret() );
            else if( !defRetVal )
            {
                dm.emitSyntaxErrorMessage( r->currentLocation(), "missing return statement." );
                return nullptr;
            }
            else
            {







|







146
147
148
149
150
151
152
153
154
155
156
157
158
159
160

        if( cfg->currentBB() && !cfg->currentBB()->terminator() )
        {
            p.flushValue();
            cb->destroyAllLiveValues( c );

            if( returnType == GetValueType< void >() )
                cfg->emitTerminator( r->currentLocation(), cir::RetVoid( r->currentLocation() ) );
            else if( !defRetVal )
            {
                dm.emitSyntaxErrorMessage( r->currentLocation(), "missing return statement." );
                return nullptr;
            }
            else
            {
171
172
173
174
175
176
177

178
179
180
181
182
183
184
185
186
187
188
                            dm.emitErrorMessage( defRetVal->locationId(), "ambiguous default return value conversion." );
                            break;
                    }

                    return nullptr;
                }


                cfg->emitTerminator( r->currentLocation(), cir::Ret( get< Value >( converted ) ) );
            }
        }

        verify::Func fv( c, cfg, returnType );
        if( !fv.verify() )
            return nullptr;

        return cfg;
    }
}







>
|










171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
                            dm.emitErrorMessage( defRetVal->locationId(), "ambiguous default return value conversion." );
                            break;
                    }

                    return nullptr;
                }

                cfg->currentBB()->append( get< Value >( converted ) );
                cfg->emitTerminator( r->currentLocation(), cir::Ret( r->currentLocation() ) );
            }
        }

        verify::Func fv( c, cfg, returnType );
        if( !fv.verify() )
            return nullptr;

        return cfg;
    }
}
Changes to bs/diagnostics/diagnosticsmanager.h.
103
104
105
106
107
108
109



110
111
112
113
114
115
116



117
118
119
120
121
122
123
            format( "Internal error: {}:{}: {}", __FILE__, __LINE__, message ) ); \
        abort(); \
    }

#define G_VAL_ASSERT( val, cond ) \
    if( !( cond ) ) \
    { \



        diagnostics::DiagnosticsManager::GetInstance().emitErrorMessage( (val).locationId(), \
            format( "Internal error: {}:{}: assertion " #cond " failed", __FILE__, __LINE__ ) ); \
        abort(); \
    }

#define G_LOC_ERROR( loc, message ) \
    { \



        diagnostics::DiagnosticsManager::GetInstance().emitErrorMessage( (loc), \
            format( "Internal error: {}:{}: {}", __FILE__, __LINE__, message ) ); \
        abort(); \
    }

#define G_LOC_ASSERT( loc, cond ) \
    if( !( cond ) ) \







>
>
>







>
>
>







103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
            format( "Internal error: {}:{}: {}", __FILE__, __LINE__, message ) ); \
        abort(); \
    }

#define G_VAL_ASSERT( val, cond ) \
    if( !( cond ) ) \
    { \
        stringstream sstr; \
        sstr << (val); \
        diagnostics::DiagnosticsContext dc( (val).locationId(), format( "Value: {}",  sstr.str() ) ); \
        diagnostics::DiagnosticsManager::GetInstance().emitErrorMessage( (val).locationId(), \
            format( "Internal error: {}:{}: assertion " #cond " failed", __FILE__, __LINE__ ) ); \
        abort(); \
    }

#define G_LOC_ERROR( loc, message ) \
    { \
        stringstream sstr; \
        sstr << (val); \
        diagnostics::DiagnosticsContext dc( (val).locationId(), format( "Value: {}",  sstr.str() ) ); \
        diagnostics::DiagnosticsManager::GetInstance().emitErrorMessage( (loc), \
            format( "Internal error: {}:{}: {}", __FILE__, __LINE__, message ) ); \
        abort(); \
    }

#define G_LOC_ASSERT( loc, cond ) \
    if( !( cond ) ) \
Changes to bs/eir/hash.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "eir.h"
#include "cir/cir.h"

namespace std
{
    size_t hash< goose::eir::Hole >::operator()( const goose::eir::Hole& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.name() ), goose::util::ComputeHash( x.kind() ) );
    }

    size_t hash< goose::eir::Vector >::operator()( const goose::eir::Vector& v ) const
    {
        auto g = goose::eir::ContainerHashGenerator( v.terms() );
        return llvm::hash_combine_range( g.begin(), g.end() );
    }

    size_t hash< goose::eir::Term >::operator()( const goose::eir::Term& x ) const
    {
        return visit( [&]< typename T >( T&& t )
        {












|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "eir.h"
#include "cir/cir.h"

namespace std
{
    size_t hash< goose::eir::Hole >::operator()( const goose::eir::Hole& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.name() ), goose::util::ComputeHash( x.kind() ) );
    }

    size_t hash< goose::eir::Vector >::operator()( const goose::eir::Vector& v ) const
    {
        auto g = ContainerHashGenerator( v.terms() );
        return llvm::hash_combine_range( g.begin(), g.end() );
    }

    size_t hash< goose::eir::Term >::operator()( const goose::eir::Term& x ) const
    {
        return visit( [&]< typename T >( T&& t )
        {
Changes to bs/eir/hash.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef GOOSE_EIR_HASH_H
#define GOOSE_EIR_HASH_H

namespace goose::eir
{
    template< typename C >
    Generator< uint64_t > ContainerHashGenerator( const C& c )
    {
        for( const auto& x : c )
            co_yield ComputeHash( x );
    }
}

namespace std
{
    template<> struct hash< goose::eir::Hole >
    {
        size_t operator()( const goose::eir::Hole& x ) const;
    };




<
<
<
<
<
<
<
<
<
<







1
2
3










4
5
6
7
8
9
10
#ifndef GOOSE_EIR_HASH_H
#define GOOSE_EIR_HASH_H











namespace std
{
    template<> struct hash< goose::eir::Hole >
    {
        size_t operator()( const goose::eir::Hole& x ) const;
    };

Changes to bs/eir/value.cpp.
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
        static auto poisonType = Value( TypeType(), TSID( poison ) ).setPoison();
        return poisonType;
    }

    // A generic poisoned value of "poisontype" type.
    const Value& PoisonValue()
    {
        static auto poisonVal = Value( ValueToEIR( PoisonType() ), 0U ).setPoison();
        return poisonVal;
    }

    Term ValueToEIR( const Value& v )
    {
        if( v.isConstant() )
        {







|







33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
        static auto poisonType = Value( TypeType(), TSID( poison ) ).setPoison();
        return poisonType;
    }

    // A generic poisoned value of "poisontype" type.
    const Value& PoisonValue()
    {
        static auto poisonVal = Value( ValueToEIR( PoisonType() ), Term( 0U ) ).setPoison();
        return poisonVal;
    }

    Term ValueToEIR( const Value& v )
    {
        if( v.isConstant() )
        {
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
        if( sort == "constant"_sid )
            return Value( type, val, locationId );

        auto cir = Decompose( val, Val< ptr< void > >() );
        if( !cir )
            return nullopt;

        return Value( type, static_pointer_cast< cir::Instruction >( ptr< void >( *cir ) ), locationId );
    }

    Term ValueToEIR( const ValuePattern& v )
    {
        return VEC( TSID( value ), v.sort(), v.type(), v.val(), v.locationId() );
    }








|







103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
        if( sort == "constant"_sid )
            return Value( type, val, locationId );

        auto cir = Decompose( val, Val< ptr< void > >() );
        if( !cir )
            return nullopt;

        return Value( type, static_pointer_cast< cir::InstrSeq >( ptr< void >( *cir ) ), locationId );
    }

    Term ValueToEIR( const ValuePattern& v )
    {
        return VEC( TSID( value ), v.sort(), v.type(), v.val(), v.locationId() );
    }

Changes to bs/eir/value.h.
1
2
3
4
5
6

7
8
9
10
11
12
13
#ifndef GOOSE_EIR_VALUE_H
#define GOOSE_EIR_VALUE_H

namespace goose::cir
{
    class Instruction;

}

namespace goose::eir
{
    class Value;
    class ValuePattern;







>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef GOOSE_EIR_VALUE_H
#define GOOSE_EIR_VALUE_H

namespace goose::cir
{
    class Instruction;
    using InstrSeq = list< Instruction >;
}

namespace goose::eir
{
    class Value;
    class ValuePattern;

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
                m_valOrCIR( forward< VL >( valOrCIR ) ),
                m_locationId( loc )
            {}

            const auto& type() const { return m_type; }
            const auto& val() const { return get< Term >( m_valOrCIR ); }
            auto& val() { return get< Term >( m_valOrCIR ); }
            ptr< cir::Instruction > cir() const
            {
                const auto* ppCir = get_if< ptr< cir::Instruction > >( &m_valOrCIR );
                if( !ppCir )
                    return nullptr;
                return *ppCir;
            }

            auto locationId() const { return m_locationId; }
            auto&& setLocationId( LocationId id )







|

|







31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
                m_valOrCIR( forward< VL >( valOrCIR ) ),
                m_locationId( loc )
            {}

            const auto& type() const { return m_type; }
            const auto& val() const { return get< Term >( m_valOrCIR ); }
            auto& val() { return get< Term >( m_valOrCIR ); }
            ptr< cir::InstrSeq > cir() const
            {
                const auto* ppCir = get_if< ptr< cir::InstrSeq > >( &m_valOrCIR );
                if( !ppCir )
                    return nullptr;
                return *ppCir;
            }

            auto locationId() const { return m_locationId; }
            auto&& setLocationId( LocationId id )
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
                if( m_type != rhs.m_type )
                    return m_type < rhs.m_type;
                return m_valOrCIR < rhs.m_valOrCIR;
            }

        private:
            Term m_type;
            variant< Term, ptr< cir::Instruction > > m_valOrCIR;
            LocationId m_locationId = LocationId::Poison();
    };

    class ValuePattern
    {
        public:
            template< typename S, typename T, typename V >







|







78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
                if( m_type != rhs.m_type )
                    return m_type < rhs.m_type;
                return m_valOrCIR < rhs.m_valOrCIR;
            }

        private:
            Term m_type;
            variant< Term, ptr< cir::InstrSeq > > m_valOrCIR;
            LocationId m_locationId = LocationId::Poison();
    };

    class ValuePattern
    {
        public:
            template< typename S, typename T, typename V >
Changes to bs/eir/vector.h.
51
52
53
54
55
56
57





58
59
60
61
62
63
64
            int32_t weightOverride() const { return m_weightOverride; }
            void setWeightOverride( int32_t wo ) { m_weightOverride = wo; }

            void reserve( size_t n )
            {
                m_terms.reserve( n );
            }






            template< typename T >
            void append( T&& term )
            {
                if constexpr( !is_same_v< Repetition, remove_cvref_t< T > > )
                {
                    // We want the outer structures to have more







>
>
>
>
>







51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
            int32_t weightOverride() const { return m_weightOverride; }
            void setWeightOverride( int32_t wo ) { m_weightOverride = wo; }

            void reserve( size_t n )
            {
                m_terms.reserve( n );
            }

            void resize( size_t n )
            {
                m_terms.resize( n );
            }

            template< typename T >
            void append( T&& term )
            {
                if constexpr( !is_same_v< Repetition, remove_cvref_t< T > > )
                {
                    // We want the outer structures to have more
72
73
74
75
76
77
78








79
80
81
82
83
84
85
                    m_terms.emplace_back( forward< T >( term ) );
                }
                else
                {
                    setRepetitionTerm( term.m_term );
                }
            }









            template< typename... T >
            static auto Make( T&&... terms )
            {
                auto v = make_shared< Vector >();
                v->reserve( sizeof... ( T ) );
                ( v->append( terms ), ... );







>
>
>
>
>
>
>
>







77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
                    m_terms.emplace_back( forward< T >( term ) );
                }
                else
                {
                    setRepetitionTerm( term.m_term );
                }
            }

            template< typename T >
            void set( size_t index, T&& term )
            {
                m_weight -= GetWeight( m_terms[index] );
                m_weight += GetWeight( term ) * 2;
                m_terms[index] = term;
            }

            template< typename... T >
            static auto Make( T&&... terms )
            {
                auto v = make_shared< Vector >();
                v->reserve( sizeof... ( T ) );
                ( v->append( terms ), ... );
Changes to bs/execute/binaryops.cpp.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#include "execute.h"
#include "builtins/builtins.h"
#include "binaryops.inl"

using namespace goose;
using namespace goose::execute;
using namespace goose::builtins;

optional< Value > VM::execute( const cir::And& bo )
{
    return executeLogicBinOp( bo, []< typename T >( const T& lhs, const T& rhs )
    {
        if constexpr( is_same_v< T, bool > )
        {
            return lhs && rhs;
        }
        else
        {
            return lhs & rhs;
        }
    } );
}

optional< Value > VM::execute( const cir::Or& bo )
{
    return executeLogicBinOp( bo, []< typename T >( const T& lhs, const T& rhs )
    {
        if constexpr( is_same_v< T, bool > )
        {
            return lhs || rhs;
        }
        else
        {
            return lhs | rhs;
        }
    } );
}

optional< Value > VM::execute( const cir::Xor& bo )
{
    return executeLogicBinOp( bo, []< typename T >( const T& lhs, const T& rhs )
    {
        if constexpr( is_same_v< T, bool > )
        {
            auto l = static_cast< uint8_t >( lhs );
            auto r = static_cast< uint8_t >( rhs );
            return !!( l ^ r );
        }
        else
        {
            return lhs ^ rhs;
        }
    } );
}

optional< Value > VM::execute( const cir::Shl& bo )
{
    return executeShiftBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs.shl( rhs );
    } );
}

optional< Value > VM::execute( const cir::LShr& bo )
{
    return executeShiftBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs.lshr( rhs );
    } );
}

optional< Value > VM::execute( const cir::AShr& bo )
{
    return executeShiftBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs.ashr( rhs );
    } );
}

optional< Value > VM::execute( const cir::Add& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs + rhs;
    } );
}

optional< Value > VM::execute( const cir::Sub& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs - rhs;
    } );
}

optional< Value > VM::execute( const cir::Mul& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs * rhs;
    } );
}

optional< Value > VM::execute( const cir::UDiv& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs / rhs;
    } );
}

optional< Value > VM::execute( const cir::SDiv& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs / rhs;
    } );
}

optional< Value > VM::execute( const cir::URem& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs % rhs;
    } );
}

optional< Value > VM::execute( const cir::SRem& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs % rhs;
    } );
}

optional< Value > VM::execute( const cir::Eq& bo )
{
    return executeEqualityBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs == rhs;
    } );
}

optional< Value > VM::execute( const cir::Neq& bo )
{
    return executeEqualityBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs != rhs;
    } );
}

optional< Value > VM::execute( const cir::UGT& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs > rhs;
    } );
}

optional< Value > VM::execute( const cir::UGE& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs >= rhs;
    } );
}

optional< Value > VM::execute( const cir::ULT& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs < rhs;
    } );
}

optional< Value > VM::execute( const cir::ULE& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs <= rhs;
    } );
}

optional< Value > VM::execute( const cir::SGT& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs > rhs;
    } );
}

optional< Value > VM::execute( const cir::SGE& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs >= rhs;
    } );
}

optional< Value > VM::execute( const cir::SLT& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs < rhs;
    } );
}

optional< Value > VM::execute( const cir::SLE& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs <= rhs;
    } );
}








|














|














|
















|







|







|







|







|







|







|







|







|







|







|







|







|







|







|







|







|







|







|







|






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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#include "execute.h"
#include "builtins/builtins.h"
#include "binaryops.inl"

using namespace goose;
using namespace goose::execute;
using namespace goose::builtins;

bool VM::execute( const cir::And& bo )
{
    return executeLogicBinOp( bo, []< typename T >( const T& lhs, const T& rhs )
    {
        if constexpr( is_same_v< T, bool > )
        {
            return lhs && rhs;
        }
        else
        {
            return lhs & rhs;
        }
    } );
}

bool VM::execute( const cir::Or& bo )
{
    return executeLogicBinOp( bo, []< typename T >( const T& lhs, const T& rhs )
    {
        if constexpr( is_same_v< T, bool > )
        {
            return lhs || rhs;
        }
        else
        {
            return lhs | rhs;
        }
    } );
}

bool VM::execute( const cir::Xor& bo )
{
    return executeLogicBinOp( bo, []< typename T >( const T& lhs, const T& rhs )
    {
        if constexpr( is_same_v< T, bool > )
        {
            auto l = static_cast< uint8_t >( lhs );
            auto r = static_cast< uint8_t >( rhs );
            return !!( l ^ r );
        }
        else
        {
            return lhs ^ rhs;
        }
    } );
}

bool VM::execute( const cir::Shl& bo )
{
    return executeShiftBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs.shl( rhs );
    } );
}

bool VM::execute( const cir::LShr& bo )
{
    return executeShiftBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs.lshr( rhs );
    } );
}

bool VM::execute( const cir::AShr& bo )
{
    return executeShiftBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs.ashr( rhs );
    } );
}

bool VM::execute( const cir::Add& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs + rhs;
    } );
}

bool VM::execute( const cir::Sub& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs - rhs;
    } );
}

bool VM::execute( const cir::Mul& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs * rhs;
    } );
}

bool VM::execute( const cir::UDiv& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs / rhs;
    } );
}

bool VM::execute( const cir::SDiv& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs / rhs;
    } );
}

bool VM::execute( const cir::URem& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs % rhs;
    } );
}

bool VM::execute( const cir::SRem& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs % rhs;
    } );
}

bool VM::execute( const cir::Eq& bo )
{
    return executeEqualityBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs == rhs;
    } );
}

bool VM::execute( const cir::Neq& bo )
{
    return executeEqualityBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs != rhs;
    } );
}

bool VM::execute( const cir::UGT& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs > rhs;
    } );
}

bool VM::execute( const cir::UGE& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs >= rhs;
    } );
}

bool VM::execute( const cir::ULT& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs < rhs;
    } );
}

bool VM::execute( const cir::ULE& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs <= rhs;
    } );
}

bool VM::execute( const cir::SGT& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs > rhs;
    } );
}

bool VM::execute( const cir::SGE& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs >= rhs;
    } );
}

bool VM::execute( const cir::SLT& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs < rhs;
    } );
}

bool VM::execute( const cir::SLE& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs <= rhs;
    } );
}
Changes to bs/execute/binaryops.inl.
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
28

29

30

31
32
33
34

35
36
37
38
39
40

41
42
43
44
45
46

47
48
49
50
51
52

53
54
55


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
114
115
116
117
118
119
120
121
122
123
124
125

126
127
128
129
130
131

132
133
134


135
136
137
138
139
140




141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159

160
161
162
163
164
165

166
167
168


169
170
171
172
173
#ifndef GOOSE_EXECUTE_BINARYOPS_INL
#define GOOSE_EXECUTE_BINARYOPS_INL

namespace goose::execute
{
    template< typename F >
    optional< Value > VM::executeEqualityBinOp( const cir::BinaryOp& bo, F&& func )
    {




        if( bo.lhs().isPoison() || bo.rhs().isPoison() )
            return PoisonValue();

        assert( bo.lhs().type() == bo.rhs().type() );
        if( bo.lhs().type() != bo.rhs().type() )
            return PoisonValue();

        auto lval = Evaluate( bo.lhs(), *this );
        if( lval.isPoison() )
            return PoisonValue();
        if( !lval.isConstant() )
            return nullopt;

        auto rval = Evaluate( bo.rhs(), *this );
        if( rval.isPoison()  )
            return PoisonValue();
        if( !rval.isConstant() )
            return nullopt;

        if( auto lbool = FromValue< bool >( lval ) )

            return ToValue( func( *lbool, *FromValue< bool >( rval ) ) );



        if( auto lint = FromValue< APSInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );
            return ToValue( func( *lint, move( rint ) ) );

        }

        if( auto lint = FromValue< BigInt >( lval ) )
        {
            auto rint = *FromValue< BigInt >( rval );
            return ToValue( func( *lint, move( rint ) ) );

        }

        if( auto lc = FromValue< char32_t >( lval ) )
        {
            auto rc = *FromValue< char32_t >( rval );
            return ToValue( func( *lc, rc ) );

        }

        if( auto lstr = FromValue< string >( lval ) )
        {
            const auto& rstr = *FromValue< string >( rval );
            return ToValue( func( *lstr, rstr ) );

        }

        assert( false );


        return PoisonValue();
    }

    template< typename F >
    optional< Value > VM::executeLogicBinOp( const cir::BinaryOp& bo, F&& func )
    {




        if( bo.lhs().isPoison() || bo.rhs().isPoison() )
            return PoisonValue();

        assert( bo.lhs().type() == bo.rhs().type() );
        if( bo.lhs().type() != bo.rhs().type() )
            return PoisonValue();

        auto lval = Evaluate( bo.lhs(), *this );
        if( lval.isPoison()  )
            return PoisonValue();
        if( !lval.isConstant() )
            return nullopt;

        auto rval = Evaluate( bo.rhs(), *this );
        if( rval.isPoison()  )
            return PoisonValue();
        if( !rval.isConstant() )
            return nullopt;

        if( auto lbool = FromValue< bool >( lval ) )

            return ToValue( func( *lbool, *FromValue< bool >( rval ) ) );



        if( auto lint = FromValue< APSInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );
            return ToValue( func( *lint, move( rint ) ) );

        }

        if( auto lint = FromValue< BigInt >( lval ) )
        {
            auto rint = *FromValue< BigInt >( rval );
            return ToValue( func( *lint, move( rint ) ) );

        }

        assert( false );


        return PoisonValue();
    }

    template< typename F >
    optional< Value > VM::executeBinOp( const cir::BinaryOp& bo, F&& func )
    {




        if( bo.lhs().isPoison() || bo.rhs().isPoison() )
            return PoisonValue();

        assert( bo.lhs().type() == bo.rhs().type() );
        if( bo.lhs().type() != bo.rhs().type() )
            return PoisonValue();




        auto lval = Evaluate( bo.lhs(), *this );
        if( lval.isPoison()  )
            return PoisonValue();
        if( !lval.isConstant() )
            return nullopt;

        auto rval = Evaluate( bo.rhs(), *this );
        if( rval.isPoison()  )
            return PoisonValue();
        if( !rval.isConstant() )
            return nullopt;

        if( auto lint = FromValue< APSInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );
            return ToValue( func( *lint, move( rint ) ) );

        }

        if( auto lint = FromValue< BigInt >( lval ) )
        {
            const auto& rint = *FromValue< BigInt >( rval );
            return ToValue( func( *lint, rint ) );

        }

        assert( false );


        return PoisonValue();
    }

    template< typename F >
    optional< Value > VM::executeShiftBinOp( const cir::BinaryOp& bo, F&& func )
    {




        if( bo.lhs().isPoison() || bo.rhs().isPoison() )
            return PoisonValue();

        auto lval = Evaluate( bo.lhs(), *this );
         if( lval.isPoison()  )
            return PoisonValue();
        if( !lval.isConstant() )
            return nullopt;

        auto rval = Evaluate( bo.rhs(), *this );
        if( rval.isPoison()  )
            return PoisonValue();
        if( !rval.isConstant() )
            return nullopt;

        if( auto lint = FromValue< APSInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );
            return ToValue( APSInt( func( *lint, move( rint ) ), lint->isUnsigned() ) );

        }

        if( auto lint = FromValue< BigInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );
            return ToValue( BigInt( func( *lint, move( rint ) ) ) );

        }

        assert( false );


        return PoisonValue();
    }
}

#endif






|

>
>
>
>
|
|

|
|
|

|

|

|

|

|

|


>
|
>
|
>



|
>





|
>





|
>





|
>


|
>
>
|



|

>
>
>
>
|
|

|
|
|

|

|

|

|

|

|


>
|
>
|
>



|
>





|
>


|
>
>
|



|

>
>
>
>
|
|

<
<
<
>
>
>

|

|

|

|

|

|




|
>





|
>


|
>
>
|



|

>
>
>
>
|
|

|

|

|

|

|

|




|
>





|
>


|
>
>
|




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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#ifndef GOOSE_EXECUTE_BINARYOPS_INL
#define GOOSE_EXECUTE_BINARYOPS_INL

namespace goose::execute
{
    template< typename F >
    bool VM::executeEqualityBinOp( const cir::BinaryOp& bo, F&& func )
    {
        auto rhs = pop();
        auto lhs = pop();
        if( !lhs || !rhs )
            return false;
        if( lhs->isPoison() || rhs->isPoison() )
            return false;

        assert( lhs->type() == rhs->type() );
        if( lhs->type() != rhs->type() )
            return false;

        auto lval = Evaluate( *lhs, *this );
        if( lval.isPoison() )
            return false;
        if( !lval.isConstant() )
            return false;

        auto rval = Evaluate( *rhs, *this );
        if( rval.isPoison()  )
            return false;
        if( !rval.isConstant() )
            return false;

        if( auto lbool = FromValue< bool >( lval ) )
        {
            push( ToValue( func( *lbool, *FromValue< bool >( rval ) ) ) );
            return true;
        }

        if( auto lint = FromValue< APSInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );
            push( ToValue( func( *lint, move( rint ) ) ) );
            return true;
        }

        if( auto lint = FromValue< BigInt >( lval ) )
        {
            auto rint = *FromValue< BigInt >( rval );
            push( ToValue( func( *lint, move( rint ) ) ) );
            return true;
        }

        if( auto lc = FromValue< char32_t >( lval ) )
        {
            auto rc = *FromValue< char32_t >( rval );
            push( ToValue( func( *lc, rc ) ) );
            return true;
        }

        if( auto lstr = FromValue< string >( lval ) )
        {
            const auto& rstr = *FromValue< string >( rval );
            push( ToValue( func( *lstr, rstr ) ) );
            return true;
        }

        G_TRACE_VAL( lval )
        G_TRACE_VAL( rval )
        G_ERROR( "binop execution failure")
        return false;
    }

    template< typename F >
    bool VM::executeLogicBinOp( const cir::BinaryOp& bo, F&& func )
    {
        auto rhs = pop();
        auto lhs = pop();
        if( !lhs || !rhs )
            return false;
        if( lhs->isPoison() || rhs->isPoison() )
            return false;

        assert( lhs->type() == rhs->type() );
        if( lhs->type() != rhs->type() )
            return false;

        auto lval = Evaluate( *lhs, *this );
        if( lval.isPoison()  )
            return false;
        if( !lval.isConstant() )
            return false;

        auto rval = Evaluate( *rhs, *this );
        if( rval.isPoison()  )
            return false;
        if( !rval.isConstant() )
            return false;

        if( auto lbool = FromValue< bool >( lval ) )
        {
            push( ToValue( func( *lbool, *FromValue< bool >( rval ) ) ) );
            return true;
        }

        if( auto lint = FromValue< APSInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );
            push( ToValue( func( *lint, move( rint ) ) ) );
            return true;
        }

        if( auto lint = FromValue< BigInt >( lval ) )
        {
            auto rint = *FromValue< BigInt >( rval );
            push( ToValue( func( *lint, move( rint ) ) ) );
            return true;
        }

        G_TRACE_VAL( lval )
        G_TRACE_VAL( rval )
        G_ERROR( "binop execution failure")
        return false;
    }

    template< typename F >
    bool VM::executeBinOp( const cir::BinaryOp& bo, F&& func )
    {
        auto rhs = pop();
        auto lhs = pop();
        if( !lhs || !rhs )
            return false;
        if( lhs->isPoison() || rhs->isPoison() )
            return false;




        assert( lhs->type() == rhs->type() );
        if( lhs->type() != rhs->type() )
            return false;

        auto lval = Evaluate( *lhs, *this );
        if( lval.isPoison()  )
            return false;
        if( !lval.isConstant() )
            return false;

        auto rval = Evaluate( *rhs, *this );
        if( rval.isPoison()  )
            return false;
        if( !rval.isConstant() )
            return false;

        if( auto lint = FromValue< APSInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );
            push( ToValue( func( *lint, move( rint ) ) ) );
            return true;
        }

        if( auto lint = FromValue< BigInt >( lval ) )
        {
            const auto& rint = *FromValue< BigInt >( rval );
            push( ToValue( func( *lint, rint ) ) );
            return true;
        }

        G_TRACE_VAL( lval )
        G_TRACE_VAL( rval )
        G_ERROR( "binop execution failure")
        return false;
    }

    template< typename F >
    bool VM::executeShiftBinOp( const cir::BinaryOp& bo, F&& func )
    {
        auto rhs = pop();
        auto lhs = pop();
        if( !lhs || !rhs )
            return false;
        if( lhs->isPoison() || rhs->isPoison() )
            return false;

        auto lval = Evaluate( *lhs, *this );
         if( lval.isPoison()  )
            return false;
        if( !lval.isConstant() )
            return false;

        auto rval = Evaluate( *rhs, *this );
        if( rval.isPoison()  )
            return false;
        if( !rval.isConstant() )
            return false;

        if( auto lint = FromValue< APSInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );
            push( ToValue( APSInt( func( *lint, move( rint ) ), lint->isUnsigned() ) ) );
            return true;
        }

        if( auto lint = FromValue< BigInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );
            push( ToValue( BigInt( func( *lint, move( rint ) ) ) ) );
            return true;
        }

        G_TRACE_VAL( lval )
        G_TRACE_VAL( rval )
        G_ERROR( "binop execution failure")
        return false;
    }
}

#endif
Changes to bs/execute/eval.cpp.
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

        if( !v.isConstant() )
        {
            const auto& cir = val.cir();
            if( !cir )
                return v.setPoison();

            auto result = vm.execute( *cir );

            // Execution may fail: there are some cases when we can't really
            // be sure that eager evaluation is possible until we actually try.
            // In this case we forget about the eager evaluation and return the
            // value as is.



            // Not returning a value isn't a failure, for instance
            // if the value we just evaluated was a call to a function returning void.
            // So we return a void typed value in this case, otherwise the original
            // value might get evaluated again. And since void function are usually
            // called for their side effects, we don't want to evaluate them twice.

            if( !result )
                return v;

            if( result->isPoison() )
                return *result;

            v = move( *result );







<
<




>
>






>







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

        if( !v.isConstant() )
        {
            const auto& cir = val.cir();
            if( !cir )
                return v.setPoison();



            // Execution may fail: there are some cases when we can't really
            // be sure that eager evaluation is possible until we actually try.
            // In this case we forget about the eager evaluation and return the
            // value as is.
            if( !vm.execute( *cir ) )
                return v;

            // Not returning a value isn't a failure, for instance
            // if the value we just evaluated was a call to a function returning void.
            // So we return a void typed value in this case, otherwise the original
            // value might get evaluated again. And since void function are usually
            // called for their side effects, we don't want to evaluate them twice.
            auto result = vm.pop();
            if( !result )
                return v;

            if( result->isPoison() )
                return *result;

            v = move( *result );
Changes to bs/execute/vm.cpp.
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
28
29
30
31
32


33
34
35











36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54




55
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

114
115
116
117
118

119
120
121

122
123
124
125

126
127
128
129
130
131
132
133
134



135

136



137

138


139
140


141
142
143
144
145
146
147
148
149






150
151
152
153
154
155
156
157
158
159

160
161
162
163




164
165
166
167
168
169
170
171
172
173
174

175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201

202

203
204







205
206




207
208
209
210
211
212
213
214
215
216
217
218
219
220
221

222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245


246


247

248
249








250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270


271
272
273
274
275
276

277
278
279
280
281
282
283
284
285
286
287

288
289
290
291
292
293
294
295
296
297
298
299
300
301

302
303
304
305
306
307
308
309
310





311
312
313

314
315
316

317
318
319
320
321
322
323
#include "execute.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::execute;
using namespace goose::builtins;

uint32_t VM::ms_remainingBranchInstExecutions = 1 << 24;




























optional< Value > VM::execute( CFG& cfg )
{

    return execute( cfg.entryBB() );


}

optional< Value > VM::execute( ptr< BasicBlock > bb )
{
    m_retVal = nullopt;
    auto pbbBackup = m_pPreviousBB;

    while( bb )
    {




        for( auto&& instr : *bb )

            execute( instr );



        if( !bb->terminator() )
            break;

        m_pPreviousBB = bb;
        bb = executeTerminator( *bb->terminator() );
    }

    m_pPreviousBB = pbbBackup;


    return m_retVal;
}












optional< Value > VM::execute( const cir::Instruction& instr )
{
    return visit( [&]( auto&& e )
    {
        return execute( e );
    }, instr.content() );
}

optional< Value > VM::execute( const cir::Call& call )
{
    if( !( ms_remainingBranchInstExecutions ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "execute: compilation time execution budget exceeded." );
        return PoisonValue();
    }

    --ms_remainingBranchInstExecutions;





    if( call.func().isPoison() )
        DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );

    auto func = Evaluate( call.func(), *this );
    if( func.isPoison() )
        return PoisonValue();

    if( !func.isConstant() )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "execute: function evaluation failed." );
        return PoisonValue();
    }

    if( IsExternalFunc( func ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "execute: can't call external functions." );
        return PoisonValue();
    }

    bool poisoned = false;
    const auto& vec = *get< pvec >( call.args() );

    if( IsBuiltinFunc( func ) )
    {


        auto newVec = vec.transform( [&]( auto&& x ) -> optional< Term >

        {
            auto val = EIRToValue( x );
            assert( val );


            auto newVal = Evaluate( *val, *this );
            if( newVal.isPoison() )
                poisoned = true;

            // This is not an error, it just mean we can't eagerly
            // evaluate this call. So we shouldn't return poison here,
            // but simply no result.
            if( !newVal.isConstant() )
                return nullopt;

            return ValueToEIR( newVal );
        } );

        if( poisoned )
            return PoisonValue();


        if( !newVec )

            return nullopt;

        return ExecuteBuiltinFuncCall( func, TERM( newVec ) );
    }

    const auto* pFunc = GetFuncCIR( func );

    if( !pFunc || !pFunc->isValid() )
        return PoisonValue();

    auto savedStackSize = m_stack.size();


    for( auto&& a : vec.terms() )
    {
        auto val = EIRToValue( a );
        assert( val );


        auto newVal = Evaluate( *val, *this );
        if( newVal.isPoison()  )

        {
            m_stack.resize( savedStackSize );
            return PoisonValue();
        }


        if( !newVal.isConstant() )
        {
            m_stack.resize( savedStackSize );
            return nullopt;
        }

        m_stack.emplace_back( make_shared< Term >( ValueToEIR( newVal ) ) );
    }





    swap( m_currentFrameStart, savedStackSize );



    auto result = execute( *pFunc->body() );

    swap( m_currentFrameStart, savedStackSize );



    m_stack.resize( savedStackSize );


    return result;
}

optional< Value > VM::ExecuteBuiltinFuncCall( const Value& func, const Term& args )
{
    const auto& f = GetBuiltinFuncWrapper( func );
    return f( args );
}







optional< Value > VM::execute( const cir::VarAddr& va )
{
    auto stackIndex = m_currentFrameStart + va.varIndex();
    if( stackIndex >= m_stack.size() )
        return nullopt;

    if( !m_stack[stackIndex] )
        return nullopt;

    return ToValue( m_stack[stackIndex].get() );

}

optional< Value > VM::execute( const cir::TempAddr& ta )
{




    auto stackIndex = m_currentFrameStart + ta.tempIndex();
    if( m_stack.size() <= stackIndex )
        m_stack.resize( stackIndex + 1 );

    if( stackIndex >= m_stack.size() )
        return nullopt;

    if( !m_stack[stackIndex] )
        m_stack[stackIndex] = make_shared< Term >( ValueToEIR( Evaluate( ta.initValue(), *this ) ) );

    return ToValue( m_stack[stackIndex].get() );

}

optional< Value > VM::execute( const cir::Select& s )
{
    auto baseAddr = execute( *s.baseAddr() );
    if( !baseAddr || baseAddr->isPoison() )
        return nullopt;

    auto pvoid = FromValue< Term* >( *baseAddr );
    if( !pvoid )
        return nullopt;

    auto val = EIRToValue( *static_cast< Term* >( pvoid ) );
    if( !val )
        return nullopt;

    // We only support tuples now. In the future, Select will also be used to
    // add an offset to another pointer.
    // Everything else (structs, classes, containers, etc.) should build on top
    // of those two fundamental types.
    if( !IsTuple( *val ) )
    {
        cout << "execute: select: value is not a tuple\n";
        return nullopt;
    }

    assert( val->isConstant() );

    return ToValue( &GetTupleElement( *val, s.memberIndex() ) );

}








optional< Value > VM::execute( const cir::CreateTemporary& ct )
{




    auto stackIndex = m_currentFrameStart + ct.index();
    if( m_stack.size() <= stackIndex )
        m_stack.resize( stackIndex + 1 );

    m_stack[stackIndex] = make_shared< Term >( ValueToEIR( Evaluate( ct.value(), *this ) ) );
    return nullopt;
}

optional< Value > VM::execute( const cir::GetTemporary& gt )
{
    auto stackIndex = gt.index() + m_currentFrameStart;
    if( stackIndex >= m_stack.size() )
        return nullopt;

    return EIRToValue( *m_stack[stackIndex] );

}

optional< Value > VM::execute( const cir::AllocVar& av )
{
    auto stackIndex = m_currentFrameStart + av.index();

    if( m_stack.size() <= stackIndex )
        m_stack.resize( stackIndex + 1 );

    m_stack[stackIndex] = BuildUninitializedValue( av.type() );
    return nullopt;
}

optional< Value > VM::execute( const cir::Load& l )
{
    auto baseAddr = execute( *l.addr() );
    if( !baseAddr || baseAddr->isPoison() )
        return nullopt;

    auto addr = FromValue< Term* >( *baseAddr );
    if( !addr )
        return nullopt;

    return EIRToValue( *addr );


}




optional< Value > VM::execute( const cir::Store& s )
{








    auto baseAddr = execute( *s.addr() );
    if( !baseAddr || baseAddr->isPoison() )
        return nullopt;

    auto addr = FromValue< Term* >( *baseAddr );
    if( !addr )
        return nullopt;

    auto result = Evaluate( s.val(), *this );
    if( !result.isConstant() )
        return PoisonValue();

    *addr = ValueToEIR( result );
    return nullopt;
}

optional< Value > VM::execute( const cir::Phi& p )
{
    auto stackIndex = m_currentFrameStart + p.destIndex();
    if( m_stack.size() <= stackIndex )
        m_stack.resize( stackIndex + 1 );



    p.forAllIncomings( [&]( auto&& bb, auto&& val )
    {
        if( bb == m_pPreviousBB )
        {
            m_stack[stackIndex] = make_shared< Term >( ValueToEIR( Evaluate( val, *this ) ) );

            return false;
        }

        return true;
    } );

    return PoisonValue();
}

optional< Value > VM::execute( const cir::Not& uo )
{

    if( uo.operand().isPoison() )
        return PoisonValue();

    auto opval = Evaluate( uo.operand(), *this );
    if( opval.isPoison() )
        return PoisonValue();
    if( !opval.isConstant() )
        return nullopt;

    auto boolVal = FromValue< bool >( opval );
    if( !boolVal )
        return PoisonValue();

    return ToValue( !*boolVal );

}

ptr< BasicBlock > VM::executeTerminator( const cir::Terminator& terminator )
{
    return visit( [&]( auto&& e )
    {
        return executeTerminator( e );
    }, terminator.content() );
}






ptr< BasicBlock > VM::executeTerminator( const cir::Ret& r )
{

    if( r.value() )
        m_retVal = Evaluate( *r.value(), *this );


    return nullptr;
}

ptr< BasicBlock > VM::executeTerminator( const cir::Branch& b )
{
    if( !( ms_remainingBranchInstExecutions ) )
    {









>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


>
|
>
>


|






>
>
>
>
|
>
|
>
>









>
>
|


>
>
>
>
>
>
>
>
>
>
>
|







|





|




>
>
>
>
|


|

|





|



<
<
<
|
|
|
<
<



>
>
|
>

|
|
>



|

<
<
<

|

|
<
|
<
<

>
|
>
|
<
<





|

|
>

|

|
|
>



>
|
|
|
|
>
|
<
|
<
|
|
|
|
|
>
>
>

>
|
>
>
>
|
>
|
>
>
|
|
>
>
|








>
>
>
>
>
>
|



|


|

|
>


|

>
>
>
>

<
<


|


|

|
>


|

|
|
|



|



|







|
|



>
|
>


>
>
>
>
>
>
>
|

>
>
>
>




|
|


|



|

|
>


|







|


|

|
|
|



|

|
>
>
|
>
>
|
>
|

>
>
>
>
>
>
>
>
|
|
|



|

<
<
<
<

|


|




>
>






>






|


|

>
|
|

|

|

|



|

|
>









>
>
>
>
>



>
|
|

>







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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
114
115
116
117
118
119
120
121
122
123



124
125
126


127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142



143
144
145
146

147


148
149
150
151
152


153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177

178

179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236


237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355




356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
#include "execute.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::execute;
using namespace goose::builtins;

uint32_t VM::ms_remainingBranchInstExecutions = 1 << 24;

VM::StackFrameHelper::StackFrameHelper( VM& vmIn, CFG& cfg, bool shouldActivate ) :
    vm( vmIn )
{
    savedStackSize = vm.m_stack.size();
    savedFrameStart = vm.m_currentFrameStart;

    // Reserve stack space for the temporaries + some more for local scratch space
    // The stack will be extended as necessary if that's not enough
    // (TODO define that as a constant somewhere)
    vm.m_stack.reserve( vm.m_stack.size() + cfg.temporariesCount() + 16 );
    vm.m_stack.resize( vm.m_stack.size() + cfg.temporariesCount() );

    if( shouldActivate )
        activate();
}

void VM::StackFrameHelper::activate()
{
    vm.m_currentFrameStart = savedStackSize;
}

VM::StackFrameHelper::~StackFrameHelper()
{
    vm.m_stack.resize( savedStackSize );
    vm.m_currentFrameStart = savedFrameStart;
}

optional< Value > VM::execute( CFG& cfg )
{
    StackFrameHelper sfh( *this, cfg );
    if( !execute( cfg.entryBB() ) )
        return nullopt;
    return pop();
}

bool VM::execute( ptr< BasicBlock > bb )
{
    m_retVal = nullopt;
    auto pbbBackup = m_pPreviousBB;

    while( bb )
    {
        const auto* pInstrs = bb->runnableInstructions();
        if( !pInstrs )
            return false;

        for( auto&& instr : *pInstrs )
        {
            if( !execute( instr ) )
                return false;
        }

        if( !bb->terminator() )
            break;

        m_pPreviousBB = bb;
        bb = executeTerminator( *bb->terminator() );
    }

    m_pPreviousBB = pbbBackup;
    if( m_retVal )
        push( *m_retVal );
    return true;
}

bool VM::execute( const cir::InstrSeq& is )
{
    for( const auto& instr : is )
    {
        if( !execute( instr ) )
            return false;
    }

    return true;
}

bool VM::execute( const cir::Instruction& instr )
{
    return visit( [&]( auto&& e )
    {
        return execute( e );
    }, instr.content() );
}

bool VM::execute( const cir::Call& call )
{
    if( !( ms_remainingBranchInstExecutions ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "execute: compilation time execution budget exceeded." );
        return false;
    }

    --ms_remainingBranchInstExecutions;

    auto callFunc = pop();
    if( !callFunc )
        return false;

    if( callFunc->isPoison() )
        DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );

    auto func = Evaluate( *callFunc, *this );
    if( func.isPoison() )
        return false;

    if( !func.isConstant() )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "execute: function evaluation failed." );
        return false;
    }

    if( IsExternalFunc( func ) )



        return false;

    auto argCount = call.numArgs();



    if( IsBuiltinFunc( func ) )
    {
        auto newVec = make_shared< Vector >();
        newVec->resize( argCount );

        for( uint32_t argIndex = 0; argIndex < argCount; ++argIndex )
        {
            auto val = pop();
            if( !val )
                return false;

            auto newVal = Evaluate( *val, *this );
            if( newVal.isPoison() )
                return false;




            if( !newVal.isConstant() )
                return false;

            newVec->set( argCount - argIndex - 1, ValueToEIR( newVal ) );

        }



        auto result = ExecuteBuiltinFuncCall( func, TERM( newVec ) );
        if( result )
            push( *result );
        return true;


    }

    const auto* pFunc = GetFuncCIR( func );

    if( !pFunc || !pFunc->isValid() )
        return false;

    // Pop and eval all args before creating our stackframe
    llvm::SmallVector< Term, 8 > evaluatedArgs( argCount, {} );

    for( uint32_t argIndex = 0; argIndex < argCount; ++argIndex )
    {
        auto val = pop();
        if( !val )
            return false;

        auto newVal = Evaluate( *val, *this );
        if( newVal.isPoison()  )
            return false;

        if( !newVal.isConstant() )
            return false;

        evaluatedArgs[argCount - argIndex - 1] = ValueToEIR( newVal );
    }



    optional< Value > result;

    {
        StackFrameHelper sfh( *this, *pFunc->body(), false );

        uint32_t argIndex = 0;
        for( auto&& a : evaluatedArgs )
            sfh.set( argIndex++, make_shared< Term >( move( a ) ) );

        sfh.activate();

        // Directly execute the entry BB rather than the CFG: we already have setup the stack frame
        // execute( CFG ) is intended for situations where we need to execute a CFG directly without
        // it being part of a function
        if( !execute( pFunc->body()->entryBB() ) )
            return false;

        if( *GetFuncRType( func ) != GetValueType< void >() )
            result = pop();
    }

    if( result )
        push( *result );
    return true;
}

optional< Value > VM::ExecuteBuiltinFuncCall( const Value& func, const Term& args )
{
    const auto& f = GetBuiltinFuncWrapper( func );
    return f( args );
}

bool VM::execute( const cir::Constant& cst )
{
    push( cst.value() );
    return true;
}

bool VM::execute( const cir::VarAddr& va )
{
    auto stackIndex = m_currentFrameStart + va.varIndex();
    if( stackIndex >= m_stack.size() )
        return false;

    if( !m_stack[stackIndex] )
        return false;

    push( ToValue( m_stack[stackIndex].get() ) );
    return true;
}

bool VM::execute( const cir::TempAddr& ta )
{
    auto initVal = pop();
    if( !initVal )
        return false;

    auto stackIndex = m_currentFrameStart + ta.tempIndex();



    if( stackIndex >= m_stack.size() )
        return false;

    if( !m_stack[stackIndex] )
        m_stack[stackIndex] = make_shared< Term >( ValueToEIR( Evaluate( *initVal, *this ) ) );

    push( ToValue( m_stack[stackIndex].get() ) );
    return true;
}

bool VM::execute( const cir::Select& s )
{
    auto baseAddr = pop();
    if( !baseAddr )
        return false;

    auto pvoid = FromValue< Term* >( *baseAddr );
    if( !pvoid )
        return false;

    auto val = EIRToValue( *static_cast< Term* >( pvoid ) );
    if( !val )
        return false;

    // We only support tuples now. In the future, Select will also be used to
    // add an offset to another pointer.
    // Everything else (structs, classes, containers, etc.) should build on top
    // of those two fundamental types.
    if( !IsTuple( *val ) )
    {
        G_VAL_ERROR( *val, "execute: select: value is not a tuple" );
        return false;
    }

    assert( val->isConstant() );

    push( ToValue( &GetTupleElement( *val, s.memberIndex() ) ) );
    return true;
}

// TODO do we still need to handle this here now that the CFG tells us in advance how many slots to reserve?
// (we could return the uninitialized value in Load if the slot ptr is null)
// Initialization could be explicit with a store, like wtf did I do that
// only used by shortcut eval operators btw
//  -> oh yeah I remember its because I don't want to actually emit stores for these in llvm. fuck
// ok maybe leave this one be but it doesn't need to extend the stack, it can just check if its big enough
// and fuck off if it isn't, just so that invalid CIR code emited by user extensions doesn't crash
bool VM::execute( const cir::CreateTemporary& ct )
{
    auto val = pop();
    if( !val )
        return false;

    auto stackIndex = m_currentFrameStart + ct.index();
    if( m_stack.size() <= stackIndex )
        m_stack.resize( stackIndex + 1 );

    m_stack[stackIndex] = make_shared< Term >( ValueToEIR( Evaluate( *val, *this ) ) );
    return true;
}

bool VM::execute( const cir::GetTemporary& gt )
{
    auto stackIndex = gt.index() + m_currentFrameStart;
    if( stackIndex >= m_stack.size() )
        return false;

    push( *EIRToValue( *m_stack[stackIndex] ) );
    return true;
}

bool VM::execute( const cir::AllocVar& av )
{
    auto stackIndex = m_currentFrameStart + av.index();

    if( m_stack.size() <= stackIndex )
        m_stack.resize( stackIndex + 1 );

    m_stack[stackIndex] = BuildUninitializedValue( av.type() );
    return true;
}

bool VM::execute( const cir::Load& l )
{
    auto baseAddr = pop();
    if( !baseAddr )
        return false;

    auto addr = FromValue< Term* >( *baseAddr );
    if( !addr )
        return false;

    auto val = EIRToValue( *addr );
    if( !val )
        return false;

    push( *EIRToValue( *addr ) );
    return true;
}

bool VM::execute( const cir::Store& s )
{
    auto val = pop();
    if( !val )
        return false;

    auto result = Evaluate( *val, *this );
    if( !result.isConstant() )
        return false;

    auto baseAddr = pop();
    if( !baseAddr )
        return false;

    auto addr = FromValue< Term* >( *baseAddr );
    if( !addr )
        return false;





    *addr = ValueToEIR( result );
    return true;
}

bool VM::execute( const cir::Phi& p )
{
    auto stackIndex = m_currentFrameStart + p.destIndex();
    if( m_stack.size() <= stackIndex )
        m_stack.resize( stackIndex + 1 );

    bool success = false;

    p.forAllIncomings( [&]( auto&& bb, auto&& val )
    {
        if( bb == m_pPreviousBB )
        {
            m_stack[stackIndex] = make_shared< Term >( ValueToEIR( Evaluate( val, *this ) ) );
            success = true;
            return false;
        }

        return true;
    } );

    return success;
}

bool VM::execute( const cir::Not& uo )
{
    auto operand = pop();
    if( !operand || operand->isPoison() )
        return false;

    auto opval = Evaluate( *operand, *this );
    if( opval.isPoison() )
        return false;
    if( !opval.isConstant() )
        return false;

    auto boolVal = FromValue< bool >( opval );
    if( !boolVal )
        return false;

    push( ToValue( !*boolVal ) );
    return true;
}

ptr< BasicBlock > VM::executeTerminator( const cir::Terminator& terminator )
{
    return visit( [&]( auto&& e )
    {
        return executeTerminator( e );
    }, terminator.content() );
}

ptr< BasicBlock > VM::executeTerminator( const cir::RetVoid& r )
{
    return nullptr;
}

ptr< BasicBlock > VM::executeTerminator( const cir::Ret& r )
{
    auto retVal = pop();
    if( !retVal )
        return nullptr;

    m_retVal = Evaluate( *retVal, *this );
    return nullptr;
}

ptr< BasicBlock > VM::executeTerminator( const cir::Branch& b )
{
    if( !( ms_remainingBranchInstExecutions ) )
    {
339
340
341
342
343
344
345




346
347
348
349
350
351
352
353
354
355
356
357
358
359
            "execute: compilation time execution budget exceeded." );
        m_retVal = PoisonValue();
        return nullptr;
    }

    --ms_remainingBranchInstExecutions;





    auto cond = Evaluate( cb.cond(), *this );
    if( cond.isPoison() )
        return nullptr;

    if( !cond.isConstant() )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( cb.cond().locationId(),
            "execute: branch condition evaluation failed." );
        m_retVal = PoisonValue();
        return nullptr;
    }

    if( *FromValue< bool >( cond ) )
        return cb.trueDest().lock();







>
>
>
>
|





|







445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
            "execute: compilation time execution budget exceeded." );
        m_retVal = PoisonValue();
        return nullptr;
    }

    --ms_remainingBranchInstExecutions;

    auto condVal = pop();
    if( !condVal )
        return nullptr;

    auto cond = Evaluate( *condVal, *this );
    if( cond.isPoison() )
        return nullptr;

    if( !cond.isConstant() )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( condVal->locationId(),
            "execute: branch condition evaluation failed." );
        m_retVal = PoisonValue();
        return nullptr;
    }

    if( *FromValue< bool >( cond ) )
        return cb.trueDest().lock();
Changes to bs/execute/vm.h.
10
11
12
13
14
15
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
44
45
46
47
48
49
50
51
52
53
54
55
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
        public:
            static void SetExecutionBudget( uint32_t b )
            {
                ms_remainingBranchInstExecutions = b;
            }

            optional< Value > execute( CFG& cfg );

            optional< Value > execute( ptr< BasicBlock > bb );

            optional< Value > execute( const cir::Instruction& instr );
            optional< Value > execute( const cir::Call& call );

            optional< Value > execute( const cir::VarAddr& va );
            optional< Value > execute( const cir::TempAddr& ta );
            optional< Value > execute( const cir::Select& s );
            optional< Value > execute( const cir::CreateTemporary& ct );
            optional< Value > execute( const cir::GetTemporary& gt );
            optional< Value > execute( const cir::AllocVar& av );
            optional< Value > execute( const cir::Load& l );
            optional< Value > execute( const cir::Store& s );
            optional< Value > execute( const cir::Phi& p );

            optional< Value > execute( const cir::Not& uo );
            optional< Value > execute( const cir::And& bo );
            optional< Value > execute( const cir::Or& bo );
            optional< Value > execute( const cir::Xor& bo );
            optional< Value > execute( const cir::Shl& bo );
            optional< Value > execute( const cir::LShr& bo );
            optional< Value > execute( const cir::AShr& bo );

            optional< Value > execute( const cir::Add& bo );
            optional< Value > execute( const cir::Sub& bo );
            optional< Value > execute( const cir::Mul& bo );
            optional< Value > execute( const cir::UDiv& bo );
            optional< Value > execute( const cir::SDiv& bo );
            optional< Value > execute( const cir::URem& bo );
            optional< Value > execute( const cir::SRem& bo );

            optional< Value > execute( const cir::Eq& bo );
            optional< Value > execute( const cir::Neq& bo );
            optional< Value > execute( const cir::UGT& bo );
            optional< Value > execute( const cir::UGE& bo );
            optional< Value > execute( const cir::ULT& bo );
            optional< Value > execute( const cir::ULE& bo );
            optional< Value > execute( const cir::SGT& bo );
            optional< Value > execute( const cir::SGE& bo );
            optional< Value > execute( const cir::SLT& bo );
            optional< Value > execute( const cir::SLE& bo );

            template< typename T >
            optional< Value > execute( const T& )
            {
                return PoisonValue();
            }

            ptr< BasicBlock > executeTerminator( const cir::Terminator& terminator );

            ptr< BasicBlock > executeTerminator( const cir::Ret& r );
            ptr< BasicBlock > executeTerminator( const cir::Branch& b );
            ptr< BasicBlock > executeTerminator( const cir::CondBranch& cb );
            ptr< BasicBlock > executeTerminator( const cir::GhostBranch& gb );

            template< typename T >
            ptr< BasicBlock > executeTerminator( const T& )
            {
                return nullptr;
            }


















        private:


















            static optional< Value > ExecuteBuiltinFuncCall( const Value& func, const Term& args );
            static ptr< Term > BuildUninitializedValue( const Value& type );

            template< typename F >
            optional< Value > executeEqualityBinOp( const cir::BinaryOp& bo, F&& func );

            template< typename F >
            optional< Value > executeLogicBinOp( const cir::BinaryOp& bo, F&& func );

            template< typename F >
            optional< Value > executeBinOp( const cir::BinaryOp& bo, F&& func );

            template< typename F >
            optional< Value > executeShiftBinOp( const cir::BinaryOp& bo, F&& func );

            llvm::SmallVector< ptr< Term >, 8 > m_stack;
            optional< Value > m_retVal;
            size_t m_currentFrameStart = 0;

            // Used to interpret Phi instructions.
            ptr< BasicBlock > m_pPreviousBB;







>
|
|
|
|
>
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|

|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|


|

|



>











>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




|


|


|


|







10
11
12
13
14
15
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
44
45
46
47
48
49
50
51
52
53
54
55
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
        public:
            static void SetExecutionBudget( uint32_t b )
            {
                ms_remainingBranchInstExecutions = b;
            }

            optional< Value > execute( CFG& cfg );

            bool execute( ptr< BasicBlock > bb );
            bool execute( const cir::InstrSeq& is );
            bool execute( const cir::Instruction& instr );
            bool execute( const cir::Call& call );
            bool execute( const cir::Constant& cst );
            bool execute( const cir::VarAddr& va );
            bool execute( const cir::TempAddr& ta );
            bool execute( const cir::Select& s );
            bool execute( const cir::CreateTemporary& ct );
            bool execute( const cir::GetTemporary& gt );
            bool execute( const cir::AllocVar& av );
            bool execute( const cir::Load& l );
            bool execute( const cir::Store& s );
            bool execute( const cir::Phi& p );

            bool execute( const cir::Not& uo );
            bool execute( const cir::And& bo );
            bool execute( const cir::Or& bo );
            bool execute( const cir::Xor& bo );
            bool execute( const cir::Shl& bo );
            bool execute( const cir::LShr& bo );
            bool execute( const cir::AShr& bo );

            bool execute( const cir::Add& bo );
            bool execute( const cir::Sub& bo );
            bool execute( const cir::Mul& bo );
            bool execute( const cir::UDiv& bo );
            bool execute( const cir::SDiv& bo );
            bool execute( const cir::URem& bo );
            bool execute( const cir::SRem& bo );

            bool execute( const cir::Eq& bo );
            bool execute( const cir::Neq& bo );
            bool execute( const cir::UGT& bo );
            bool execute( const cir::UGE& bo );
            bool execute( const cir::ULT& bo );
            bool execute( const cir::ULE& bo );
            bool execute( const cir::SGT& bo );
            bool execute( const cir::SGE& bo );
            bool execute( const cir::SLT& bo );
            bool execute( const cir::SLE& bo );

            template< typename T >
            bool execute( const T& )
            {
                return false;
            }

            ptr< BasicBlock > executeTerminator( const cir::Terminator& terminator );
            ptr< BasicBlock > executeTerminator( const cir::RetVoid& r );
            ptr< BasicBlock > executeTerminator( const cir::Ret& r );
            ptr< BasicBlock > executeTerminator( const cir::Branch& b );
            ptr< BasicBlock > executeTerminator( const cir::CondBranch& cb );
            ptr< BasicBlock > executeTerminator( const cir::GhostBranch& gb );

            template< typename T >
            ptr< BasicBlock > executeTerminator( const T& )
            {
                return nullptr;
            }

            void push( const Value& v )
            {
                m_stack.emplace_back( make_shared< Term >( ValueToEIR( v ) ) );
            }

            optional< Value > pop()
            {
                if( m_stack.empty() || m_stack.size() <= m_currentFrameStart )
                    return nullopt;

                assert( m_stack.back() );

                auto result = move( *m_stack.back() );
                m_stack.resize( m_stack.size() - 1 );
                return EIRToValue( result );
            }

        private:
            struct StackFrameHelper
            {
                StackFrameHelper( VM& vm, CFG& cfg, bool shouldActivate = true );
                ~StackFrameHelper();

                void activate();

                template< typename T >
                void set( size_t index, T&& val )
                {
                    vm.m_stack[index + savedStackSize] = forward< T >( val );
                }

                size_t savedStackSize = 0;
                size_t savedFrameStart = 0;
                VM& vm;
            };

            static optional< Value > ExecuteBuiltinFuncCall( const Value& func, const Term& args );
            static ptr< Term > BuildUninitializedValue( const Value& type );

            template< typename F >
            bool executeEqualityBinOp( const cir::BinaryOp& bo, F&& func );

            template< typename F >
            bool executeLogicBinOp( const cir::BinaryOp& bo, F&& func );

            template< typename F >
            bool executeBinOp( const cir::BinaryOp& bo, F&& func );

            template< typename F >
            bool executeShiftBinOp( const cir::BinaryOp& bo, F&& func );

            llvm::SmallVector< ptr< Term >, 8 > m_stack;
            optional< Value > m_retVal;
            size_t m_currentFrameStart = 0;

            // Used to interpret Phi instructions.
            ptr< BasicBlock > m_pPreviousBB;
Changes to bs/g0api/compiler.cpp.
49
50
51
52
53
54
55





























56
57
58
59
60
61
62
                    DiagnosticsManager::GetInstance().emitErrorMessage( enable.locationId(),
                        "this doesn't evaluate to a constant." );
                    return;
                }

                verify::Func::SetDumpSolverOnSuccess( *FromValue< bool >( enable ) );
            } );






























        RegisterBuiltinFunc< Intrinsic< void ( uint32_t ) > >( e, "#SetExecutionBudget"_sid,
            []( auto&& c, const Value& budget )
            {
                static bool used = false;

                if( !budget.isConstant() )







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







49
50
51
52
53
54
55
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
                    DiagnosticsManager::GetInstance().emitErrorMessage( enable.locationId(),
                        "this doesn't evaluate to a constant." );
                    return;
                }

                verify::Func::SetDumpSolverOnSuccess( *FromValue< bool >( enable ) );
            } );

        RegisterBuiltinFunc< Intrinsic< void ( CustomPattern< Value, FuncPattern >, string ) > >( e, "#DumpFunctionCFG"_sid,
            []( auto&& c, const Value& funcVal, const Value& filename )
            {
                if( !funcVal.isConstant() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( funcVal.locationId(),
                        "this doesn't evaluate to a constant." );
                    return;
                }

                if( !filename.isConstant() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( filename.locationId(),
                        "this doesn't evaluate to a constant." );
                    return;
                }

                auto name = *FromValue< string >( filename );
                auto func = CompileFunc( c, funcVal );
                if( func.isPoison() || DiagnosticsManager::GetInstance().errorsWereEmitted() )
                    return;

                auto f = *FromValue< builtins::Func >( func );
                if( !f.cir() )
                    return;

                CfgViz( name.c_str(), *f.cir() );
            } );

        RegisterBuiltinFunc< Intrinsic< void ( uint32_t ) > >( e, "#SetExecutionBudget"_sid,
            []( auto&& c, const Value& budget )
            {
                static bool used = false;

                if( !budget.isConstant() )
Changes to bs/g0api/extensibility/cir.cpp.
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
namespace
{
    template< typename C, typename T, typename O, O opCode, typename... Params >
    void RegisterMkOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
    {
        RegisterBuiltinFunc< TypeWrapper< ptr< C > >
            ( ConstantParam< uint8_t, static_cast< uint8_t >( opCode ) >, Params... ) >( e, pOvlSet,
            []( uint8_t, const Params&... params ) -> TypeWrapper< ptr< C > >
            {
                return make_shared< C >( T( WrappedValueAccessor< Params >::Get( params )... ) );
            } );
    }

    template< typename C, typename T, typename O, O opCode, typename... Getters >
    void RegisterUnpackOverload( Env& e, const ptr< OverloadSet >& pOvlSet, Getters... getters )
    {
        using tup_type = tuple< WrapType<
            remove_cvref_t< decltype( ( static_cast< T* >( nullptr )->*getters )() ) > >... >;

        RegisterBuiltinFunc< bool ( ConstantParam< uint8_t, static_cast< uint8_t >( opCode ) >,
            TypeWrapper< ptr< C > >, TermRef< tup_type > ) >( e, pOvlSet,
            [&]( uint8_t, const TypeWrapper< ptr< C > >& instr, TermRef< tup_type >& out )
            {
                const T* pt = get_if< T >( &instr->content() );

                if( !pt )
                    return false;

                out = tup_type( ( pt->*getters )()... );







|













|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
namespace
{
    template< typename C, typename T, typename O, O opCode, typename... Params >
    void RegisterMkOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
    {
        RegisterBuiltinFunc< TypeWrapper< ptr< C > >
            ( ConstantParam< uint8_t, static_cast< uint8_t >( opCode ) >, Params... ) >( e, pOvlSet,
            []( uint8_t, const auto&... params ) -> TypeWrapper< ptr< C > >
            {
                return make_shared< C >( T( WrappedValueAccessor< Params >::Get( params )... ) );
            } );
    }

    template< typename C, typename T, typename O, O opCode, typename... Getters >
    void RegisterUnpackOverload( Env& e, const ptr< OverloadSet >& pOvlSet, Getters... getters )
    {
        using tup_type = tuple< WrapType<
            remove_cvref_t< decltype( ( static_cast< T* >( nullptr )->*getters )() ) > >... >;

        RegisterBuiltinFunc< bool ( ConstantParam< uint8_t, static_cast< uint8_t >( opCode ) >,
            TypeWrapper< ptr< C > >, TermRef< tup_type > ) >( e, pOvlSet,
            [&]( uint8_t, const auto& instr, auto& out )
            {
                const T* pt = get_if< T >( &instr->content() );

                if( !pt )
                    return false;

                out = tup_type( ( pt->*getters )()... );
84
85
86
87
88
89
90
91

92
93
94
95
96
97
98
        SGT,
        SGE,
        SLT,
        SLE,

        Assert,
        Placeholder,
        PHOverride

    };

    template< typename T, InstrOpCode opCode, typename... Params >
    void RegisterMkInstrOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
    {
        RegisterMkOverload< Instruction, T, InstrOpCode, opCode, Params... >( e, pOvlSet );
    }







|
>







84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
        SGT,
        SGE,
        SLT,
        SLE,

        Assert,
        Placeholder,
        PHOverrideSet,
        PHOverrideClear
    };

    template< typename T, InstrOpCode opCode, typename... Params >
    void RegisterMkInstrOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
    {
        RegisterMkOverload< Instruction, T, InstrOpCode, opCode, Params... >( e, pOvlSet );
    }
111
112
113
114
115
116
117
118
119
120
121
122
123

124
125
126
127
128
129
130
            remove_cvref_t< decltype( ( static_cast< T* >( nullptr )->*getters )() ) > >... >( e, MkInstr );
        RegisterUnpackInstrOverload< T, opCode >( e, UnpackInstr );
    }

    template< typename T, InstrOpCode opCode >
    void RegisterBinOpInstrOverloads( Env& e, const ptr< OverloadSet >& MkInstr, const ptr< OverloadSet >& UnpackInstr )
    {
        RegisterInstrOverloads< T, opCode >( e, MkInstr, UnpackInstr, &T::lhs, &T::rhs );
    }

    // This enum must match the order of the types in the Terminator variant.
    enum class TermiOpCode
    {

        Ret,
        Branch,
        CondBranch,
        Break,
        Continue
    };








|





>







112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
            remove_cvref_t< decltype( ( static_cast< T* >( nullptr )->*getters )() ) > >... >( e, MkInstr );
        RegisterUnpackInstrOverload< T, opCode >( e, UnpackInstr );
    }

    template< typename T, InstrOpCode opCode >
    void RegisterBinOpInstrOverloads( Env& e, const ptr< OverloadSet >& MkInstr, const ptr< OverloadSet >& UnpackInstr )
    {
        RegisterInstrOverloads< T, opCode >( e, MkInstr, UnpackInstr, &T::locationId );
    }

    // This enum must match the order of the types in the Terminator variant.
    enum class TermiOpCode
    {
        RetVoid,
        Ret,
        Branch,
        CondBranch,
        Break,
        Continue
    };

191
192
193
194
195
196
197
198

199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
        DefineConstant( e, "InstrOpCodeULE"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::ULE ) ) ) );
        DefineConstant( e, "InstrOpCodeSGT"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SGT ) ) ) );
        DefineConstant( e, "InstrOpCodeSGE"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SGE ) ) ) );
        DefineConstant( e, "InstrOpCodeSLT"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SLT ) ) ) );
        DefineConstant( e, "InstrOpCodeSLE"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SLE ) ) ) );
        DefineConstant( e, "InstrOpCodeAssert"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Assert ) ) ) );
        DefineConstant( e, "InstrOpCodePlaceholder"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Placeholder ) ) ) );
        DefineConstant( e, "InstrOpCodePHOverride"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::PHOverride ) ) ) );


        RegisterBuiltinFunc< uint8_t ( TypeWrapper< ptr< Instruction > > ) >( e, "GetInstrOpCode"_sid,
            []( const TypeWrapper< ptr< Instruction > >& t )
            {
                return t->content().index();
            } );

        // MkInstr and UnpackInstr overloads
        auto MkInstr = CreateOverloadSet( e, "MkInstr"_sid );
        auto UnpackInstr = CreateOverloadSet( e, "UnpackInstr"_sid );

        RegisterInstrOverloads< Call, InstrOpCode::Call >( e, MkInstr, UnpackInstr, &Call::func, &Call::args );
        RegisterInstrOverloads< VarAddr, InstrOpCode::VarAddr >( e, MkInstr, UnpackInstr, &VarAddr::varIndex, &VarAddr::type );
        RegisterInstrOverloads< TempAddr, InstrOpCode::TempAddr >( e, MkInstr, UnpackInstr, &TempAddr::tempIndex, &TempAddr::initValue );
        RegisterInstrOverloads< Select, InstrOpCode::Select >( e, MkInstr, UnpackInstr, &Select::baseAddr, &Select::memberIndex );
        RegisterInstrOverloads< CreateTemporary, InstrOpCode::CreateTemporary >( e, MkInstr, UnpackInstr, &CreateTemporary::index, &CreateTemporary::value );
        RegisterInstrOverloads< GetTemporary, InstrOpCode::GetTemporary >( e, MkInstr, UnpackInstr, &GetTemporary::type, &GetTemporary::index );
        RegisterInstrOverloads< AllocVar, InstrOpCode::AllocVar >( e, MkInstr, UnpackInstr, &AllocVar::type, &AllocVar::index );
        RegisterInstrOverloads< Load, InstrOpCode::Load >( e, MkInstr, UnpackInstr, &Load::addr, &Load::type );
        RegisterInstrOverloads< Store, InstrOpCode::Store >( e, MkInstr, UnpackInstr, &Store::addr, &Store::type, &Store::val, &Store::destLocId );
        RegisterInstrOverloads< Phi, InstrOpCode::Phi >( e, MkInstr, UnpackInstr, &Phi::type, &Phi::numIncomings, &Phi::destIndex );
        RegisterInstrOverloads< Not, InstrOpCode::Not >( e, MkInstr, UnpackInstr, &Not::operand );

        RegisterBinOpInstrOverloads< And, InstrOpCode::And >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< Or, InstrOpCode::Or >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< Xor, InstrOpCode::Xor >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< Implies, InstrOpCode::Implies >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< Shl, InstrOpCode::Shl >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< LShr, InstrOpCode::LShr >( e, MkInstr, UnpackInstr );







|
>


|








|
|
|
|
|
|
|
|
|
|
|







193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
        DefineConstant( e, "InstrOpCodeULE"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::ULE ) ) ) );
        DefineConstant( e, "InstrOpCodeSGT"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SGT ) ) ) );
        DefineConstant( e, "InstrOpCodeSGE"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SGE ) ) ) );
        DefineConstant( e, "InstrOpCodeSLT"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SLT ) ) ) );
        DefineConstant( e, "InstrOpCodeSLE"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SLE ) ) ) );
        DefineConstant( e, "InstrOpCodeAssert"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Assert ) ) ) );
        DefineConstant( e, "InstrOpCodePlaceholder"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Placeholder ) ) ) );
        DefineConstant( e, "InstrOpCodePHOverrideSet"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::PHOverrideSet ) ) ) );
        DefineConstant( e, "InstrOpCodePHOverrideClear"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::PHOverrideClear ) ) ) );

        RegisterBuiltinFunc< uint8_t ( TypeWrapper< ptr< Instruction > > ) >( e, "GetInstrOpCode"_sid,
            []( const auto& t )
            {
                return t->content().index();
            } );

        // MkInstr and UnpackInstr overloads
        auto MkInstr = CreateOverloadSet( e, "MkInstr"_sid );
        auto UnpackInstr = CreateOverloadSet( e, "UnpackInstr"_sid );

        RegisterInstrOverloads< Call, InstrOpCode::Call >( e, MkInstr, UnpackInstr, &Call::numArgs, &Call::locationId );
        RegisterInstrOverloads< VarAddr, InstrOpCode::VarAddr >( e, MkInstr, UnpackInstr, &VarAddr::varIndex, &VarAddr::type, &VarAddr::locationId );
        RegisterInstrOverloads< TempAddr, InstrOpCode::TempAddr >( e, MkInstr, UnpackInstr, &TempAddr::tempIndex, &TempAddr::locationId );
        RegisterInstrOverloads< Select, InstrOpCode::Select >( e, MkInstr, UnpackInstr, &Select::memberIndex, &Select::locationId );
        RegisterInstrOverloads< CreateTemporary, InstrOpCode::CreateTemporary >( e, MkInstr, UnpackInstr, &CreateTemporary::index, &CreateTemporary::locationId );
        RegisterInstrOverloads< GetTemporary, InstrOpCode::GetTemporary >( e, MkInstr, UnpackInstr, &GetTemporary::type, &GetTemporary::index, &GetTemporary::locationId );
        RegisterInstrOverloads< AllocVar, InstrOpCode::AllocVar >( e, MkInstr, UnpackInstr, &AllocVar::type, &AllocVar::index, &AllocVar::locationId );
        RegisterInstrOverloads< Load, InstrOpCode::Load >( e, MkInstr, UnpackInstr, &Load::type, &Load::locationId );
        RegisterInstrOverloads< Store, InstrOpCode::Store >( e, MkInstr, UnpackInstr, &Store::type, &Store::srcLocId, &Store::destLocId );
        RegisterInstrOverloads< Phi, InstrOpCode::Phi >( e, MkInstr, UnpackInstr, &Phi::type, &Phi::numIncomings, &Phi::destIndex, &Phi::locationId );
        RegisterInstrOverloads< Not, InstrOpCode::Not >( e, MkInstr, UnpackInstr, &Not::locationId );

        RegisterBinOpInstrOverloads< And, InstrOpCode::And >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< Or, InstrOpCode::Or >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< Xor, InstrOpCode::Xor >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< Implies, InstrOpCode::Implies >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< Shl, InstrOpCode::Shl >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< LShr, InstrOpCode::LShr >( e, MkInstr, UnpackInstr );
240
241
242
243
244
245
246
247
248
249

250
251
252
253
254
255

256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
















381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
        RegisterBinOpInstrOverloads< ULT, InstrOpCode::ULT >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< ULE, InstrOpCode::ULE >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< SGT, InstrOpCode::SGT >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< SGE, InstrOpCode::SGE >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< SLT, InstrOpCode::SLT >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< SLE, InstrOpCode::SLE >( e, MkInstr, UnpackInstr );

        RegisterInstrOverloads< Assert, InstrOpCode::Assert >( e, MkInstr, UnpackInstr, &Assert::cond );
        RegisterInstrOverloads< Placeholder, InstrOpCode::Placeholder >( e, MkInstr, UnpackInstr, &Placeholder::type, &Placeholder::name );
        RegisterInstrOverloads< PHOverride, InstrOpCode::PHOverride >( e, MkInstr, UnpackInstr, &PHOverride::name, &PHOverride::phVal, &PHOverride::val );


        // TODO functions to manipulate Phi's incomings array

        ////////////////////////////
        // Terminator
        ////////////////////////////

        DefineConstant( e, "TermiOpCodeRet"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Ret ) ) ) );
        DefineConstant( e, "TermiOpCodeBranch"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Branch ) ) ) );
        DefineConstant( e, "TermiOpCodeCondBranch"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::CondBranch ) ) ) );
        DefineConstant( e, "TermiOpCodeBreak"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Break ) ) ) );
        DefineConstant( e, "TermiOpCodeContinue"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Continue ) ) ) );

        // MkTermi and UnpackTermi overloads
        auto MkTermi = CreateOverloadSet( e, "MkTermi"_sid );
        auto UnpackTermi = CreateOverloadSet( e, "UnpackTermi"_sid );

        RegisterMkTermiOverload< Ret, TermiOpCode::Ret >( e, MkTermi );
        RegisterMkTermiOverload< Ret, TermiOpCode::Ret, TypeWrapper< Value > >( e, MkTermi );
        RegisterBuiltinFunc< bool ( ConstantParam< uint8_t, static_cast< uint8_t >( TermiOpCode::Ret ) >,
            TypeWrapper< ptr< cir::Terminator > >, TermRef< TypeWrapper< Value > > ) >
            ( e, "UnpackTermi"_sid,
            []( uint8_t, const TypeWrapper< ptr< cir::Terminator > >& t, TermRef< TypeWrapper< Value > >& out )
            {
                const auto* pRet = get_if< Ret >( &t->content() );
                if( !pRet || !pRet->value() )
                    return false;

                out = *pRet->value();
                return true;
            } );

        RegisterTermiOverloads< Branch, TermiOpCode::Branch >( e, MkTermi, UnpackTermi, &Branch::dest );
        RegisterTermiOverloads< CondBranch, TermiOpCode::CondBranch >( e, MkTermi, UnpackTermi, &CondBranch::cond, &CondBranch::trueDest, &CondBranch::falseDest );
        RegisterTermiOverloads< Break, TermiOpCode::Break >( e, MkTermi, UnpackTermi, &Break::level );
        RegisterTermiOverloads< Continue, TermiOpCode::Continue >( e, MkTermi, UnpackTermi, &Continue::level );

        ////////////////////////////
        // BasicBlock
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< LocationId > ( TypeWrapper< ptr< BasicBlock > > ) >( e, "GetBasicBlockLocation"_sid,
            []( const TypeWrapper< ptr< BasicBlock > >& pBB ) -> TypeWrapper< LocationId >
            {
                return pBB->locationId();
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< BasicBlock > >, TypeWrapper< LocationId > ) >( e, "SetBasicBlockLocation"_sid,
            []( const TypeWrapper< ptr< BasicBlock > >& pBB, const TypeWrapper< LocationId >& loc )
            {
                pBB->setLocationId( loc );
            } );

        RegisterBuiltinFunc< uint32_t ( TypeWrapper< ptr< BasicBlock > > ) >( e, "GetBasicBlockLoopId"_sid,
            []( const TypeWrapper< ptr< BasicBlock > >& pBB )
            {
                return pBB->loopId();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< BasicBlock > > ) >( e, "IsBasicBlockLoopHeader"_sid,
            []( const TypeWrapper< ptr< BasicBlock > >& pBB )
            {
                return pBB->isLoopHeader();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< BasicBlock > >, TermRef< TypeWrapper< ptr< Terminator > > > ) >
            ( e, "GetBasicBlockTerminator"_sid,
            []( const TypeWrapper< ptr< BasicBlock > >& pBB, TermRef< TypeWrapper< ptr< Terminator > > >& out )
            {
                const auto& bb = *pBB.get();

                if( !bb.terminator() )
                    return false;

                out = make_shared< Terminator >( *bb.terminator() );
                return true;
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< BasicBlock > >, TypeWrapper< ptr< Terminator > > ) >( e, "SetBasicBlockTerminator"_sid,
            []( const TypeWrapper< ptr< BasicBlock > >& pBB, const TypeWrapper< ptr< Terminator > >& termi )
            {
                pBB->setTerminator( *termi.get() );
            } );

        RegisterBuiltinFunc< uint32_t ( TypeWrapper< ptr< BasicBlock > > ) >( e, "GetBasicBlockInstrCount"_sid,
            []( const TypeWrapper< ptr< BasicBlock > >& pBB )
            {
                return pBB->size();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< BasicBlock > >, uint32_t, TermRef< TypeWrapper< ptr< Instruction > > > ) >
            ( e, "GetBasicBlockInstr"_sid,
            []( const TypeWrapper< ptr< BasicBlock > >& pBB, uint32_t index, TermRef< TypeWrapper< ptr< Instruction > > >& out )
            {
                const auto& bb = *pBB.get();

                if( index >= bb.size() )
                    return false;

                out = make_shared< Instruction >( bb[index] );
                return true;
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< BasicBlock > > ) >( e, "BasicBlockInstrsClear"_sid,
            []( const TypeWrapper< ptr< BasicBlock > >& pBB )
            {
                pBB->clear();
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< BasicBlock > >, uint32_t ) >( e, "BasicBlockInstrsReserve"_sid,
            []( const TypeWrapper< ptr< BasicBlock > >& pBB, uint32_t count )
            {
                pBB->reserve( count );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< BasicBlock > >, uint32_t, TypeWrapper< ptr< Instruction > > ) >
            ( e, "SetBasicBlockInstr"_sid,
            []( const TypeWrapper< ptr< BasicBlock > >& pBB, uint32_t index, TypeWrapper< ptr< Instruction > >& instr )
            {
                auto& bb = *pBB.get();

                if( index >= bb.size() )
                    return;

                bb[index] = *instr.get();
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< BasicBlock > >, TypeWrapper< ptr< Instruction > > ) >
            ( e, "AppendBasicBlockInstr"_sid,
            []( const TypeWrapper< ptr< BasicBlock > >& pBB, TypeWrapper< ptr< Instruction > >& instr )
            {
                auto& bb = *pBB.get();
                bb.emplace_back( *instr.get() );
















            } );

        ////////////////////////////
        // CFG
        ////////////////////////////
        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< CFG > > ) >( e, "IsCFGPoisoned"_sid,
            []( const TypeWrapper< ptr< CFG > >& pCFG )
            {
                return pCFG->isPoisoned();
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< CFG > > ) >( e, "PoisonCFG"_sid,
            []( const TypeWrapper< ptr< CFG > >& pCFG )
            {
                pCFG->poison();
            } );

        RegisterBuiltinFunc< uint32_t ( TypeWrapper< ptr< CFG > > ) >( e, "GetCFGBasicBlockCount"_sid,
            []( const TypeWrapper< ptr< CFG > >& pCFG )
            {
                return static_cast< uint32_t >( pCFG->count() );
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< CFG > >, uint32_t, TermRef< TypeWrapper< ptr< BasicBlock > > > ) >
            ( e, "GetCFGBasicBlock"_sid,
            []( const TypeWrapper< ptr< CFG > >& pCFG, uint32_t index, TermRef< TypeWrapper< ptr< BasicBlock > > >& out )
            {
                const auto& cfg = *pCFG.get();

                if( cfg.count() <= index )
                    return false;

                out = cfg.getBB( index );
                return true;
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< CFG > >, TermRef< TypeWrapper< ptr< BasicBlock > > > ) >
            ( e, "GetCFGEntryBasicBlock"_sid,
            []( const TypeWrapper< ptr< CFG > >& pCFG, TermRef< TypeWrapper< ptr< BasicBlock > > >& out )
            {
                const auto& cfg = *pCFG.get();

                if( cfg.count() == 0 )
                    return false;

                out = cfg.entryBB();
                return true;
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< CFG > >, TermRef< TypeWrapper< ptr< BasicBlock > > > ) >
            ( e, "GetCFGLastBasicBlock"_sid,
            []( const TypeWrapper< ptr< CFG > >& pCFG, TermRef< TypeWrapper< ptr< BasicBlock > > >& out )
            {
                const auto& cfg = *pCFG.get();

                if( cfg.count() == 0 )
                    return false;

                out = cfg.lastBB();
                return true;
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< CFG > >, TermRef< TypeWrapper< ptr< BasicBlock > > > ) >
            ( e, "GetCFGCurrentBasicBlock"_sid,
            []( const TypeWrapper< ptr< CFG > >& pCFG, TermRef< TypeWrapper< ptr< BasicBlock > > >& out )
            {
                const auto& cfg = *pCFG.get();

                if( !cfg.currentBB() )
                    return false;

                out = cfg.currentBB();
                return true;
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< CFG > >, TypeWrapper< ptr< BasicBlock > > ) >
            ( e, "SetCFGCurrentBasicBlock"_sid,
            []( const TypeWrapper< ptr< CFG > >& pCFG, TypeWrapper< ptr< BasicBlock > >& pBB )
            {
                auto& cfg = *pCFG.get();
                cfg.setCurrentBB( pBB.get() );
            } );

        RegisterBuiltinFunc< TypeWrapper< ptr< BasicBlock > > ( TypeWrapper< ptr< CFG > > ) >
            ( e, "CFGCreateBasicBlock"_sid,
            []( const TypeWrapper< ptr< CFG > >& pCFG ) -> TypeWrapper< ptr< BasicBlock > >
            {
                auto& cfg = *pCFG.get();
                return cfg.createBB();
            } );

        RegisterBuiltinFunc< uint32_t ( TypeWrapper< ptr< CFG > > ) >
            ( e, "CFGGetNewTemporaryIndex"_sid,
            []( const TypeWrapper< ptr< CFG > >& pCFG )
            {
                auto& cfg = *pCFG.get();
                return cfg.getNewTemporaryIndex();
            } );

        ////////////////////////////
        // Decorator
        ////////////////////////////
        RegisterBuiltinFunc< void ( TypeWrapper< ptr< Decorator > >, TypeWrapper< ptr< Instruction > > ) >
            ( e, "DecoratorAddPrologueInstr"_sid,
            []( const TypeWrapper< ptr< Decorator > >& pBB, TypeWrapper< ptr< Instruction > >& instr )
            {
                auto& bb = *pBB.get();
                bb.addPrologueInstr( *instr.get() );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< Decorator > >, TypeWrapper< ptr< Instruction > > ) >
            ( e, "DecoratorAddOperationInstr"_sid,
            []( const TypeWrapper< ptr< Decorator > >& pBB, TypeWrapper< ptr< Instruction > >& instr )
            {
                auto& bb = *pBB.get();
                bb.addOperationInstr( *instr.get() );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< Decorator > >, TypeWrapper< ptr< Instruction > > ) >
            ( e, "DecoratorAddEpilogueInstr"_sid,
            []( const TypeWrapper< ptr< Decorator > >& pBB, TypeWrapper< ptr< Instruction > >& instr )
            {
                auto& bb = *pBB.get();
                bb.addEpilogueInstr( *instr.get() );
            } );
    }
}







|
|
|
>






>










<
<
<
<
<
<
<
<
<
<
|
<
<
<
|

|







|





|





|





|






|











|




<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<



|


|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>






|





|





|






|












|












|












|












|







|







|










|







|







|






243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270










271



272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323





324




































325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
        RegisterBinOpInstrOverloads< ULT, InstrOpCode::ULT >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< ULE, InstrOpCode::ULE >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< SGT, InstrOpCode::SGT >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< SGE, InstrOpCode::SGE >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< SLT, InstrOpCode::SLT >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< SLE, InstrOpCode::SLE >( e, MkInstr, UnpackInstr );

        RegisterInstrOverloads< Assert, InstrOpCode::Assert >( e, MkInstr, UnpackInstr, &Assert::locationId );
        RegisterInstrOverloads< Placeholder, InstrOpCode::Placeholder >( e, MkInstr, UnpackInstr, &Placeholder::type, &Placeholder::name, &Placeholder::locationId );
        RegisterInstrOverloads< PHOverrideSet, InstrOpCode::PHOverrideSet >( e, MkInstr, UnpackInstr, &PHOverrideSet::name, &PHOverrideSet::locationId );
        RegisterInstrOverloads< PHOverrideClear, InstrOpCode::PHOverrideClear >( e, MkInstr, UnpackInstr, &PHOverrideClear::name, &PHOverrideClear::locationId );

        // TODO functions to manipulate Phi's incomings array

        ////////////////////////////
        // Terminator
        ////////////////////////////
        DefineConstant( e, "TermiOpCodeRetVoid"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::RetVoid ) ) ) );
        DefineConstant( e, "TermiOpCodeRet"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Ret ) ) ) );
        DefineConstant( e, "TermiOpCodeBranch"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Branch ) ) ) );
        DefineConstant( e, "TermiOpCodeCondBranch"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::CondBranch ) ) ) );
        DefineConstant( e, "TermiOpCodeBreak"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Break ) ) ) );
        DefineConstant( e, "TermiOpCodeContinue"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Continue ) ) ) );

        // MkTermi and UnpackTermi overloads
        auto MkTermi = CreateOverloadSet( e, "MkTermi"_sid );
        auto UnpackTermi = CreateOverloadSet( e, "UnpackTermi"_sid );











        RegisterTermiOverloads< RetVoid, TermiOpCode::RetVoid >( e, MkTermi, UnpackTermi, &RetVoid::locationId );



        RegisterTermiOverloads< Ret, TermiOpCode::Ret >( e, MkTermi, UnpackTermi, &Ret::locationId );
        RegisterTermiOverloads< Branch, TermiOpCode::Branch >( e, MkTermi, UnpackTermi, &Branch::dest );
        RegisterTermiOverloads< CondBranch, TermiOpCode::CondBranch >( e, MkTermi, UnpackTermi, &CondBranch::trueDest, &CondBranch::falseDest );
        RegisterTermiOverloads< Break, TermiOpCode::Break >( e, MkTermi, UnpackTermi, &Break::level );
        RegisterTermiOverloads< Continue, TermiOpCode::Continue >( e, MkTermi, UnpackTermi, &Continue::level );

        ////////////////////////////
        // BasicBlock
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< LocationId > ( TypeWrapper< ptr< BasicBlock > > ) >( e, "GetBasicBlockLocation"_sid,
            []( const auto& pBB ) -> TypeWrapper< LocationId >
            {
                return pBB->locationId();
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< BasicBlock > >, TypeWrapper< LocationId > ) >( e, "SetBasicBlockLocation"_sid,
            []( const auto& pBB, const auto& loc )
            {
                pBB->setLocationId( loc );
            } );

        RegisterBuiltinFunc< uint32_t ( TypeWrapper< ptr< BasicBlock > > ) >( e, "GetBasicBlockLoopId"_sid,
            []( const auto& pBB )
            {
                return pBB->loopId();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< BasicBlock > > ) >( e, "IsBasicBlockLoopHeader"_sid,
            []( const auto& pBB )
            {
                return pBB->isLoopHeader();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< BasicBlock > >, TermRef< TypeWrapper< ptr< Terminator > > > ) >
            ( e, "GetBasicBlockTerminator"_sid,
            []( const auto& pBB, auto& out )
            {
                const auto& bb = *pBB.get();

                if( !bb.terminator() )
                    return false;

                out = make_shared< Terminator >( *bb.terminator() );
                return true;
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< BasicBlock > >, TypeWrapper< ptr< Terminator > > ) >( e, "SetBasicBlockTerminator"_sid,
            []( const auto& pBB, const auto& termi )
            {
                pBB->setTerminator( *termi.get() );
            } );






        // TODO InstrSeq, which will be annoying to deal with (we can't just iterate by indexing it so we'll need to figure something out for the api)





































        RegisterBuiltinFunc< void ( TypeWrapper< ptr< BasicBlock > >, TypeWrapper< ptr< Instruction > > ) >
            ( e, "AppendBasicBlockInstr"_sid,
            []( const auto& pBB, auto& instr )
            {
                auto& bb = *pBB.get();
                bb.append( *instr.get() );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< BasicBlock > >, TypeWrapper< ptr< InstrSeq > > ) >
            ( e, "AppendBasicBlockInstr"_sid,
            []( const auto& pBB, auto& is )
            {
                auto& bb = *pBB.get();
                bb.append( *is.get() );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< BasicBlock > >, TypeWrapper< Value > ) >
            ( e, "AppendBasicBlockInstr"_sid,
            []( const auto& pBB, auto& val )
            {
                auto& bb = *pBB.get();
                bb.append( val.get() );
            } );

        ////////////////////////////
        // CFG
        ////////////////////////////
        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< CFG > > ) >( e, "IsCFGPoisoned"_sid,
            []( const auto& pCFG )
            {
                return pCFG->isPoisoned();
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< CFG > > ) >( e, "PoisonCFG"_sid,
            []( const auto& pCFG )
            {
                pCFG->poison();
            } );

        RegisterBuiltinFunc< uint32_t ( TypeWrapper< ptr< CFG > > ) >( e, "GetCFGBasicBlockCount"_sid,
            []( const auto& pCFG )
            {
                return static_cast< uint32_t >( pCFG->count() );
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< CFG > >, uint32_t, TermRef< TypeWrapper< ptr< BasicBlock > > > ) >
            ( e, "GetCFGBasicBlock"_sid,
            []( const auto& pCFG, uint32_t index, auto& out )
            {
                const auto& cfg = *pCFG.get();

                if( cfg.count() <= index )
                    return false;

                out = cfg.getBB( index );
                return true;
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< CFG > >, TermRef< TypeWrapper< ptr< BasicBlock > > > ) >
            ( e, "GetCFGEntryBasicBlock"_sid,
            []( const auto& pCFG, auto& out )
            {
                const auto& cfg = *pCFG.get();

                if( cfg.count() == 0 )
                    return false;

                out = cfg.entryBB();
                return true;
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< CFG > >, TermRef< TypeWrapper< ptr< BasicBlock > > > ) >
            ( e, "GetCFGLastBasicBlock"_sid,
            []( const auto& pCFG, auto& out )
            {
                const auto& cfg = *pCFG.get();

                if( cfg.count() == 0 )
                    return false;

                out = cfg.lastBB();
                return true;
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< CFG > >, TermRef< TypeWrapper< ptr< BasicBlock > > > ) >
            ( e, "GetCFGCurrentBasicBlock"_sid,
            []( const auto& pCFG, auto& out )
            {
                const auto& cfg = *pCFG.get();

                if( !cfg.currentBB() )
                    return false;

                out = cfg.currentBB();
                return true;
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< CFG > >, TypeWrapper< ptr< BasicBlock > > ) >
            ( e, "SetCFGCurrentBasicBlock"_sid,
            []( const auto& pCFG, auto& pBB )
            {
                auto& cfg = *pCFG.get();
                cfg.setCurrentBB( pBB.get() );
            } );

        RegisterBuiltinFunc< TypeWrapper< ptr< BasicBlock > > ( TypeWrapper< ptr< CFG > > ) >
            ( e, "CFGCreateBasicBlock"_sid,
            []( const auto& pCFG ) -> TypeWrapper< ptr< BasicBlock > >
            {
                auto& cfg = *pCFG.get();
                return cfg.createBB();
            } );

        RegisterBuiltinFunc< uint32_t ( TypeWrapper< ptr< CFG > > ) >
            ( e, "CFGGetNewTemporaryIndex"_sid,
            []( const auto& pCFG )
            {
                auto& cfg = *pCFG.get();
                return cfg.getNewTemporaryIndex();
            } );

        ////////////////////////////
        // Decorator
        ////////////////////////////
        RegisterBuiltinFunc< void ( TypeWrapper< ptr< Decorator > >, TypeWrapper< ptr< Instruction > > ) >
            ( e, "DecoratorAddPrologueInstr"_sid,
            []( const auto& pBB, auto& instr )
            {
                auto& bb = *pBB.get();
                bb.addPrologueInstr( *instr.get() );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< Decorator > >, TypeWrapper< ptr< Instruction > > ) >
            ( e, "DecoratorAddOperationInstr"_sid,
            []( const auto& pBB, auto& instr )
            {
                auto& bb = *pBB.get();
                bb.addOperationInstr( *instr.get() );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< Decorator > >, TypeWrapper< ptr< Instruction > > ) >
            ( e, "DecoratorAddEpilogueInstr"_sid,
            []( const auto& pBB, auto& instr )
            {
                auto& bb = *pBB.get();
                bb.addEpilogueInstr( *instr.get() );
            } );
    }
}
Changes to bs/g0api/extensibility/context.cpp.
9
10
11
12
13
14
15
16
17
18
19
20
21
namespace goose::g0api
{
    void SetupContextExtensibilityFuncs( Env& e )
    {
        wptr< Env > pEnv = e.shared_from_this();

        RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< ptr< Context > > ) >( e, "ContextGetIdentity"_sid,
            []( const TypeWrapper< ptr< Context > >& c )
            {
                return c->identity();
            } );
    }
}







|





9
10
11
12
13
14
15
16
17
18
19
20
21
namespace goose::g0api
{
    void SetupContextExtensibilityFuncs( Env& e )
    {
        wptr< Env > pEnv = e.shared_from_this();

        RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< ptr< Context > > ) >( e, "ContextGetIdentity"_sid,
            []( const auto& c )
            {
                return c->identity();
            } );
    }
}
Changes to bs/g0api/extensibility/eir.cpp.
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
        DefineConstant( e, "TermTypeVec"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::Vec ) ) ) );
        DefineConstant( e, "TermTypeBigInt"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::BigInt ) ) ) );
        DefineConstant( e, "TermTypeFixedInt"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::FixedInt ) ) ) );
        DefineConstant( e, "TermTypeInternal"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::Internal ) ) ) );

        // Functions
        RegisterBuiltinFunc< BigInt ( TypeWrapper< Term > ) >( e, "GetTermType"_sid,
            []( const TypeWrapper< Term >& t )
            {
                return BigInt::FromU32( min< uint8_t >( t.get().index(), static_cast< uint8_t >( TermType::Internal ) ) );
            } );

        ////////////////////////////
        // MkTerm overloads
        ////////////////////////////







|







101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
        DefineConstant( e, "TermTypeVec"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::Vec ) ) ) );
        DefineConstant( e, "TermTypeBigInt"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::BigInt ) ) ) );
        DefineConstant( e, "TermTypeFixedInt"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::FixedInt ) ) ) );
        DefineConstant( e, "TermTypeInternal"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::Internal ) ) ) );

        // Functions
        RegisterBuiltinFunc< BigInt ( TypeWrapper< Term > ) >( e, "GetTermType"_sid,
            []( const auto& t )
            {
                return BigInt::FromU32( min< uint8_t >( t.get().index(), static_cast< uint8_t >( TermType::Internal ) ) );
            } );

        ////////////////////////////
        // MkTerm overloads
        ////////////////////////////
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
        auto GetTermValue = CreateOverloadSet( e, "GetTermValue"_sid );
        RegisterGetTermValueOverload< uint64_t >( e, GetTermValue );
        RegisterGetTermValueOverload< TypeWrapper< LocationId > >( e, GetTermValue );
        RegisterGetTermValueOverload< string >( e, GetTermValue );
        RegisterGetTermValueOverload< TypeWrapper< StringId > >( e, GetTermValue );

        RegisterBuiltinFunc< bool ( TypeWrapper< Term >, TermRef< uint8_t > ) >( e, "GetDelimiterTermValue"_sid,
            []( const TypeWrapper< Term >& t, TermRef< uint8_t >& tref )
            {
                const auto* pVal = get_if< Delimiter >( &t.get() );
                if( !pVal )
                    return false;

                tref = static_cast< uint8_t >( *pVal );
                return true;







|







139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
        auto GetTermValue = CreateOverloadSet( e, "GetTermValue"_sid );
        RegisterGetTermValueOverload< uint64_t >( e, GetTermValue );
        RegisterGetTermValueOverload< TypeWrapper< LocationId > >( e, GetTermValue );
        RegisterGetTermValueOverload< string >( e, GetTermValue );
        RegisterGetTermValueOverload< TypeWrapper< StringId > >( e, GetTermValue );

        RegisterBuiltinFunc< bool ( TypeWrapper< Term >, TermRef< uint8_t > ) >( e, "GetDelimiterTermValue"_sid,
            []( const auto& t, auto& tref )
            {
                const auto* pVal = get_if< Delimiter >( &t.get() );
                if( !pVal )
                    return false;

                tref = static_cast< uint8_t >( *pVal );
                return true;
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
        RegisterGetTermValueOverload< TypeWrapper< APSInt > >( e, GetTermValue );

        ////////////////////////////
        // LocationId
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< LocationId > ( TypeWrapper< LocationId >, TypeWrapper< LocationId > ) >( e,
            "MkSpanningLocation"_sid,
            []( const TypeWrapper< LocationId >& loc1, const TypeWrapper< LocationId >& loc2 ) -> TypeWrapper< LocationId >
            {
                return static_cast< LocationId >( Location::CreateSpanningLocation( loc1.get(), loc2.get() ) );
            } );

        ////////////////////////////
        // StringId
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< StringId > ( string ) >( e, "MkStringId"_sid,
            []( string s ) -> TypeWrapper< StringId >
            {
                return s;
            } );

        ////////////////////////////
        // Hole
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< Hole > ( TypeWrapper< StringId > ) >( e, "MkHole"_sid,
            []( const TypeWrapper< StringId >& name ) -> TypeWrapper< Hole >
            {
                return Hole( name );
            } );

        RegisterBuiltinFunc< TypeWrapper< Hole > ( TypeWrapper< StringId >, TypeWrapper< Term > ) >( e, "MkHole"_sid,
            []( const TypeWrapper< StringId >& name, const TypeWrapper< Term >& kind ) -> TypeWrapper< Hole >
            {
                return Hole( name, kind.get() );
            } );

        RegisterBuiltinFunc< TypeWrapper< StringId > ( TypeWrapper< Hole > ) >( e, "GetHoleName"_sid,
            []( const TypeWrapper< Hole >& h ) -> TypeWrapper< StringId >
            {
                return h.get().name();
            } );

        RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< Hole > ) >( e, "GetHoleKind"_sid,
            []( const TypeWrapper< Hole >& h ) -> TypeWrapper< Term >
            {
                return h.get().kind();
            } );

        ////////////////////////////
        // AnyTerm
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< StringId > ( TypeWrapper< AnyTerm > ) >( e, "GetAnyTermVarName"_sid,
            []( const TypeWrapper< AnyTerm >& at ) -> TypeWrapper< StringId >
            {
                return at.get().varName();
            } );

        RegisterBuiltinFunc< TypeWrapper< AnyTerm > ( TypeWrapper< StringId > ) >( e, "MkAnyTerm"_sid,
            []( const TypeWrapper< StringId >& name ) -> TypeWrapper< AnyTerm >
            {
                return AnyTerm( name );
            } );

        ////////////////////////////
        // VecOfLength
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< StringId > ( TypeWrapper< VecOfLength > ) >( e, "GetVecOfLengthVarName"_sid,
            []( const TypeWrapper< VecOfLength >& at ) -> TypeWrapper< StringId >
            {
                return at.get().varName();
            } );

        RegisterBuiltinFunc< TypeWrapper< VecOfLength > ( TypeWrapper< StringId > ) >( e, "MkVecOfLength"_sid,
            []( const TypeWrapper< StringId >& name ) -> TypeWrapper< VecOfLength >
            {
                return VecOfLength( name );
            } );

        ////////////////////////////
        // Vector
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< pvec > () >( e, "MkVec"_sid,
            []() -> TypeWrapper< pvec >
            {
                return make_shared< Vector >();
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< pvec >, TypeWrapper< pvec > ) >( e, "MkVecConcat"_sid,
            []( const TypeWrapper< pvec >& vec1, const TypeWrapper< pvec >& vec2 )
            {
                return make_shared< Vector >( Vector::MakeConcat( *vec1.get(), *vec2.get() ) );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< pvec >, uint32_t, TypeWrapper< Term > ) >( e, "SetTerm"_sid,
            []( const TypeWrapper< pvec >& vec, uint32_t index, const TypeWrapper< Term >& t )
            {
                vec->terms()[index] = t.get();
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< pvec >, int32_t ) >( e, "SetVecWeight"_sid,
            []( const TypeWrapper< pvec >& vec, int32_t w )
            {
                vec->setWeight( w );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< pvec >, int32_t ) >( e, "SetVecWeightOverride"_sid,
            []( const TypeWrapper< pvec >& vec, int32_t w )
            {
                vec->setWeightOverride( w );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< pvec >, TypeWrapper< Term > ) >( e, "VecAppend"_sid,
            []( const TypeWrapper< pvec >& vec, const TypeWrapper< Term >& t )
            {
                vec->append( t.get() );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< pvec >, TypeWrapper< Term > ) >( e, "VecSetRepetition"_sid,
            []( const TypeWrapper< pvec >& vec, const TypeWrapper< Term >& t )
            {
                vec->setRepetitionTerm( t.get() );
            } );

        RegisterBuiltinFunc< tuple< uint32_t, bool > ( TypeWrapper< pvec > ) >( e, "GetVecLength"_sid,
            []( const TypeWrapper< pvec >& vec )
            {
                auto vl = vec->length();
                return make_tuple( static_cast< uint32_t >( vl.minLength() ), vl.isVariable() );
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< pvec >, uint32_t, TermRef< TypeWrapper< Term > > ) >( e, "GetVecTerm"_sid,
            []( const TypeWrapper< pvec >& vec, uint32_t index, TermRef< TypeWrapper< Term > >& out )
            {
                const auto& v = *vec.get();

                if( v.length().minLength() <= index )
                    return false;

                out = v.terms()[index];
                return true;
            } );

        RegisterBuiltinFunc< int32_t ( TypeWrapper< pvec > ) >( e, "GetVecWeight"_sid,
            []( const TypeWrapper< pvec >& vec )
            {
                return vec->weight();
            } );

        RegisterBuiltinFunc< int32_t ( TypeWrapper< pvec > ) >( e, "GetVecWeightOverride"_sid,
            []( const TypeWrapper< pvec >& vec )
            {
                return vec->weightOverride();
            } );

        RegisterBuiltinFunc< int32_t ( TypeWrapper< pvec >, TermRef< TypeWrapper< Term > > ) >( e, "GetVecRepetitionTerm"_sid,
            []( const TypeWrapper< pvec >& vec, TermRef< TypeWrapper< Term > >& out )
            {
                const auto& rt = vec->repetitionTerm();

                if( !rt )
                    return false;

                out = *rt;
                return true;
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< pvec > ) >( e, "IsVecEmpty"_sid,
            []( const TypeWrapper< pvec >& vec )
            {
                return vec->empty();
            } );

        ////////////////////////////
        // Helpers
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< Term >, TypeWrapper< Term > ) >( e, "AppendToVectorTerm"_sid,
            []( const TypeWrapper< Term >& vec, TypeWrapper< Term > t ) -> TypeWrapper< Term >
            {
                return AppendToVectorTerm( vec, t );
            } );

        ////////////////////////////
        // Integers
        ////////////////////////////







|

















|





|





|





|








|





|








|





|














|





|





|





|





|





|





|






|











|





|





|











|








|







161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
        RegisterGetTermValueOverload< TypeWrapper< APSInt > >( e, GetTermValue );

        ////////////////////////////
        // LocationId
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< LocationId > ( TypeWrapper< LocationId >, TypeWrapper< LocationId > ) >( e,
            "MkSpanningLocation"_sid,
            []( const auto& loc1, const auto& loc2 ) -> TypeWrapper< LocationId >
            {
                return static_cast< LocationId >( Location::CreateSpanningLocation( loc1.get(), loc2.get() ) );
            } );

        ////////////////////////////
        // StringId
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< StringId > ( string ) >( e, "MkStringId"_sid,
            []( string s ) -> TypeWrapper< StringId >
            {
                return s;
            } );

        ////////////////////////////
        // Hole
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< Hole > ( TypeWrapper< StringId > ) >( e, "MkHole"_sid,
            []( const auto& name ) -> TypeWrapper< Hole >
            {
                return Hole( name );
            } );

        RegisterBuiltinFunc< TypeWrapper< Hole > ( TypeWrapper< StringId >, TypeWrapper< Term > ) >( e, "MkHole"_sid,
            []( const auto& name, const auto& kind ) -> TypeWrapper< Hole >
            {
                return Hole( name, kind.get() );
            } );

        RegisterBuiltinFunc< TypeWrapper< StringId > ( TypeWrapper< Hole > ) >( e, "GetHoleName"_sid,
            []( const auto& h ) -> TypeWrapper< StringId >
            {
                return h.get().name();
            } );

        RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< Hole > ) >( e, "GetHoleKind"_sid,
            []( const auto& h ) -> TypeWrapper< Term >
            {
                return h.get().kind();
            } );

        ////////////////////////////
        // AnyTerm
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< StringId > ( TypeWrapper< AnyTerm > ) >( e, "GetAnyTermVarName"_sid,
            []( const auto& at ) -> TypeWrapper< StringId >
            {
                return at.get().varName();
            } );

        RegisterBuiltinFunc< TypeWrapper< AnyTerm > ( TypeWrapper< StringId > ) >( e, "MkAnyTerm"_sid,
            []( const auto& name ) -> TypeWrapper< AnyTerm >
            {
                return AnyTerm( name );
            } );

        ////////////////////////////
        // VecOfLength
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< StringId > ( TypeWrapper< VecOfLength > ) >( e, "GetVecOfLengthVarName"_sid,
            []( const auto& at ) -> TypeWrapper< StringId >
            {
                return at.get().varName();
            } );

        RegisterBuiltinFunc< TypeWrapper< VecOfLength > ( TypeWrapper< StringId > ) >( e, "MkVecOfLength"_sid,
            []( const auto& name ) -> TypeWrapper< VecOfLength >
            {
                return VecOfLength( name );
            } );

        ////////////////////////////
        // Vector
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< pvec > () >( e, "MkVec"_sid,
            []() -> TypeWrapper< pvec >
            {
                return make_shared< Vector >();
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< pvec >, TypeWrapper< pvec > ) >( e, "MkVecConcat"_sid,
            []( const auto& vec1, const auto& vec2 )
            {
                return make_shared< Vector >( Vector::MakeConcat( *vec1.get(), *vec2.get() ) );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< pvec >, uint32_t, TypeWrapper< Term > ) >( e, "SetTerm"_sid,
            []( const auto& vec, uint32_t index, const auto& t )
            {
                vec->terms()[index] = t.get();
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< pvec >, int32_t ) >( e, "SetVecWeight"_sid,
            []( const auto& vec, int32_t w )
            {
                vec->setWeight( w );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< pvec >, int32_t ) >( e, "SetVecWeightOverride"_sid,
            []( const auto& vec, int32_t w )
            {
                vec->setWeightOverride( w );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< pvec >, TypeWrapper< Term > ) >( e, "VecAppend"_sid,
            []( const auto& vec, const auto& t )
            {
                vec->append( t.get() );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< pvec >, TypeWrapper< Term > ) >( e, "VecSetRepetition"_sid,
            []( const auto& vec, const auto& t )
            {
                vec->setRepetitionTerm( t.get() );
            } );

        RegisterBuiltinFunc< tuple< uint32_t, bool > ( TypeWrapper< pvec > ) >( e, "GetVecLength"_sid,
            []( const auto& vec )
            {
                auto vl = vec->length();
                return make_tuple( static_cast< uint32_t >( vl.minLength() ), vl.isVariable() );
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< pvec >, uint32_t, TermRef< TypeWrapper< Term > > ) >( e, "GetVecTerm"_sid,
            []( const auto& vec, uint32_t index, auto& out )
            {
                const auto& v = *vec.get();

                if( v.length().minLength() <= index )
                    return false;

                out = v.terms()[index];
                return true;
            } );

        RegisterBuiltinFunc< int32_t ( TypeWrapper< pvec > ) >( e, "GetVecWeight"_sid,
            []( const auto& vec )
            {
                return vec->weight();
            } );

        RegisterBuiltinFunc< int32_t ( TypeWrapper< pvec > ) >( e, "GetVecWeightOverride"_sid,
            []( const auto& vec )
            {
                return vec->weightOverride();
            } );

        RegisterBuiltinFunc< int32_t ( TypeWrapper< pvec >, TermRef< TypeWrapper< Term > > ) >( e, "GetVecRepetitionTerm"_sid,
            []( const auto& vec, auto& out )
            {
                const auto& rt = vec->repetitionTerm();

                if( !rt )
                    return false;

                out = *rt;
                return true;
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< pvec > ) >( e, "IsVecEmpty"_sid,
            []( const auto& vec )
            {
                return vec->empty();
            } );

        ////////////////////////////
        // Helpers
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< Term >, TypeWrapper< Term > ) >( e, "AppendToVectorTerm"_sid,
            []( const auto& vec, const auto& t ) -> TypeWrapper< Term >
            {
                return AppendToVectorTerm( vec, t );
            } );

        ////////////////////////////
        // Integers
        ////////////////////////////
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
                    ai.setIsSigned( false );

                return ToValue( TypeWrapper< APSInt >( s ) );
            } );

        auto ToBigInt = CreateOverloadSet( e, "ToBigInt"_sid );
        RegisterBuiltinFunc< Eager< BigInt > ( TypeWrapper< APSInt > ) >( e, ToBigInt,
            []( const TypeWrapper< APSInt >& fInt )
            {
                return fInt;
            } );

        RegisterBuiltinFunc< Eager< BigInt > ( char32_t ) >( e, ToBigInt,
            []( char32_t c )
            {
                return BigInt::FromU32( c );
            } );

        ////////////////////////////
        // Propositions
        ////////////////////////////
        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< Context > >, TypeWrapper< Value >, TermRef< TypeWrapper< ptr< Propositions > > > ) >( e, "GetTypePredicates"_sid,
            []( const TypeWrapper< ptr< Context > >& c, const TypeWrapper< Value >& type, TermRef< TypeWrapper< ptr< Propositions > > >& out )
            {
                if( !ParseTypePredicates( *c.get(), type.get() ) )
                    return false;

                auto ppPreds = GetTypePredicates( type );

                if( !ppPreds || !( *ppPreds ) )
                    return false;

                out = *ppPreds;
                return true;
            } );

        RegisterBuiltinFunc< uint32_t ( TypeWrapper< ptr< Propositions > > ) >( e, "GetPropositionsCount"_sid,
            []( const TypeWrapper< ptr< Propositions > >& preds )
            {
                return preds->props().size();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< Propositions > >, uint32_t, TermRef< TypeWrapper< Value > > ) >( e, "GetProposition"_sid,
            []( const TypeWrapper< ptr< Propositions > >& preds, uint32_t index, TermRef< TypeWrapper< Value > >& out )
            {
                if( preds->props().size() <= index )
                    return false;

                out = preds->props()[index];
                return true;
            } );
    }
}







|














|














|





|









361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
                    ai.setIsSigned( false );

                return ToValue( TypeWrapper< APSInt >( s ) );
            } );

        auto ToBigInt = CreateOverloadSet( e, "ToBigInt"_sid );
        RegisterBuiltinFunc< Eager< BigInt > ( TypeWrapper< APSInt > ) >( e, ToBigInt,
            []( const auto& fInt )
            {
                return fInt;
            } );

        RegisterBuiltinFunc< Eager< BigInt > ( char32_t ) >( e, ToBigInt,
            []( char32_t c )
            {
                return BigInt::FromU32( c );
            } );

        ////////////////////////////
        // Propositions
        ////////////////////////////
        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< Context > >, TypeWrapper< Value >, TermRef< TypeWrapper< ptr< Propositions > > > ) >( e, "GetTypePredicates"_sid,
            []( const auto& c, const auto& type, auto& out ) -> bool
            {
                if( !ParseTypePredicates( *c.get(), type.get() ) )
                    return false;

                auto ppPreds = GetTypePredicates( type );

                if( !ppPreds || !( *ppPreds ) )
                    return false;

                out = *ppPreds;
                return true;
            } );

        RegisterBuiltinFunc< uint32_t ( TypeWrapper< ptr< Propositions > > ) >( e, "GetPropositionsCount"_sid,
            []( const auto& preds )
            {
                return preds->props().size();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< Propositions > >, uint32_t, TermRef< TypeWrapper< Value > > ) >( e, "GetProposition"_sid,
            []( const auto& preds, uint32_t index, auto& out )
            {
                if( preds->props().size() <= index )
                    return false;

                out = preds->props()[index];
                return true;
            } );
    }
}
Changes to bs/g0api/extensibility/value.cpp.
14
15
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
44
45
46
47
48
49
50
51
52
53
54
55
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

        ////////////////////////////
        // MkValue overloads
        ////////////////////////////
        auto MkValue = CreateOverloadSet( e, "MkValue"_sid );

        RegisterBuiltinFunc< TypeWrapper< Value > ( TypeWrapper< Term >, TypeWrapper< Term > ) >( e, MkValue,
            []( const TypeWrapper< Term >& type, const TypeWrapper< Term >& val ) -> TypeWrapper< Value >
            {
                return Value( type.get(), val.get() );
            } );

        RegisterBuiltinFunc< TypeWrapper< Value > ( TypeWrapper< Term >, TypeWrapper< ptr< Instruction > > ) >( e, MkValue,
            []( const TypeWrapper< Term >& type, const TypeWrapper< ptr< Instruction > >& cir ) -> TypeWrapper< Value >
            {
                return Value( type.get(), cir.get() );
































            } );

        RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< Value > ) >( e, "GetValueType"_sid,
            []( const TypeWrapper< Value >& val ) -> TypeWrapper< Term >
            {
                return val.get().type();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< Value >, TermRef< TypeWrapper< Term > > ) >( e, "GetValueVal"_sid,
            []( const TypeWrapper< Value >& val, TermRef< TypeWrapper< Term > >& out ) -> bool
            {
                if( !val.get().isConstant() )
                    return false;

                out = val.get().val();
                return true;
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< Value >, TermRef< TypeWrapper< ptr< Instruction > > > ) >( e, "GetValueCIR"_sid,
            []( const TypeWrapper< Value >& val, TermRef< TypeWrapper< ptr< Instruction > > >& out ) -> bool
            {
                if( val.get().isConstant() )
                    return false;

                out = val.get().cir();
                return true;
            } );

        RegisterBuiltinFunc< TypeWrapper< LocationId > ( TypeWrapper< Value > ) >( e, "GetValueLocation"_sid,
            []( const TypeWrapper< Value >& val ) -> TypeWrapper< LocationId >
            {
                return val.get().locationId();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< Value > ) >( e, "IsPoisonValue"_sid,
            []( const TypeWrapper< Value >& val )
            {
                return val.get().isPoison();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< Value > ) >( e, "IsConstantValue"_sid,
            []( const TypeWrapper< Value >& val )
            {
                return val.get().isConstant();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< Value > ) >( e, "IsTypeValue"_sid,
            []( const TypeWrapper< Value >& val )
            {
                return val.get().isType();
            } );

        RegisterBuiltinFunc< TypeWrapper< Value > ( TypeWrapper< Value >, TypeWrapper< LocationId > ) >( e, "SetValueLocation"_sid,
            []( const TypeWrapper< Value >& v, const TypeWrapper< LocationId >& loc ) -> TypeWrapper< Value >
            {
                Value val( v );
                return val.setLocationId( loc );
            } );

        RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< Value > ) >( e, "ValueToEIR"_sid,
            []( const TypeWrapper< Value >& val ) -> TypeWrapper< Term >
            {
                return ValueToEIR( val.get() );
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< Term >, TermRef< TypeWrapper< Value > > ) >( e, "EIRToValue"_sid,
            []( const TypeWrapper< Term >& t, TermRef< TypeWrapper< Value > >& out )
            {
                auto val = EIRToValue( t.get() );

                if( !val )
                    return false;

                out = move( *val );
                return true;
            } );

        RegisterBuiltinFunc< TypeWrapper< Value > ( CustomPattern< Value, ValuePatternT > ) >( e, "WrapValue"_sid,
            []( const Value& v ) -> TypeWrapper< Value >
            {
                return v;
            } );
    }
}







|




|
|

|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



|





|








|
|









|





|





|





|





|






|





|











|





14
15
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
44
45
46
47
48
49
50
51
52
53
54
55
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145

        ////////////////////////////
        // MkValue overloads
        ////////////////////////////
        auto MkValue = CreateOverloadSet( e, "MkValue"_sid );

        RegisterBuiltinFunc< TypeWrapper< Value > ( TypeWrapper< Term >, TypeWrapper< Term > ) >( e, MkValue,
            []( const auto& type, const auto& val ) -> TypeWrapper< Value >
            {
                return Value( type.get(), val.get() );
            } );

        RegisterBuiltinFunc< TypeWrapper< Value > ( TypeWrapper< Term > ) >( e, MkValue,
            []( const auto& type ) -> TypeWrapper< Value >
            {
                return Value( type.get(), make_shared< InstrSeq >() );
            } );

        auto AppendValueInstr = CreateOverloadSet( e, "AppendValueInstr"_sid );
        RegisterBuiltinFunc< bool ( TypeWrapper< Value >, TypeWrapper< ptr< Instruction > > ) >( e, AppendValueInstr,
            []( auto& val, const auto& pInstr )
            {
                if( val.get().isConstant() )
                    return false;

                val.get().cir()->emplace_back( *pInstr.get() );
                return true;
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< Value >, TypeWrapper< ptr< InstrSeq > > ) >( e, AppendValueInstr,
            []( auto& val, const auto& pIS )
            {
                if( val.get().isConstant() )
                    return false;

                for( auto&& x : *pIS.get() )
                    val.get().cir()->emplace_back( x );
                return true;
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< Value >, TypeWrapper< Value > ) >( e, AppendValueInstr,
            []( auto& val, const auto& valToAppend )
            {
                if( val.get().isConstant() )
                    return false;

                val.get().cir()->emplace_back( valToAppend.get() );
                return true;
            } );

        RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< Value > ) >( e, "GetValueType"_sid,
            []( const auto& val ) -> TypeWrapper< Term >
            {
                return val.get().type();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< Value >, TermRef< TypeWrapper< Term > > ) >( e, "GetValueVal"_sid,
            []( const auto& val, auto& out )
            {
                if( !val.get().isConstant() )
                    return false;

                out = val.get().val();
                return true;
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< Value >, TermRef< TypeWrapper< ptr< InstrSeq > > > ) >( e, "GetValueCIR"_sid,
            []( const auto& val, auto& out )
            {
                if( val.get().isConstant() )
                    return false;

                out = val.get().cir();
                return true;
            } );

        RegisterBuiltinFunc< TypeWrapper< LocationId > ( TypeWrapper< Value > ) >( e, "GetValueLocation"_sid,
            []( const auto& val ) -> TypeWrapper< LocationId >
            {
                return val.get().locationId();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< Value > ) >( e, "IsPoisonValue"_sid,
            []( const auto& val )
            {
                return val.get().isPoison();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< Value > ) >( e, "IsConstantValue"_sid,
            []( const auto& val )
            {
                return val.get().isConstant();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< Value > ) >( e, "IsTypeValue"_sid,
            []( const auto& val )
            {
                return val.get().isType();
            } );

        RegisterBuiltinFunc< TypeWrapper< Value > ( TypeWrapper< Value >, TypeWrapper< LocationId > ) >( e, "SetValueLocation"_sid,
            []( const auto& v, const auto& loc ) -> TypeWrapper< Value >
            {
                Value val( v );
                return val.setLocationId( loc );
            } );

        RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< Value > ) >( e, "ValueToEIR"_sid,
            []( const auto& val ) -> TypeWrapper< Term >
            {
                return ValueToEIR( val.get() );
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< Term >, TermRef< TypeWrapper< Value > > ) >( e, "EIRToValue"_sid,
            []( const auto& t, auto& out )
            {
                auto val = EIRToValue( t.get() );

                if( !val )
                    return false;

                out = move( *val );
                return true;
            } );

        RegisterBuiltinFunc< TypeWrapper< Value > ( CustomPattern< Value, ValuePatternT > ) >( e, "WrapValue"_sid,
            []( const auto& v ) -> TypeWrapper< Value >
            {
                return v;
            } );
    }
}
Changes to bs/g0api/types.cpp.
76
77
78
79
80
81
82

83
84
85
86
87
88
89

        ////////////////////////////
        // CIR types
        ////////////////////////////
        SetupWrapperForType< ptr< CFG > >( e, "CFG"_sid, pEquals, pNotEquals );
        SetupWrapperForType< ptr< BasicBlock > >( e, "BasicBlock"_sid, pEquals, pNotEquals );
        SetupWrapperForType< ptr< Instruction > >( e, "Instruction"_sid, pEquals, pNotEquals );

        SetupWrapperForType< ptr< Terminator > >( e, "Terminator"_sid, pEquals, pNotEquals );
        SetupWrapperForType< ptr< Decorator > >( e, "Decorator"_sid, pEquals, pNotEquals );

        ////////////////////////////
        // Context
        ////////////////////////////
        SetupWrapperForType< ptr< Context > >( e, "Context"_sid, pEquals, pNotEquals );







>







76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

        ////////////////////////////
        // CIR types
        ////////////////////////////
        SetupWrapperForType< ptr< CFG > >( e, "CFG"_sid, pEquals, pNotEquals );
        SetupWrapperForType< ptr< BasicBlock > >( e, "BasicBlock"_sid, pEquals, pNotEquals );
        SetupWrapperForType< ptr< Instruction > >( e, "Instruction"_sid, pEquals, pNotEquals );
        SetupWrapperForType< ptr< InstrSeq > >( e, "InstrSeq"_sid, pEquals, pNotEquals );
        SetupWrapperForType< ptr< Terminator > >( e, "Terminator"_sid, pEquals, pNotEquals );
        SetupWrapperForType< ptr< Decorator > >( e, "Decorator"_sid, pEquals, pNotEquals );

        ////////////////////////////
        // Context
        ////////////////////////////
        SetupWrapperForType< ptr< Context > >( e, "Context"_sid, pEquals, pNotEquals );
Changes to bs/g0api/types.h.
69
70
71
72
73
74
75






76
77
78
79
80
81
82
    };

    template<>
    struct TypeWrapperTraits< ptr< cir::Instruction > >
    {
        static auto typeId() { return "Instruction"_sid; }
    };







    template<>
    struct TypeWrapperTraits< ptr< cir::Terminator > >
    {
        static auto typeId() { return "Terminator"_sid; }
    };








>
>
>
>
>
>







69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
    };

    template<>
    struct TypeWrapperTraits< ptr< cir::Instruction > >
    {
        static auto typeId() { return "Instruction"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< cir::InstrSeq > >
    {
        static auto typeId() { return "InstrSeq"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< cir::Terminator > >
    {
        static auto typeId() { return "Terminator"_sid; }
    };

Changes to bs/parse/bracketblock.cpp.
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
        // Create a new identity for the predicates, which imports everything from the parent identity
        // and also makes @val visible as a placeholder for the type's value.
        preds->setIdentity( VEC( Env::NewUniqueId() ) );
        c.env()->addVisibilityRule( c.identity(), preds->identity() );

        auto name = "@val"_sid;
        auto typeTerm = ValueToEIR( type );
        auto phVal = ValueToEIR( BuildComputedValue( typeTerm, cir::Placeholder( typeTerm, name ) ) );

        auto valuePlaceholderIdentity = AppendToVectorTerm( preds->identity(), TERM( name ) );
        c.env()->storeValue( valuePlaceholderIdentity, ANYTERM( _ ), phVal );

        // The placeholder is also available as the default lhs value, so "@val" can be omitted in many cases
        auto defLhsIdentity = AppendToVectorTerm( preds->identity(), TSID( 0_def_lhs ) );
        c.env()->storeValue( defLhsIdentity, ANYTERM( _ ), move( phVal ) );







|







128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
        // Create a new identity for the predicates, which imports everything from the parent identity
        // and also makes @val visible as a placeholder for the type's value.
        preds->setIdentity( VEC( Env::NewUniqueId() ) );
        c.env()->addVisibilityRule( c.identity(), preds->identity() );

        auto name = "@val"_sid;
        auto typeTerm = ValueToEIR( type );
        auto phVal = ValueToEIR( BuildComputedValue( typeTerm, cir::Placeholder( typeTerm, name, {} ) ) );

        auto valuePlaceholderIdentity = AppendToVectorTerm( preds->identity(), TERM( name ) );
        c.env()->storeValue( valuePlaceholderIdentity, ANYTERM( _ ), phVal );

        // The placeholder is also available as the default lhs value, so "@val" can be omitted in many cases
        auto defLhsIdentity = AppendToVectorTerm( preds->identity(), TSID( 0_def_lhs ) );
        c.env()->storeValue( defLhsIdentity, ANYTERM( _ ), move( phVal ) );
Changes to bs/parse/parser.inl.
10
11
12
13
14
15
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
        {
            DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );
            builtins::PoisonBuilder( context() );
        }

        flushValue();

        if( val.isConstant() || !cir::CanValueBeEagerlyEvaluated( val ) )
        {
            m_lastValue = forward< V >( val );
            return;
        }

        if( !verify::VerifyCompTimeExpr( context(), val ) )
        {
            m_lastValue = PoisonValue();
            return;
        }

        execute::VM vm;
        m_lastValue = execute::Evaluate( val, vm );

        // If the value we just pushed is of void type, it was pushed only for its side effects
        // (for instance a SetVar instruction), so it should be flushed immediately.
        if( m_lastValue && m_lastValue->type() == GetValueType< void >() )
            flushValue();
    }

    template< typename T >
    optional< uint32_t > Parser::getPrecedence( const Term&, const T& )
    {
        return nullopt;







<
<
|
<
|
|
<
<
<
<
<
|
<
<



|







10
11
12
13
14
15
16


17

18
19





20


21
22
23
24
25
26
27
28
29
30
31
        {
            DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );
            builtins::PoisonBuilder( context() );
        }

        flushValue();



        m_lastValue = forward< V >( val );


        if( !context().locationId().invalid() )





            m_lastValue->setLocationId( context().locationId() );



        // If the value we just pushed is of void type, it was pushed only for its side effects
        // (for instance a SetVar instruction), so it should be flushed immediately.
        if( m_lastValue->type() == GetValueType< void >() )
            flushValue();
    }

    template< typename T >
    optional< uint32_t > Parser::getPrecedence( const Term&, const T& )
    {
        return nullopt;
Changes to bs/parse/rule-helpers.h.
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
    void BuildParseRule( sema::Env& env, StringId name, const Term& identity, R&&... ruleBuilders )
    {
        Rule r;
        ( ( ruleBuilders( env, r, name ) ), ... );
        RegisterRule( env, identity, move( r ) );
    }

    template< typename F >
    auto PrefixOp( uint32_t precedence, F&& func )
    {
        return [&,precedence]( auto&& e, auto&& r, auto&& name )
        {
            MakePrefixOp( e, r, name, precedence, forward< F >( func ) );
        };
    }

    template< typename F >
    auto PostfixOp( uint32_t precedence, F&& func )
    {
        return [&,precedence]( auto&& e, auto&& r, auto&& name )
        {
            MakePostfixOp( e, r, name, precedence, forward< F >( func ) );
        };
    }

    template< typename F >
    auto LeftAssInfixOp( uint32_t precedence, F&& func )
    {
        return [&,precedence]( auto&& e, auto&& r, auto&& name )
        {
            MakeLeftAssInfixOp( e, r, name, precedence, forward< F >( func ) );
        };
    }

    template< typename F >
    auto RightAssInfixOp( uint32_t precedence, F&& func )
    {
        return [&,precedence]( auto&& e, auto&& r, auto&& name )
        {
            MakeRightAssInfixOp( e, r, name, precedence, forward< F >( func ) );
        };
    }

    template< typename F >
    void MakePrefixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func );

    template< typename F >
    void MakePostfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func );

    template< typename F >







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







20
21
22
23
24
25
26




































27
28
29
30
31
32
33
    void BuildParseRule( sema::Env& env, StringId name, const Term& identity, R&&... ruleBuilders )
    {
        Rule r;
        ( ( ruleBuilders( env, r, name ) ), ... );
        RegisterRule( env, identity, move( r ) );
    }





































    template< typename F >
    void MakePrefixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func );

    template< typename F >
    void MakePostfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func );

    template< typename F >
Changes to bs/parse/rule-helpers.inl.
35
36
37
38
39
40
41


42
43
44
45
46
47
48
49
50
                p.pushValue( PoisonValue() );
                return true;
            }

            DiagnosticsContext dc( locationId, false );

            auto loc = Location::CreateSpanningLocation( locationId, rightVal->locationId() );


            auto result = func( p, move( *rightVal ) );
            p.pushValue( move( result ).setLocationId( loc ) );
            return true;
        } );
    }

    template< typename F >
    void MakePostfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func )
    {







>
>

|







35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
                p.pushValue( PoisonValue() );
                return true;
            }

            DiagnosticsContext dc( locationId, false );

            auto loc = Location::CreateSpanningLocation( locationId, rightVal->locationId() );
            sema::Context::CurrentLocationGuard clg( p.context(), loc );

            auto result = func( p, move( *rightVal ) );
            p.pushValue( move( result ) );
            return true;
        } );
    }

    template< typename F >
    void MakePostfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func )
    {
65
66
67
68
69
70
71


72
73
74
75
76
77
78
79
80
81
                    p.pushValue( PoisonValue() );
                    return true;
                }

                DiagnosticsContext dc( locationId, false );

                auto loc = Location::CreateSpanningLocation( leftVal->locationId(), locationId );


                auto result = func( p, move( *leftVal ) );
                p.pushValue( move( result ).setLocationId( loc ) );

                return true;
            } );
    }

    template< typename F >
    void MakeLeftAssInfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func )
    {







>
>

|
<







67
68
69
70
71
72
73
74
75
76
77

78
79
80
81
82
83
84
                    p.pushValue( PoisonValue() );
                    return true;
                }

                DiagnosticsContext dc( locationId, false );

                auto loc = Location::CreateSpanningLocation( leftVal->locationId(), locationId );
                sema::Context::CurrentLocationGuard clg( p.context(), loc );

                auto result = func( p, move( *leftVal ) );
                p.pushValue( move( result ) );

                return true;
            } );
    }

    template< typename F >
    void MakeLeftAssInfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func )
    {
108
109
110
111
112
113
114


115
116
117
118
119
120
121
122
123
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                            "expected an expression.", 0 );
                        return false;
                    }
                }

                auto loc = Location::CreateSpanningLocation( locationId, rightVal->locationId() );


                auto result = func( p, *EIRToValue( *defLhsVal ), move( *rightVal ) );
                p.pushValue( move( result ).setLocationId( loc ) );
                return true;
            } );
        }

        r.setInfixFunc(
            [=]( const Parser& p ) { return precedence; },
            [=]( Parser& p, LocationId locationId, uint32_t prec )







>
>

|







111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                            "expected an expression.", 0 );
                        return false;
                    }
                }

                auto loc = Location::CreateSpanningLocation( locationId, rightVal->locationId() );
                sema::Context::CurrentLocationGuard clg( p.context(), loc );

                auto result = func( p, *EIRToValue( *defLhsVal ), move( *rightVal ) );
                p.pushValue( move( result ) );
                return true;
            } );
        }

        r.setInfixFunc(
            [=]( const Parser& p ) { return precedence; },
            [=]( Parser& p, LocationId locationId, uint32_t prec )
157
158
159
160
161
162
163

164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189

                DiagnosticsContext dcl( leftVal->locationId(), false );
                DiagnosticsContext dcr( rightVal->locationId(), false );

                DiagnosticsContext dc( locationId, false );

                auto loc = Location::CreateSpanningLocation( leftVal->locationId(), rightVal->locationId() );


                // If there is a default lhs value available in our current context, fail silently
                // and put back the leftVal. The parsing will then end and drop the current expression (leftVal), and
                // try parsing this again as a prefix operator with the default lhs value. That's not hackish at all, honest
                auto defLhsVal = p.context().env()->retrieveValue( "0_def_lhs"_sid, p.context().identity() );
                if( defLhsVal )
                {
                    VerbosityContext vc( Verbosity::Silent );

                    auto result = func( p, *leftVal, move( *rightVal ) );
                    if( result.isPoison() )
                        return false;

                    p.pushValue( move( result ).setLocationId( loc ) );
                    return true;
                }

                auto result = func( p, move( *leftVal ), move( *rightVal ) );
                p.pushValue( move( result ).setLocationId( loc ) );
                return true;
            } );
    }

    template< typename F >
    void MakeRightAssInfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func )
    {







>













|




|







162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195

                DiagnosticsContext dcl( leftVal->locationId(), false );
                DiagnosticsContext dcr( rightVal->locationId(), false );

                DiagnosticsContext dc( locationId, false );

                auto loc = Location::CreateSpanningLocation( leftVal->locationId(), rightVal->locationId() );
                sema::Context::CurrentLocationGuard clg( p.context(), loc );

                // If there is a default lhs value available in our current context, fail silently
                // and put back the leftVal. The parsing will then end and drop the current expression (leftVal), and
                // try parsing this again as a prefix operator with the default lhs value. That's not hackish at all, honest
                auto defLhsVal = p.context().env()->retrieveValue( "0_def_lhs"_sid, p.context().identity() );
                if( defLhsVal )
                {
                    VerbosityContext vc( Verbosity::Silent );

                    auto result = func( p, *leftVal, move( *rightVal ) );
                    if( result.isPoison() )
                        return false;

                    p.pushValue( move( result ) );
                    return true;
                }

                auto result = func( p, move( *leftVal ), move( *rightVal ) );
                p.pushValue( move( result ) );
                return true;
            } );
    }

    template< typename F >
    void MakeRightAssInfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func )
    {
213
214
215
216
217
218
219


220
221
222
223
224
225
226
227
228
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                            "expected an expression.", 0 );
                        return false;
                    }
                }

                auto loc = Location::CreateSpanningLocation( locationId, rightVal->locationId() );


                auto result = func( p, *EIRToValue( *defLhsVal ), move( *rightVal ) );
                p.pushValue( move( result ).setLocationId( loc ) );
                return true;
            } );
        }

        r.setInfixFunc(
            [=]( const Parser& p ) { return precedence; },
            [=]( Parser& p, LocationId locationId, uint32_t prec )







>
>

|







219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                            "expected an expression.", 0 );
                        return false;
                    }
                }

                auto loc = Location::CreateSpanningLocation( locationId, rightVal->locationId() );
                sema::Context::CurrentLocationGuard clg( p.context(), loc );

                auto result = func( p, *EIRToValue( *defLhsVal ), move( *rightVal ) );
                p.pushValue( move( result ) );
                return true;
            } );
        }

        r.setInfixFunc(
            [=]( const Parser& p ) { return precedence; },
            [=]( Parser& p, LocationId locationId, uint32_t prec )
263
264
265
266
267
268
269

270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291

                DiagnosticsContext dcl( leftVal->locationId(), false );
                DiagnosticsContext dcr( rightVal->locationId(), false );

                DiagnosticsContext dc( locationId, false );

                auto loc = Location::CreateSpanningLocation( leftVal->locationId(), rightVal->locationId() );


                auto defLhsVal = p.context().env()->retrieveValue( "0_def_lhs"_sid, p.context().identity() );
                if( defLhsVal )
                {
                    VerbosityContext vc( Verbosity::Silent );

                    auto result = func( p, move( *leftVal ), move( *rightVal ) );
                    if( result.isPoison() )
                        return false;

                    p.pushValue( move( result ).setLocationId( loc ) );
                    return true;
                }

                auto result = func( p, move( *leftVal ), move( *rightVal ) );
                p.pushValue( move( result ).setLocationId( loc ) );
                return true;
            } );
    }
}

#endif







>










|




|






271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300

                DiagnosticsContext dcl( leftVal->locationId(), false );
                DiagnosticsContext dcr( rightVal->locationId(), false );

                DiagnosticsContext dc( locationId, false );

                auto loc = Location::CreateSpanningLocation( leftVal->locationId(), rightVal->locationId() );
                sema::Context::CurrentLocationGuard clg( p.context(), loc );

                auto defLhsVal = p.context().env()->retrieveValue( "0_def_lhs"_sid, p.context().identity() );
                if( defLhsVal )
                {
                    VerbosityContext vc( Verbosity::Silent );

                    auto result = func( p, move( *leftVal ), move( *rightVal ) );
                    if( result.isPoison() )
                        return false;

                    p.pushValue( move( result ) );
                    return true;
                }

                auto result = func( p, move( *leftVal ), move( *rightVal ) );
                p.pushValue( move( result ) );
                return true;
            } );
    }
}

#endif
Changes to bs/sema/context.h.
40
41
42
43
44
45
46






















47
48
49
50
51
52
53


54
55
56
57
                m_pEnv = pEnv;
            }

            void setIdentity( const Term& identity )
            {
                m_identity = identity;
            }























        private:
            ptr< Env >          m_pEnv;
            Term                m_identity;
            optional< Term >    m_returnType;

            Value               m_builder;


    };
}

#endif







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







>
>




40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
                m_pEnv = pEnv;
            }

            void setIdentity( const Term& identity )
            {
                m_identity = identity;
            }

            auto locationId() const { return m_currentLoc; }

            class CurrentLocationGuard
            {
                public:
                    CurrentLocationGuard( Context& c, LocationId loc ) :
                        m_context( c ),
                        m_previousLoc( c.m_currentLoc )
                    {
                        c.m_currentLoc = loc;
                    }

                    ~CurrentLocationGuard()
                    {
                        m_context.m_currentLoc = m_previousLoc;
                    }

                private:
                    Context& m_context;
                    LocationId m_previousLoc;
            };

        private:
            ptr< Env >          m_pEnv;
            Term                m_identity;
            optional< Term >    m_returnType;

            Value               m_builder;

            LocationId          m_currentLoc;
    };
}

#endif
Changes to bs/sema/lower.cpp.
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
    optional< Value > LowerConstantForRuntime( const Context& c, const Value& val )
    {
        if( val.type() == GetValueType< bool >() || IsRuntimeType( *EIRToValue( val.type() ) ) )
            return val;

        DiagnosticsContext dc( val.locationId(), "When invoking LowerConstantForRuntime." );
        auto result = InvokeOverloadSet( c, c.env()->extLowerConstantForRuntime(), AppendToTuple( EmptyTuple(), val ) );
        if( result.isPoison() || !IsRuntimeType( *EIRToValue( result.type() ) ) )
            return nullopt;

        return result;
    }

    optional< Value > LowerTypeForVerification( const Context& c, const Value& type )
    {







|







23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
    optional< Value > LowerConstantForRuntime( const Context& c, const Value& val )
    {
        if( val.type() == GetValueType< bool >() || IsRuntimeType( *EIRToValue( val.type() ) ) )
            return val;

        DiagnosticsContext dc( val.locationId(), "When invoking LowerConstantForRuntime." );
        auto result = InvokeOverloadSet( c, c.env()->extLowerConstantForRuntime(), AppendToTuple( EmptyTuple(), val ) );
        if( result.isPoison() )
            return nullopt;

        return result;
    }

    optional< Value > LowerTypeForVerification( const Context& c, const Value& type )
    {
Changes to bs/sema/tests/tctrie-merge.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
#include "sema/sema.h"
#include "builtins/builtins.h"

using namespace std;
using namespace goose;
using namespace goose::eir;
using namespace goose::sema;
using namespace goose::builtins;

SCENARIO( "TCTrie merge works", "[tctrie-merge]" )
{
    WHEN( "Merging various expressions into an typechecking trie" )
    {
        auto vec1 = Vector::Make( TSTR( "foo" ), TSTR( "bar" ) );




<





<







1
2
3

4
5
6
7
8

9
10
11
12
13
14
15
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
#include "sema/sema.h"


using namespace std;
using namespace goose;
using namespace goose::eir;
using namespace goose::sema;


SCENARIO( "TCTrie merge works", "[tctrie-merge]" )
{
    WHEN( "Merging various expressions into an typechecking trie" )
    {
        auto vec1 = Vector::Make( TSTR( "foo" ), TSTR( "bar" ) );

Changes to bs/sema/tests/tctrie-typecheck.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
#include "sema/sema.h"
#include "builtins/builtins.h"

using namespace std;
using namespace goose;
using namespace goose::eir;
using namespace goose::sema;
using namespace goose::builtins;

namespace
{
    auto GetSortedSolutions( const ptr< Env >& e, ptr< TCTrie< string > >& trie, const Vector& vec )
    {
        Context ctxt( e, VEC( TSID( g0 ) ) );
        TypeCheckingContext context( ctxt );



<





<







1
2
3

4
5
6
7
8

9
10
11
12
13
14
15
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
#include "sema/sema.h"


using namespace std;
using namespace goose;
using namespace goose::eir;
using namespace goose::sema;


namespace
{
    auto GetSortedSolutions( const ptr< Env >& e, ptr< TCTrie< string > >& trie, const Vector& vec )
    {
        Context ctxt( e, VEC( TSID( g0 ) ) );
        TypeCheckingContext context( ctxt );
Changes to bs/sema/tests/unify-holes.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
#include "sema/sema.h"
#include "builtins/builtins.h"

using namespace std;
using namespace goose;
using namespace goose::eir;
using namespace goose::sema;
using namespace goose::builtins;

namespace
{
    // Verifies that the unification of lhs and rhs yields only one solution, that it is complete,
    // and that this solution is the expected one.
    void CheckForUniqueSolution( const Term& lhs, const Term& rhs, const Term& expectedSolution, const TypeCheckingScore& expectedScore )
    {



<





<







1
2
3

4
5
6
7
8

9
10
11
12
13
14
15
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
#include "sema/sema.h"


using namespace std;
using namespace goose;
using namespace goose::eir;
using namespace goose::sema;


namespace
{
    // Verifies that the unification of lhs and rhs yields only one solution, that it is complete,
    // and that this solution is the expected one.
    void CheckForUniqueSolution( const Term& lhs, const Term& rhs, const Term& expectedSolution, const TypeCheckingScore& expectedScore )
    {
Changes to bs/util/util.h.
74
75
76
77
78
79
80







81
82
83
84
85
86
87
namespace goose::util
{
    template< typename T >
    auto ComputeHash( const T& x )
    {
        return std::hash< T >()( x );
    }








    template< typename T >
    class NullInit
    {
        public:
            NullInit() {}








>
>
>
>
>
>
>







74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
namespace goose::util
{
    template< typename T >
    auto ComputeHash( const T& x )
    {
        return std::hash< T >()( x );
    }

    template< typename C >
    Generator< uint64_t > ContainerHashGenerator( const C& c )
    {
        for( const auto& x : c )
            co_yield ComputeHash( x );
    }

    template< typename T >
    class NullInit
    {
        public:
            NullInit() {}

Changes to bs/verify/basicblock.cpp.
37
38
39
40
41
42
43
44

45

46

47

48
49
50
51
        {
            m_currentBBPredicate = c.bool_const( format( "b{}", bbid ).c_str() );
            m_builder.add( *m_currentBBPredicate == *pred );
        }

        m_builder.setCurrentPredicate( m_currentBBPredicate );

        for( auto&& x : bb )

            BuildZ3Op( m_builder, x );



        // We pass the non-remapped index here because handleTerminator will do its own remapping.

        handleTerminator( bb.index(), *bb.terminator() );
        return !m_builder.hasCheckFailed();
    }
}







|
>
|
>
|
>

>
|
|


37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
        {
            m_currentBBPredicate = c.bool_const( format( "b{}", bbid ).c_str() );
            m_builder.add( *m_currentBBPredicate == *pred );
        }

        m_builder.setCurrentPredicate( m_currentBBPredicate );

        for( auto&& x : bb.instructions() )
        {
            if( !BuildZ3Op( m_builder, x ) )
                m_builder.setcheckFailed();
        }

        // We pass the non-remapped index here because handleTerminator will do its own remapping.
        if( bb.terminator() )
            handleTerminator( bb.index(), *bb.terminator() );
        return true;
    }
}
Changes to bs/verify/builder.cpp.
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
}

optional< Z3Val > Builder::retrieveVar( uint32_t index, uint32_t bbIndex )
{
    return m_varTracker.retrieve( *this, bbIndex, index );
}

optional< Z3Val > Builder::setVar( uint32_t index, const Value& val )
{
    if( !cfg() )
        return nullopt;

    auto zv = BuildZ3ExprFromValue( *this, val );
    if( zv )
        return m_varTracker.set( *this, index, move( *zv ) );

    return nullopt;
}

const Z3Val& Builder::setVar( uint32_t index, Z3Val&& v )
{
    return m_varTracker.set( *this, index, move( v ) );
}

optional< Z3Val > Builder::retrieveGFC( const cir::GhostCall& gc, uint32_t bbIndex )
{
    return m_gfcTracker.retrieve( *this, bbIndex, gc );
}

void Builder::setGFC( const cir::GhostCall& gc, Z3Val&& v )
{
    return m_gfcTracker.set( *this, gc, move( v ) );
}







|
<
<
<
<
<
<
<
<
<
<
<
<




|

|


|

|

70
71
72
73
74
75
76
77












78
79
80
81
82
83
84
85
86
87
88
89
90
}

optional< Z3Val > Builder::retrieveVar( uint32_t index, uint32_t bbIndex )
{
    return m_varTracker.retrieve( *this, bbIndex, index );
}

optional< Z3Val > Builder::setVar( uint32_t index, Z3Val&& v )












{
    return m_varTracker.set( *this, index, move( v ) );
}

optional< Z3Val > Builder::retrieveGFC( const GhostFuncApplication& gfa, uint32_t bbIndex )
{
    return m_gfcTracker.retrieve( *this, bbIndex, gfa );
}

void Builder::setGFC( const GhostFuncApplication& gfa, Z3Val&& v )
{
    return m_gfcTracker.set( *this, gfa, move( v ) );
}
Changes to bs/verify/builder.h.
11
12
13
14
15
16
17
18
19
20
21


22
23
24
25
26
27
28
    using AssHandler = function< bool ( const z3::expr&, const z3::expr&, LocationId ) >;

    // Helper class that tracks the states of a few things
    // to build z3 expressions from a cfg.
    class Builder
    {
        public:
            Builder( const sema::Context& c, z3::solver& solver, Remapper* remapper = nullptr ) :
                m_context( &c ),
                m_solver( &solver ),
                m_remapper( remapper )


            {
                auto& zc = GetZ3Context();

                // BasicBlock 1 is always considered to be the entry point, as such its predicate value
                // is always assumed true.
                add( zc.bool_const( "b1" ) == zc.bool_val( true ) );
            }







|


|
>
>







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    using AssHandler = function< bool ( const z3::expr&, const z3::expr&, LocationId ) >;

    // Helper class that tracks the states of a few things
    // to build z3 expressions from a cfg.
    class Builder
    {
        public:
            Builder( const sema::Context& c, z3::solver& solver, size_t numVars, Remapper* remapper = nullptr ) :
                m_context( &c ),
                m_solver( &solver ),
                m_remapper( remapper ),
                m_varTracker( numVars ),
                m_gfcTracker( numVars )
            {
                auto& zc = GetZ3Context();

                // BasicBlock 1 is always considered to be the entry point, as such its predicate value
                // is always assumed true.
                add( zc.bool_const( "b1" ) == zc.bool_val( true ) );
            }
53
54
55
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

            void add( const z3::expr& e );
            void assume( const z3::expr& e );
            bool checkAssertion( const z3::expr& e, LocationId locationId );

            void setLoadMustAssume( bool lma ) { m_loadMustAssume = lma; }
            bool mustLoadAssume() const { return m_loadMustAssume; }






            optional< Z3Val > retrieveVar( uint32_t index, uint32_t bbIndex = ~0 );
            optional< Z3Val > setVar( uint32_t index, const Value& val );
            const Z3Val& setVar( uint32_t index, Z3Val&& v );

            optional< Z3Val > retrieveGFC( const cir::GhostCall& gc, uint32_t bbIndex = ~0 );
            void setGFC( const cir::GhostCall& gc, Z3Val&& v );

            const z3::expr* retrievePlaceholder( StringId sid ) const;
            void setPlaceholder( StringId sid, const z3::expr& expr );
            void unsetPlaceholder( StringId sid );

            uint32_t newUniqueId() { return m_nextUniqueId++; }

            bool hasCheckFailed() const { return m_checkFailed; }














            template< typename F >
            void setAssertionHandler( F&& handler )
            {
                m_assertionHandler = forward< F >( handler );
            }
            auto assertionHandler() const { return m_assertionHandler; }








>
>
>
>
>

|
<

|
|








>
>
>
>
>
>
>
>
>
>
>
>
>







55
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

            void add( const z3::expr& e );
            void assume( const z3::expr& e );
            bool checkAssertion( const z3::expr& e, LocationId locationId );

            void setLoadMustAssume( bool lma ) { m_loadMustAssume = lma; }
            bool mustLoadAssume() const { return m_loadMustAssume; }

            void resetVarStorage( uint32_t size )
            {
                m_varTracker = VarTracker( size );
            }

            optional< Z3Val > retrieveVar( uint32_t index, uint32_t bbIndex = ~0 );
            optional< Z3Val > setVar( uint32_t index, Z3Val&& v );


            optional< Z3Val > retrieveGFC( const GhostFuncApplication& gfa, uint32_t bbIndex = ~0 );
            void setGFC( const GhostFuncApplication& gfa, Z3Val&& v );

            const z3::expr* retrievePlaceholder( StringId sid ) const;
            void setPlaceholder( StringId sid, const z3::expr& expr );
            void unsetPlaceholder( StringId sid );

            uint32_t newUniqueId() { return m_nextUniqueId++; }

            bool hasCheckFailed() const { return m_checkFailed; }
            void setcheckFailed() { m_checkFailed = true; }

            template< typename T >
            void push( T&& val )
            {
                m_stack.push( forward< T >( val ) );
            }

            template< typename T = Z3Val >
            optional< T > pop()
            {
                return m_stack.pop< T >( *this );
            }

            template< typename F >
            void setAssertionHandler( F&& handler )
            {
                m_assertionHandler = forward< F >( handler );
            }
            auto assertionHandler() const { return m_assertionHandler; }
97
98
99
100
101
102
103


104
105
106
107
108
109
110

            AssHandler m_assertionHandler;

            unordered_map< StringId, z3::expr > m_placeholders;

            VarTracker m_varTracker;
            GFCTracker m_gfcTracker;



            // TODO: investigate why this not being static breaks things, since it should
            // be saved and restored whenever the solver is, but apparently it causes name collisions
            // that invalidate the formulas. This works fine like this but it makes z3 formulas dumps
            // less readable (due to super large variable name indices) and dependent on function checking order.
            static uint32_t m_nextUniqueId;








>
>







116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131

            AssHandler m_assertionHandler;

            unordered_map< StringId, z3::expr > m_placeholders;

            VarTracker m_varTracker;
            GFCTracker m_gfcTracker;

            Stack m_stack;

            // TODO: investigate why this not being static breaks things, since it should
            // be saved and restored whenever the solver is, but apparently it causes name collisions
            // that invalidate the formulas. This works fine like this but it makes z3 formulas dumps
            // less readable (due to super large variable name indices) and dependent on function checking order.
            static uint32_t m_nextUniqueId;

Changes to bs/verify/call.cpp.
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

28
29
30
31
32
33
34
35
36


37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
#include "verify.h"
#include "builtins/builtins.h"
#include "helpers.inl"

using namespace goose::diagnostics;

namespace goose::verify
{
    optional< Z3Val > BuildZ3Op( Builder& b, const Call& instr )
    {








        auto func = FromValue< builtins::Func >( instr.func() );
        if( !func )


            return nullopt;






        const auto& ft = func->type();
        const auto& fvi = ft.verifInfos();
        if( !fvi )
            return nullopt;



        auto retExpr = BuildZ3ConstantFromType( b, ft.returnType(), format( "r{}", b.newUniqueId() ) );

        // Create a temporary builder to construct the z3 expressions out of the
        // function's pre-conditions and postconditions, configured
        // to perform the necessary replacements for arguments and for the
        // return value placeholder.
        Builder cb = b;



        cb.setLoadMustAssume( false );


        // Inject the arguments in the context.
        // They will be picked up by getVars instructions when refered to
        // by the verification conditions.
        uint32_t index = 0;

        ForEachInVectorTerm( instr.args(), [&]( auto&& t )
        {
            auto arg = *EIRToValue( t );



            if( auto zv = BuildZ3ExprFromValue( b, arg ) )
                cb.setVar( index++, move( *zv ) );

            return true;
        } );

        // Asserts the parameter types's predicates.
        uint32_t varId = 0;

        ForEachInVectorTerm( ft.params(), [&]( auto&& param )
        {
            auto result = Decompose( param,
                Vec(
                    Lit( "value"_sid ),
                    Lit( "param"_sid ),
                    SubTerm(),
                    SubTerm(),
                    Val< LocationId >()
                )
            );

            if( !result )
                return true;

            auto&& [type, val, locId] = *result;
            auto paramVal = BuildComputedValue( type, Load( make_shared< Instruction >( VarAddr( varId++, type ) ), type ) );

            if( auto zv = BuildZ3ExprFromValue( cb, paramVal ) )
            {
                ForEachPredicate( cb, type, zv->expr, [&]( auto&& z3expr, auto locId )
                {
                    DiagnosticsContext dc( instr.func().locationId(), "At this call." );
                    b.checkAssertion( z3expr, locId );
                } );
            }

            return true;
        } );

        // Setup the return value placeholder
        if( retExpr )
            cb.setPlaceholder( "@result"_sid, retExpr->expr );

        // Check preconditions.
        const auto& preConds = fvi->preConds();
        preConds->forEach( [&]( auto&& val )
        {
            if( auto zv = BuildZ3ExprFromValue( cb, val ) )
            {
                DiagnosticsContext dc( instr.func().locationId(), "At this call." );
                b.checkAssertion( zv->expr, val.locationId() );
            }
        } );

        // Add the return type's predicates as assumptions.
        ForEachPredicate( cb, ft.returnType(), retExpr->expr, [&]( auto&& z3expr, auto locId )
        {
            b.assume( z3expr );
        } );

        // Add postconditions as assumptions.
        const auto& postConds = fvi->postConds();
        postConds->forEach( [&]( auto&& val )
        {
            if( auto zv = BuildZ3ExprFromValue( cb, val ) )
                b.assume( zv->expr );
        } );



        return retExpr;
    }
}








|

>
>
>
>
>
>
>
>
|

>
>
|
>
>
>
>
>




|

>
>
|






>
>
>
|
>




|

|

|
>
>

<
|
|
<
<




















|

|
<
|
|
|
|
|
<












|

|














|



>
>
|


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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include "verify.h"
#include "builtins/builtins.h"
#include "helpers.inl"

using namespace goose::diagnostics;

namespace goose::verify
{
    bool BuildZ3Op( Builder& b, const Call& instr )
    {
        auto fVal = b.pop< Value >();
        if( !fVal )
            return false;

        auto rt = builtins::GetFuncRType( *fVal );
        if( !rt )
            return false;

        auto func = FromValue< builtins::Func >( *fVal );
        if( !func )
        {
            if( rt == GetValueType< void >() )
                return true;

            auto result = BuildZ3ConstantFromType( b, *rt, format( "r{}", b.newUniqueId() ) );
            b.push( move( result ) );
            return true;
        }

        const auto& ft = func->type();
        const auto& fvi = ft.verifInfos();
        if( !fvi )
            return false;

        optional< Z3Val > retExpr;
        if( rt != GetValueType< void >() )
            retExpr = BuildZ3ConstantFromType( b, ft.returnType(), format( "r{}", b.newUniqueId() ) );

        // Create a temporary builder to construct the z3 expressions out of the
        // function's pre-conditions and postconditions, configured
        // to perform the necessary replacements for arguments and for the
        // return value placeholder.
        Builder cb = b;
        auto fcir = func->cir();
        if( fcir && fcir->body() )
            cb.resetVarStorage( fcir->body()->temporariesCount() );
        else
            cb.resetVarStorage( 0 );

        // Inject the arguments in the context.
        // They will be picked up by getVars instructions when refered to
        // by the verification conditions.
        uint32_t argCount = instr.numArgs();

        for( uint32_t argIndex = 0; argIndex < argCount; ++argIndex )
        {
            auto zv = b.pop();
            if( !zv )
                return false;


            cb.setVar( argCount - argIndex - 1, move( *zv ) );
        }



        // Asserts the parameter types's predicates.
        uint32_t varId = 0;

        ForEachInVectorTerm( ft.params(), [&]( auto&& param )
        {
            auto result = Decompose( param,
                Vec(
                    Lit( "value"_sid ),
                    Lit( "param"_sid ),
                    SubTerm(),
                    SubTerm(),
                    Val< LocationId >()
                )
            );

            if( !result )
                return true;

            auto&& [type, val, locId] = *result;
            auto paramVal = BuildComputedValue( type, VarAddr( varId++, type, locId ), Load( type, locId ) );

            auto zv = BuildZ3ExprFromValue( cb, paramVal );

            ForEachPredicate( cb, type, zv.expr, [&]( auto&& z3expr, auto locId )
            {
                DiagnosticsContext dc( fVal->locationId(), "At this call." );
                b.checkAssertion( z3expr, locId );
            } );


            return true;
        } );

        // Setup the return value placeholder
        if( retExpr )
            cb.setPlaceholder( "@result"_sid, retExpr->expr );

        // Check preconditions.
        const auto& preConds = fvi->preConds();
        preConds->forEach( [&]( auto&& val )
        {
            if( auto zv = TryBuildZ3ExprFromValue( cb, val ) )
            {
                DiagnosticsContext dc( fVal->locationId(), "At this call." );
                b.checkAssertion( zv->expr, val.locationId() );
            }
        } );

        // Add the return type's predicates as assumptions.
        ForEachPredicate( cb, ft.returnType(), retExpr->expr, [&]( auto&& z3expr, auto locId )
        {
            b.assume( z3expr );
        } );

        // Add postconditions as assumptions.
        const auto& postConds = fvi->postConds();
        postConds->forEach( [&]( auto&& val )
        {
            if( auto zv = TryBuildZ3ExprFromValue( cb, val ) )
                b.assume( zv->expr );
        } );

        if( retExpr )
            b.push( move( *retExpr ) );
        return true;
    }
}
Changes to bs/verify/cfg.cpp.
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    bool Func::buildZ3Expressions( const cir::BasicBlock& bb, queue< const BasicBlock* >* parentWorkQueue )
    {
        uint32_t currentLoopId = m_remapper.getCurrentLoopId();

        queue< const BasicBlock* > workQueue;
        workQueue.push( &bb );

        while( !workQueue.empty() && !m_builder.hasCheckFailed() )
        {
            const auto* pBB = workQueue.front();
            workQueue.pop();

            if( currentLoopId && pBB->loopId() != currentLoopId && pBB->index() != currentLoopId )
            {
                // If we encounter a successor BB that belongs to a different loop than the







|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    bool Func::buildZ3Expressions( const cir::BasicBlock& bb, queue< const BasicBlock* >* parentWorkQueue )
    {
        uint32_t currentLoopId = m_remapper.getCurrentLoopId();

        queue< const BasicBlock* > workQueue;
        workQueue.push( &bb );

        while( !workQueue.empty() )
        {
            const auto* pBB = workQueue.front();
            workQueue.pop();

            if( currentLoopId && pBB->loopId() != currentLoopId && pBB->index() != currentLoopId )
            {
                // If we encounter a successor BB that belongs to a different loop than the
31
32
33
34
35
36
37




38
39
40
41
42
43
44
                continue;
            }

            // If we encounter a loop header for a different
            // loop than the current one, check the loop.
            if( pBB->isLoopHeader() && pBB->index() != currentLoopId )
            {




                if( !checkLoop( *pBB, workQueue ) )
                    return false;

                continue;
            }

            // Make sure that every predecessor have been processed first.







>
>
>
>







31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
                continue;
            }

            // If we encounter a loop header for a different
            // loop than the current one, check the loop.
            if( pBB->isLoopHeader() && pBB->index() != currentLoopId )
            {
                // If we encounter a loop and a check previously failed, bail out
                if( m_builder.hasCheckFailed() )
                    return false;

                if( !checkLoop( *pBB, workQueue ) )
                    return false;

                continue;
            }

            // Make sure that every predecessor have been processed first.
Deleted bs/verify/comptime.cpp.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#include "verify.h"
#include "builtins/builtins.h"
#include "helpers.inl"

using namespace goose::builtins;
using namespace goose::diagnostics;

namespace goose::verify
{
    bool VerifyCompTimeExpr( Builder& b, const Call& instr );

    template< typename T >
    bool VerifyCompTimeExpr( Builder& b, const T& instr )
    {
        return true;
    }

    bool VerifyCompTimeExpr( const sema::Context& c, const Value& val )
    {
        if( val.isConstant() || val.isPoison() )
            return true;

        z3::solver solver( GetZ3Context() );
        Builder b( c, solver );

        if( Func::TraceMode() )
        {
            cout << "=== Checking compilation-time expression ===\n";
            b.setTraceMode( true );
        }

        b.setAssertionHandler( [&]( auto&& expr, auto&& exprToCheck, LocationId locationId )
        {
            if( Func::TraceMode() )
                return true;

            z3::expr_vector exprVec( GetZ3Context() );
            exprVec.push_back( exprToCheck );

            switch( solver.check( exprVec ) )
            {
                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.

                    if( Func::DumpSolverOnSuccess() )
                        cout << "Solver dump:\n" << solver << "check_unsat: " << exprToCheck << endl << endl;

                    return true;

                case z3::check_result::sat:
                    DiagnosticsManager::GetInstance().emitErrorMessage( locationId, "assert"_sid,
                        "this condition is not met." );

                    if( Func::DumpSolverOnFailure() )
                        cout << "Solver dump:\n" << solver << "check_unsat: " << exprToCheck << endl << endl;

                    return false;

                default:
                    DiagnosticsManager::GetInstance().emitErrorMessage( locationId, "assert"_sid,
                        "couldn't verify this condition." );

                    if( Func::DumpSolverOnFailure() )
                        cout << "Solver dump:\n" << solver << "check_unsat: " << exprToCheck << endl << endl;

                    return false;
            }
        } );

        bool result = visit( [&]( auto&& instr )
        {
            return VerifyCompTimeExpr( b, instr );
        }, val.cir()->content() );

        if( Func::TraceMode() )
            cout << '\n';

        return result;
    }

    bool VerifyCompTimeExpr( Builder& b, const Call& instr )
    {
        auto func = FromValue< builtins::Func >( instr.func() );

        if( !func )
            return true;

        const auto& ft = func->type();
        const auto& fvi = ft.verifInfos();
        assert( fvi );

        // Inject the arguments in the context.
        // They will be picked up by getVars instructions when refered to
        // by the verification conditions.
        uint32_t index = 0;

        bool eagerEvalFailed = false;

        ForEachInVectorTerm( instr.args(), [&]( auto&& t )
        {
            auto arg = EIRToValue( t );

            if( !arg->isConstant() )
            {
                execute::VM vm;
                arg = execute::Evaluate( *arg, vm );

                if( !arg->isConstant() )
                {
                    eagerEvalFailed = true;
                    return false;
                }
            }

            assert( arg );

            if( auto zv = BuildZ3ExprFromValue( b, *arg ) )
                b.setVar( index++, move( *zv ) );

            return true;
        } );

        // It may turn out it wasn't actually eager evaluable, so we can't check the expression
        // as a comptime expr but it isn't an error.
        if( eagerEvalFailed )
            return true;

        // Check the parameter types's predicates.
        uint32_t varId = 0;

        ForEachInVectorTerm( ft.params(), [&]( auto&& param )
        {
            auto result = Decompose( param,
                Vec(
                    Lit( "value"_sid ),
                    Lit( "param"_sid ),
                    SubTerm(),
                    SubTerm(),
                    Val< LocationId >()
                )
            );

            if( !result )
                return true;

            auto&& [type, val, locId] = *result;
            auto rt = ValueToEIR( ToValue( builtins::ReferenceType{ type, ConstAccessSpecifer() } ) );

            if( auto zv = LoadFromAddress( b, cir::VarAddr( varId++, type ) ) )
            {
                ForEachPredicate( b, type, zv->expr, [&]( auto&& z3expr, auto locId )
                {
                    DiagnosticsContext dc( instr.func().locationId(), "At this compilation-time call." );
                    b.checkAssertion( z3expr, locId );
                } );
            }

            return true;
        } );

        // Check preconditions.
        const auto& preConds = fvi->preConds();
        preConds->forEach( [&]( auto&& val )
        {
            if( auto zv = BuildZ3ExprFromValue( b, val ) )
            {
                DiagnosticsContext dc( instr.func().locationId(), "At this compilation-time call." );
                b.checkAssertion( zv->expr, val.locationId() );
            }
        } );

        return !b.hasCheckFailed();
    }
}
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






























































































































































































































































































































































Changes to bs/verify/func.cpp.
13
14
15
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
44
45
46
47
48
49

    Func::Func( const sema::Context& c, const builtins::Func& func ) :
        m_func( &func ),
        m_cfg( func.cir()->body() ),
        m_retType( func.type().returnType() ),
        m_solver( GetZ3Context(), z3::solver::simple() ),
        m_remapper( m_cfg->count() + 1 ),
        m_builder( c, m_solver, &m_remapper )
    {}

    Func::Func( const sema::Context& c, const ptr< cir::CFG >& cfg, const Term& retType ) :
        m_cfg( cfg ),
        m_retType( retType ),
        m_solver( GetZ3Context(), z3::solver::simple() ),
        m_remapper( m_cfg->count() + 1 ),
        m_builder( c, m_solver, &m_remapper )
    {}

    bool Func::verify()
    {
        ProfileZoneScoped;

        if( m_func )
            m_viz.setFunctionIdentity( m_func->cir()->identity() );

        ComputeLoops( m_cfg );
        ComputeLoopModifiedAddrs( m_cfg );

        try
        {






            m_builder.setTraceMode( ms_TraceMode );
            if( ms_TraceMode )
                cout << "=== Begin function verification trace ===\n";

            m_builder.setAssertionHandler( [&]( auto&& expr, auto&& exprToCheck, LocationId locationId )
            {
                return checkAssertion( expr, exprToCheck, locationId );







|







|














>
>
>
>
>
>







13
14
15
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
44
45
46
47
48
49
50
51
52
53
54
55

    Func::Func( const sema::Context& c, const builtins::Func& func ) :
        m_func( &func ),
        m_cfg( func.cir()->body() ),
        m_retType( func.type().returnType() ),
        m_solver( GetZ3Context(), z3::solver::simple() ),
        m_remapper( m_cfg->count() + 1 ),
        m_builder( c, m_solver, m_cfg->temporariesCount(), &m_remapper )
    {}

    Func::Func( const sema::Context& c, const ptr< cir::CFG >& cfg, const Term& retType ) :
        m_cfg( cfg ),
        m_retType( retType ),
        m_solver( GetZ3Context(), z3::solver::simple() ),
        m_remapper( m_cfg->count() + 1 ),
        m_builder( c, m_solver, m_cfg->temporariesCount(), &m_remapper )
    {}

    bool Func::verify()
    {
        ProfileZoneScoped;

        if( m_func )
            m_viz.setFunctionIdentity( m_func->cir()->identity() );

        ComputeLoops( m_cfg );
        ComputeLoopModifiedAddrs( m_cfg );

        try
        {
            // Force the old z3 arith solver for now as there is apparently an issue
            // with arith and bitvector (https://github.com/Z3Prover/z3/issues/5106)
            // This prevents verification-c-fail-test-8 from hanging with recent versions
            // of z3
            m_solver.set( "arith.solver", 2U );

            m_builder.setTraceMode( ms_TraceMode );
            if( ms_TraceMode )
                cout << "=== Begin function verification trace ===\n";

            m_builder.setAssertionHandler( [&]( auto&& expr, auto&& exprToCheck, LocationId locationId )
            {
                return checkAssertion( expr, exprToCheck, locationId );
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
                        )
                    );

                    if( !result )
                        return true;

                    auto&& [type, val, locId] = *result;
                    auto paramVal = BuildComputedValue( type, Load( make_shared< Instruction >( VarAddr( varId, type ) ), type ) );


                    // Initialize every parameter containing variable with a freshly named constant of the right type.
                    if( auto paramInit = BuildZ3ConstantFromType( m_builder, type, format( "p{}", varId ) ) )
                        m_builder.setVar( varId, move( *paramInit ) );

                    ++varId;

                    if( auto zv = BuildZ3ExprFromValue( m_builder, paramVal ) )
                    {
                        ForEachPredicate( m_builder, type, zv->expr, [&]( auto&& z3expr, auto locId )
                        {

                            m_builder.assume( z3expr );
                        } );
                    }

                    return true;
                } );

                // Add assumptions of the function's requirements
                const auto& reqs = m_func->type().verifInfos()->preConds();
                reqs->forEach( [&]( auto&& val )
                {
                    // Don't do any error handling here, it should already have been taken care of
                    // by the condition verifier.
                    if( auto zv = BuildZ3ExprFromValue( m_builder, val ) )
                        m_builder.assume( zv->expr );
                } );
            }

            bool result = buildZ3Expressions( *m_cfg->entryBB(), nullptr );

            if( ms_TraceMode )







|
>


|
|



|
<
|
|
>

|
<










|







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
                        )
                    );

                    if( !result )
                        return true;

                    auto&& [type, val, locId] = *result;
                    auto paramVal = BuildComputedValue( type, VarAddr( varId, type, locId ), Load( type, locId ) )
                        .setLocationId( locId );

                    // Initialize every parameter containing variable with a freshly named constant of the right type.
                    auto paramInit = BuildZ3ConstantFromType( m_builder, type, format( "p{}", varId ) );
                    m_builder.setVar( varId, move( paramInit ) );

                    ++varId;

                    auto zv = BuildZ3ExprFromValue( m_builder, paramVal );

                    ForEachPredicate( m_builder, type, zv.expr, [&]( auto&& z3expr, auto locId )
                    {
                        if( z3expr.is_bool() )
                            m_builder.assume( z3expr );
                    } );


                    return true;
                } );

                // Add assumptions of the function's requirements
                const auto& reqs = m_func->type().verifInfos()->preConds();
                reqs->forEach( [&]( auto&& val )
                {
                    // Don't do any error handling here, it should already have been taken care of
                    // by the condition verifier.
                    if( auto zv = TryBuildZ3ExprFromValue( m_builder, val ) )
                        m_builder.assume( zv->expr );
                } );
            }

            bool result = buildZ3Expressions( *m_cfg->entryBB(), nullptr );

            if( ms_TraceMode )
Changes to bs/verify/func.h.
32
33
34
35
36
37
38

39
40
41
42
43
44
45

            template< typename T >
            bool handleTerminator( uint32_t bbIndex, const T& t )
            {
                return true;
            }


            bool handleTerminator( uint32_t bbIndex, const cir::Ret& t );
            bool handleTerminator( uint32_t bbIndex, const cir::Branch& t );
            bool handleTerminator( uint32_t bbIndex, const cir::CondBranch& t );
            bool handleTerminator( uint32_t bbIndex, const cir::GhostBranch& t );

            z3::expr makeEdgeExpression( uint32_t srcBBId, uint32_t destBBId, const z3::expr& cond );








>







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

            template< typename T >
            bool handleTerminator( uint32_t bbIndex, const T& t )
            {
                return true;
            }

            bool handleTerminator( uint32_t bbIndex, const cir::RetVoid& t );
            bool handleTerminator( uint32_t bbIndex, const cir::Ret& t );
            bool handleTerminator( uint32_t bbIndex, const cir::Branch& t );
            bool handleTerminator( uint32_t bbIndex, const cir::CondBranch& t );
            bool handleTerminator( uint32_t bbIndex, const cir::GhostBranch& t );

            z3::expr makeEdgeExpression( uint32_t srcBBId, uint32_t destBBId, const z3::expr& cond );

Changes to bs/verify/gfctracker.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 "verify.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::verify;

uint32_t GFCTracker::getGFIndex( const cir::GhostCall& gc )
{
    uint32_t gfIndex = 0;
    auto it = m_ghostFuncIndices.find( gc.func() );
    if( it != m_ghostFuncIndices.end() )
        gfIndex = it->second;
    else
    {
        gfIndex = m_ghostFuncIndices.size();
        m_ghostFuncIndices.emplace( gc.func(), gfIndex );
    }

    return gfIndex;
}

z3::expr GFCTracker::buildFunctionApplication( Builder& b, uint32_t uid, const GhostFuncDeclCache::GFDecl& gfDecl, const Value& gf )
{






|


|





|







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

using namespace goose;
using namespace goose::verify;

uint32_t GFCTracker::getGFIndex( const GhostFuncApplication& gfa )
{
    uint32_t gfIndex = 0;
    auto it = m_ghostFuncIndices.find( gfa.func() );
    if( it != m_ghostFuncIndices.end() )
        gfIndex = it->second;
    else
    {
        gfIndex = m_ghostFuncIndices.size();
        m_ghostFuncIndices.emplace( gfa.func(), gfIndex );
    }

    return gfIndex;
}

z3::expr GFCTracker::buildFunctionApplication( Builder& b, uint32_t uid, const GhostFuncDeclCache::GFDecl& gfDecl, const Value& gf )
{
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
                SubTerm(),
                Val< LocationId >()
            )
        );
        assert( result );
        auto&& [sort, type, val, locId] = *result;

        z3args.emplace_back( BuildZ3ConstantFromType( b, type, format( "gca{}_{}", argIndex, uid ) )->expr );
        ++argIndex;
        return true;
    } );

    return gfDecl.z3decl( z3args.size(), z3args.data() );
}

z3::expr GFCTracker::buildFunctionApplication( Builder& b, uint32_t uid, const GhostFuncDeclCache::GFDecl& gfDecl, const cir::GhostCall& gc )
{
    llvm::SmallVector< z3::expr, 8 > z3args;
    z3args.reserve( VecSize( gc.args() ) + 1 );
    z3args.emplace_back( GetZ3Context().int_val( uid ) );

    ForEachInVectorTerm( gc.args(), [&]( auto&& t )
    {
        z3args.emplace_back( BuildZ3ExprFromValue( b, *EIRToValue( t ) )->expr );
        return true;
    } );

    return gfDecl.z3decl( z3args.size(), z3args.data() );
}

uint32_t GFCTracker::getCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex ) const
{
    const auto* gfcs = m_gfcStorage.get( gfIndex );







|







|


|


|
<
|
<
<







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

61


62
63
64
65
66
67
68
                SubTerm(),
                Val< LocationId >()
            )
        );
        assert( result );
        auto&& [sort, type, val, locId] = *result;

        z3args.emplace_back( BuildZ3ConstantFromType( b, type, format( "gca{}_{}", argIndex, uid ) ).expr );
        ++argIndex;
        return true;
    } );

    return gfDecl.z3decl( z3args.size(), z3args.data() );
}

z3::expr GFCTracker::buildFunctionApplication( Builder& b, uint32_t uid, const GhostFuncDeclCache::GFDecl& gfDecl, const GhostFuncApplication& gfa )
{
    llvm::SmallVector< z3::expr, 8 > z3args;
    z3args.reserve( gfa.args().size() + 1 );
    z3args.emplace_back( GetZ3Context().int_val( uid ) );

    for( auto&& zv : gfa.args() )

        z3args.emplace_back( zv.expr );



    return gfDecl.z3decl( z3args.size(), z3args.data() );
}

uint32_t GFCTracker::getCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex ) const
{
    const auto* gfcs = m_gfcStorage.get( gfIndex );
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147

148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182

183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228

229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248

249
250
251
252
253
254
255
256
257
258
259
void GFCTracker::setCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex, uint32_t uid )
{
    auto* gfcs = m_gfcStorage.get( gfIndex );
    if( !gfcs )
    {
        GFCState newGFCS;
        newGFCS.resize( bbIndex + 1, 0 );
        gfcs = &m_gfcStorage.set( gfIndex, move( newGFCS ) );

    }
    else if( gfcs->size() <= bbIndex )
        gfcs->resize( bbIndex + 1, 0 );

    (*gfcs)[ bbIndex ] = uid;
}

void GFCTracker::setForBasicBlock( Builder& b, uint32_t bbIndex, const cir::GhostCall& gc,
    uint32_t gfIndex, const GhostFuncDeclCache::GFDecl& gfDecl, z3::expr&& expr )
{
    uint32_t oldUid = getCurrentUidForBasicBlock( bbIndex, gfIndex );
    uint32_t newUid = b.newUniqueId();

    auto& c = GetZ3Context();
    auto predicate = c.bool_const( format( "b{}", bbIndex ).c_str() );

    if( oldUid == 0 )
    {
        auto apply = buildFunctionApplication( b, newUid, gfDecl, gc );
        b.add( z3::implies( predicate, apply == Coerce( expr, { apply, gfDecl.returnType } ) ) );

    }
    else
    {
        // Not the first time we set a value for this ghost func:
        // we need to express that its new value at the current assignation point is the new one only if
        // the args are equal to the passed args, otherwise it is equal to whatever it was at its previous
        // assignment.
        auto argCount = VecSize( gc.args() );

        llvm::SmallVector< z3::expr, 8 > z3args;
        z3args.reserve( argCount + 1 );
        z3args.emplace_back( c.int_val( newUid ) );

        uint32_t argIndex = 0;

        z3::expr_vector z3argComparisons( c );
        z3argComparisons.resize( argCount );

        z3::expr_vector z3argsVars( c );
        z3argsVars.resize( argCount );

        ForEachInVectorTerm( gc.args(), [&]( auto&& t )
        {
            auto arg = *EIRToValue( t );

            auto argConst = BuildZ3ConstantFromType( b, arg.type(), format( "gca{}_{}", argIndex, newUid ) )->expr;
            z3args.emplace_back( argConst );
            z3argsVars.set( argIndex, argConst );

            auto argExpr = BuildZ3ExprFromValue( b, arg );
            if( argExpr )
            {
                auto comp = argConst == argExpr->expr;
                z3argComparisons.set( argIndex, comp );
            }

            ++argIndex;
            return true;
        } );


        auto newApply = gfDecl.z3decl( z3args.size(), z3args.data() );

        z3args[0] = c.int_val( oldUid );
        auto previousApply = gfDecl.z3decl( z3args.size(), z3args.data() );

        // AAAAAAAH
        b.add( z3::forall( z3argsVars, z3::implies( predicate, newApply == z3::ite(
            z3::mk_and( z3argComparisons ),
            Coerce( expr, { newApply, gfDecl.returnType } ),
            previousApply
        ) ) ) );
    }

    setCurrentUidForBasicBlock( bbIndex, gfIndex, newUid );
}

optional< Z3Val > GFCTracker::retrieve( Builder& b, uint32_t bbIndex, const cir::GhostCall& gc )
{
    if( bbIndex == b.currentBBIndex() )
        return nullopt;

    if( bbIndex == ~0U )
        bbIndex = b.currentBBIndex();

    auto gfDecl = GhostFuncDeclCache::GetInstance()->getDecl( b.context(), gc.func() );
    if( !gfDecl )
        return nullopt;

    auto gft = *FromValue< builtins::FuncType >( *EIRToValue( gc.func().type() ) );

    uint32_t gfIndex = getGFIndex( gc );
    auto uid = getCurrentUidForBasicBlock( bbIndex, gfIndex );
    if( uid )
        return Z3Val{ buildFunctionApplication( b, uid, *gfDecl, gc ), *EIRToValue( gft.returnType() ) };


    if( !b.cfg() )
        return nullopt;

    uid = b.newUniqueId();
    auto& c = GetZ3Context();

    optional< z3::expr > lhsExpr;
    llvm::SmallVector< z3::expr, 8 > z3args;
    z3::expr_vector z3argsVars( c );
    optional< z3::expr > newApply;

    // Model the ssa phi operation by creating a series of equality assertions, each implied by
    // one of the possible incoming edge condition for the current basic block.
    if( b.remapper() )
    {
        b.remapper()->forEachIncomingEdge( bbIndex, [&]( auto&& predIndex, auto&& expr )
        {
            if( auto predVal = retrieve( b, predIndex, gc ) )
            {
                if( !lhsExpr )
                    lhsExpr = buildFunctionApplication( b, uid, *gfDecl, gc );

                if( z3args.empty() )
                {
                    auto argCount = VecSize( gc.args() );

                    z3args.reserve( argCount + 1 );
                    z3args.emplace_back( c.int_val( uid ) );

                    z3argsVars.resize( argCount );

                    uint32_t argIndex = 0;

                    ForEachInVectorTerm( gc.args(), [&]( auto&& t )
                    {
                        auto arg = *EIRToValue( t );

                        auto argConst = BuildZ3ConstantFromType( b, arg.type(), format( "gca{}_{}", argIndex, uid ) )->expr;
                        z3args.emplace_back( argConst );
                        z3argsVars.set( argIndex, argConst );


                        ++argIndex;
                        return true;
                    } );


                    newApply = gfDecl->z3decl( z3args.size(), z3args.data() );
                }

                auto predUid = getCurrentUidForBasicBlock( predIndex, gfIndex );
                z3args[0] = c.int_val( predUid );
                auto previousApply = gfDecl->z3decl( z3args.size(), z3args.data() );

                b.add( z3::forall( z3argsVars, z3::implies( c.bool_const( format( "e{}_{}", predIndex, bbIndex ).c_str() ),
                    *newApply == previousApply ) ) );
            }
        } );
    }

    if( !lhsExpr )
        return nullopt;

    setCurrentUidForBasicBlock( bbIndex, gfIndex, uid );
    setForBasicBlock( b, bbIndex, gc, gfIndex, *gfDecl, move( *lhsExpr ) );
    return Z3Val{ buildFunctionApplication( b, uid, *gfDecl, gc ), *EIRToValue( gft.returnType() ) };

}

void GFCTracker::set( Builder& b, const cir::GhostCall& gc, Z3Val&& val )
{
    auto gfDecl = GhostFuncDeclCache::GetInstance()->getDecl( b.context(), gc.func() );
    if( !gfDecl )
        return;

    uint32_t gfIndex = getGFIndex( gc );
    return setForBasicBlock( b, b.currentBBIndex(), gc, gfIndex, *gfDecl, move( val.expr ) );
}







|
>







|










|
|
>







|













|

<
<
|



<
<
<
|
|
|
<

<
<
>









|







|







|



|

|


|
>


















|


|



|








|

<
<
|



<

<
<
>


















|
|
>


|

|



|
|

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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130


131
132
133
134



135
136
137

138


139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211


212
213
214
215

216


217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
void GFCTracker::setCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex, uint32_t uid )
{
    auto* gfcs = m_gfcStorage.get( gfIndex );
    if( !gfcs )
    {
        GFCState newGFCS;
        newGFCS.resize( bbIndex + 1, 0 );
        auto& gfcsRef = m_gfcStorage.set( gfIndex, move( newGFCS ) );
        gfcs = &gfcsRef;
    }
    else if( gfcs->size() <= bbIndex )
        gfcs->resize( bbIndex + 1, 0 );

    (*gfcs)[ bbIndex ] = uid;
}

void GFCTracker::setForBasicBlock( Builder& b, uint32_t bbIndex, const GhostFuncApplication& gfa,
    uint32_t gfIndex, const GhostFuncDeclCache::GFDecl& gfDecl, z3::expr&& expr )
{
    uint32_t oldUid = getCurrentUidForBasicBlock( bbIndex, gfIndex );
    uint32_t newUid = b.newUniqueId();

    auto& c = GetZ3Context();
    auto predicate = c.bool_const( format( "b{}", bbIndex ).c_str() );

    if( oldUid == 0 )
    {
        auto apply = buildFunctionApplication( b, newUid, gfDecl, gfa );
        b.add( z3::implies( predicate, apply == Coerce( expr, { apply, gfDecl.returnType,
            gfa.locationId() } ) ) );
    }
    else
    {
        // Not the first time we set a value for this ghost func:
        // we need to express that its new value at the current assignation point is the new one only if
        // the args are equal to the passed args, otherwise it is equal to whatever it was at its previous
        // assignment.
        auto argCount = gfa.args().size();

        llvm::SmallVector< z3::expr, 8 > z3args;
        z3args.reserve( argCount + 1 );
        z3args.emplace_back( c.int_val( newUid ) );

        uint32_t argIndex = 0;

        z3::expr_vector z3argComparisons( c );
        z3argComparisons.resize( argCount );

        z3::expr_vector z3argsVars( c );
        z3argsVars.resize( argCount );

        for( auto&& arg : gfa.args() )
        {


            auto argConst = BuildZ3ConstantFromType( b, arg.type, format( "gca{}_{}", argIndex, newUid ) ).expr;
            z3args.emplace_back( argConst );
            z3argsVars.set( argIndex, argConst );




            auto comp = argConst == arg.expr;
            z3argComparisons.set( argIndex, comp );


            ++argIndex;


        }

        auto newApply = gfDecl.z3decl( z3args.size(), z3args.data() );

        z3args[0] = c.int_val( oldUid );
        auto previousApply = gfDecl.z3decl( z3args.size(), z3args.data() );

        // AAAAAAAH
        b.add( z3::forall( z3argsVars, z3::implies( predicate, newApply == z3::ite(
            z3::mk_and( z3argComparisons ),
            Coerce( expr, { newApply, gfDecl.returnType, gfa.locationId() } ),
            previousApply
        ) ) ) );
    }

    setCurrentUidForBasicBlock( bbIndex, gfIndex, newUid );
}

optional< Z3Val > GFCTracker::retrieve( Builder& b, uint32_t bbIndex, const GhostFuncApplication& gfa )
{
    if( bbIndex == b.currentBBIndex() )
        return nullopt;

    if( bbIndex == ~0U )
        bbIndex = b.currentBBIndex();

    auto gfDecl = GhostFuncDeclCache::GetInstance()->getDecl( b.context(), gfa.func() );
    if( !gfDecl )
        return nullopt;

    auto gft = *FromValue< builtins::FuncType >( *EIRToValue( gfa.func().type() ) );

    uint32_t gfIndex = getGFIndex( gfa );
    auto uid = getCurrentUidForBasicBlock( bbIndex, gfIndex );
    if( uid )
        return Z3Val{ buildFunctionApplication( b, uid, *gfDecl, gfa ), *EIRToValue( gft.returnType() ),
            gfa.locationId() };

    if( !b.cfg() )
        return nullopt;

    uid = b.newUniqueId();
    auto& c = GetZ3Context();

    optional< z3::expr > lhsExpr;
    llvm::SmallVector< z3::expr, 8 > z3args;
    z3::expr_vector z3argsVars( c );
    optional< z3::expr > newApply;

    // Model the ssa phi operation by creating a series of equality assertions, each implied by
    // one of the possible incoming edge condition for the current basic block.
    if( b.remapper() )
    {
        b.remapper()->forEachIncomingEdge( bbIndex, [&]( auto&& predIndex, auto&& expr )
        {
            if( auto predVal = retrieve( b, predIndex, gfa ) )
            {
                if( !lhsExpr )
                    lhsExpr = buildFunctionApplication( b, uid, *gfDecl, gfa );

                if( z3args.empty() )
                {
                    auto argCount = gfa.args().size();

                    z3args.reserve( argCount + 1 );
                    z3args.emplace_back( c.int_val( uid ) );

                    z3argsVars.resize( argCount );

                    uint32_t argIndex = 0;

                    for( auto&& arg : gfa.args() )
                    {


                        auto argConst = BuildZ3ConstantFromType( b, arg.type, format( "gca{}_{}", argIndex, uid ) ).expr;
                        z3args.emplace_back( argConst );
                        z3argsVars.set( argIndex, argConst );


                        ++argIndex;


                    }

                    newApply = gfDecl->z3decl( z3args.size(), z3args.data() );
                }

                auto predUid = getCurrentUidForBasicBlock( predIndex, gfIndex );
                z3args[0] = c.int_val( predUid );
                auto previousApply = gfDecl->z3decl( z3args.size(), z3args.data() );

                b.add( z3::forall( z3argsVars, z3::implies( c.bool_const( format( "e{}_{}", predIndex, bbIndex ).c_str() ),
                    *newApply == previousApply ) ) );
            }
        } );
    }

    if( !lhsExpr )
        return nullopt;

    setCurrentUidForBasicBlock( bbIndex, gfIndex, uid );
    setForBasicBlock( b, bbIndex, gfa, gfIndex, *gfDecl, move( *lhsExpr ) );
    return Z3Val{ buildFunctionApplication( b, uid, *gfDecl, gfa ), *EIRToValue( gft.returnType() ),
        gfa.locationId() };
}

void GFCTracker::set( Builder& b, const GhostFuncApplication& gfa, Z3Val&& val )
{
    auto gfDecl = GhostFuncDeclCache::GetInstance()->getDecl( b.context(), gfa.func() );
    if( !gfDecl )
        return;

    uint32_t gfIndex = getGFIndex( gfa );
    return setForBasicBlock( b, b.currentBBIndex(), gfa, gfIndex, *gfDecl, move( val.expr ) );
}
Changes to bs/verify/gfctracker.h.
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
28
29
#ifndef GOOSE_VERIFY_GFCTRACKER_H
#define GOOSE_VERIFY_GFCTRACKER_H

namespace goose::verify
{
    class Builder;

    class GFCTracker
    {
        public:




            optional< Z3Val > retrieve( Builder& b, uint32_t bbIndex, const cir::GhostCall& gc );
            void set( Builder& b, const cir::GhostCall& gc, Z3Val&& val );

        private:
            uint32_t getGFIndex( const cir::GhostCall& gc );
            z3::expr buildFunctionApplication( Builder& b, uint32_t uid, const GhostFuncDeclCache::GFDecl& gfDecl, const Value& gf );
            z3::expr buildFunctionApplication( Builder& b, uint32_t uid, const GhostFuncDeclCache::GFDecl& gfDecl, const cir::GhostCall& gc );

            uint32_t getCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex ) const;
            void setCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex, uint32_t uid );

            void setForBasicBlock( Builder& b, uint32_t bbIndex, const cir::GhostCall& gc,
                uint32_t gfIndex, const GhostFuncDeclCache::GFDecl& gfDecl, z3::expr&& expr );

            // Each ghost func have a unique index within the current function.
            // (0: invalid index)
            unordered_map< Value, uint32_t > m_ghostFuncIndices;

            // For each ghostfunc closure, store its last index.










>
>
>
>
|
|


|

|




|







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
28
29
30
31
32
33
#ifndef GOOSE_VERIFY_GFCTRACKER_H
#define GOOSE_VERIFY_GFCTRACKER_H

namespace goose::verify
{
    class Builder;

    class GFCTracker
    {
        public:
            GFCTracker( size_t size ) :
                m_gfcStorage( size )
            {}

            optional< Z3Val > retrieve( Builder& b, uint32_t bbIndex, const GhostFuncApplication& gfa );
            void set( Builder& b, const GhostFuncApplication& gfa, Z3Val&& val );

        private:
            uint32_t getGFIndex( const GhostFuncApplication& gfa );
            z3::expr buildFunctionApplication( Builder& b, uint32_t uid, const GhostFuncDeclCache::GFDecl& gfDecl, const Value& gf );
            z3::expr buildFunctionApplication( Builder& b, uint32_t uid, const GhostFuncDeclCache::GFDecl& gfDecl, const GhostFuncApplication& gfa );

            uint32_t getCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex ) const;
            void setCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex, uint32_t uid );

            void setForBasicBlock( Builder& b, uint32_t bbIndex, const GhostFuncApplication& gfa,
                uint32_t gfIndex, const GhostFuncDeclCache::GFDecl& gfDecl, z3::expr&& expr );

            // Each ghost func have a unique index within the current function.
            // (0: invalid index)
            unordered_map< Value, uint32_t > m_ghostFuncIndices;

            // For each ghostfunc closure, store its last index.
Changes to bs/verify/ghostfunc.h.
17
18
19
20
21
22
23

























24
25
26
            optional< GFDecl > getDecl( const sema::Context& c, const Value& gf );

        private:
            optional< GFDecl > createDecl( const sema::Context& c, const Value& gf );

            unordered_map< Value, GFDecl > m_decls;
    };

























}

#endif







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



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
44
45
46
47
48
49
50
51
            optional< GFDecl > getDecl( const sema::Context& c, const Value& gf );

        private:
            optional< GFDecl > createDecl( const sema::Context& c, const Value& gf );

            unordered_map< Value, GFDecl > m_decls;
    };

    class GhostFuncApplication
    {
        public:
            GhostFuncApplication( const cir::Value& func, LocationId loc ) :
                m_func( func ),
                m_loc( loc )
            {}

            GhostFuncApplication( cir::Value&& func, LocationId loc ) :
                m_func( move( func ) ),
                m_loc( loc )
            {}

            const auto& func() const { return m_func; }
            const auto& args() const { return m_args; }
            auto& args() { return m_args; }

            auto locationId() const { return m_loc; }

        private:
            eir::Value m_func;
            llvm::SmallVector< Z3Val, 4 > m_args;
            LocationId m_loc;
    };
}

#endif
Changes to bs/verify/helpers.inl.
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

        auto& tp = **optTypePreds;
        const auto* prevValPH = b.retrievePlaceholder( "@val"_sid );
        b.setPlaceholder( "@val"_sid, valExpr );

        tp.forEach( [&]( auto&& p )
        {
            if( auto zv = BuildZ3ExprFromValue( b, p ) )
                func( zv->expr, p.locationId() );

            return true;
        } );

        if( prevValPH )
            b.setPlaceholder( "@val"_sid, *prevValPH );
        else







|
|







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

        auto& tp = **optTypePreds;
        const auto* prevValPH = b.retrievePlaceholder( "@val"_sid );
        b.setPlaceholder( "@val"_sid, valExpr );

        tp.forEach( [&]( auto&& p )
        {
            auto zv = BuildZ3ExprFromValue( b, p );
            func( zv.expr, p.locationId() );

            return true;
        } );

        if( prevValPH )
            b.setPlaceholder( "@val"_sid, *prevValPH );
        else
Added bs/verify/instrseq.cpp.
















































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
#include "verify.h"
#include "builtins/builtins.h"
#include "diagnostics/diagnostics.h"
#include "helpers.inl"

using namespace goose;
using namespace goose::verify;
using namespace goose::diagnostics;

namespace
{
    bool CheckAssertion( z3::solver& solver, const z3::expr& expr, const z3::expr& exprToCheck, LocationId locationId )
    {
        if( verify::Func::TraceMode() )
            return true;

        ProfileZoneScoped;

        z3::expr_vector exprVec( GetZ3Context() );
        exprVec.push_back( exprToCheck );

        solver.push();

        bool result = false;

        switch( solver.check( exprVec ) )
        {
            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.

                if( verify::Func::DumpSolverOnSuccess() )
                    cout << "Solver dump:\n" << solver << "check_unsat: " << exprToCheck << endl << endl;

                result = true;
                break;

            case z3::check_result::sat:
                DiagnosticsManager::GetInstance().emitErrorMessage( locationId, "assert"_sid,
                    "this condition may not be met." );

                if( verify::Func::DumpSolverOnFailure() )
                {
                    cout << "Solver dump:\n" << solver << "check_unsat: " << exprToCheck << endl << endl;
                    cout << "Model dump:\n" << solver.get_model() << endl << endl;
                }

                result = false;
                break;

            default:
                DiagnosticsManager::GetInstance().emitErrorMessage( locationId, "assert"_sid,
                    "couldn't verify this condition." );

                if( verify::Func::DumpSolverOnFailure() )
                    cout << "Solver dump:\n" << solver << "check_unsat: " << exprToCheck << endl << endl;

                result = false;
                break;
        }

        solver.pop();
        return result;
    }

}

namespace goose::verify
{
    bool VerifyInstrSeq( const sema::Context& c, const InstrSeq& is )
    {
        z3::solver solver( GetZ3Context(), z3::solver::simple() );
        Builder b( c, solver, 0 );

        if( Func::TraceMode() )
        {
            cout << "=== Checking instr seq ===\n";
            b.setTraceMode( true );
        }

        b.setAssertionHandler( [&]( auto&& expr, auto&& exprToCheck, LocationId locationId )
        {
            return CheckAssertion( solver, expr, exprToCheck, locationId );
        } );

        return BuildZ3Op( b, is ) && !b.hasCheckFailed();
    }
}
Changes to bs/verify/loop.cpp.
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
            // Havoc all the addresses modified during the current loop and emit
            // the first unrolling for the induction case.
            m_remapper.nextLoopIteration();
            uint32_t firstInductionIterationHeaderIndex = m_remapper.remapBBId( header );
            m_builder.setCurrentBB( firstInductionIterationHeaderIndex );

            if( ms_TraceMode )
                cout << "  == Havocing modified addresses for loop " << header.index() << endl;

            m_cfg->forEachAddressModifiedByLoop( header.index(), [&]( auto&& type, auto&& addr )
            {
                HavocAddress( m_builder, firstInductionIterationHeaderIndex, type, addr );
            } );

            if( ms_TraceMode )
                cout << "  == Unrolling first induction case for loop " << header.index() << endl;

            buildZ3Expressions( header, &parentWorkQueue );








|

|

|







75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
            // Havoc all the addresses modified during the current loop and emit
            // the first unrolling for the induction case.
            m_remapper.nextLoopIteration();
            uint32_t firstInductionIterationHeaderIndex = m_remapper.remapBBId( header );
            m_builder.setCurrentBB( firstInductionIterationHeaderIndex );

            if( ms_TraceMode )
                cout << "  == Havocing modified storage locations for loop " << header.index() << endl;

            m_cfg->forEachStorageLocationModifiedByLoop( header.index(), [&]( auto&& type, auto&& sloc )
            {
                HavocStorageLocation( m_builder, firstInductionIterationHeaderIndex, type, sloc );
            } );

            if( ms_TraceMode )
                cout << "  == Unrolling first induction case for loop " << header.index() << endl;

            buildZ3Expressions( header, &parentWorkQueue );

Changes to bs/verify/meson.build.
1
2
3
4
5
6
7
8

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
goose_verify = library( 'goose-verify',
    'verify.cpp',
    'type.cpp',
    'value.cpp',
    'storage.cpp',
    'phi.cpp',
    'call.cpp',
    'builder.cpp',

    'remapper.cpp',
    'vartracker.cpp',
    'gfctracker.cpp',
    'propositions.cpp',
    'func.cpp',
    'ghostfunc.cpp',
    'cfg.cpp',
    'loop.cpp',
    'basicblock.cpp',
    'terminator.cpp',
    'comptime.cpp',
    'verifyviz.cpp',

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








>










|





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
goose_verify = library( 'goose-verify',
    'verify.cpp',
    'type.cpp',
    'value.cpp',
    'storage.cpp',
    'phi.cpp',
    'call.cpp',
    'builder.cpp',
    'stack.cpp',
    'remapper.cpp',
    'vartracker.cpp',
    'gfctracker.cpp',
    'propositions.cpp',
    'func.cpp',
    'ghostfunc.cpp',
    'cfg.cpp',
    'loop.cpp',
    'basicblock.cpp',
    'terminator.cpp',
    'instrseq.cpp',
    'verifyviz.cpp',

    include_directories: bsinc,
    dependencies: [z3_dep, tracy_dep]
)
Changes to bs/verify/phi.cpp.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include "verify.h"
#include "builtins/builtins.h"

namespace goose::verify
{
    optional< Z3Val > BuildZ3Op( Builder& b, const Phi& instr )
    {
        const auto* remapper = b.remapper();
        if( !remapper )
            return nullopt;

        auto newVar = BuildZ3ConstantFromType( b, instr.type(), format( "v{}", b.newUniqueId() ) );
        if( !newVar )
            return nullopt;

        auto& c = GetZ3Context();
        uint32_t bbIndex = b.currentBBIndex();

        // Model the ssa phi operation by creating a series of equality assertions, each implied by
        // one of the possible incoming edge condition for the current basic block.
        remapper->forEachIncomingEdge( bbIndex, [&]( auto&& predIndex, auto&& predExpr )
        {
            uint32_t origPredIndex = remapper->getOriginalBBIndex( predIndex );

            // Match the incoming edge with the correct one in the phi instruction.
            instr.forAllIncomings( [&]( auto&& bb, auto&& val )
            {
                if( bb->index() != origPredIndex )
                    return true;

                auto zv = BuildZ3ExprFromValue( b, val );
                if( !zv )
                    return true;

                b.add( z3::implies( c.bool_const( format( "e{}_{}", predIndex, bbIndex ).c_str() ),
                    newVar->expr == Coerce( *zv, *newVar ) ) );

                return true;
            } );
        } );

        b.setVar( instr.destIndex(), Z3Val( *newVar ) );
        return *newVar;
    }
}





|



|


<
<

















<
<
<

|





|
|


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
28
29



30
31
32
33
34
35
36
37
38
39
40
#include "verify.h"
#include "builtins/builtins.h"

namespace goose::verify
{
    bool BuildZ3Op( Builder& b, const Phi& instr )
    {
        const auto* remapper = b.remapper();
        if( !remapper )
            return false;

        auto newVar = BuildZ3ConstantFromType( b, instr.type(), format( "v{}", b.newUniqueId() ) );



        auto& c = GetZ3Context();
        uint32_t bbIndex = b.currentBBIndex();

        // Model the ssa phi operation by creating a series of equality assertions, each implied by
        // one of the possible incoming edge condition for the current basic block.
        remapper->forEachIncomingEdge( bbIndex, [&]( auto&& predIndex, auto&& predExpr )
        {
            uint32_t origPredIndex = remapper->getOriginalBBIndex( predIndex );

            // Match the incoming edge with the correct one in the phi instruction.
            instr.forAllIncomings( [&]( auto&& bb, auto&& val )
            {
                if( bb->index() != origPredIndex )
                    return true;

                auto zv = BuildZ3ExprFromValue( b, val );



                b.add( z3::implies( c.bool_const( format( "e{}_{}", predIndex, bbIndex ).c_str() ),
                    newVar.expr == Coerce( zv, newVar ) ) );

                return true;
            } );
        } );

        b.setVar( instr.destIndex(), move( newVar ) );
        return true;
    }
}
Changes to bs/verify/propositions.cpp.
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
28
29
30
31
#include "verify.h"
#include "diagnostics/diagnostics.h"
#include "builtins/builtins.h"

using namespace goose::verify;
using namespace goose::diagnostics;

Propositions::Propositions( const sema::Context& c ) :
    m_context( c ),
    m_solver( GetZ3Context() ),
    m_builder( m_context, m_solver )
{}

bool Propositions::addPropositions( const ValueVec& assList )
{
    for( auto&& val : assList )
    {
        if( val.isPoison() )
            return false;

        if( val.type() != GetValueType< bool >() )
            continue;

        auto zv = BuildZ3ExprFromValue( m_builder, val );
        if( !zv )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( val.locationId(),
                "invalid proposition." );
            return false;
        }











|












|







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
28
29
30
31
#include "verify.h"
#include "diagnostics/diagnostics.h"
#include "builtins/builtins.h"

using namespace goose::verify;
using namespace goose::diagnostics;

Propositions::Propositions( const sema::Context& c ) :
    m_context( c ),
    m_solver( GetZ3Context() ),
    m_builder( m_context, m_solver, 0 )
{}

bool Propositions::addPropositions( const ValueVec& assList )
{
    for( auto&& val : assList )
    {
        if( val.isPoison() )
            return false;

        if( val.type() != GetValueType< bool >() )
            continue;

        auto zv = TryBuildZ3ExprFromValue( m_builder, val );
        if( !zv )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( val.locationId(),
                "invalid proposition." );
            return false;
        }

Added bs/verify/stack.cpp.




















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
#include "verify.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::builtins;

namespace goose::verify
{
    optional< Z3Val > Stack::PopAsZ3Val( Builder& b )
    {
        auto result = visit( [&]< typename T >( const T& elem ) -> optional< Z3Val >
        {
            if constexpr( is_same_v< T, Value > )
                return BuildZ3ExprFromValue( b, elem );
            else if constexpr( is_same_v< T, Address > )
                return BuildAddressExpr( elem );
            else
                return nullopt;
        }, m_stack.top() );

        if( result )
            m_stack.pop();

        return result;
    }
}
Added bs/verify/stack.h.




















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#ifndef GOOSE_VERIFY_STACK_H
#define GOOSE_VERIFY_STACK_H

namespace goose::verify
{
    class Stack
    {
        public:
            template< typename T >
            void push( T&& x )
            {
                m_stack.push( forward< T >( x ) );
            }

            template< typename T >
            optional< T > pop( Builder& b )
            {
                if( m_stack.empty() )
                    return nullopt;

                if( !holds_alternative< T >( m_stack.top() ) )
                {
                    if constexpr( is_same_v< T, Z3Val > )
                        return PopAsZ3Val( b );
                    else
                        return nullopt;
                }

                optional< T > result = move( get< T >( m_stack.top() ) );
                m_stack.pop();
                return result;
            }

        private:
            optional< Z3Val > PopAsZ3Val( Builder& b );

            using Slot = variant< Value, Z3Val, Address, GhostFuncApplication >;
            stack< Slot > m_stack;
    };
}

#endif
Changes to bs/verify/storage.cpp.
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
28
29
30
31
32
33

34

35
36


37




38
39
40
41
42
43

44
45
46



47

48
49
50


51
52
53
54
55
56

57
58
59
60
61



62
63
64
65
66
67
68
69
70
71
72
73
74
#include "verify.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::builtins;

namespace goose::verify
{




    template< typename T >
    optional< Z3Val > LoadFromAddress( Builder& b, const T& )
    {



        return nullopt;
    }

    optional< Z3Val > LoadFromAddress( Builder& b, const cir::Instruction& addr )
    {

        return visit( [&]( auto&& instr )

        {
            return LoadFromAddress( b, instr );
        }, addr.content() );

    }




    optional< Z3Val > LoadFromAddress( Builder& b, const cir::VarAddr& va )
    {
        auto zv = b.retrieveVar( va.varIndex() );
        if( zv )
            return zv;

        auto v = BuildZ3ConstantFromType( b, va.type(), format( "var{}", b.newUniqueId() ) );
        if( !v )
            return nullopt;

        return b.setVar( va.varIndex(), move( *v ) );

    }


    optional< Z3Val > LoadFromAddress( Builder& b, const cir::TempAddr& ta )


    {




        auto zv = b.retrieveVar( ta.tempIndex() );
        if( zv )
            return zv;

        return b.setVar( ta.tempIndex(), ta.initValue() );
    }


    optional< Z3Val > LoadFromAddress( Builder& b, const cir::Select& s )
    {



        auto val = LoadFromAddress( b, *s.baseAddr() );

        if( !val )
            return nullopt;



        auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), ValueToEIR( val->type ) );
        if( !tinfo )
            return nullopt;

        // The only aggregate type that we handle for now are tuples.
        // TODO: arrays

        auto elemType = GetTupleTypeElement( val->type, s.memberIndex() );
        auto elemExpr = tinfo->proj( val->expr, s.memberIndex() );
        return Z3Val{ move( elemExpr ), *EIRToValue( elemType ) };
    }




    optional< Z3Val > LoadFromAddress( Builder& b, const cir::GhostCall& gc )
    {
        return b.retrieveGFC( gc );
    }

    using SelectPath = llvm::SmallVector< uint32_t, 8 >;
    optional< z3::expr > ModifyAggregate( Builder& b, const Z3Val& aggregate, const SelectPath& path, uint32_t index, Z3Val&& valToStore )
    {
        auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), ValueToEIR( aggregate.type ) );
        if( !tinfo )
            return nullopt;

        // The only aggregate type that we handle for now are tuples.








>
>
>
>
|
|

>
>
>
|


|

>
|
>

|
|
>
|
>
>
>
|
<
|
<
<
|
|
|
|
<
|
<
>
|
>
|
|
>
>
|
>
>
>
>
|
<
|
|
<
|
>
|
|
|
>
>
>
|
>



>
>
|
|
|

|
|
>
|
|
|
|

>
>
>
|

|


<







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
28
29
30
31
32
33
34
35

36


37
38
39
40

41

42
43
44
45
46
47
48
49
50
51
52
53
54

55
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
#include "verify.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::builtins;

namespace goose::verify
{
    optional< Z3Val > LoadVar( Builder& b, uint32_t index )
    {
        return b.retrieveVar( index );
    }

    optional< Z3Val > LoadVar( Builder& b, uint32_t index, const Term& type )
    {
        auto result = b.retrieveVar( index );
        if( !result )
            result = BuildZ3ConstantFromType( b, type, format( "var{}", index ) );
        return result;
    }

    optional< Z3Val > LoadFromBaseAddress( Builder& b, const Address& addr )
    {
        optional< Z3Val > val;

        switch( addr.origin() )
        {
            case Address::Origin::Stack:
                val = LoadVar( b, addr.path().front() );
                break;

            case Address::Origin::Heap:
                // TODO
                break;
        }




        return val;
    }

    optional< Z3Val > LoadFromBaseAddress( Builder& b, const Address& addr, const Term& type )

    {

        optional< Z3Val > val;

        switch( addr.origin() )
        {
            case Address::Origin::Stack:
                val = LoadVar( b, addr.path().front(), type );
                break;

            case Address::Origin::Heap:
                // TODO
                break;
        }


        return val;
    }


    optional< Z3Val > LoadFromAddress( Builder& b, const Address& addr, const Term& type )
    {
        optional< Z3Val > val;

        if( addr.path().size() == 1 )
            val = LoadFromBaseAddress( b, addr, type );
        else
            val = LoadFromBaseAddress( b, addr );

        if( !val )
            return nullopt;

        for( uint32_t i = 1; i < addr.path().size(); ++i )
        {
            auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), ValueToEIR( val->type ) );
            if( !tinfo )
                return nullopt;

            // The only aggregate type that we handle for now are tuples.
            // TODO: arrays
            auto memIndex = addr.path()[i];
            auto elemType = GetTupleTypeElement( val->type, memIndex );
            auto elemExpr = tinfo->proj( val->expr, memIndex );
            val = Z3Val{ move( elemExpr ), *EIRToValue( elemType ), addr.locationId() };
        }

        return val;
    }

    optional< Z3Val > LoadFromAddress( Builder& b, const GhostFuncApplication& gfa )
    {
        return b.retrieveGFC( gfa );
    }


    optional< z3::expr > ModifyAggregate( Builder& b, const Z3Val& aggregate, const SelectPath& path, uint32_t index, Z3Val&& valToStore )
    {
        auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), ValueToEIR( aggregate.type ) );
        if( !tinfo )
            return nullopt;

        // The only aggregate type that we handle for now are tuples.
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
















































168
169
170





171
172
173



174


175
176
            // Copy all elements from the existing value as is,
            // except for the one pointed to by the current path index.
            if( i == elemIndex )
            {
                // If we didn't reach the end of the path yet, recurse.
                // Otherwise, it means we finally reached the nested member that we wanted to modify,
                // so push the new value.
                if( index > 0 )
                {
                    auto newElem = ModifyAggregate( b, Z3Val{ move( elemExpr ), *EIRToValue( elemType ) }, path, --index, move( valToStore ) );

                    if( !newElem )
                        return nullopt;

                    args.push_back( move( *newElem ) );
                }
                else
                    args.push_back( valToStore.expr );
            }
            else
                args.push_back( move( elemExpr ) );
        }

        return tinfo->ctor( args );
    }

    template< typename T >
    void StoreToAddress( Builder& b, const T&, Z3Val&& val )
    {}

    void StoreToAddress( Builder& b, const cir::Instruction& addr, Z3Val&& val )
    {
        visit( [&]( auto&& instr )
        {
            StoreToAddress( b, instr, move( val ) );
        }, addr.content() );
    }

    void StoreToAddress( Builder& b, const cir::VarAddr& va, Z3Val&& val )
    {
        b.setVar( va.varIndex(), move( val ) );



    }

    void StoreToAddress( Builder& b, const cir::TempAddr& ta, Z3Val&& val )
    {
        b.setVar( ta.tempIndex(), move( val ) );
    }

    void StoreToAddress( Builder& b, const cir::Select& s, Z3Val&& val )
    {
        SelectPath path;
        path.emplace_back( s.memberIndex() );

        auto pBaseAddr = s.baseAddr();
        for(;;)
        {
            const auto* pSel = get_if< cir::Select >( &pBaseAddr->content() );
            if( !pSel )
                break;

            path.emplace_back( pSel->memberIndex() );
            pBaseAddr = pSel->baseAddr();
        }

        // If the address contains a path (a series of nested Select cir instructions),
        // it means we are storing into a member (of a member) of an aggregate type.
        // In this case, we need to load the existing value of the aggregate,
        // rebuild it with the modified member, and store it.
        auto aggregate = LoadFromAddress( b, *pBaseAddr );
        if( !aggregate )
            return;

        if( auto newAggregateToStore = ModifyAggregate( b, *aggregate, path, 0, move( val ) ) )
            StoreToAddress( b, *pBaseAddr, Z3Val{ *newAggregateToStore, aggregate->type } );
    }

    void StoreToAddress( Builder& b, const cir::GhostCall& gc, Z3Val&& val )
    {
        b.setGFC( gc, move( val ) );
    }

















































    void HavocAddress( Builder& b, uint32_t bbIndex, const Term& type, const cir::Instruction& addr )
    {
        auto valToStore = BuildZ3ConstantFromType( b, type, format( "v{}", b.newUniqueId() ) );





        if( !valToStore )
            return;




        StoreToAddress( b, addr, move( *valToStore ) );


    }
}







|

|
>















<
|
<
|
<
<
|

|
|
<
|
<
|
<
>
>
>
|
|
<
|
<
<
<
|

<
<
|
<
<

|
<
|
|
<
<
|
<
|



|



|
|


|

|


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|


>
>
>
>
>
|
<
|
>
>
>
|
>
>


112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137

138

139


140
141
142
143

144

145

146
147
148
149
150

151



152
153


154


155
156

157
158


159

160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233

234
235
236
237
238
239
240
241
242
            // Copy all elements from the existing value as is,
            // except for the one pointed to by the current path index.
            if( i == elemIndex )
            {
                // If we didn't reach the end of the path yet, recurse.
                // Otherwise, it means we finally reached the nested member that we wanted to modify,
                // so push the new value.
                if( --index > 0 )
                {
                    auto newElem = ModifyAggregate( b, Z3Val{ move( elemExpr ), *EIRToValue( elemType ), aggregate.loc },
                        path, index, move( valToStore ) );
                    if( !newElem )
                        return nullopt;

                    args.push_back( move( *newElem ) );
                }
                else
                    args.push_back( valToStore.expr );
            }
            else
                args.push_back( move( elemExpr ) );
        }

        return tinfo->ctor( args );
    }


    void StoreToBaseAddress( Builder& b, const Address& addr, Z3Val&& val )

    {


        switch( addr.origin() )
        {
            case Address::Origin::Stack:
                b.setVar( addr.path().front(), move( val ) );

                break;



            case Address::Origin::Heap:
                // TODO
                break;
        }
    }





    void StoreToAddress( Builder& b, const Address& addr, Z3Val&& val )
    {


        if( addr.path().size() == 1 )


        {
            StoreToBaseAddress( b, addr, move( val ) );

            return;
        }




        // If the address contains a path (the result of a series of Select cir instructions),
        // it means we are storing into a member (of a member) of an aggregate type.
        // In this case, we need to load the existing value of the aggregate,
        // rebuild it with the modified member, and store it.
        auto aggregate = LoadFromBaseAddress( b, addr );
        if( !aggregate )
            return;

        if( auto newAggregateToStore = ModifyAggregate( b, *aggregate, addr.path(), 1, move( val ) ) )
            StoreToBaseAddress( b, addr, Z3Val{ *newAggregateToStore, aggregate->type, addr.locationId() } );
    }

    void StoreToAddress( Builder& b, const GhostFuncApplication& gfa, Z3Val&& val )
    {
        b.setGFC( gfa, move( val ) );
    }

    Z3Val BuildAddressExpr( const Address& addr )
    {
        auto& zc = GetZ3Context();
        z3::expr_vector zvec( zc );
        zvec.resize( addr.path().size() );

        // TODO handle non-stack origin
        uint32_t i = 0;
        for( auto&& index : addr.path() )
        {
            auto zindex = zc.int_val( index ).unit();
            zvec.set( i++, zindex );
        }

        return
        {
            z3::concat( zvec ),
            *EIRToValue( GetValueType< builtins::MemLoc >() ),
            addr.locationId()
        };
    }

    optional< verify::GhostFuncApplication > CIRGFAToVerifyGFA( Builder& b, const cir::GhostFuncApplication& cgfa )
    {
        verify::GhostFuncApplication vgfa( cgfa.func(), cgfa.locationId() );
        vgfa.args().reserve( cgfa.args().size() );

        for( auto&& a : cgfa.args() )
        {
            visit( [&]< typename T >( const T& x )
            {
                if constexpr( is_same_v< T, eir::Value > )
                {
                    vgfa.args().emplace_back( BuildZ3ExprFromValue( b, x ) );
                }
                else if constexpr( is_same_v< T, Address > )
                {
                    vgfa.args().emplace_back( BuildAddressExpr( x ) );
                }
            }, a );
        }

        if( vgfa.args().size() != cgfa.args().size() )
            return nullopt;

        return vgfa;
    }

    void HavocStorageLocation( Builder& b, uint32_t bbIndex, const Term& type, const StorageLocation& sloc )
    {
        auto valToStore = BuildZ3ConstantFromType( b, type, format( "v{}", b.newUniqueId() ) );

        visit( [&]< typename T >( const T& x )
        {
            if constexpr( is_same_v< T, Address > )
            {
                StoreToAddress( b, x, move( valToStore ) );

            }
            else if constexpr( is_same_v< T, cir::GhostFuncApplication >)
            {
                if( auto vgfa = CIRGFAToVerifyGFA( b, x ) )
                    StoreToAddress( b, *vgfa, move( valToStore ) );
            }
        }, sloc );
    }
}
Changes to bs/verify/storage.h.
1
2
3
4
5





6




7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef GOOSE_VERIFY_STORAGE_H
#define GOOSE_VERIFY_STORAGE_H

namespace goose::verify
{





    extern optional< Z3Val > LoadFromAddress( Builder& b, const cir::Instruction& addr );




    extern optional< Z3Val > LoadFromAddress( Builder& b, const cir::VarAddr& va );
    extern optional< Z3Val > LoadFromAddress( Builder& b, const cir::TempAddr& ta );
    extern optional< Z3Val > LoadFromAddress( Builder& b, const cir::Select& s );
    extern optional< Z3Val > LoadFromAddress( Builder& b, const cir::GhostCall& gc );

    extern void StoreToAddress( Builder& b, const cir::Instruction& addr, Z3Val&& val );
    extern void StoreToAddress( Builder& b, const cir::VarAddr& va, Z3Val&& val );
    extern void StoreToAddress( Builder& b, const cir::TempAddr& ta, Z3Val&& val );
    extern void StoreToAddress( Builder& b, const cir::Select& s, Z3Val&& val );
    extern void StoreToAddress( Builder& b, const cir::GhostCall& gc, Z3Val&& val );

    extern void HavocAddress( Builder& b, uint32_t bbIndex, const Term& type, const cir::Instruction& addr );
}

#endif





>
>
>
>
>
|
>
>
>
>
|
|
|
|

<
|
|
|
|

|



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
28
29
#ifndef GOOSE_VERIFY_STORAGE_H
#define GOOSE_VERIFY_STORAGE_H

namespace goose::verify
{
    // Currently, we only synthethize z3 formulas for load/stores to constant addresses.
    // Stores to symbolic addresses are ignored, loads from symbolic addresses return
    // an unknown variable of the requisite type.
    //
    // Therefore the verifier can't currently reason about a possible aliasing between
    // a symbolic address and another address.
    //
    // This should be addressed (...) at higher level by assuming that two addresses tagged
    // with the same lifetime will always alias (ie modifiyng the pointeed from one of them
    // invalidates every other pointee with the same lifetime).
    extern optional< Z3Val > LoadVar( Builder& b, uint32_t index );
    extern optional< Z3Val > LoadVar( Builder& b, uint32_t index, const Term& type );
    extern optional< Z3Val > LoadFromAddress( Builder& b, const Address& addr, const Term& type );
    extern optional< Z3Val > LoadFromAddress( Builder& b, const GhostFuncApplication& gfa );


    extern void StoreToAddress( Builder& b, const Address& addr, Z3Val&& val );
    extern void StoreToAddress( Builder& b, const GhostFuncApplication& gfa, Z3Val&& val );

    extern Z3Val BuildAddressExpr( const Address& addr );

    extern void HavocStorageLocation( Builder& b, uint32_t bbIndex, const Term& type, const StorageLocation& sloc );
}

#endif
Changes to bs/verify/terminator.cpp.
10
11
12
13
14
15
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
    bool Func::handleTerminator( uint32_t bbIndex, const cir::Terminator& t )
    {
        return visit( [&]( auto&& t )
        {
            return handleTerminator( bbIndex, t );
        }, t.content() );
    }




























    bool Func::handleTerminator( uint32_t bbIndex, const cir::Ret& tr )
    {


        // Emit the "ensures" expressions as assertions.
        if( !m_func )
            return true;


        // Deal with the return value.
        Builder cb = m_builder;

        if( auto retVal = tr.value() )
        {
            if( auto retExpr = BuildZ3ExprFromValue( m_builder, *retVal ) )
            {
                // Set the @result placeholder for the func's post conditions
                cb.setPlaceholder( "@result"_sid, retExpr->expr );

                // Emit the return type's predicates as assertions.
                ForEachPredicate( cb, m_retType, retExpr->expr, [&]( auto&& z3expr, auto locId )
                {
                    DiagnosticsContext dc( retVal->locationId(), "When returning this." );
                    m_builder.checkAssertion( z3expr, locId );
                } );
            }
        }

        const auto& postConds = m_func->type().verifInfos()->postConds();
        bool success = true;

        postConds->forEach( [&]( auto&& cond )
        {
            // Don't do any error handling here, it should already have been taken care of
            // by the condition verifier.
            if( auto zv = BuildZ3ExprFromValue( cb, cond ) )
            {
                bool succ = false;

                // TODO: we don't have a useful location here in the case of a return from a void func.
                // we'll have to add it to the ret statement. The case can only happen when checking post conditions
                // of mutable ref params, which aren't implemented yet.
                if( tr.value() )
                {
                    DiagnosticsContext dc( tr.value()->locationId(), "When returning this." );
                    succ = m_builder.checkAssertion( zv->expr, cond.locationId() );
                }
                else
                    succ = m_builder.checkAssertion( zv->expr, cond.locationId() );

                success = success && succ;
            }
        } );

        return success;
    }









>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


>
>




>
|


<
<
|
|
|
|

|
|
|
<
|
|
<









|

<
<
<
<
<
<
<
<
|
<
<
<
<







10
11
12
13
14
15
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
44
45
46
47
48
49
50
51
52
53
54
55
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
    bool Func::handleTerminator( uint32_t bbIndex, const cir::Terminator& t )
    {
        return visit( [&]( auto&& t )
        {
            return handleTerminator( bbIndex, t );
        }, t.content() );
    }

    bool Func::handleTerminator( uint32_t bbIndex, const cir::RetVoid& tr )
    {
        // Emit the "ensures" expressions as assertions.
        if( !m_func )
            return true;

        DiagnosticsContext dc( tr.locationId(), "When returning here." );

        Builder cb = m_builder;

        const auto& postConds = m_func->type().verifInfos()->postConds();
        bool success = true;

        postConds->forEach( [&]( auto&& cond )
        {
            // Don't do any error handling here, it should already have been taken care of
            // by the condition verifier.
            if( auto zv = TryBuildZ3ExprFromValue( cb, cond ) )
            {
                bool succ = m_builder.checkAssertion( zv->expr, cond.locationId() );
                success = success && succ;
            }
        } );

        return success;
    }

    bool Func::handleTerminator( uint32_t bbIndex, const cir::Ret& tr )
    {
        auto retExpr = m_builder.pop();

        // Emit the "ensures" expressions as assertions.
        if( !m_func )
            return true;

        DiagnosticsContext dc( retExpr->loc, "When returning this." );

        Builder cb = m_builder;



        if( retExpr )
        {
            // Set the @result placeholder for the func's post conditions
            cb.setPlaceholder( "@result"_sid, retExpr->expr );

            // Emit the return type's predicates as assertions.
            ForEachPredicate( cb, m_retType, retExpr->expr, [&]( auto&& z3expr, auto locId )
            {

                m_builder.checkAssertion( z3expr, locId );
            } );

        }

        const auto& postConds = m_func->type().verifInfos()->postConds();
        bool success = true;

        postConds->forEach( [&]( auto&& cond )
        {
            // Don't do any error handling here, it should already have been taken care of
            // by the condition verifier.
            if( auto zv = TryBuildZ3ExprFromValue( cb, cond ) )
            {








                bool succ = m_builder.checkAssertion( zv->expr, cond.locationId() );




                success = success && succ;
            }
        } );

        return success;
    }

95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
        uint32_t srcBBId = m_remapper.remapBBId( *bb );
        uint32_t trueBBId = m_remapper.remapOutgoingEdge( *bb, *pTrueDestBB );
        uint32_t falseBBId = m_remapper.remapOutgoingEdge( *bb, *pFalseDestBB );

        m_viz.addEdge( srcBBId, trueBBId );
        m_viz.addEdge( srcBBId, falseBBId );

        if( auto cond = BuildZ3ExprFromValue( m_builder, t.cond() ) )
        {
            m_remapper.addEdge( srcBBId, trueBBId, cond->expr );
            m_remapper.addEdge( srcBBId, falseBBId, !cond->expr );
            return true;
        }

        // In order to fail gracefully in case we couldn't convert the condition,







|







109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
        uint32_t srcBBId = m_remapper.remapBBId( *bb );
        uint32_t trueBBId = m_remapper.remapOutgoingEdge( *bb, *pTrueDestBB );
        uint32_t falseBBId = m_remapper.remapOutgoingEdge( *bb, *pFalseDestBB );

        m_viz.addEdge( srcBBId, trueBBId );
        m_viz.addEdge( srcBBId, falseBBId );

        if( auto cond = m_builder.pop() )
        {
            m_remapper.addEdge( srcBBId, trueBBId, cond->expr );
            m_remapper.addEdge( srcBBId, falseBBId, !cond->expr );
            return true;
        }

        // In order to fail gracefully in case we couldn't convert the condition,
Changes to bs/verify/type.cpp.
12
13
14
15
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
44
45
46
TypeCache* TypeCache::GetInstance()
{
    static unique_ptr< TypeCache > tcache( new TypeCache() );
    return tcache.get();
}

TypeCache::TypeCache() :
    m_memLocSort( GetZ3Context() )

{
    auto s = GetZ3Context().int_sort();
    m_memLocSort = GetZ3Context().seq_sort( s );

}

optional< TypeInfo > TypeCache::getTypeInfo( const sema::Context& c, const Term& type )
{
    auto it = m_typeInfos.find( type );
    if( it != m_typeInfos.end() )
        return it->second;

    auto tinfo = CreateTypeInfo( c, type );
    if( !tinfo )
        return nullopt;

    m_typeInfos.emplace( type, *tinfo );
    return tinfo;
}


















optional< TypeInfo > TypeCache::CreateTypeInfo( const sema::Context& c, const Value& typeVal )
{
    // Handle all non-aggregate types (bool, signed int, unsigned int, floats) directly.
    // TODO: some are missing and will be dealt with later on.
    if( ValueToEIR( typeVal ) == GetValueType< bool >() )
    {
        return TypeInfo { GetZ3Context().bool_sort(),
            []( auto&& b )







|
>



>








|







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|







12
13
14
15
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
TypeCache* TypeCache::GetInstance()
{
    static unique_ptr< TypeCache > tcache( new TypeCache() );
    return tcache.get();
}

TypeCache::TypeCache() :
    m_memLocSort( GetZ3Context() ),
    m_uninterpretedSort( GetZ3Context() )
{
    auto s = GetZ3Context().int_sort();
    m_memLocSort = GetZ3Context().seq_sort( s );
    m_uninterpretedSort = GetZ3Context().uninterpreted_sort( "goose_uninterpreted_sort" );
}

optional< TypeInfo > TypeCache::getTypeInfo( const sema::Context& c, const Term& type )
{
    auto it = m_typeInfos.find( type );
    if( it != m_typeInfos.end() )
        return it->second;

    auto tinfo = createTypeInfo( c, type );
    if( !tinfo )
        return nullopt;

    m_typeInfos.emplace( type, *tinfo );
    return tinfo;
}

const TypeInfo& TypeCache::getUninterpretedTypeInfo() const
{
    static TypeInfo UnintTypeInfo { m_uninterpretedSort,
        [&]( auto&& b )
        {
            return GetZ3Context().constant( format( "uc{}", b.newUniqueId() ).c_str(), m_uninterpretedSort );
        },
        [&]( auto&& b, auto&& val )
        {
            return GetZ3Context().constant( format( "uv{}", b.newUniqueId() ).c_str(), m_uninterpretedSort );
        },
        {}, {}
    };

    return UnintTypeInfo;
}

optional< TypeInfo > TypeCache::createTypeInfo( const Value& typeVal )
{
    // Handle all non-aggregate types (bool, signed int, unsigned int, floats) directly.
    // TODO: some are missing and will be dealt with later on.
    if( ValueToEIR( typeVal ) == GetValueType< bool >() )
    {
        return TypeInfo { GetZ3Context().bool_sort(),
            []( auto&& b )
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
        return TypeInfo { m_memLocSort,
            [&]( auto&& b )
            {
                return GetZ3Context().constant( format( "ml{}", b.newUniqueId() ).c_str(), m_memLocSort );
            },
            [&]( auto&& b, auto&& val )
            {
                if( auto result = BuildZ3Op( b, *val.cir() ) )
                    return result->expr;

                return GetZ3Context().constant( format( "ml{}", b.newUniqueId() ).c_str(), m_memLocSort );
            },
            {}, {}
        };
    }

    if( ValueToEIR( typeVal ) == GetValueType< MemLoc >() )
    {
        return TypeInfo { m_memLocSort,
            [&]( auto&& b )
            {
                return GetZ3Context().constant( format( "ml{}", b.newUniqueId() ).c_str(), m_memLocSort );
            },
            {}, // TODO val (not needed for now)
            {}, {}
        };
    }

    return nullopt;
}

optional< TypeInfo > TypeCache::CreateTypeInfo( const sema::Context& c, const Term& type )
{
    auto typeVal = *EIRToValue( type );

    auto tinfo = CreateTypeInfo( c, typeVal );
    if( tinfo )
        return tinfo;

    // The only non aggregate type that we handle at the moment are tuples.
    // Other higher level types such as structs and classes need to be lowered
    // to tuples by a suitable overload of LowerTypeForVerification.
    // TODO: Later we'll also need to handle additional types such as arrays
    // and strings here.
    auto loweredType = LowerTypeForVerification( c, typeVal );
    if( !loweredType || loweredType->isPoison() )
        return nullopt;

    if( !IsTupleType( *loweredType ) )
        return CreateTypeInfo( c, *loweredType );

    auto sortName = format( "tupsort{}", ms_nextUniqueId++ );
    auto size = TupleTypeSize( *loweredType );

    llvm::SmallVector< TypeInfo, 8 > elemTInfos;

    llvm::SmallVector< string, 8 > elemNames;







|
|

|




















|



|













|







135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
        return TypeInfo { m_memLocSort,
            [&]( auto&& b )
            {
                return GetZ3Context().constant( format( "ml{}", b.newUniqueId() ).c_str(), m_memLocSort );
            },
            [&]( auto&& b, auto&& val )
            {
                if( !BuildZ3Op( b, *val.cir() ) )
                    return GetZ3Context().constant( format( "ml{}", b.newUniqueId() ).c_str(), m_memLocSort );

                return b.pop()->expr;
            },
            {}, {}
        };
    }

    if( ValueToEIR( typeVal ) == GetValueType< MemLoc >() )
    {
        return TypeInfo { m_memLocSort,
            [&]( auto&& b )
            {
                return GetZ3Context().constant( format( "ml{}", b.newUniqueId() ).c_str(), m_memLocSort );
            },
            {}, // TODO val (not needed for now)
            {}, {}
        };
    }

    return nullopt;
}

optional< TypeInfo > TypeCache::createTypeInfo( const sema::Context& c, const Term& type )
{
    auto typeVal = *EIRToValue( type );

    auto tinfo = createTypeInfo( typeVal );
    if( tinfo )
        return tinfo;

    // The only non aggregate type that we handle at the moment are tuples.
    // Other higher level types such as structs and classes need to be lowered
    // to tuples by a suitable overload of LowerTypeForVerification.
    // TODO: Later we'll also need to handle additional types such as arrays
    // and strings here.
    auto loweredType = LowerTypeForVerification( c, typeVal );
    if( !loweredType || loweredType->isPoison() )
        return nullopt;

    if( !IsTupleType( *loweredType ) )
        return createTypeInfo( *loweredType );

    auto sortName = format( "tupsort{}", ms_nextUniqueId++ );
    auto size = TupleTypeSize( *loweredType );

    llvm::SmallVector< TypeInfo, 8 > elemTInfos;

    llvm::SmallVector< string, 8 > elemNames;
Changes to bs/verify/type.h.
16
17
18
19
20
21
22

23
24
25
26
27
28
29
30
31

32
33
34
35
36
37
    // (for instances tuples), we keep a cache to avoid recreating them multiple times.
    class TypeCache
    {
        public:
            static TypeCache* GetInstance();

            optional< TypeInfo > getTypeInfo( const sema::Context& c, const Term& type );


        private:
            TypeCache();

            optional< TypeInfo > CreateTypeInfo( const sema::Context& c, const Value& typeVal );
            optional< TypeInfo > CreateTypeInfo( const sema::Context& c, const Term& type );

            unordered_map< Term, TypeInfo > m_typeInfos;
            z3::sort m_memLocSort;


            static uint32_t ms_nextUniqueId;
    };
}

#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
    // (for instances tuples), we keep a cache to avoid recreating them multiple times.
    class TypeCache
    {
        public:
            static TypeCache* GetInstance();

            optional< TypeInfo > getTypeInfo( const sema::Context& c, const Term& type );
            const TypeInfo& getUninterpretedTypeInfo() const;

        private:
            TypeCache();

            optional< TypeInfo > createTypeInfo( const Value& typeVal );
            optional< TypeInfo > createTypeInfo( const sema::Context& c, const Term& type );

            unordered_map< Term, TypeInfo > m_typeInfos;
            z3::sort m_memLocSort;
            z3::sort m_uninterpretedSort;

            static uint32_t ms_nextUniqueId;
    };
}

#endif
Deleted bs/verify/valtracker.inl.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#ifndef GOOSE_VERIFY_VALTRACKER_INL
#define GOOSE_VERIFY_VALTRACKER_INL

namespace goose::verify
{
    template< typename T > template< typename... P >
    Z3Val& ValueTracker< T >::setForBasicBlock( Builder& b, uint32_t bbIndex, uint32_t index, Z3Val&& val, P&&... closureParams )
    {
        auto* vs = m_valStorage.get( index );
        if( !vs )
        {
            ValState newVS;
            newVS.resize( bbIndex + 1 );
            vs = &m_valStorage.set( index, move( newVS ) );
        }
        else if( vs->size() <= bbIndex )
            vs->resize( bbIndex + 1 );

        auto& c = GetZ3Context();
        auto predicate = c.bool_const( format( "b{}", bbIndex ).c_str() );

        auto lhsExpr = T::BuildExprToAssign( b, val.type, bbIndex, index, forward< P >( closureParams )... );
        T::BuildAssignment( b, predicate, lhsExpr, val );

        (*vs)[ bbIndex ] = move( lhsExpr );
        return *(*vs)[ bbIndex ];
    }

    template< typename T > template< typename... P >
    optional< Z3Val > ValueTracker< T >::retrieve( Builder& b, uint32_t bbIndex, uint32_t index, P&&... closureParams )
    {
        if( bbIndex == b.currentBBIndex() )
            return  nullopt;

        if( bbIndex == ~0U )
            bbIndex = b.currentBBIndex();

        auto zv = getForBasicBlock( bbIndex, index );
        if( zv )
            return zv;

        if( !b.cfg() )
            return nullopt;

        optional< Z3Val > lhsExpr;

        // Model the ssa phi operation by creating a series of equality assertions, each implied by
        // one of the possible incoming edge condition for the current basic block.
        if( b.remapper() )
        {
            b.remapper()->forEachIncomingEdge( bbIndex, [&]( auto&& predIndex, auto&& expr )
            {
                if( auto predVal = retrieve( b, predIndex, index, forward< P >( closureParams )... ) )
                {
                    if( !lhsExpr )
                        lhsExpr = T::BuildExprToAssign( b, predVal->type, bbIndex, index, forward< P >( closureParams )... );

                    T::BuildPhiEdge( b, predIndex, bbIndex, *lhsExpr, *predVal );
                }
            } );
        }

        if( !lhsExpr )
            return nullopt;

        return setForBasicBlock( b, bbIndex, index, move( *lhsExpr ) );
    }

    template< typename T > template< typename... P >
    const Z3Val& ValueTracker< T >::set( Builder& b, uint32_t index, Z3Val&& val, P&&... closureParams )
    {
        return setForBasicBlock( b, b.currentBBIndex(), index, move( val ), forward< P >( closureParams )... );
    }
}

#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
























































































































































Changes to bs/verify/value.cpp.
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
28
29
30
31
32
33











34
35
36
37
38
39

40
41
42
43
44
45
46
47
#include "verify.h"
#include "builtins/builtins.h"
#include "helpers.inl"

using namespace goose::diagnostics;

namespace goose::verify
{
    optional< Z3Val > BuildZ3ValFromConstant( Builder& b, const Value& val )















    {
        auto loweredVal = LowerConstantForVerification( b.context(), val );
        if( !loweredVal || loweredVal->isPoison() )
            return nullopt;

        auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), loweredVal->type() );
        if( !tinfo )
            return nullopt;

        auto zexpr = tinfo->build( b, *loweredVal );
        return Z3Val { move( zexpr ), *EIRToValue( val.type() ) };
    }

    optional< Z3Val > BuildZ3ConstantFromType( Builder& b, const Value& type, const string& name )










    {
        auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), ValueToEIR( type ) );
        if( !tinfo )
            return nullopt;

        assert( tinfo->sort );
        return Z3Val { GetZ3Context().constant( name.c_str(), *tinfo->sort ), type };
    }

    optional< Z3Val > BuildZ3ConstantFromType( Builder& b, const Term& type, const string& name )











    {
        auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), type );
        if( !tinfo )
            return nullopt;

        assert( tinfo->sort );

        return Z3Val { GetZ3Context().constant( name.c_str(), *tinfo->sort ), *EIRToValue( type ) };
    }

    z3::expr GetAsBitVec( const z3::expr& expr, const Value& type )
    {
        if( expr.is_bv() )
            return expr;









|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>










|


|
>
>
>
>
>
>
>
>
>
>






|


|
>
>
>
>
>
>
>
>
>
>
>






>
|







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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
#include "verify.h"
#include "builtins/builtins.h"
#include "helpers.inl"

using namespace goose::diagnostics;

namespace goose::verify
{
    Z3Val BuildZ3ValFromConstant( Builder& b, const Value& val )
    {
        optional< TypeInfo > tinfo;

        auto loweredVal = LowerConstantForVerification( b.context(), val );
        if( loweredVal && !loweredVal->isPoison() )
            tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), loweredVal->type() );

        if( !tinfo )
            tinfo = TypeCache::GetInstance()->getUninterpretedTypeInfo();

        auto zexpr = tinfo->build( b, *loweredVal );
        return Z3Val { move( zexpr ), *EIRToValue( val.type() ), val.locationId() };
    }

    optional< Z3Val > TryBuildZ3ValFromConstant( Builder& b, const Value& val )
    {
        auto loweredVal = LowerConstantForVerification( b.context(), val );
        if( !loweredVal || loweredVal->isPoison() )
            return nullopt;

        auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), loweredVal->type() );
        if( !tinfo )
            return nullopt;

        auto zexpr = tinfo->build( b, *loweredVal );
        return Z3Val { move( zexpr ), *EIRToValue( val.type() ), val.locationId() };
    }

    Z3Val BuildZ3ConstantFromType( Builder& b, const Value& type, const string& name )
    {
        auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), ValueToEIR( type ) );
        if( !tinfo )
            tinfo = TypeCache::GetInstance()->getUninterpretedTypeInfo();

        assert( tinfo->sort );
        return Z3Val { GetZ3Context().constant( name.c_str(), *tinfo->sort ), type, type.locationId() };
    }

    optional< Z3Val > TryBuildZ3ConstantFromType( Builder& b, const Value& type, const string& name )
    {
        auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), ValueToEIR( type ) );
        if( !tinfo )
            return nullopt;

        assert( tinfo->sort );
        return Z3Val { GetZ3Context().constant( name.c_str(), *tinfo->sort ), type, type.locationId() };
    }

    Z3Val BuildZ3ConstantFromType( Builder& b, const Term& type, const string& name )
    {
        auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), type );
        if( !tinfo )
            tinfo = TypeCache::GetInstance()->getUninterpretedTypeInfo();

        assert( tinfo->sort );
        auto tval = *EIRToValue( type );
        return Z3Val { GetZ3Context().constant( name.c_str(), *tinfo->sort ), tval, tval.locationId() };
    }

    optional< Z3Val > TryBuildZ3ConstantFromType( Builder& b, const Term& type, const string& name )
    {
        auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), type );
        if( !tinfo )
            return nullopt;

        assert( tinfo->sort );
        auto tval = *EIRToValue( type );
        return Z3Val { GetZ3Context().constant( name.c_str(), *tinfo->sort ), tval, tval.locationId() };
    }

    z3::expr GetAsBitVec( const z3::expr& expr, const Value& type )
    {
        if( expr.is_bv() )
            return expr;

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
114
115

116


117
118
119
120
121
122
123

124
125
126
127
128

129
130
131
132
133
134
135
136








137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154

155
156
157
158
159
160
161
162
163
164
165

166
167
168
169
170

171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187

188


189
190
191
192
193
194
195

196
197
198
199
200

201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223












224
225
226
227
228
229
230
231
232
233





234



235
236
237
238
239
240
241
242
243





244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264



265



266
267
268
269
270
271
272
273




274
275
276
277
278
279
280
281


282
283

284


285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486


487
488
489
490
491
492
493
494

495

496

497


498
499
500
501
502


503
504
505
506
507
508
509
510

511
512
513
514
515
516
517
518
519
520
521





522

523


524
525
526

527
528
529
530


531
532

533
534
535
536
537
538
539





540

541
542
543
544
545
546
547

548
549

550






























551
552
553
554
555
556
557
558
559















560
561
562
563
564
565
566
567
568
569
570
571
572
        else if( to.expr.is_bv() )
            return GetAsBitVec( expr, to.type );
        else
            return expr;
    }

    template< typename I, typename F >
    optional< Z3Val > BuildZ3UnaryExpr( Builder& b, const I& instr, F&& func )
    {
        auto operand = BuildZ3ExprFromValue( b, instr.operand() );
        if( !operand )
            return nullopt;

        return Z3Val{ func( operand->expr ), operand->type };

    }

    template< typename I, typename F >
    optional< Z3Val > BuildZ3BinExpr( Builder& b, const I& instr, F&& func )
    {
        auto lhs = BuildZ3ExprFromValue( b, instr.lhs() );
        if( !lhs )
            return nullopt;

        auto rhs = BuildZ3ExprFromValue( b, instr.rhs() );
        if( !rhs )
            return nullopt;

        if( lhs->expr.get_sort().sort_kind() == rhs->expr.get_sort().sort_kind() )

            return Z3Val{ func( lhs->expr, rhs->expr, lhs->type ), lhs->type };



        // If we are trying to do an operation on a mix of bitvec and int,
        // convert the int to a bitvec first.
        if( lhs->expr.is_bv() )
        {
            assert( rhs->expr.is_int() );
            return Z3Val{ func( lhs->expr, GetAsBitVec( *rhs ), lhs->type ), lhs->type };

        }
        else
        {
            assert( lhs->expr.is_int() );
            return Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ), lhs->type };

        }

        return nullopt;
    }

    template< typename I, typename F >
    optional< Z3Val > BuildZ3BinBitwiseExpr( Builder& b, const I& instr, F&& func )
    {








        if( instr.lhs().type() == GetValueType< BigInt >() )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( instr.lhs().locationId(), "verifier error: bitwise operands can't be ct_int." );
            return nullopt;
        }

        if( instr.rhs().type() == GetValueType< BigInt >() )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( instr.rhs().locationId(), "verifier error: bitwise operands can't be ct_int." );
            return nullopt;
        }

        auto lhs = BuildZ3ExprFromValue( b, instr.lhs() );
        if( !lhs )
            return nullopt;

        auto rhs = BuildZ3ExprFromValue( b, instr.rhs() );
        if( !rhs )

            return nullopt;

        if( lhs->expr.get_sort().sort_kind() == rhs->expr.get_sort().sort_kind() )
            return Z3Val{ func( lhs->expr, rhs->expr, lhs->type ), lhs->type };

        // If we are trying to do an operation on a mix of bitvec and int,
        // convert the int to a bitvec first.
        if( lhs->expr.is_bv() )
        {
            assert( rhs->expr.is_int() );
            return Z3Val{ func( lhs->expr, GetAsBitVec( *rhs ), lhs->type ), lhs->type };

        }
        else
        {
            assert( lhs->expr.is_int() );
            return Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ), lhs->type };

        }

        return nullopt;
    }

    template< typename I, typename F >
    optional< Z3Val > BuildZ3BinBoolExpr( Builder& b, const I& instr, F&& func )
    {
        auto lhs = BuildZ3ExprFromValue( b, instr.lhs() );
        if( !lhs )
            return nullopt;

        auto rhs = BuildZ3ExprFromValue( b, instr.rhs() );
        if( !rhs )
            return nullopt;

        if( lhs->expr.get_sort().sort_kind() == rhs->expr.get_sort().sort_kind() )

            return Z3Val{ func( lhs->expr, rhs->expr, lhs->type ), *EIRToValue( GetValueType< bool >() ) };



        // If we are trying to do an operation on a mix of bitvec and int,
        // convert the int to a bitvec first.
        if( lhs->expr.is_bv() )
        {
            assert( rhs->expr.is_int() );
            return Z3Val{ func( lhs->expr, GetAsBitVec( *rhs ), lhs->type ), *EIRToValue( GetValueType< bool >() ) };

        }
        else
        {
            assert( lhs->expr.is_int() );
            return Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ), *EIRToValue( GetValueType< bool >() ) };

        }

        return nullopt;
    }

    template< typename I, typename F >
    optional< Z3Val > BuildZ3BinLogicExpr( Builder& b, const I& instr, F&& func )
    {
        auto lhs = BuildZ3ExprFromValue( b, instr.lhs() );
        if( !lhs )
            return nullopt;

        auto rhs = BuildZ3ExprFromValue( b, instr.rhs() );
        if( !rhs )
            return nullopt;

        if( !lhs->expr.is_bool() )
            return nullopt;

        if( !rhs->expr.is_bool() )
            return nullopt;

        return Z3Val{ func( lhs->expr, rhs->expr, lhs->type ), *EIRToValue( GetValueType< bool >() ) };












    }

    template< typename T >
    optional< Z3Val > BuildZ3Op( Builder& b, const T& instr )
    {
        return nullopt;
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Load& instr )
    {





        auto zv = LoadFromAddress( b, *instr.addr() );



        if( !zv )
            return nullopt;

        ForEachPredicate( b, instr.type(), zv->expr, [&]( auto&& z3expr, auto locId )
        {
            if( b.mustLoadAssume() )
                b.assume( z3expr );
        } );






        return zv;
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Store& instr )
    {
        auto zv = BuildZ3ExprFromValue( b, instr.val() );
        if( !zv )
            return nullopt;

        ForEachPredicate( b, instr.type(), zv->expr, [&]( auto&& z3expr, auto locId )
        {
            if( !instr.destLocId().invalid() && !instr.val().locationId().invalid() )
            {
                DiagnosticsContext dc( instr.destLocId(), "...to this." );
                DiagnosticsContext dc2( instr.val().locationId(), "When assigning this..." );
                b.checkAssertion( z3expr, locId );
            }
            else
                b.checkAssertion( z3expr, locId );
        } );




        StoreToAddress( b, *instr.addr(), move( *zv ) );



        return nullopt;
    }

    // Implemented in call.cpp
    extern optional< Z3Val > BuildZ3Op( Builder& b, const Call& instr );

    optional< Z3Val > BuildZ3Op( Builder& b, const CreateTemporary& instr )
    {




        b.setVar( instr.index(), instr.value() );
        return nullopt;
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const GetTemporary& instr )
    {
        auto zv = b.retrieveVar( instr.index() );
        if( zv )


            return zv;


        return BuildZ3ConstantFromType( b, instr.type(), format( "v{}", instr.index() ) );


    }

    optional< Z3Val > BuildZ3Op( Builder& b, const AllocVar& instr )
    {
        auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), ValueToEIR( instr.type() ) );
        if( !tinfo )
            return nullopt;

        b.setVar( instr.index(), Z3Val{ tinfo->undefined( b ), instr.type() } );
        return nullopt;
    }

    // Implemented in phi.cpp
    optional< Z3Val > BuildZ3Op( Builder& b, const Phi& instr );

    // TODO: LoadConstStr. Build a z3 str value.

    optional< Z3Val > BuildZ3Op( Builder& b, const Not& instr )
    {
        return BuildZ3UnaryExpr( b, instr, []( auto&& operand )
        {
            if( operand.is_bool() )
                return !operand;

            assert( operand.is_bv() );
            return ~operand;
        } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const And& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
        {
            if( lhs.is_bool() && rhs.is_bool() )
                return lhs && rhs;

            auto lhsBV = GetAsBitVec( lhs, type );
            auto rhsBV = GetAsBitVec( rhs, type );
            return lhsBV & rhsBV;
        } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Or& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
        {
            if( lhs.is_bool() && rhs.is_bool() )
                return lhs || rhs;

            auto lhsBV = GetAsBitVec( lhs, type );
            auto rhsBV = GetAsBitVec( rhs, type );
            return lhsBV | rhsBV;
        } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Xor& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
        {
            if( lhs.is_bool() && rhs.is_bool() )
                return lhs ^ rhs;

            auto lhsBV = GetAsBitVec( lhs, type );
            auto rhsBV = GetAsBitVec( rhs, type );
            return lhsBV ^ rhsBV;
        } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Implies& instr )
    {
        return BuildZ3BinLogicExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
        {
            return z3::implies( lhs, rhs );
        } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Shl& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
        {
            auto lhsBV = GetAsBitVec( lhs, type );
            auto rhsBV = GetAsBitVec( rhs, type );
            return z3::shl( lhsBV, rhsBV );
        } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const LShr& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
        {
            auto lhsBV = GetAsBitVec( lhs, type );
            auto rhsBV = GetAsBitVec( rhs, type );
            return z3::lshr( lhsBV, rhsBV );
        } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const AShr& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
        {
            auto lhsBV = GetAsBitVec( lhs, type );
            auto rhsBV = GetAsBitVec( rhs, type );
            return z3::ashr( lhsBV, rhsBV );
        } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Add& instr )
    {
        return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs + rhs; } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Sub& instr )
    {
        return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs - rhs; } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Mul& instr )
    {
        return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs * rhs; } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const UDiv& instr )
    {
        return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::udiv( lhs, rhs ); } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const SDiv& instr )
    {
        return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs / rhs; } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const URem& instr )
    {
        return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::urem( lhs, rhs ); } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const SRem& instr )
    {
        return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
        {
            if( lhs.is_bv() )
                return z3::srem( lhs, rhs );

            return z3::rem( lhs, rhs );
        } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Eq& instr )
    {
        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs == rhs; } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Neq& instr )
    {
        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs != rhs; } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const UGT& instr )
    {
        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::ugt( lhs, rhs ); } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const UGE& instr )
    {
        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::uge( lhs, rhs ); } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const ULT& instr )
    {
        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::ult( lhs, rhs ); } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const ULE& instr )
    {
        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::ule( lhs, rhs ); } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const SGT& instr )
    {
        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs > rhs; } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const SGE& instr )
    {
        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs >= rhs; } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const SLT& instr )
    {
        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs < rhs; } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const SLE& instr )
    {
        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs <= rhs; } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Assert& instr )
    {
        // TODO: refactor the return value here to be able to indicate failure versus "nothing to do"
        auto cond = BuildZ3ExprFromValue( b, instr.cond() );
        if( cond )


            b.checkAssertion( cond->expr, instr.cond().locationId() );
        return nullopt;
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Placeholder& instr )
    {
        const auto* expr = b.retrievePlaceholder( instr.name() );
        if( expr )

            return Z3Val{ *expr, *EIRToValue( instr.type() ) };



        return BuildZ3ConstantFromType( b, instr.type(), format( "p{}", instr.name().str() ) );


    }

    optional< Z3Val > BuildZ3Op( Builder& b, const PHOverride& instr )
    {
        const auto* savedExpr = b.retrievePlaceholder( instr.name() );



        auto newExpr = BuildZ3ExprFromValue( b, instr.phVal() );
        if( newExpr )
            b.setPlaceholder( instr.name(), newExpr->expr );
        else
            b.unsetPlaceholder( instr.name() );

        auto result = BuildZ3ExprFromValue( b, instr.val() );


        if( savedExpr )
            b.setPlaceholder( instr.name(), *savedExpr );
        else
            b.unsetPlaceholder( instr.name() );

        return result;
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const VarAddr& instr )
    {





        return Z3Val

        {


            GetZ3Context().int_val( instr.varIndex() ).unit(),
            *EIRToValue( GetValueType< builtins::MemLoc >() )
        };

    }

    optional< Z3Val > BuildZ3Op( Builder& b, const TempAddr& instr )
    {


        return Z3Val
        {

            GetZ3Context().int_val( instr.tempIndex() ).unit(),
            *EIRToValue( GetValueType< builtins::MemLoc >() )
        };
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Select& instr )
    {





        auto baseLoc = BuildZ3Op( b, *instr.baseAddr() );

        if( !baseLoc )
            return nullopt;

        return Z3Val
        {
            z3::concat( baseLoc->expr, GetZ3Context().int_val( instr.memberIndex() ).unit() ),
            *EIRToValue( GetValueType< builtins::MemLoc >() )

        };
    }
































    optional< Z3Val > BuildZ3Op( Builder& b, const cir::Instruction& instr )
    {
        return visit( [&]( auto&& e )
        {
            return BuildZ3Op( b, e );
        }, instr.content() );
    }

    optional< Z3Val > BuildZ3ExprFromValue( Builder& b, const Value& val )















    {
        if( val.isPoison() )
            return nullopt;

        if( val.isConstant() )
            return BuildZ3ValFromConstant( b, val );

        if( auto expr = BuildZ3Op( b, *val.cir() )  )
            return expr;

        return BuildZ3ConstantFromType( b, val.type(), format( "val{}", b.newUniqueId() ) );
    }
}







|

|

|

|
>



|

|
|
|

|
|
|


>
|
>
>






|
>




|
>


|



|

>
>
>
>
>
>
>
>
|

|
|


|

|
|


<
|
<
|
<
<
>
|
|
<
<






|
>




|
>


|



|

|
|
|

|
|
|


>
|
>
>






|
>




|
>


|



|

|
|
|

|
|
|


|


|

|
>
>
>
>
>
>
>
>
>
>
>
>



|

|


|

>
>
>
>
>
|
>
>
>

|

<
<
|
|
<
|
>
>
>
>
>
|


|

|

|



|


|






>
>
>
|
>
>
>
|



|

|

>
>
>
>
|
|


|



>
>
|
|
>
|
>
>


|



|

|
|



|



|











|












|












|












|







|









|









|









|




|




|




|




|




|




|










|




|




|




|




|




|




|




|




|




|




|

<
|
|
>
>
|
|


|



>
|
>
|
>
|
>
>


|

|
>
>

<
<
|
|
<
|
|
>
|
<
<
<
|
<
|


|

>
>
>
>
>
|
>
|
>
>
|
<
<
>


|

>
>
|
|
>
|
<
<


|

>
>
>
>
>
|
>
|
|

|

|
|
>
|
|
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|







|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>





|

|
|

|


125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199

200

201


202
203
204


205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310


311
312

313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573

574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601


602
603

604
605
606
607



608

609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624


625
626
627
628
629
630
631
632
633
634
635


636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
        else if( to.expr.is_bv() )
            return GetAsBitVec( expr, to.type );
        else
            return expr;
    }

    template< typename I, typename F >
    bool BuildZ3UnaryExpr( Builder& b, const I& instr, F&& func )
    {
        auto operand = b.pop();
        if( !operand )
            return false;

        b.push( Z3Val{ func( operand->expr ), operand->type, instr.locationId() } );
        return true;
    }

    template< typename I, typename F >
    bool BuildZ3BinExpr( Builder& b, const I& instr, F&& func )
    {
        auto rhs = b.pop();
        if( !rhs )
            return false;

        auto lhs = b.pop();
        if( !lhs )
            return false;

        if( lhs->expr.get_sort().sort_kind() == rhs->expr.get_sort().sort_kind() )
        {
            b.push( Z3Val{ func( lhs->expr, rhs->expr, lhs->type ), lhs->type, instr.locationId() } );
            return true;
        }

        // If we are trying to do an operation on a mix of bitvec and int,
        // convert the int to a bitvec first.
        if( lhs->expr.is_bv() )
        {
            assert( rhs->expr.is_int() );
            b.push( Z3Val{ func( lhs->expr, GetAsBitVec( *rhs ), lhs->type ), lhs->type, instr.locationId() } );
            return true;
        }
        else
        {
            assert( lhs->expr.is_int() );
            b.push( Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ), lhs->type, instr.locationId() } );
            return true;
        }

        return false;
    }

    template< typename I, typename F >
    bool BuildZ3BinBitwiseExpr( Builder& b, const I& instr, F&& func )
    {
        auto rhs = b.pop();
        if( !rhs )
            return false;

        auto lhs = b.pop();
        if( !lhs )
            return false;

        if( ValueToEIR( lhs->type ) == GetValueType< BigInt >() )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( 0, "verifier error: bitwise operands can't be ct_int." );
            return false;
        }

        if( ValueToEIR( rhs->type ) == GetValueType< BigInt >() )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( 0, "verifier error: bitwise operands can't be ct_int." );
            return false;
        }


        if( lhs->expr.get_sort().sort_kind() == rhs->expr.get_sort().sort_kind() )

        {


            b.push( Z3Val{ func( lhs->expr, rhs->expr, lhs->type ), lhs->type, instr.locationId() } );
            return true;
        }



        // If we are trying to do an operation on a mix of bitvec and int,
        // convert the int to a bitvec first.
        if( lhs->expr.is_bv() )
        {
            assert( rhs->expr.is_int() );
            b.push( Z3Val{ func( lhs->expr, GetAsBitVec( *rhs ), lhs->type ), lhs->type, instr.locationId() } );
            return true;
        }
        else
        {
            assert( lhs->expr.is_int() );
            b.push( Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ), lhs->type, instr.locationId() } );
            return true;
        }

        return false;
    }

    template< typename I, typename F >
    bool BuildZ3BinBoolExpr( Builder& b, const I& instr, F&& func )
    {
        auto rhs = b.pop();
        if( !rhs )
            return false;

        auto lhs = b.pop();
        if( !lhs )
            return false;

        if( lhs->expr.get_sort().sort_kind() == rhs->expr.get_sort().sort_kind() )
        {
            b.push( Z3Val{ func( lhs->expr, rhs->expr, lhs->type ), *EIRToValue( GetValueType< bool >() ), instr.locationId() } );
            return true;
        }

        // If we are trying to do an operation on a mix of bitvec and int,
        // convert the int to a bitvec first.
        if( lhs->expr.is_bv() )
        {
            assert( rhs->expr.is_int() );
            b.push( Z3Val{ func( lhs->expr, GetAsBitVec( *rhs ), lhs->type ), *EIRToValue( GetValueType< bool >() ), instr.locationId() } );
            return true;
        }
        else
        {
            assert( lhs->expr.is_int() );
            b.push( Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ), *EIRToValue( GetValueType< bool >() ), instr.locationId() } );
            return true;
        }

        return false;
    }

    template< typename I, typename F >
    bool BuildZ3BinLogicExpr( Builder& b, const I& instr, F&& func )
    {
        auto rhs = b.pop();
        if( !rhs )
            return false;

        auto lhs = b.pop();
        if( !lhs )
            return false;

        if( !lhs->expr.is_bool() )
            return false;

        if( !rhs->expr.is_bool() )
            return false;

        b.push( Z3Val{ func( lhs->expr, rhs->expr, lhs->type ), *EIRToValue( GetValueType< bool >() ), instr.locationId() } );
        return true;
    }

    bool BuildZ3Op( Builder& b, const cir::InstrSeq& is )
    {
        for( const auto& instr : is )
        {
            if( !BuildZ3Op( b, instr ) )
                return false;
        }

        return true;
    }

    template< typename T >
    bool BuildZ3Op( Builder& b, const T& instr )
    {
        return false;
    }

    bool BuildZ3Op( Builder& b, const Load& instr )
    {
        optional< Z3Val > zv;

        if( auto cstAddr = b.pop< Address >() )
            zv = LoadFromAddress( b, *cstAddr, instr.type() );
        else if( auto gfa = b.pop< GhostFuncApplication >() )
            zv = LoadFromAddress( b, *gfa );
        else if( auto symAddr = b.pop< Z3Val >() )
            zv = BuildZ3ConstantFromType( b, instr.type(), format( "val{}", b.newUniqueId() ) );

        if( !zv )
            return false;



        if( b.mustLoadAssume() )
            ForEachPredicate( b, instr.type(), zv->expr, [&]( auto&& z3expr, auto locId )

            {
                b.assume( z3expr );
            } );

        zv->loc = instr.locationId();
        b.push( move( *zv ) );
        return true;
    }

    bool BuildZ3Op( Builder& b, const Store& instr )
    {
        auto zv = b.pop();
        if( !zv )
            return false;

        ForEachPredicate( b, instr.type(), zv->expr, [&]( auto&& z3expr, auto locId )
        {
            if( !instr.srcLocId().invalid() && !instr.destLocId().invalid() )
            {
                DiagnosticsContext dc( instr.destLocId(), "...to this." );
                DiagnosticsContext dc2( instr.srcLocId(), "When assigning this..." );
                b.checkAssertion( z3expr, locId );
            }
            else
                b.checkAssertion( z3expr, locId );
        } );

        if( auto cstAddr = b.pop< Address >() )
            StoreToAddress( b, *cstAddr, move( *zv ) );
        else if( auto gfa = b.pop< GhostFuncApplication >() )
            StoreToAddress( b, *gfa, move( *zv ) );
        else
            b.pop();

        return true;
    }

    // Implemented in call.cpp
    extern bool BuildZ3Op( Builder& b, const Call& instr );

    bool BuildZ3Op( Builder& b, const CreateTemporary& instr )
    {
        auto zv = b.pop();
        if( !zv )
            return false;

        b.setVar( instr.index(), move( *zv ) );
        return true;
    }

    bool BuildZ3Op( Builder& b, const GetTemporary& instr )
    {
        auto zv = b.retrieveVar( instr.index() );
        if( zv )
        {
            b.push( move( *zv ) );
            return true;
        }

        auto result = BuildZ3ConstantFromType( b, instr.type(), format( "v{}", instr.index() ) );
        b.push( move( result ) );
        return true;
    }

    bool BuildZ3Op( Builder& b, const AllocVar& instr )
    {
        auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), ValueToEIR( instr.type() ) );
        if( !tinfo )
            tinfo = TypeCache::GetInstance()->getUninterpretedTypeInfo();

        b.setVar( instr.index(), Z3Val{ tinfo->undefined( b ), instr.type(), instr.locationId() } );
        return true;
    }

    // Implemented in phi.cpp
    bool BuildZ3Op( Builder& b, const Phi& instr );

    // TODO: LoadConstStr. Build a z3 str value.

    bool BuildZ3Op( Builder& b, const Not& instr )
    {
        return BuildZ3UnaryExpr( b, instr, []( auto&& operand )
        {
            if( operand.is_bool() )
                return !operand;

            assert( operand.is_bv() );
            return ~operand;
        } );
    }

    bool BuildZ3Op( Builder& b, const And& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
        {
            if( lhs.is_bool() && rhs.is_bool() )
                return lhs && rhs;

            auto lhsBV = GetAsBitVec( lhs, type );
            auto rhsBV = GetAsBitVec( rhs, type );
            return lhsBV & rhsBV;
        } );
    }

    bool BuildZ3Op( Builder& b, const Or& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
        {
            if( lhs.is_bool() && rhs.is_bool() )
                return lhs || rhs;

            auto lhsBV = GetAsBitVec( lhs, type );
            auto rhsBV = GetAsBitVec( rhs, type );
            return lhsBV | rhsBV;
        } );
    }

    bool BuildZ3Op( Builder& b, const Xor& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
        {
            if( lhs.is_bool() && rhs.is_bool() )
                return lhs ^ rhs;

            auto lhsBV = GetAsBitVec( lhs, type );
            auto rhsBV = GetAsBitVec( rhs, type );
            return lhsBV ^ rhsBV;
        } );
    }

    bool BuildZ3Op( Builder& b, const Implies& instr )
    {
        return BuildZ3BinLogicExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
        {
            return z3::implies( lhs, rhs );
        } );
    }

    bool BuildZ3Op( Builder& b, const Shl& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
        {
            auto lhsBV = GetAsBitVec( lhs, type );
            auto rhsBV = GetAsBitVec( rhs, type );
            return z3::shl( lhsBV, rhsBV );
        } );
    }

    bool BuildZ3Op( Builder& b, const LShr& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
        {
            auto lhsBV = GetAsBitVec( lhs, type );
            auto rhsBV = GetAsBitVec( rhs, type );
            return z3::lshr( lhsBV, rhsBV );
        } );
    }

    bool BuildZ3Op( Builder& b, const AShr& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
        {
            auto lhsBV = GetAsBitVec( lhs, type );
            auto rhsBV = GetAsBitVec( rhs, type );
            return z3::ashr( lhsBV, rhsBV );
        } );
    }

    bool BuildZ3Op( Builder& b, const Add& instr )
    {
        return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs + rhs; } );
    }

    bool BuildZ3Op( Builder& b, const Sub& instr )
    {
        return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs - rhs; } );
    }

    bool BuildZ3Op( Builder& b, const Mul& instr )
    {
        return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs * rhs; } );
    }

    bool BuildZ3Op( Builder& b, const UDiv& instr )
    {
        return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::udiv( lhs, rhs ); } );
    }

    bool BuildZ3Op( Builder& b, const SDiv& instr )
    {
        return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs / rhs; } );
    }

    bool BuildZ3Op( Builder& b, const URem& instr )
    {
        return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::urem( lhs, rhs ); } );
    }

    bool BuildZ3Op( Builder& b, const SRem& instr )
    {
        return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
        {
            if( lhs.is_bv() )
                return z3::srem( lhs, rhs );

            return z3::rem( lhs, rhs );
        } );
    }

    bool BuildZ3Op( Builder& b, const Eq& instr )
    {
        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs == rhs; } );
    }

    bool BuildZ3Op( Builder& b, const Neq& instr )
    {
        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs != rhs; } );
    }

    bool BuildZ3Op( Builder& b, const UGT& instr )
    {
        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::ugt( lhs, rhs ); } );
    }

    bool BuildZ3Op( Builder& b, const UGE& instr )
    {
        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::uge( lhs, rhs ); } );
    }

    bool BuildZ3Op( Builder& b, const ULT& instr )
    {
        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::ult( lhs, rhs ); } );
    }

    bool BuildZ3Op( Builder& b, const ULE& instr )
    {
        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::ule( lhs, rhs ); } );
    }

    bool BuildZ3Op( Builder& b, const SGT& instr )
    {
        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs > rhs; } );
    }

    bool BuildZ3Op( Builder& b, const SGE& instr )
    {
        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs >= rhs; } );
    }

    bool BuildZ3Op( Builder& b, const SLT& instr )
    {
        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs < rhs; } );
    }

    bool BuildZ3Op( Builder& b, const SLE& instr )
    {
        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs <= rhs; } );
    }

    bool BuildZ3Op( Builder& b, const Assert& instr )
    {

        auto cond = b.pop();
        if( !cond )
            return false;

        b.checkAssertion( cond->expr, cond->loc );
        return true;
    }

    bool BuildZ3Op( Builder& b, const Placeholder& instr )
    {
        const auto* expr = b.retrievePlaceholder( instr.name() );
        if( expr )
        {
            b.push( Z3Val{ *expr, *EIRToValue( instr.type() ), instr.locationId() } );
            return true;
        }

        auto result = BuildZ3ConstantFromType( b, instr.type(), format( "p{}", instr.name().str() ) );
        b.push( move( result ) );
        return true;
    }

    bool BuildZ3Op( Builder& b, const PHOverrideSet& instr )
    {
        auto phExpr = b.pop();
        if( !phExpr )
            return false;



        b.setPlaceholder( instr.name(), phExpr->expr );
        return true;

    }

    bool BuildZ3Op( Builder& b, const PHOverrideClear& instr )
    {



        b.unsetPlaceholder( instr.name() );

        return true;
    }

    bool BuildZ3Op( Builder& b, const Constant& instr )
    {
        auto v = TryBuildZ3ExprFromValue( b, instr.value() );
        if( v )
            b.push( move( *v ) );
        else
            b.push( instr.value() );
        return true;
    }

    bool BuildZ3Op( Builder& b, const VarAddr& instr )
    {
        b.push( Address( Address::Origin::Stack, instr.varIndex(), instr.locationId() ) );


        return true;
    }

    bool BuildZ3Op( Builder& b, const TempAddr& instr )
    {
        auto initVal = b.pop();
        if( !initVal )
            return false;

        b.push( Address( Address::Origin::Stack, instr.tempIndex(), instr.locationId() ) );
        return !!b.setVar( instr.tempIndex(), move( *initVal ) );


    }

    bool BuildZ3Op( Builder& b, const Select& instr )
    {
        if( auto cstBaseLoc = b.pop< Address >() )
        {
            b.push( Address::Select( move( *cstBaseLoc ), instr.memberIndex(), instr.locationId() ) );
            return true;
        }

        auto symBaseLoc = b.pop();
        if( !symBaseLoc )
            return false;

        b.push( Z3Val
        {
            z3::concat( symBaseLoc->expr, GetZ3Context().int_val( instr.memberIndex() ).unit() ),
            *EIRToValue( GetValueType< builtins::MemLoc >() ),
            instr.locationId()
        } );

        return true;
    }

    bool BuildZ3Op( Builder& b, const GhostCall& instr )
    {
        auto gfunc = b.pop< Value >();
        if( !gfunc )
            return false;

        auto argCount = instr.numArgs();
        llvm::SmallVector< Z3Val, 16 > args;
        args.reserve( argCount );

        for( uint32_t argIndex = 0; argIndex < argCount; ++argIndex )
        {
            auto zv = b.pop();
            if( !zv )
                return false;

            args.emplace_back( move( *zv ) );
        }

        GhostFuncApplication gfa( move( *gfunc ), instr.locationId() );

        gfa.args().reserve( args.size() );
        for( auto it = args.rbegin(); it != args.rend(); ++it )
            gfa.args().emplace_back( move( *it ) );

        b.push( move( gfa ) );
        return true;
    }

    bool BuildZ3Op( Builder& b, const cir::Instruction& instr )
    {
        return visit( [&]( auto&& e )
        {
            return BuildZ3Op( b, e );
        }, instr.content() );
    }

    Z3Val BuildZ3ExprFromValue( Builder& b, const Value& val )
    {
        if( val.isConstant() )
            return BuildZ3ValFromConstant( b, val );

        if( BuildZ3Op( b, *val.cir() ) )
        {
            auto result = b.pop();
            if( result )
                return *result;
        }

        return BuildZ3ConstantFromType( b, val.type(), format( "val{}", b.newUniqueId() ) );
    }

    optional< Z3Val > TryBuildZ3ExprFromValue( Builder& b, const Value& val )
    {
        if( val.isPoison() )
            return nullopt;

        if( val.isConstant() )
            return TryBuildZ3ValFromConstant( b, val );

        if( BuildZ3Op( b, *val.cir() ) )
            return b.pop();

        return TryBuildZ3ConstantFromType( b, val.type(), format( "val{}", b.newUniqueId() ) );
    }
}
Changes to bs/verify/value.h.
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
#ifndef GOOSE_VERIFY_VALUE_H
#define GOOSE_VERIFY_VALUE_H

#include "z3++.h"
#include "cir/cir.h"

namespace goose::verify
{
    struct Z3Val
    {
        z3::expr expr;
        eir::Value type;

    };

    extern optional< Z3Val > BuildZ3ValFromConstant( Builder& b, const Value& val );
    extern optional< Z3Val > BuildZ3ExprFromValue( Builder& b, const Value& val );
    extern optional< Z3Val > BuildZ3ConstantFromType( Builder& b, const Value& type, const string& name );
    extern optional< Z3Val > BuildZ3ConstantFromType( Builder& b, const Term& type, const string& name );






    extern optional< Z3Val > BuildZ3Op( Builder& b, const cir::Instruction& instr );


    extern z3::expr GetAsBitVec( const z3::expr& expr, const Value& type );
    extern z3::expr GetAsBitVec( const Z3Val& zv );

    extern z3::expr GetAsInt( const z3::expr& expr, const Value& type );
    extern z3::expr GetAsInt( const Z3Val& zv );













>


|
|
|
|
>
>
>
>
>
>
|
>







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
28
29
30
31
32
33
34
#ifndef GOOSE_VERIFY_VALUE_H
#define GOOSE_VERIFY_VALUE_H

#include "z3++.h"
#include "cir/cir.h"

namespace goose::verify
{
    struct Z3Val
    {
        z3::expr expr;
        eir::Value type;
        LocationId loc;
    };

    extern Z3Val BuildZ3ValFromConstant( Builder& b, const Value& val );
    extern Z3Val BuildZ3ExprFromValue( Builder& b, const Value& val );
    extern Z3Val BuildZ3ConstantFromType( Builder& b, const Value& type, const string& name );
    extern Z3Val BuildZ3ConstantFromType( Builder& b, const Term& type, const string& name );

    extern optional< Z3Val > TryBuildZ3ValFromConstant( Builder& b, const Value& val );
    extern optional< Z3Val > TryBuildZ3ExprFromValue( Builder& b, const Value& val );
    extern optional< Z3Val > TryBuildZ3ConstantFromType( Builder& b, const Value& type, const string& name );
    extern optional< Z3Val > TryBuildZ3ConstantFromType( Builder& b, const Term& type, const string& name );

    extern bool BuildZ3Op( Builder& b, const cir::Instruction& instr );
    extern bool BuildZ3Op( Builder& b, const cir::InstrSeq& is );

    extern z3::expr GetAsBitVec( const z3::expr& expr, const Value& type );
    extern z3::expr GetAsBitVec( const Z3Val& zv );

    extern z3::expr GetAsInt( const z3::expr& expr, const Value& type );
    extern z3::expr GetAsInt( const Z3Val& zv );

Changes to bs/verify/vartracker.cpp.
12
13
14
15
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

    if( vs->size() <= bbIndex )
        return nullopt;

    return (*vs)[ bbIndex ];
}

Z3Val& VarTracker::setForBasicBlock( Builder& b, uint32_t bbIndex, uint32_t index, Z3Val&& val )
{
    auto* vs = m_varStorage.get( index );
    if( !vs )
    {
        VarState newVS;
        newVS.resize( bbIndex + 1 );
        vs = &m_varStorage.set( index, move( newVS ) );

    }
    else if( vs->size() <= bbIndex )
        vs->resize( bbIndex + 1 );

    auto& c = GetZ3Context();
    auto predicate = c.bool_const( format( "b{}", bbIndex ).c_str() );

    auto lhsExpr = *BuildZ3ConstantFromType( b, val.type, format( "v{}_{}_{}", index, bbIndex, b.newUniqueId() ) );
    b.add( z3::implies( predicate, lhsExpr.expr == Coerce( val, lhsExpr ) ) );

    (*vs)[ bbIndex ] = move( lhsExpr );
    return *(*vs)[ bbIndex ];
}

optional< Z3Val > VarTracker::retrieve( Builder& b, uint32_t bbIndex, uint32_t index )







|






|
>







|







12
13
14
15
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

    if( vs->size() <= bbIndex )
        return nullopt;

    return (*vs)[ bbIndex ];
}

optional< Z3Val > VarTracker::setForBasicBlock( Builder& b, uint32_t bbIndex, uint32_t index, Z3Val&& val )
{
    auto* vs = m_varStorage.get( index );
    if( !vs )
    {
        VarState newVS;
        newVS.resize( bbIndex + 1 );
        auto& vsRef = m_varStorage.set( index, move( newVS ) );
        vs = &vsRef;
    }
    else if( vs->size() <= bbIndex )
        vs->resize( bbIndex + 1 );

    auto& c = GetZ3Context();
    auto predicate = c.bool_const( format( "b{}", bbIndex ).c_str() );

    auto lhsExpr = BuildZ3ConstantFromType( b, val.type, format( "v{}_{}_{}", index, bbIndex, b.newUniqueId() ) );
    b.add( z3::implies( predicate, lhsExpr.expr == Coerce( val, lhsExpr ) ) );

    (*vs)[ bbIndex ] = move( lhsExpr );
    return *(*vs)[ bbIndex ];
}

optional< Z3Val > VarTracker::retrieve( Builder& b, uint32_t bbIndex, uint32_t index )
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
        auto& c = GetZ3Context();

        b.remapper()->forEachIncomingEdge( bbIndex, [&]( auto&& predIndex, auto&& expr )
        {
            if( auto predVal = retrieve( b, predIndex, index ) )
            {
                if( !lhsExpr )
                    lhsExpr = *BuildZ3ConstantFromType( b, predVal->type, format( "v{}_{}_{}", index, bbIndex, b.newUniqueId() ) );

                b.add( z3::implies( c.bool_const( format( "e{}_{}", predIndex, bbIndex ).c_str() ),
                    lhsExpr->expr == Coerce( *predVal, *lhsExpr ) ) );
            }
        } );
    }

    if( !lhsExpr )
        return nullopt;

    return setForBasicBlock( b, bbIndex, index, move( *lhsExpr ) );
}

const Z3Val& VarTracker::set( Builder& b, uint32_t index, Z3Val&& val )
{
    return setForBasicBlock( b, b.currentBBIndex(), index, move( val ) );
}







|













|



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
        auto& c = GetZ3Context();

        b.remapper()->forEachIncomingEdge( bbIndex, [&]( auto&& predIndex, auto&& expr )
        {
            if( auto predVal = retrieve( b, predIndex, index ) )
            {
                if( !lhsExpr )
                    lhsExpr = BuildZ3ConstantFromType( b, predVal->type, format( "v{}_{}_{}", index, bbIndex, b.newUniqueId() ) );

                b.add( z3::implies( c.bool_const( format( "e{}_{}", predIndex, bbIndex ).c_str() ),
                    lhsExpr->expr == Coerce( *predVal, *lhsExpr ) ) );
            }
        } );
    }

    if( !lhsExpr )
        return nullopt;

    return setForBasicBlock( b, bbIndex, index, move( *lhsExpr ) );
}

optional< Z3Val > VarTracker::set( Builder& b, uint32_t index, Z3Val&& val )
{
    return setForBasicBlock( b, b.currentBBIndex(), index, move( val ) );
}
Changes to bs/verify/vartracker.h.
1
2
3
4
5
6
7
8
9
10




11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef GOOSE_VERIFY_VARTRACKER_H
#define GOOSE_VERIFY_VARTRACKER_H

namespace goose::verify
{
    class Builder;

    class VarTracker
    {
        public:




            optional< Z3Val > retrieve( Builder& b, uint32_t bbIndex, uint32_t index );
            const Z3Val& set( Builder& b, uint32_t index, Z3Val&& val );

        private:
            optional< Z3Val > getForBasicBlock( uint32_t bbIndex, uint32_t index ) const;
            Z3Val& setForBasicBlock( Builder& b, uint32_t bbIndex, uint32_t index, Z3Val&& val );

            // For each variable, store its last value as a z3 expression.
            using VarState = llvm::SmallVector< optional< Z3Val >, 16 >;
            using VarStorage = cir::TempStorage< VarState >;

            VarStorage m_varStorage;
    };










>
>
>
>

|



|







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
#ifndef GOOSE_VERIFY_VARTRACKER_H
#define GOOSE_VERIFY_VARTRACKER_H

namespace goose::verify
{
    class Builder;

    class VarTracker
    {
        public:
            VarTracker( size_t size ) :
                m_varStorage( size )
            {}

            optional< Z3Val > retrieve( Builder& b, uint32_t bbIndex, uint32_t index );
            optional< Z3Val > set( Builder& b, uint32_t index, Z3Val&& val );

        private:
            optional< Z3Val > getForBasicBlock( uint32_t bbIndex, uint32_t index ) const;
            optional< Z3Val > setForBasicBlock( Builder& b, uint32_t bbIndex, uint32_t index, Z3Val&& val );

            // For each variable, store its last value as a z3 expression.
            using VarState = llvm::SmallVector< optional< Z3Val >, 16 >;
            using VarStorage = cir::TempStorage< VarState >;

            VarStorage m_varStorage;
    };
Changes to bs/verify/verify.h.
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

25
26
27
28
29
30
31
32
33
34
{
    using namespace eir;
    using namespace cir;

    class Builder;

    extern z3::context& GetZ3Context();

    extern bool VerifyCompTimeExpr( const sema::Context& c, const Value& val );
}

#include "helpers.h"
#include "verifyviz.h"

#include "type.h"
#include "value.h"

#include "storage.h"
#include "remapper.h"
#include "ghostfunc.h"
#include "vartracker.h"
#include "gfctracker.h"
#include "builder.h"
#include "propositions.h"
#include "func.h"

#endif







<
|







>

|
|







9
10
11
12
13
14
15

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
    using namespace eir;
    using namespace cir;

    class Builder;

    extern z3::context& GetZ3Context();

    extern bool VerifyInstrSeq( const sema::Context& c, const InstrSeq& is );
}

#include "helpers.h"
#include "verifyviz.h"

#include "type.h"
#include "value.h"
#include "ghostfunc.h"
#include "storage.h"
#include "stack.h"
#include "remapper.h"
#include "vartracker.h"
#include "gfctracker.h"
#include "builder.h"
#include "propositions.h"
#include "func.h"

#endif
Changes to lib/prelude/ref_verification.g0.
9
10
11
12
13
14
15

16
17
18
19
20
21
22
23

    Value v

    uint(32) i = 1
    while i < count
    {
        GetProposition( predicates, i, v )

        result = MkValue( ValueToEIR( WrapValue( bool ) ), MkInstr( InstrOpCodeAnd, result, v ) )
        i += 1
    }

    return result
}

void EmitPredicatesEquivalenceCheck( Context c, LocationId argLoc, Value refType1, Value refType2 )







>
|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

    Value v

    uint(32) i = 1
    while i < count
    {
        GetProposition( predicates, i, v )
        AppendValueInstr( result, v )
        AppendValueInstr( result, MkInstr( InstrOpCodeAnd, GetValueLocation( v ) ) )
        i += 1
    }

    return result
}

void EmitPredicatesEquivalenceCheck( Context c, LocationId argLoc, Value refType1, Value refType2 )
47
48
49
50
51
52
53


54
55
56
57
58
59
60
61
62
63
    if !ContextGetCFG( c, cfg )
        return

    BasicBlock bb
    if !GetCFGCurrentBasicBlock( cfg, bb )
        return



    var cond = MkValue( ValueToEIR( WrapValue( bool ) ), MkInstr( InstrOpCodeEq, lhsExpr, rhsExpr ) )
    cond = SetValueLocation( cond, argLoc )
    AppendBasicBlockInstr( bb, MkInstr( InstrOpCodeAssert, cond ) )

    PushDiagnosticsContext( GetValueLocation( refType2 ), "...to a mutable reference parameter of this type.", true )
    PushDiagnosticsContext( GetValueLocation( refType1 ), "When passing a mutable reference to this type..." )

    DefineCustomDiagnostic( argLoc, MkStringId( "assert" ), "the argument's and the parameter's type predicates are not equivalent." )

    PopDiagnosticsContext()







>
>
|
<
|







48
49
50
51
52
53
54
55
56
57

58
59
60
61
62
63
64
65
    if !ContextGetCFG( c, cfg )
        return

    BasicBlock bb
    if !GetCFGCurrentBasicBlock( cfg, bb )
        return

    AppendBasicBlockInstr( bb, lhsExpr )
    AppendBasicBlockInstr( bb, rhsExpr )
    AppendBasicBlockInstr( bb, MkInstr( InstrOpCodeEq, argLoc ) )

    AppendBasicBlockInstr( bb, MkInstr( InstrOpCodeAssert, argLoc ) )

    PushDiagnosticsContext( GetValueLocation( refType2 ), "...to a mutable reference parameter of this type.", true )
    PushDiagnosticsContext( GetValueLocation( refType1 ), "When passing a mutable reference to this type..." )

    DefineCustomDiagnostic( argLoc, MkStringId( "assert" ), "the argument's and the parameter's type predicates are not equivalent." )

    PopDiagnosticsContext()
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
114
115


116
117
118
119
120
121
122
    if !GetCFGCurrentBasicBlock( cfg, bb )
        return

    RefTypeDesc rt
    if !EIRToRefTypeDesc( GetValueType( arg ), rt )
        return

    Instruction refCIR
    if !GetValueCIR( arg, refCIR )
        return

    // Create a dereference of the arg to fill the predicates' @val placeholder
    var referedType, var refBhv = UnpackRefTypeDesc( rt )
    var argDeref = MkValue( referedType, MkInstr( InstrOpCodeLoad, refCIR, referedType ) )
    SetValueLocation( argDeref, GetValueLocation( arg ) )





    PushDiagnosticsContext( GetValueLocation( refType ), "...to a reference parameter of this type.", true )
    PushDiagnosticsContext( GetValueLocation( arg ), "When passing this..." )

    Value p

    var count = GetPropositionsCount( pred )
    uint(32) i = 0
    while i < count
    {
        GetProposition( pred, i, p )

        var cond = MkValue( ValueToEIR( WrapValue( bool ) ),
            MkInstr( InstrOpCodePHOverride, MkStringId( "@val" ), argDeref, p ) )

        var pLoc = GetValueLocation( p )
        cond = SetValueLocation( cond, pLoc )

        AppendBasicBlockInstr( bb, MkInstr( InstrOpCodeAssert, cond ) )

        DefineCustomDiagnostic( pLoc, MkStringId( "assert" ) )

        i += 1
    }



    PopDiagnosticsContext()
    PopDiagnosticsContext()
}

// When passing a mutref to a mutref, we need to verify that their predicates are equivalent.
intrinsic mut ref $$T _ConvertFuncArg( mut ref $$T arg, mut ref $$U )







|





<
|
>
>
>
>











|
<
<

<
<
>
|





>
>







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
114
115
116
117
118
119
120
121
122
123
124
125
126
    if !GetCFGCurrentBasicBlock( cfg, bb )
        return

    RefTypeDesc rt
    if !EIRToRefTypeDesc( GetValueType( arg ), rt )
        return

    InstrSeq refCIR
    if !GetValueCIR( arg, refCIR )
        return

    // Create a dereference of the arg to fill the predicates' @val placeholder
    var referedType, var refBhv = UnpackRefTypeDesc( rt )

    var aLoc = GetValueLocation( arg )

    AppendBasicBlockInstr( bb, refCIR )
    AppendBasicBlockInstr( bb, MkInstr( InstrOpCodeLoad, referedType, aLoc ) )
    AppendBasicBlockInstr( bb, MkInstr( InstrOpCodePHOverrideSet, MkStringId( "@val" ), aLoc ) )

    PushDiagnosticsContext( GetValueLocation( refType ), "...to a reference parameter of this type.", true )
    PushDiagnosticsContext( GetValueLocation( arg ), "When passing this..." )

    Value p

    var count = GetPropositionsCount( pred )
    uint(32) i = 0
    while i < count
    {
        GetProposition( pred, i, p )
        var pLoc = GetValueLocation( p )





        AppendBasicBlockInstr( bb, p )
        AppendBasicBlockInstr( bb, MkInstr( InstrOpCodeAssert, pLoc ) )

        DefineCustomDiagnostic( pLoc, MkStringId( "assert" ) )

        i += 1
    }

    AppendBasicBlockInstr( bb, MkInstr( InstrOpCodePHOverrideClear, MkStringId( "@val" ), aLoc ) )

    PopDiagnosticsContext()
    PopDiagnosticsContext()
}

// When passing a mutref to a mutref, we need to verify that their predicates are equivalent.
intrinsic mut ref $$T _ConvertFuncArg( mut ref $$T arg, mut ref $$U )
Changes to tests/g0/codegen/arithops.g0.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var module = CGModuleCreate( "arith ops test" )

using entryPoint = void( uint( 8 ) a, uint( 8 ) b , sint( 16 ) c, sint( 16 ) d ) requires [b!=0 d!=0] {
    -c
    a + b
    a - b
    a * b
    a / b
    c / d
    a % b
    c % d
}

CGGenerateFunction( module, entryPoint, "main" )
CGModuleEmitLLVMIr( module, "tests/g0/codegen/arithops.ll" )




|
|
|
|
|
|
|




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var module = CGModuleCreate( "arith ops test" )

using entryPoint = void( uint( 8 ) a, uint( 8 ) b , sint( 16 ) c, sint( 16 ) d ) requires [b!=0 d!=0] {
    -c
    var v1 = a + b
    var v2 = a - b
    var v3 = a * b
    var v4 = a / b
    var v5 = c / d
    var v6 = a % b
    var v7 = c % d
}

CGGenerateFunction( module, entryPoint, "main" )
CGModuleEmitLLVMIr( module, "tests/g0/codegen/arithops.ll" )
Changes to tests/g0/codegen/arithops.ll.
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
28
29

30
31
32

33
34
35

36
37
; ModuleID = 'arith ops test'
source_filename = "arith ops test"

define void @main(i8 %0, i8 %1, i16 %2, i16 %3) {
  %5 = alloca i8, align 1
  %6 = alloca i8, align 1
  %7 = alloca i16, align 2
  %8 = alloca i16, align 2







  store i8 %0, i8* %5, align 1
  store i8 %1, i8* %6, align 1
  store i16 %2, i16* %7, align 2
  store i16 %3, i16* %8, align 2
  %9 = load i16, i16* %7, align 2
  %10 = sub i16 0, %9
  %11 = load i8, i8* %5, align 1
  %12 = load i8, i8* %6, align 1
  %13 = add i8 %11, %12

  %14 = load i8, i8* %5, align 1
  %15 = load i8, i8* %6, align 1
  %16 = sub i8 %14, %15

  %17 = load i8, i8* %5, align 1
  %18 = load i8, i8* %6, align 1
  %19 = mul i8 %17, %18

  %20 = load i8, i8* %5, align 1
  %21 = load i8, i8* %6, align 1
  %22 = udiv i8 %20, %21

  %23 = load i16, i16* %7, align 2
  %24 = load i16, i16* %8, align 2
  %25 = sdiv i16 %23, %24

  %26 = load i8, i8* %5, align 1
  %27 = load i8, i8* %6, align 1
  %28 = urem i8 %26, %27

  %29 = load i16, i16* %7, align 2
  %30 = load i16, i16* %8, align 2
  %31 = srem i16 %29, %30

  ret void
}








>
>
>
>
>
>
>




<
<
|
|
|
>
|
|
|
>
|
|
|
>
|
|
|
>
|
|
|
>
|
|
|
>
|
|
|
>


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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
; ModuleID = 'arith ops test'
source_filename = "arith ops test"

define void @main(i8 %0, i8 %1, i16 %2, i16 %3) {
  %5 = alloca i8, align 1
  %6 = alloca i8, align 1
  %7 = alloca i16, align 2
  %8 = alloca i16, align 2
  %9 = alloca i8, align 1
  %10 = alloca i8, align 1
  %11 = alloca i8, align 1
  %12 = alloca i8, align 1
  %13 = alloca i16, align 2
  %14 = alloca i8, align 1
  %15 = alloca i16, align 2
  store i8 %0, i8* %5, align 1
  store i8 %1, i8* %6, align 1
  store i16 %2, i16* %7, align 2
  store i16 %3, i16* %8, align 2


  %16 = load i8, i8* %5, align 1
  %17 = load i8, i8* %6, align 1
  %18 = add i8 %16, %17
  store i8 %18, i8* %9, align 1
  %19 = load i8, i8* %5, align 1
  %20 = load i8, i8* %6, align 1
  %21 = sub i8 %19, %20
  store i8 %21, i8* %10, align 1
  %22 = load i8, i8* %5, align 1
  %23 = load i8, i8* %6, align 1
  %24 = mul i8 %22, %23
  store i8 %24, i8* %11, align 1
  %25 = load i8, i8* %5, align 1
  %26 = load i8, i8* %6, align 1
  %27 = udiv i8 %25, %26
  store i8 %27, i8* %12, align 1
  %28 = load i16, i16* %7, align 2
  %29 = load i16, i16* %8, align 2
  %30 = sdiv i16 %28, %29
  store i16 %30, i16* %13, align 2
  %31 = load i8, i8* %5, align 1
  %32 = load i8, i8* %6, align 1
  %33 = urem i8 %31, %32
  store i8 %33, i8* %14, align 1
  %34 = load i16, i16* %7, align 2
  %35 = load i16, i16* %8, align 2
  %36 = srem i16 %34, %35
  store i16 %36, i16* %15, align 2
  ret void
}
Changes to tests/g0/codegen/bitwiseops.g0.
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
}

using entryPoint = void( uint( 8 ) a, uint( 8 ) b , sint( 16 ) c, sint( 16 ) d ) {
    lomarf( a, b )
    lomarf( 219, d )
    lomarf( d, 69 )

    a ^ b
    c ^ d

    c ^ 1337
    1337 ^ c

    ~d

    a | b
    a & b

    a << 4
    a >> 4
    c << 4
    c >> 4
}

CGGenerateFunction( module, entryPoint, "main" )
CGModuleEmitLLVMIr( module, "tests/g0/codegen/bitwiseops.ll" )







|
|

|
|

|

|
|

|
|
|
|




11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
}

using entryPoint = void( uint( 8 ) a, uint( 8 ) b , sint( 16 ) c, sint( 16 ) d ) {
    lomarf( a, b )
    lomarf( 219, d )
    lomarf( d, 69 )

    var v1 = a ^ b
    var v2 = c ^ d

    var v3 = c ^ 1337
    var v4 = 1337 ^ c

    var v5 = ~d

    var v6 = a | b
    var v7 = a & b

    var v8 = a << 4
    var v9 = a >> 4
    var v10 = c << 4
    var v11 = c >> 4
}

CGGenerateFunction( module, entryPoint, "main" )
CGModuleEmitLLVMIr( module, "tests/g0/codegen/bitwiseops.ll" )
Changes to tests/g0/codegen/bitwiseops.ll.
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

28
29

30
31

32
33
34

35
36
37

38
39

40
41

42
43

44
45

46
47
48
49
50
51
52
; ModuleID = 'bitwise ops test'
source_filename = "bitwise ops test"

define void @main(i8 %0, i8 %1, i16 %2, i16 %3) {
  %5 = alloca i8, align 1
  %6 = alloca i8, align 1
  %7 = alloca i16, align 2
  %8 = alloca i16, align 2











  store i8 %0, i8* %5, align 1
  store i8 %1, i8* %6, align 1
  store i16 %2, i16* %7, align 2
  store i16 %3, i16* %8, align 2
  %9 = load i8, i8* %5, align 1
  %10 = load i8, i8* %6, align 1
  %11 = call i8 @"_2_3s2:g0s3#20#s6:lomarf_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i8i0_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i8i0hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i8i0h$3nPi0"(i8 %9, i8 %10)
  %12 = load i16, i16* %8, align 2
  %13 = call i16 @"_2_3s2:g0s3#20#s6:lomarf_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i10i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i10i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i10i1h$3nPi0"(i16 219, i16 %12)
  %14 = load i16, i16* %8, align 2
  %15 = call i16 @"_2_3s2:g0s3#20#s6:lomarf_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i10i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i10i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i10i1h$3nPi0"(i16 %14, i16 69)
  %16 = load i8, i8* %5, align 1
  %17 = load i8, i8* %6, align 1
  %18 = xor i8 %16, %17

  %19 = load i16, i16* %7, align 2
  %20 = load i16, i16* %8, align 2
  %21 = xor i16 %19, %20

  %22 = load i16, i16* %7, align 2
  %23 = xor i16 %22, 1337

  %24 = load i16, i16* %7, align 2
  %25 = xor i16 1337, %24

  %26 = load i16, i16* %8, align 2
  %27 = xor i16 %26, -1

  %28 = load i8, i8* %5, align 1
  %29 = load i8, i8* %6, align 1
  %30 = or i8 %28, %29

  %31 = load i8, i8* %5, align 1
  %32 = load i8, i8* %6, align 1
  %33 = and i8 %31, %32

  %34 = load i8, i8* %5, align 1
  %35 = shl i8 %34, 4

  %36 = load i8, i8* %5, align 1
  %37 = lshr i8 %36, 4

  %38 = load i16, i16* %7, align 2
  %39 = shl i16 %38, 4

  %40 = load i16, i16* %7, align 2
  %41 = ashr i16 %40, 4

  ret void
}

define private i8 @"_2_3s2:g0s3#20#s6:lomarf_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i8i0_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i8i0hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i8i0h$3nPi0"(i8 %0, i8 %1) {
  %3 = alloca i8, align 1
  %4 = alloca i8, align 1
  store i8 %0, i8* %3, align 1








>
>
>
>
>
>
>
>
>
>
>




|
|
|
|
|
|
|
|
|
|
>
|
|
|
>
|
|
>
|
|
>
|
|
>
|
|
|
>
|
|
|
>
|
|
>
|
|
>
|
|
>
|
|
>







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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
; ModuleID = 'bitwise ops test'
source_filename = "bitwise ops test"

define void @main(i8 %0, i8 %1, i16 %2, i16 %3) {
  %5 = alloca i8, align 1
  %6 = alloca i8, align 1
  %7 = alloca i16, align 2
  %8 = alloca i16, align 2
  %9 = alloca i8, align 1
  %10 = alloca i16, align 2
  %11 = alloca i16, align 2
  %12 = alloca i16, align 2
  %13 = alloca i16, align 2
  %14 = alloca i8, align 1
  %15 = alloca i8, align 1
  %16 = alloca i8, align 1
  %17 = alloca i8, align 1
  %18 = alloca i16, align 2
  %19 = alloca i16, align 2
  store i8 %0, i8* %5, align 1
  store i8 %1, i8* %6, align 1
  store i16 %2, i16* %7, align 2
  store i16 %3, i16* %8, align 2
  %20 = load i8, i8* %5, align 1
  %21 = load i8, i8* %6, align 1
  %22 = call i8 @"_2_3s2:g0s3#20#s6:lomarf_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i8i0_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i8i0hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i8i0h$3nPi0"(i8 %20, i8 %21)
  %23 = load i16, i16* %8, align 2
  %24 = call i16 @"_2_3s2:g0s3#20#s6:lomarf_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i10i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i10i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i10i1h$3nPi0"(i16 219, i16 %23)
  %25 = load i16, i16* %8, align 2
  %26 = call i16 @"_2_3s2:g0s3#20#s6:lomarf_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i10i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i10i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i10i1h$3nPi0"(i16 %25, i16 69)
  %27 = load i8, i8* %5, align 1
  %28 = load i8, i8* %6, align 1
  %29 = xor i8 %27, %28
  store i8 %29, i8* %9, align 1
  %30 = load i16, i16* %7, align 2
  %31 = load i16, i16* %8, align 2
  %32 = xor i16 %30, %31
  store i16 %32, i16* %10, align 2
  %33 = load i16, i16* %7, align 2
  %34 = xor i16 %33, 1337
  store i16 %34, i16* %11, align 2
  %35 = load i16, i16* %7, align 2
  %36 = xor i16 1337, %35
  store i16 %36, i16* %12, align 2
  %37 = load i16, i16* %8, align 2
  %38 = xor i16 %37, -1
  store i16 %38, i16* %13, align 2
  %39 = load i8, i8* %5, align 1
  %40 = load i8, i8* %6, align 1
  %41 = or i8 %39, %40
  store i8 %41, i8* %14, align 1
  %42 = load i8, i8* %5, align 1
  %43 = load i8, i8* %6, align 1
  %44 = and i8 %42, %43
  store i8 %44, i8* %15, align 1
  %45 = load i8, i8* %5, align 1
  %46 = shl i8 %45, 4
  store i8 %46, i8* %16, align 1
  %47 = load i8, i8* %5, align 1
  %48 = lshr i8 %47, 4
  store i8 %48, i8* %17, align 1
  %49 = load i16, i16* %7, align 2
  %50 = shl i16 %49, 4
  store i16 %50, i16* %18, align 2
  %51 = load i16, i16* %7, align 2
  %52 = ashr i16 %51, 4
  store i16 %52, i16* %19, align 2
  ret void
}

define private i8 @"_2_3s2:g0s3#20#s6:lomarf_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i8i0_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i8i0hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i8i0h$3nPi0"(i8 %0, i8 %1) {
  %3 = alloca i8, align 1
  %4 = alloca i8, align 1
  store i8 %0, i8* %3, align 1
Changes to tests/g0/codegen/comparisonops.g0.
1
2
3


4
5
6
7
8
9
10
11
12
13
14
15
16
17
var module = CGModuleCreate( "comparison ops test" )

using entryPoint = void( uint( 8 ) a, uint( 8 ) b , sint( 16 ) c, sint( 16 ) d ) {


    a == b
    a != b
    a > b
    a < b
    a >= b
    a <= b
    c > d
    c < d
    c >= d
    c <= d
}

CGGenerateFunction( module, entryPoint, "main" )
CGModuleEmitLLVMIr( module, "tests/g0/codegen/comparisonops.ll" )



>
>
|
|
|
|
|
|
|
|
|
|




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var module = CGModuleCreate( "comparison ops test" )

using entryPoint = void( uint( 8 ) a, uint( 8 ) b , sint( 16 ) c, sint( 16 ) d ) {
    bool x = false

    x = a == b
    x = a != b
    x = a > b
    x = a < b
    x = a >= b
    x = a <= b
    x = c > d
    x = c < d
    x = c >= d
    x = c <= d
}

CGGenerateFunction( module, entryPoint, "main" )
CGModuleEmitLLVMIr( module, "tests/g0/codegen/comparisonops.ll" )
Changes to tests/g0/codegen/comparisonops.ll.
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

28
29
30

31
32
33

34
35
36

37
38
39

40
41
42

43
44
; ModuleID = 'comparison ops test'
source_filename = "comparison ops test"

define void @main(i8 %0, i8 %1, i16 %2, i16 %3) {
  %5 = alloca i8, align 1
  %6 = alloca i8, align 1
  %7 = alloca i16, align 2
  %8 = alloca i16, align 2

  store i8 %0, i8* %5, align 1
  store i8 %1, i8* %6, align 1
  store i16 %2, i16* %7, align 2
  store i16 %3, i16* %8, align 2

  %9 = load i8, i8* %5, align 1
  %10 = load i8, i8* %6, align 1
  %11 = icmp eq i8 %9, %10

  %12 = load i8, i8* %5, align 1
  %13 = load i8, i8* %6, align 1
  %14 = icmp ne i8 %12, %13

  %15 = load i8, i8* %5, align 1
  %16 = load i8, i8* %6, align 1
  %17 = icmp ugt i8 %15, %16

  %18 = load i8, i8* %5, align 1
  %19 = load i8, i8* %6, align 1
  %20 = icmp ult i8 %18, %19

  %21 = load i8, i8* %5, align 1
  %22 = load i8, i8* %6, align 1
  %23 = icmp uge i8 %21, %22

  %24 = load i8, i8* %5, align 1
  %25 = load i8, i8* %6, align 1
  %26 = icmp ule i8 %24, %25

  %27 = load i16, i16* %7, align 2
  %28 = load i16, i16* %8, align 2
  %29 = icmp sgt i16 %27, %28

  %30 = load i16, i16* %7, align 2
  %31 = load i16, i16* %8, align 2
  %32 = icmp slt i16 %30, %31

  %33 = load i16, i16* %7, align 2
  %34 = load i16, i16* %8, align 2
  %35 = icmp sge i16 %33, %34

  %36 = load i16, i16* %7, align 2
  %37 = load i16, i16* %8, align 2
  %38 = icmp sle i16 %36, %37

  ret void
}








>




>
|
|
|
>
|
|
|
>
|
|
|
>
|
|
|
>
|
|
|
>
|
|
|
>
|
|
|
>
|
|
|
>
|
|
|
>
|
|
|
>


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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
; ModuleID = 'comparison ops test'
source_filename = "comparison ops test"

define void @main(i8 %0, i8 %1, i16 %2, i16 %3) {
  %5 = alloca i8, align 1
  %6 = alloca i8, align 1
  %7 = alloca i16, align 2
  %8 = alloca i16, align 2
  %9 = alloca i1, align 1
  store i8 %0, i8* %5, align 1
  store i8 %1, i8* %6, align 1
  store i16 %2, i16* %7, align 2
  store i16 %3, i16* %8, align 2
  store i1 false, i1* %9, align 1
  %10 = load i8, i8* %5, align 1
  %11 = load i8, i8* %6, align 1
  %12 = icmp eq i8 %10, %11
  store i1 %12, i1* %9, align 1
  %13 = load i8, i8* %5, align 1
  %14 = load i8, i8* %6, align 1
  %15 = icmp ne i8 %13, %14
  store i1 %15, i1* %9, align 1
  %16 = load i8, i8* %5, align 1
  %17 = load i8, i8* %6, align 1
  %18 = icmp ugt i8 %16, %17
  store i1 %18, i1* %9, align 1
  %19 = load i8, i8* %5, align 1
  %20 = load i8, i8* %6, align 1
  %21 = icmp ult i8 %19, %20
  store i1 %21, i1* %9, align 1
  %22 = load i8, i8* %5, align 1
  %23 = load i8, i8* %6, align 1
  %24 = icmp uge i8 %22, %23
  store i1 %24, i1* %9, align 1
  %25 = load i8, i8* %5, align 1
  %26 = load i8, i8* %6, align 1
  %27 = icmp ule i8 %25, %26
  store i1 %27, i1* %9, align 1
  %28 = load i16, i16* %7, align 2
  %29 = load i16, i16* %8, align 2
  %30 = icmp sgt i16 %28, %29
  store i1 %30, i1* %9, align 1
  %31 = load i16, i16* %7, align 2
  %32 = load i16, i16* %8, align 2
  %33 = icmp slt i16 %31, %32
  store i1 %33, i1* %9, align 1
  %34 = load i16, i16* %7, align 2
  %35 = load i16, i16* %8, align 2
  %36 = icmp sge i16 %34, %35
  store i1 %36, i1* %9, align 1
  %37 = load i16, i16* %7, align 2
  %38 = load i16, i16* %8, align 2
  %39 = icmp sle i16 %37, %38
  store i1 %39, i1* %9, align 1
  ret void
}
Changes to tests/g0/codegen/if.ll.
15
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
44
45
46
47
48
49

define private void @"_2_3s2:g0s3#20#s3:meh_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0v_2q_2_5Vr_5Vo_5Vo_3ti1i0_4T_2RPplhs1:_n_5Vr_5Vo_5Vo_3ti1i0_4T_2RPplh$3nPi0"(i1 %0, i1 %1) {
  %3 = alloca i1, align 1
  %4 = alloca i1, align 1
  store i1 %0, i1* %3, align 1
  store i1 %1, i1* %4, align 1
  %5 = load i1, i1* %3, align 1
  br i1 %5, label %6, label %12

6:                                                ; preds = %2
  %7 = load i1, i1* %4, align 1
  %8 = xor i1 %7, true
  br i1 %8, label %9, label %10

9:                                                ; preds = %6
  call void @puts([7 x i8]* @.str0)
  br label %11

10:                                               ; preds = %6
  call void @puts([5 x i8]* @.str2)
  br label %11

11:                                               ; preds = %10, %9

  br label %13

12:                                               ; preds = %2
  call void @puts([4 x i8]* @.str3)
  br label %13

13:                                               ; preds = %12, %11
  call void @puts([3 x i8]* @.str1)
  ret void
}

declare void @puts(i8*)







|




|

|
|
|


|
|

|
>
|

|
<


|





15
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
44
45
46
47
48
49

define private void @"_2_3s2:g0s3#20#s3:meh_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0v_2q_2_5Vr_5Vo_5Vo_3ti1i0_4T_2RPplhs1:_n_5Vr_5Vo_5Vo_3ti1i0_4T_2RPplh$3nPi0"(i1 %0, i1 %1) {
  %3 = alloca i1, align 1
  %4 = alloca i1, align 1
  store i1 %0, i1* %3, align 1
  store i1 %1, i1* %4, align 1
  %5 = load i1, i1* %3, align 1
  br i1 %5, label %6, label %9

6:                                                ; preds = %2
  %7 = load i1, i1* %4, align 1
  %8 = xor i1 %7, true
  br i1 %8, label %10, label %11

9:                                                ; preds = %2
  call void @puts([4 x i8]* @.str3)
  br label %13

10:                                               ; preds = %6
  call void @puts([7 x i8]* @.str0)
  br label %12

11:                                               ; preds = %6
  call void @puts([5 x i8]* @.str2)
  br label %12

12:                                               ; preds = %11, %10

  br label %13

13:                                               ; preds = %9, %12
  call void @puts([3 x i8]* @.str1)
  ret void
}

declare void @puts(i8*)
Changes to tests/g0/codegen/logicops.ll.
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

define private i1 @"_2_3s2:g0s3#20#s6:lomarf_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_4T_2RPpl_2q_2_5Vr_5Vo_5Vo_3ti1i0_4T_2RPplhs1:_n_5Vr_5Vo_5Vo_3ti1i0_4T_2RPplh$3nPi0"(i1 %0, i1 %1) {
  %3 = alloca i1, align 1
  %4 = alloca i1, align 1
  store i1 %0, i1* %3, align 1
  store i1 %1, i1* %4, align 1
  %5 = load i1, i1* %3, align 1
  br i1 %5, label %8, label %6

6:                                                ; preds = %2
  %7 = load i1, i1* %4, align 1
  br label %8

8:                                                ; preds = %6, %2
  %9 = phi i1 [ true, %2 ], [ %7, %6 ]
  ret i1 %9
}

define private i1 @"_2_3s2:g0s3#20#s4:blah_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_4T_2RPpl_2q_2_5Vr_5Vo_5Vo_3ti1i0_4T_2RPplhs1:_n_5Vr_5Vo_5Vo_3ti1i0_4T_2RPplh$3nPi0"(i1 %0, i1 %1) {
  %3 = alloca i1, align 1
  %4 = alloca i1, align 1
  store i1 %0, i1* %3, align 1
  store i1 %1, i1* %4, align 1







|

|
|
|

|
|
|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

define private i1 @"_2_3s2:g0s3#20#s6:lomarf_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_4T_2RPpl_2q_2_5Vr_5Vo_5Vo_3ti1i0_4T_2RPplhs1:_n_5Vr_5Vo_5Vo_3ti1i0_4T_2RPplh$3nPi0"(i1 %0, i1 %1) {
  %3 = alloca i1, align 1
  %4 = alloca i1, align 1
  store i1 %0, i1* %3, align 1
  store i1 %1, i1* %4, align 1
  %5 = load i1, i1* %3, align 1
  br i1 %5, label %6, label %8

6:                                                ; preds = %8, %2
  %7 = phi i1 [ true, %2 ], [ %9, %8 ]
  ret i1 %7

8:                                                ; preds = %2
  %9 = load i1, i1* %4, align 1
  br label %6
}

define private i1 @"_2_3s2:g0s3#20#s4:blah_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_4T_2RPpl_2q_2_5Vr_5Vo_5Vo_3ti1i0_4T_2RPplhs1:_n_5Vr_5Vo_5Vo_3ti1i0_4T_2RPplh$3nPi0"(i1 %0, i1 %1) {
  %3 = alloca i1, align 1
  %4 = alloca i1, align 1
  store i1 %0, i1* %3, align 1
  store i1 %1, i1* %4, align 1
Changes to tests/g0/codegen/while.ll.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52


53

54
; ModuleID = 'while test'
source_filename = "while test"

define void @main() {
  %1 = alloca i32, align 4
  %2 = alloca i1, align 1
  %3 = alloca i1, align 1
  store i32 0, i32* %1, align 4
  store i1 true, i1* %2, align 1
  store i1 true, i1* %3, align 1
  br label %4

4:                                                ; preds = %20, %9, %0
  %5 = load i32, i32* %1, align 4
  %6 = icmp ult i32 %5, 10
  br i1 %6, label %7, label %23

7:                                                ; preds = %4
  %8 = load i1, i1* %2, align 1
  br i1 %8, label %9, label %10

9:                                                ; preds = %7



  br label %4

10:                                               ; preds = %7
  br label %11

11:                                               ; preds = %17, %10
  %12 = load i32, i32* %1, align 4
  %13 = icmp ugt i32 %12, 50
  br i1 %13, label %14, label %20

14:                                               ; preds = %11
  %15 = load i1, i1* %3, align 1
  br i1 %15, label %16, label %17

16:                                               ; preds = %14
  br label %20

17:                                               ; preds = %14
  %18 = load i32, i32* %1, align 4
  %19 = add i32 %18, 33
  store i32 %19, i32* %1, align 4
  br label %11

20:                                               ; preds = %16, %11
  %21 = load i32, i32* %1, align 4
  %22 = sub i32 %21, 2
  store i32 %22, i32* %1, align 4
  br label %4

23:                                               ; preds = %4


  ret void

}












|


|



|

|
>
>
>


|
|

|
|
|
|

|
|
<
<
<
|

|

|

|

|
<
<
<
|

|
>
>
|
>

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
28
29
30
31
32
33
34
35
36
37



38
39
40
41
42
43
44
45
46



47
48
49
50
51
52
53
54
; ModuleID = 'while test'
source_filename = "while test"

define void @main() {
  %1 = alloca i32, align 4
  %2 = alloca i1, align 1
  %3 = alloca i1, align 1
  store i32 0, i32* %1, align 4
  store i1 true, i1* %2, align 1
  store i1 true, i1* %3, align 1
  br label %4

4:                                                ; preds = %17, %10, %0
  %5 = load i32, i32* %1, align 4
  %6 = icmp ult i32 %5, 10
  br i1 %6, label %7, label %9

7:                                                ; preds = %4
  %8 = load i1, i1* %2, align 1
  br i1 %8, label %10, label %11

9:                                                ; preds = %4
  ret void

10:                                               ; preds = %7
  br label %4

11:                                               ; preds = %7
  br label %12

12:                                               ; preds = %21, %11
  %13 = load i32, i32* %1, align 4
  %14 = icmp ugt i32 %13, 50
  br i1 %14, label %15, label %17

15:                                               ; preds = %12
  %16 = load i1, i1* %3, align 1



  br i1 %16, label %20, label %21

17:                                               ; preds = %20, %12
  %18 = load i32, i32* %1, align 4
  %19 = sub i32 %18, 2
  store i32 %19, i32* %1, align 4
  br label %4

20:                                               ; preds = %15



  br label %17

21:                                               ; preds = %15
  %22 = load i32, i32* %1, align 4
  %23 = add i32 %22, 33
  store i32 %23, i32* %1, align 4
  br label %12
}
Changes to tests/g0/function/c-fail-calling-compiletime-func.txt.
1
2
3
4
5
6
7
8
9
10
11

tests/g0/function/c-fail-calling-compiletime-func.g0:
    3 | void put( ct_char c )
      |           ^^^^^^^
      | Error: function arguments mismatch.
    3 | void put( ct_char c )
      |           ^^^^^^^
      | When invoking LowerTypeForRuntime.
     ...
    8 | put( 'g' )
      | ~~~~~~~~~~
      | When calling this.


<
<
<
<
<
<
<

|
<
>
1







2
3

4
tests/g0/function/c-fail-calling-compiletime-func.g0:







    8 | put( 'g' )
      |      ^^^

      | Error: constants with compile-time types are not supported by code generation.
Changes to tests/g0/verification/c-fail-test-4.txt.
1
2
3
4
5
6
7
8









tests/g0/verification/c-fail-test-4.g0:
    3 |     ensures [ @result > 0 ]
      |               ^^^^^^^^^^^
      |  Error: this condition may not be met.
     ...
    7 |         return b
      |                ~
      |       When returning this.

















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
tests/g0/verification/c-fail-test-4.g0:
    3 |     ensures [ @result > 0 ]
      |               ^^^^^^^^^^^
      |  Error: this condition may not be met.
     ...
    7 |         return b
      |                ~
      |       When returning this.

tests/g0/verification/c-fail-test-4.g0:
    3 |     ensures [ @result > 0 ]
      |               ^^^^^^^^^^^
      |  Error: this condition may not be met.
     ...
   16 |     return ta - tb
      |            ~~~~~~~
      |      When returning this.
Changes to tests/g0/verification/c-fail-test-7.txt.
1
2
3
4
5
6
7
8
tests/g0/verification/c-fail-test-7.g0:
   15 | sint(32)[!=0] testFunc2( sint(32)[!=0] a )
      |                                   ^^^
      |                    Error: this condition is not met.
     ...
   21 | testFunc2( 0 )
      | ~~~~~~~~~~~~~~
      | At this compilation-time call.



|



|
1
2
3
4
5
6
7
8
tests/g0/verification/c-fail-test-7.g0:
   15 | sint(32)[!=0] testFunc2( sint(32)[!=0] a )
      |                                   ^^^
      |                  Error: this condition may not be met.
     ...
   21 | testFunc2( 0 )
      | ~~~~~~~~~~~~~~
      | At this call.
Changes to tests/g0/verification/misc/c-fail-mandelbrot-bad-3.txt.
1
2
3
4
5
6
7
8
tests/g0/verification/misc/c-fail-mandelbrot-bad-3.g0:
    9 | s32 IntToFixedWidthDec( s32[<(1<<12) >(-1<<12)] i )
      |                                      ^^^^^^^^^
      |                          Error: this condition is not met.
     ...
   17 | using minU = IntToFixedWidthDec( -20000 )   // Intentional error
      |              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |             At this compilation-time call.



|



|
1
2
3
4
5
6
7
8
tests/g0/verification/misc/c-fail-mandelbrot-bad-3.g0:
    9 | s32 IntToFixedWidthDec( s32[<(1<<12) >(-1<<12)] i )
      |                                      ^^^^^^^^^
      |                        Error: this condition may not be met.
     ...
   17 | using minU = IntToFixedWidthDec( -20000 )   // Intentional error
      |              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |                     At this call.
Changes to tests/g0/verification/misc/c-fail-mandelbrot-bad-4.txt.
1
2
3
4
5
6
7
8
tests/g0/verification/misc/c-fail-mandelbrot-bad-4.g0:
    9 | s32 IntToFixedWidthDec( s32[<(1<<12) >(-1<<12)] i )
      |                             ^^^^^^^^
      |                 Error: this condition is not met.
     ...
   18 | using maxU = IntToFixedWidthDec( 100000 )   // Intentional error
      |              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |             At this compilation-time call.



|



|
1
2
3
4
5
6
7
8
tests/g0/verification/misc/c-fail-mandelbrot-bad-4.g0:
    9 | s32 IntToFixedWidthDec( s32[<(1<<12) >(-1<<12)] i )
      |                             ^^^^^^^^
      |               Error: this condition may not be met.
     ...
   18 | using maxU = IntToFixedWidthDec( 100000 )   // Intentional error
      |              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |                     At this call.
Changes to tests/g0/verification/z3gen/e-z3gen-test-1.txt.
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
28
29
30
31
32
=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Begin function verification trace ===
(=> b1 (= v0_1_59 p0))
(=> b1 (= v1_1_61 u60))
(=> b1 (= v1_1_62 v0_1_59))
(= e1_2 (and b1 (distinct v1_1_62 5)))
(= b2 e1_2)
(=> b2 (= v1_2_63 5))
(= e1_3 (and b1 (not (distinct v1_1_62 5))))
(= e2_3 (and b2 b2))
(= b3 (or e1_3 e2_3))
(= e3_4 (and b3 b3))
(= b4 e3_4)
(=> e1_3 (= v1_3_64 v1_1_62))
(=> e2_3 (= v1_3_64 v1_2_63))
(=> b3 (= v1_3_65 v1_3_64))
(=> e3_4 (= v1_4_66 v1_3_65))
(=> b4 (= v1_4_67 v1_4_66))
check_unsat (and b4 (not (= v1_4_67 5)))
(= e4_5 (and b4 b4))
(= b5 e4_5)
=== End function verification trace ===

=== Begin function verification trace ===
(=> b1 (= v0_1_69 1))
=== End function verification trace ===

<
<
<
<
<
<

|
|
|
|

|
|




|
|
|
|
|
|





|








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






=== Begin function verification trace ===
(=> b1 (= v0_1_113 p0))
(=> b1 (= v1_1_115 u114))
(=> b1 (= v1_1_116 v0_1_113))
(= e1_2 (and b1 (distinct v1_1_116 5)))
(= b2 e1_2)
(=> b2 (= v1_2_117 5))
(= e1_3 (and b1 (not (distinct v1_1_116 5))))
(= e2_3 (and b2 b2))
(= b3 (or e1_3 e2_3))
(= e3_4 (and b3 b3))
(= b4 e3_4)
(=> e1_3 (= v1_3_118 v1_1_116))
(=> e2_3 (= v1_3_118 v1_2_117))
(=> b3 (= v1_3_119 v1_3_118))
(=> e3_4 (= v1_4_120 v1_3_119))
(=> b4 (= v1_4_121 v1_4_120))
check_unsat (and b4 (not (= v1_4_121 5)))
(= e4_5 (and b4 b4))
(= b5 e4_5)
=== End function verification trace ===

=== Begin function verification trace ===
(=> b1 (= v0_1_122 1))
=== End function verification trace ===

Changes to tests/g0/verification/z3gen/e-z3gen-test-2.txt.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Begin function verification trace ===
(=> b1 (= v0_1_63 p0))
(=> b1 (= v1_1_64 p1))
assume (distinct v0_1_63 v1_1_64)
(=> b1 (= v3_1_66 u65))
(=> b1 (= v3_1_67 v0_1_63))
(=> b1 (= v4_1_69 u68))
(=> b1 (= v4_1_70 v1_1_64))
(=> b1 (= v5_1_72 u71))
(=> b1 (= v5_1_73 v3_1_67))
(=> b1 (= v6_1_75 u74))
(=> b1 (= v6_1_76 v4_1_70))
(= e1_2 (and b1 (< v5_1_73 v6_1_76)))
(= b2 e1_2)
(=> b2 (= v8_2_78 u77))
(=> e1_2 (= v6_2_79 v6_1_76))
(=> b2 (= v6_2_80 v6_2_79))
(=> b2 (= v8_2_81 v6_2_80))
(=> b2 (= v9_2_83 u82))
(=> e1_2 (= v5_2_84 v5_1_73))
(=> b2 (= v5_2_85 v5_2_84))
(=> b2 (= v9_2_86 v5_2_85))
(=> b2 (= v5_2_87 v8_2_81))
(=> b2 (= v6_2_88 v9_2_86))
(= e1_3 (and b1 (not (< v5_1_73 v6_1_76))))
(= e2_3 (and b2 b2))
(= b3 (or e1_3 e2_3))
(= e3_4 (and b3 b3))
(= b4 e3_4)
(=> e1_3 (= v5_3_89 v5_1_73))
(=> e2_3 (= v5_3_89 v5_2_87))
(=> b3 (= v5_3_90 v5_3_89))
(=> e3_4 (= v5_4_91 v5_3_90))
(=> b4 (= v5_4_92 v5_4_91))
(=> e1_3 (= v6_3_93 v6_1_76))
(=> e2_3 (= v6_3_93 v6_2_88))
(=> b3 (= v6_3_94 v6_3_93))
(=> e3_4 (= v6_4_95 v6_3_94))
(=> b4 (= v6_4_96 v6_4_95))
check_unsat (and b4 (not (> v5_4_92 v6_4_96)))
(= e4_5 (and b4 b4))
(= b5 e4_5)
=== End function verification trace ===

=== Begin function verification trace ===
(=> b1 (= v0_1_98 2))
(=> b1 (= v1_1_99 1))
check_unsat (not (distinct v0_1_98 v1_1_99))
=== End function verification trace ===

<
<
<
<
<
<
<
<
<
<
<
<
<
<

|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|




|
|
|
|
|
|
|
|
|
|
|





|
|
|
















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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50














=== Begin function verification trace ===
(=> b1 (= v0_1_113 p0))
(=> b1 (= v1_1_114 p1))
assume (distinct v0_1_113 v1_1_114)
(=> b1 (= v3_1_116 u115))
(=> b1 (= v3_1_117 v0_1_113))
(=> b1 (= v4_1_119 u118))
(=> b1 (= v4_1_120 v1_1_114))
(=> b1 (= v5_1_122 u121))
(=> b1 (= v5_1_123 v3_1_117))
(=> b1 (= v6_1_125 u124))
(=> b1 (= v6_1_126 v4_1_120))
(= e1_2 (and b1 (< v5_1_123 v6_1_126)))
(= b2 e1_2)
(=> b2 (= v8_2_128 u127))
(=> e1_2 (= v6_2_129 v6_1_126))
(=> b2 (= v6_2_130 v6_2_129))
(=> b2 (= v8_2_131 v6_2_130))
(=> b2 (= v9_2_133 u132))
(=> e1_2 (= v5_2_134 v5_1_123))
(=> b2 (= v5_2_135 v5_2_134))
(=> b2 (= v9_2_136 v5_2_135))
(=> b2 (= v5_2_137 v8_2_131))
(=> b2 (= v6_2_138 v9_2_136))
(= e1_3 (and b1 (not (< v5_1_123 v6_1_126))))
(= e2_3 (and b2 b2))
(= b3 (or e1_3 e2_3))
(= e3_4 (and b3 b3))
(= b4 e3_4)
(=> e1_3 (= v5_3_139 v5_1_123))
(=> e2_3 (= v5_3_139 v5_2_137))
(=> b3 (= v5_3_140 v5_3_139))
(=> e3_4 (= v5_4_141 v5_3_140))
(=> b4 (= v5_4_142 v5_4_141))
(=> e1_3 (= v6_3_143 v6_1_126))
(=> e2_3 (= v6_3_143 v6_2_138))
(=> b3 (= v6_3_144 v6_3_143))
(=> e3_4 (= v6_4_145 v6_3_144))
(=> b4 (= v6_4_146 v6_4_145))
check_unsat (and b4 (not (> v5_4_142 v6_4_146)))
(= e4_5 (and b4 b4))
(= b5 e4_5)
=== End function verification trace ===

=== Begin function verification trace ===
(=> b1 (= v1_1_147 1))
(=> b1 (= v0_1_148 2))
check_unsat (not (distinct v0_1_148 v1_1_147))
=== End function verification trace ===

Changes to tests/g0/verification/z3gen/e-z3gen-test-3.txt.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Begin function verification trace ===
(=> b1 (= v0_1_63 p0))
(=> b1 (= v1_1_64 p1))
assume (distinct v0_1_63 v1_1_64)
(=> b1 (= v3_1_66 u65))
(=> b1 (= v3_1_67 v0_1_63))
(=> b1 (= v4_1_69 u68))
(=> b1 (= v4_1_70 v1_1_64))
(=> b1 (= v5_1_72 u71))
(=> b1 (= v5_1_73 v3_1_67))
(=> b1 (= v6_1_75 u74))
(=> b1 (= v6_1_76 v4_1_70))
(= e1_2 (and b1 (< v5_1_73 v6_1_76)))
(= b2 e1_2)
(=> b2 (= v8_2_78 u77))
(=> e1_2 (= v6_2_79 v6_1_76))
(=> b2 (= v6_2_80 v6_2_79))
(=> b2 (= v8_2_81 v6_2_80))
(=> b2 (= v9_2_83 u82))
(=> e1_2 (= v5_2_84 v5_1_73))
(=> b2 (= v5_2_85 v5_2_84))
(=> b2 (= v9_2_86 v5_2_85))
(=> b2 (= v5_2_87 v8_2_81))
(=> b2 (= v6_2_88 v9_2_86))
(= e1_3 (and b1 (not (< v5_1_73 v6_1_76))))
(= e2_3 (and b2 b2))
(= b3 (or e1_3 e2_3))
(=> e1_3 (= v5_3_89 v5_1_73))
(=> e2_3 (= v5_3_89 v5_2_87))
(=> b3 (= v5_3_90 v5_3_89))
(=> e1_3 (= v6_3_91 v6_1_76))
(=> e2_3 (= v6_3_91 v6_2_88))
(=> b3 (= v6_3_92 v6_3_91))
check_unsat (and b3 (not (> (- v5_3_90 v6_3_92) 0)))
=== End function verification trace ===

=== Checking compilation-time expression ===
(=> b1 (= v0_1_93 2))
(=> b1 (= v1_1_94 1))
check_unsat (not (distinct v0_1_93 v1_1_94))

=== Begin function verification trace ===
=== End function verification trace ===

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|


|
|
|
|
|
|
|


|
|
|
|
|



















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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
















=== Begin function verification trace ===
(=> b1 (= v0_1_113 p0))
(=> b1 (= v1_1_114 p1))
assume (distinct v0_1_113 v1_1_114)
(=> b1 (= v3_1_116 u115))
(=> b1 (= v3_1_117 v0_1_113))
(=> b1 (= v4_1_119 u118))
(=> b1 (= v4_1_120 v1_1_114))
(=> b1 (= v5_1_122 u121))
(=> b1 (= v5_1_123 v3_1_117))
(=> b1 (= v6_1_125 u124))
(=> b1 (= v6_1_126 v4_1_120))
(= e1_2 (and b1 (< v5_1_123 v6_1_126)))
(= b2 e1_2)
(=> b2 (= v8_2_128 u127))
(=> e1_2 (= v6_2_129 v6_1_126))
(=> b2 (= v6_2_130 v6_2_129))
(=> b2 (= v8_2_131 v6_2_130))
(=> b2 (= v9_2_133 u132))
(=> e1_2 (= v5_2_134 v5_1_123))
(=> b2 (= v5_2_135 v5_2_134))
(=> b2 (= v9_2_136 v5_2_135))
(=> b2 (= v5_2_137 v8_2_131))
(=> b2 (= v6_2_138 v9_2_136))
(= e1_3 (and b1 (not (< v5_1_123 v6_1_126))))
(= e2_3 (and b2 b2))
(= b3 (or e1_3 e2_3))
(=> e1_3 (= v5_3_139 v5_1_123))
(=> e2_3 (= v5_3_139 v5_2_137))
(=> b3 (= v5_3_140 v5_3_139))
(=> e1_3 (= v6_3_141 v6_1_126))
(=> e2_3 (= v6_3_141 v6_2_138))
(=> b3 (= v6_3_142 v6_3_141))
check_unsat (and b3 (not (> (- v5_3_140 v6_3_142) 0)))
=== End function verification trace ===

=== Checking instr seq ===
(=> b1 (= v1_1_144 1))
(=> b1 (= v0_1_145 2))
check_unsat (not (distinct v0_1_145 v1_1_144))
assume (> r143 0)
=== Begin function verification trace ===
=== End function verification trace ===

Changes to tests/g0/verification/z3gen/e-z3gen-test-4.txt.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Begin function verification trace ===
(=> b1 (= v0_1_65 p0))
(=> b1 (= v1_1_66 p1))
assume (distinct v0_1_65 v1_1_66)
(=> b1 (= v3_1_68 u67))
(=> b1 (= v3_1_69 v0_1_65))
(=> b1 (= v4_1_71 u70))
(=> b1 (= v4_1_72 v1_1_66))
(=> b1 (= v5_1_74 u73))
(=> b1 (= v5_1_75 v3_1_69))
(=> b1 (= v6_1_77 u76))
(=> b1 (= v6_1_78 v4_1_72))
(= e1_2 (and b1 (< v5_1_75 v6_1_78)))
(= b2 e1_2)
(=> b2 (= v8_2_80 u79))
(=> e1_2 (= v6_2_81 v6_1_78))
(=> b2 (= v6_2_82 v6_2_81))
(=> b2 (= v8_2_83 v6_2_82))
(=> b2 (= v9_2_85 u84))
(=> e1_2 (= v5_2_86 v5_1_75))
(=> b2 (= v5_2_87 v5_2_86))
(=> b2 (= v9_2_88 v5_2_87))
(=> b2 (= v5_2_89 v8_2_83))
(=> b2 (= v6_2_90 v9_2_88))
(= e1_3 (and b1 (not (< v5_1_75 v6_1_78))))
(= e2_3 (and b2 b2))
(= b3 (or e1_3 e2_3))
(=> e1_3 (= v5_3_91 v5_1_75))
(=> e2_3 (= v5_3_91 v5_2_89))
(=> b3 (= v5_3_92 v5_3_91))
(=> e1_3 (= v6_3_93 v6_1_78))
(=> e2_3 (= v6_3_93 v6_2_90))
(=> b3 (= v6_3_94 v6_3_93))
check_unsat (and b3 (not (> (- v5_3_92 v6_3_94) 0)))
=== End function verification trace ===

=== Checking compilation-time expression ===

=== Begin function verification trace ===
(=> b1 (= v0_1_95 p0))
assume (distinct v0_1_95 0)

(=> b1 (= v0_1_97 v0_1_95))
(=> b1 (= v1_1_98 (* 2 v0_1_95)))
check_unsat (not (distinct v0_1_97 v1_1_98))
assume (> r96 0)
check_unsat (not (distinct r96 0))
=== End function verification trace ===

=== Checking compilation-time expression ===
(=> b1 (= v0_1_99 5))
check_unsat (not (distinct v0_1_99 0))

=== Begin function verification trace ===
=== End function verification trace ===

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|


|
|
|
|
|
|
|


<
<

|
|
>
|
<
|
|
|


|
|
|
|





























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
28
29
30
31
32
33
34
35
36


37
38
39
40
41

42
43
44
45
46
47
48
49
50
51
52
53


























=== Begin function verification trace ===
(=> b1 (= v0_1_113 p0))
(=> b1 (= v1_1_114 p1))
assume (distinct v0_1_113 v1_1_114)
(=> b1 (= v3_1_116 u115))
(=> b1 (= v3_1_117 v0_1_113))
(=> b1 (= v4_1_119 u118))
(=> b1 (= v4_1_120 v1_1_114))
(=> b1 (= v5_1_122 u121))
(=> b1 (= v5_1_123 v3_1_117))
(=> b1 (= v6_1_125 u124))
(=> b1 (= v6_1_126 v4_1_120))
(= e1_2 (and b1 (< v5_1_123 v6_1_126)))
(= b2 e1_2)
(=> b2 (= v8_2_128 u127))
(=> e1_2 (= v6_2_129 v6_1_126))
(=> b2 (= v6_2_130 v6_2_129))
(=> b2 (= v8_2_131 v6_2_130))
(=> b2 (= v9_2_133 u132))
(=> e1_2 (= v5_2_134 v5_1_123))
(=> b2 (= v5_2_135 v5_2_134))
(=> b2 (= v9_2_136 v5_2_135))
(=> b2 (= v5_2_137 v8_2_131))
(=> b2 (= v6_2_138 v9_2_136))
(= e1_3 (and b1 (not (< v5_1_123 v6_1_126))))
(= e2_3 (and b2 b2))
(= b3 (or e1_3 e2_3))
(=> e1_3 (= v5_3_139 v5_1_123))
(=> e2_3 (= v5_3_139 v5_2_137))
(=> b3 (= v5_3_140 v5_3_139))
(=> e1_3 (= v6_3_141 v6_1_126))
(=> e2_3 (= v6_3_141 v6_2_138))
(=> b3 (= v6_3_142 v6_3_141))
check_unsat (and b3 (not (> (- v5_3_140 v6_3_142) 0)))
=== End function verification trace ===



=== Begin function verification trace ===
(=> b1 (= v0_1_143 p0))
assume (distinct v0_1_143 0)
(=> b1 (= v1_1_145 (* 2 v0_1_143)))
(=> b1 (= v0_1_146 v0_1_143))

check_unsat (not (distinct v0_1_146 v1_1_145))
assume (> r144 0)
check_unsat (not (distinct r144 0))
=== End function verification trace ===

=== Checking instr seq ===
(=> b1 (= v0_1_148 5))
check_unsat (not (distinct v0_1_148 0))
assume (distinct r147 0)
=== Begin function verification trace ===
=== End function verification trace ===

Changes to tests/g0/verification/z3gen/e-z3gen-test-5.txt.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Begin function verification trace ===
(=> b1 (= v0_1_63 p0))
(=> b1 (= v1_1_64 p1))
assume (distinct v0_1_63 v1_1_64)
(=> b1 (= v2_1_67 (tupsort0 u65 u66)))
(=> b1 (= v2_1_68 (tupsort0 v0_1_63 (tupsort0_1 v2_1_67))))
(=> b1 (= v2_1_69 (tupsort0 (tupsort0_0 v2_1_68) v1_1_64)))
(= e1_2 (and b1 (< (tupsort0_0 v2_1_69) (tupsort0_1 v2_1_69))))
(= b2 e1_2)
(=> b2 (= v4_2_71 u70))
(=> e1_2 (= v2_2_72 v2_1_69))
(=> b2 (= v2_2_73 v2_2_72))
(=> b2 (= v4_2_74 (tupsort0_1 v2_2_73)))
(=> b2 (= v5_2_76 u75))
(=> b2 (= v5_2_77 (tupsort0_0 v2_2_73)))
(=> b2 (= v2_2_78 (tupsort0 v4_2_74 (tupsort0_1 v2_2_73))))
(=> b2 (= v2_2_79 (tupsort0 (tupsort0_0 v2_2_78) v5_2_77)))
(let ((a!1 (and b1 (not (< (tupsort0_0 v2_1_69) (tupsort0_1 v2_1_69))))))
  (= e1_3 a!1))
(= e2_3 (and b2 b2))
(= b3 (or e1_3 e2_3))
(= e3_4 (and b3 b3))
(= b4 e3_4)
(=> e1_3 (= v2_3_80 v2_1_69))
(=> e2_3 (= v2_3_80 v2_2_79))
(=> b3 (= v2_3_81 v2_3_80))
(=> e3_4 (= v2_4_82 v2_3_81))
(=> b4 (= v2_4_83 v2_4_82))
check_unsat (and b4 (not (> (tupsort0_0 v2_4_83) (tupsort0_1 v2_4_83))))
(= e4_5 (and b4 b4))
(= b5 e4_5)
=== End function verification trace ===

=== Begin function verification trace ===
(=> b1 (= v0_1_85 2))
(=> b1 (= v1_1_86 1))
check_unsat (not (distinct v0_1_85 v1_1_86))
=== End function verification trace ===

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|





|
|
|
|
|
|





|
|
|




















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
28
29
30
31
32
33
34
35
36
37
38
39


















=== Begin function verification trace ===
(=> b1 (= v0_1_113 p0))
(=> b1 (= v1_1_114 p1))
assume (distinct v0_1_113 v1_1_114)
(=> b1 (= v2_1_117 (tupsort0 u115 u116)))
(=> b1 (= v2_1_118 (tupsort0 v0_1_113 (tupsort0_1 v2_1_117))))
(=> b1 (= v2_1_119 (tupsort0 (tupsort0_0 v2_1_118) v1_1_114)))
(= e1_2 (and b1 (< (tupsort0_0 v2_1_119) (tupsort0_1 v2_1_119))))
(= b2 e1_2)
(=> b2 (= v4_2_121 u120))
(=> e1_2 (= v2_2_122 v2_1_119))
(=> b2 (= v2_2_123 v2_2_122))
(=> b2 (= v4_2_124 (tupsort0_1 v2_2_123)))
(=> b2 (= v5_2_126 u125))
(=> b2 (= v5_2_127 (tupsort0_0 v2_2_123)))
(=> b2 (= v2_2_128 (tupsort0 v4_2_124 (tupsort0_1 v2_2_123))))
(=> b2 (= v2_2_129 (tupsort0 (tupsort0_0 v2_2_128) v5_2_127)))
(let ((a!1 (and b1 (not (< (tupsort0_0 v2_1_119) (tupsort0_1 v2_1_119))))))
  (= e1_3 a!1))
(= e2_3 (and b2 b2))
(= b3 (or e1_3 e2_3))
(= e3_4 (and b3 b3))
(= b4 e3_4)
(=> e1_3 (= v2_3_130 v2_1_119))
(=> e2_3 (= v2_3_130 v2_2_129))
(=> b3 (= v2_3_131 v2_3_130))
(=> e3_4 (= v2_4_132 v2_3_131))
(=> b4 (= v2_4_133 v2_4_132))
check_unsat (and b4 (not (> (tupsort0_0 v2_4_133) (tupsort0_1 v2_4_133))))
(= e4_5 (and b4 b4))
(= b5 e4_5)
=== End function verification trace ===

=== Begin function verification trace ===
(=> b1 (= v1_1_134 1))
(=> b1 (= v0_1_135 2))
check_unsat (not (distinct v0_1_135 v1_1_134))
=== End function verification trace ===

Changes to tests/g0/verification/z3gen/e-z3gen-test-6.txt.
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
28
29
30
31
32
33
34
35
36
=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Begin function verification trace ===
(=> b1 (= v0_1_59 p0))
(=> b1 (= v1_1_60 p1))
(= e1_2 (and b1 b1))
(= b2 e1_2)
(=> e1_2 (= v0_2_61 v0_1_59))
(=> b2 (= v0_2_62 v0_2_61))
(=> e1_2 (= v1_2_63 v1_1_60))
(=> b2 (= v1_2_64 v1_2_63))
check_unsat (and b2 (not (and (= v0_2_62 33) (= v1_2_64 44))))
check_unsat (and b2 (not (or (= v0_2_62 55) (= v1_2_64 66))))
(= e2_3 (and b2 b2))
(= b3 e2_3)
=== End function verification trace ===

=== Begin function verification trace ===
(=> b1 (= v0_1_66 1))
(=> b1 (= v1_1_67 2))
=== End function verification trace ===

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

|
|


|
|
|
|
|
|





|
|


















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
















=== Begin function verification trace ===
(=> b1 (= v0_1_113 p0))
(=> b1 (= v1_1_114 p1))
(= e1_2 (and b1 b1))
(= b2 e1_2)
(=> e1_2 (= v0_2_115 v0_1_113))
(=> b2 (= v0_2_116 v0_2_115))
(=> e1_2 (= v1_2_117 v1_1_114))
(=> b2 (= v1_2_118 v1_2_117))
check_unsat (and b2 (not (and (= v0_2_116 33) (= v1_2_118 44))))
check_unsat (and b2 (not (or (= v0_2_116 55) (= v1_2_118 66))))
(= e2_3 (and b2 b2))
(= b3 e2_3)
=== End function verification trace ===

=== Begin function verification trace ===
(=> b1 (= v1_1_119 2))
(=> b1 (= v0_1_120 1))
=== End function verification trace ===

Changes to tests/g0/verification/z3gen/e-z3gen-test-bitwise.txt.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41

42
43
44
45
46
47
48
49
50
51
52
53
54
55

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
=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Begin function verification trace ===
(=> b1 (= v0_1_59 p0))
(=> b1 (= v1_1_60 p1))
(=> b1 (= v2_1_61 p2))
(=> b1 (= v3_1_62 p3))
(=> b1 (= v4_1_64 u63))
(=> b1 (= v4_1_65 43981))
(=> b1 (= v5_1_67 u66))
(=> b1 (= v5_1_68 #x0000abcd))

(let ((a!1 (- (bv2int (bvor ((_ int2bv 32) v0_1_59) ((_ int2bv 32) v1_1_60)))
              4294967296)))
(let ((a!2 (ite (bvslt (bvor ((_ int2bv 32) v0_1_59) ((_ int2bv 32) v1_1_60))
                       #x00000000)
                a!1
                (bv2int (bvor ((_ int2bv 32) v0_1_59) ((_ int2bv 32) v1_1_60))))))
  (=> b1 (= v4_1_69 a!2))))
(=> b1 (= v5_1_70 (bvor v2_1_61 v3_1_62)))
(let ((a!1 (- (bv2int (bvand ((_ int2bv 32) v4_1_69) ((_ int2bv 32) v1_1_60)))
              4294967296)))
(let ((a!2 (ite (bvslt (bvand ((_ int2bv 32) v4_1_69) ((_ int2bv 32) v1_1_60))
                       #x00000000)
                a!1
                (bv2int (bvand ((_ int2bv 32) v4_1_69) ((_ int2bv 32) v1_1_60))))))

  (=> b1 (= v4_1_71 a!2))))
(=> b1 (= v5_1_72 (bvand v5_1_70 v3_1_62)))
(let ((a!1 (- (bv2int (bvxor ((_ int2bv 32) v4_1_71) ((_ int2bv 32) v1_1_60)))
              4294967296)))
(let ((a!2 (ite (bvslt (bvxor ((_ int2bv 32) v4_1_71) ((_ int2bv 32) v1_1_60))
                       #x00000000)
                a!1
                (bv2int (bvxor ((_ int2bv 32) v4_1_71) ((_ int2bv 32) v1_1_60))))))

  (=> b1 (= v4_1_73 a!2))))
(=> b1 (= v5_1_74 (bvxor v5_1_72 v3_1_62)))
check_unsat (not (bvult v2_1_61 #x00000020))
(let ((a!1 (- (bv2int (bvshl ((_ int2bv 32) v4_1_73) v2_1_61)) 4294967296)))
(let ((a!2 (ite (bvslt (bvshl ((_ int2bv 32) v4_1_73) v2_1_61) #x00000000)
                a!1
                (bv2int (bvshl ((_ int2bv 32) v4_1_73) v2_1_61)))))
  (=> b1 (= v4_1_75 a!2))))
check_unsat (not (bvult v2_1_61 #x00000020))
(=> b1 (= v5_1_76 (bvshl v5_1_74 v2_1_61)))
check_unsat (not (bvult v2_1_61 #x00000020))
(let ((a!1 (- (bv2int (bvashr ((_ int2bv 32) v4_1_75) v2_1_61)) 4294967296)))
(let ((a!2 (ite (bvslt (bvashr ((_ int2bv 32) v4_1_75) v2_1_61) #x00000000)
                a!1
                (bv2int (bvashr ((_ int2bv 32) v4_1_75) v2_1_61)))))
  (=> b1 (= v4_1_77 a!2))))
check_unsat (not (bvult v2_1_61 #x00000020))
(=> b1 (= v5_1_78 (bvlshr v5_1_76 v2_1_61)))
=== End function verification trace ===

=== Begin function verification trace ===
(=> b1 (= v0_1_80 0))
(=> b1 (= v1_1_81 0))
(=> b1 (= v2_1_82 #x00000000))
(=> b1 (= v3_1_83 #x00000000))
=== End function verification trace ===

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

|
|
|
|
|
|
<
|
>
|

|


|
|
|
|

|


|
>
|
|
|

|


|
>
|
|
|
|
|

|
|
|
|
|
|
|

|
|
|
|



|
|
|
|


































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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
































=== Begin function verification trace ===
(=> b1 (= v0_1_113 p0))
(=> b1 (= v1_1_114 p1))
(=> b1 (= v2_1_115 p2))
(=> b1 (= v3_1_116 p3))
(=> b1 (= v4_1_118 u117))
(=> b1 (= v4_1_119 43981))

(=> b1 (= v5_1_121 u120))
(=> b1 (= v5_1_122 #x0000abcd))
(let ((a!1 (- (bv2int (bvor ((_ int2bv 32) v0_1_113) ((_ int2bv 32) v1_1_114)))
              4294967296)))
(let ((a!2 (ite (bvslt (bvor ((_ int2bv 32) v0_1_113) ((_ int2bv 32) v1_1_114))
                       #x00000000)
                a!1
                (bv2int (bvor ((_ int2bv 32) v0_1_113) ((_ int2bv 32) v1_1_114))))))
  (=> b1 (= v4_1_123 a!2))))
(=> b1 (= v5_1_124 (bvor v2_1_115 v3_1_116)))
(let ((a!1 (- (bv2int (bvand ((_ int2bv 32) v4_1_123) ((_ int2bv 32) v1_1_114)))
              4294967296)))
(let ((a!2 (ite (bvslt (bvand ((_ int2bv 32) v4_1_123) ((_ int2bv 32) v1_1_114))
                       #x00000000)
                a!1
                (bv2int (bvand ((_ int2bv 32) v4_1_123)
                               ((_ int2bv 32) v1_1_114))))))
  (=> b1 (= v4_1_125 a!2))))
(=> b1 (= v5_1_126 (bvand v5_1_124 v3_1_116)))
(let ((a!1 (- (bv2int (bvxor ((_ int2bv 32) v4_1_125) ((_ int2bv 32) v1_1_114)))
              4294967296)))
(let ((a!2 (ite (bvslt (bvxor ((_ int2bv 32) v4_1_125) ((_ int2bv 32) v1_1_114))
                       #x00000000)
                a!1
                (bv2int (bvxor ((_ int2bv 32) v4_1_125)
                               ((_ int2bv 32) v1_1_114))))))
  (=> b1 (= v4_1_127 a!2))))
(=> b1 (= v5_1_128 (bvxor v5_1_126 v3_1_116)))
check_unsat (not (bvult v2_1_115 #x00000020))
(let ((a!1 (- (bv2int (bvshl ((_ int2bv 32) v4_1_127) v2_1_115)) 4294967296)))
(let ((a!2 (ite (bvslt (bvshl ((_ int2bv 32) v4_1_127) v2_1_115) #x00000000)
                a!1
                (bv2int (bvshl ((_ int2bv 32) v4_1_127) v2_1_115)))))
  (=> b1 (= v4_1_129 a!2))))
check_unsat (not (bvult v2_1_115 #x00000020))
(=> b1 (= v5_1_130 (bvshl v5_1_128 v2_1_115)))
check_unsat (not (bvult v2_1_115 #x00000020))
(let ((a!1 (- (bv2int (bvashr ((_ int2bv 32) v4_1_129) v2_1_115)) 4294967296)))
(let ((a!2 (ite (bvslt (bvashr ((_ int2bv 32) v4_1_129) v2_1_115) #x00000000)
                a!1
                (bv2int (bvashr ((_ int2bv 32) v4_1_129) v2_1_115)))))
  (=> b1 (= v4_1_131 a!2))))
check_unsat (not (bvult v2_1_115 #x00000020))
(=> b1 (= v5_1_132 (bvlshr v5_1_130 v2_1_115)))
=== End function verification trace ===

=== Begin function verification trace ===
(=> b1 (= v3_1_133 #x00000000))
(=> b1 (= v2_1_134 #x00000000))
(=> b1 (= v1_1_135 0))
(=> b1 (= v0_1_136 0))
=== End function verification trace ===

Changes to tests/g0/verification/z3gen/e-z3gen-test-ghostfunc.txt.
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
28
29
30

31
32
33
34
35
36
37
38

39
40
41
42
43
44

45
46
47
48
49
50
51
52
53
54
55
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
114
115
116
117
118
119
120

121
122
123
124
125
126

127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157

158
159
160
161

162
163
164
165
166
167
168
169
170
171
172

173
174
175
176
177
178

179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195

196


197
198
199
200
201
202
203
204
205
206
207
208
209
210

211
212
213
214

215
216
217
218
219
220
221
222

223
224
225
226
227
228
229
230
231
232

233
234
235
236
237

238
239
240
241

242
243
244
245
246
247
248

249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267

268
269
270
271
272
273
274
275
276
277
278

279
280
281
282
283
284
285
286
287
288
289
290
=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Checking compilation-time expression ===

=== Begin function verification trace ===
(=> b1 (= v0_1_59 p0))
(=> b1 (= v1_1_60 p1))
(=> b1 (= v2_1_62 u61))
(=> b1 (= v2_1_63 v0_1_59))
(= e1_2 (and b1 b1))
(= b2 e1_2)
(=> b2
    (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
         64

         1
         2)
       3))
(=> e1_2 (= v1_2_66 v1_1_60))
(=> b2 (= v1_2_67 v1_2_66))
(forall ((gca0_65 Int) (gca1_65 Int))
  (! (let ((a!1 (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                     65

                     gca0_65
                     gca1_65)
                   (ite (and (= gca0_65 5) (= gca1_65 v1_2_67))
                        4
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                          64

                          gca0_65
                          gca1_65)))))
       (=> b2 a!1))
     :weight 0))
(= e2_3 (and b2 b2))
(= b3 e2_3)
(=> e1_2 (= v2_2_68 v2_1_63))
(=> b2 (= v2_2_69 v2_2_68))
(=> e2_3 (= v2_3_70 v2_2_69))
(=> b3 (= v2_3_71 v2_3_70))
(= e3_4 (and b3 (distinct v2_3_71 5)))
(= b4 e3_4)
(= e4_5 (and b4 b4))
(= b5 e4_5)
(forall ((gca0_74 Int) (gca1_74 Int))
  (! (=> e2_3
         (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
              74

              gca0_74
              gca1_74)
            (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
              65

              gca0_74
              gca1_74)))
     :weight 0))
(forall ((gca0_75 Int) (gca1_75 Int))
  (! (let ((a!1 (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                     75

                     gca0_75
                     gca1_75)
                   (ite (and (= gca0_75 1) (= gca1_75 2))
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                          74

                          1
                          2)
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                          74

                          gca0_75
                          gca1_75)))))
       (=> b3 a!1))
     :weight 0))
(forall ((gca0_73 Int) (gca1_73 Int))
  (! (=> e3_4
         (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
              73

              gca0_73
              gca1_73)
            (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
              75

              gca0_73
              gca1_73)))
     :weight 0))
(forall ((gca0_76 Int) (gca1_76 Int))
  (! (let ((a!1 (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                     76

                     gca0_76
                     gca1_76)
                   (ite (and (= gca0_76 1) (= gca1_76 2))
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                          73

                          1
                          2)
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                          73

                          gca0_76
                          gca1_76)))))
       (=> b4 a!1))
     :weight 0))
(forall ((gca0_72 Int) (gca1_72 Int))
  (! (=> e4_5
         (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
              72
              gca0_72
              gca1_72)
            (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
              76

              gca0_72
              gca1_72)))
     :weight 0))
(forall ((gca0_77 Int) (gca1_77 Int))
  (! (let ((a!1 (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                     77

                     gca0_77
                     gca1_77)
                   (ite (and (= gca0_77 1) (= gca1_77 2))
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                          72
                          1
                          2)
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                          72
                          gca0_77
                          gca1_77)))))
       (=> b5 a!1))
     :weight 0))
check_unsat (and b5
     (not (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
               72
               1
               2)
             3)))
(= e5_6 (and b5 b5))
(= b6 e5_6)
(=> b6 (= v2_6_78 5))
(= e3_7 (and b3 (not (distinct v2_3_71 5))))
(= e6_7 (and b6 b6))
(= b7 (or e3_7 e6_7))
(= e7_8 (and b7 b7))
(= b8 e7_8)
(forall ((gca0_80 Int) (gca1_80 Int))
  (! (=> e3_7
         (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
              80

              gca0_80
              gca1_80)
            (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
              75

              gca0_80
              gca1_80)))
     :weight 0))
(forall ((gca0_81 Int) (gca1_81 Int))
  (! (=> e5_6
         (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
              81
              gca0_81
              gca1_81)
            (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
              77

              gca0_81
              gca1_81)))
     :weight 0))
(forall ((gca0_82 Int) (gca1_82 Int))
  (! (let ((a!1 (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                     82

                     gca0_82
                     gca1_82)
                   (ite (and (= gca0_82 1) (= gca1_82 2))
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                          81
                          1
                          2)
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                          81
                          gca0_82
                          gca1_82)))))
       (=> b6 a!1))
     :weight 0))
(forall ((gca0_80 Int) (gca1_80 Int))
  (! (=> e6_7
         (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
              80

              gca0_80


              gca1_80)
            (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
              82
              gca0_80
              gca1_80)))
     :weight 0))
(forall ((gca0_83 Int) (gca1_83 Int))
  (! (let ((a!1 (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                     83
                     gca0_83
                     gca1_83)
                   (ite (and (= gca0_83 1) (= gca1_83 2))
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                          80

                          1
                          2)
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                          80

                          gca0_83
                          gca1_83)))))
       (=> b7 a!1))
     :weight 0))
(forall ((gca0_79 Int) (gca1_79 Int))
  (! (=> e7_8
         (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
              79

              gca0_79
              gca1_79)
            (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
              83
              gca0_79
              gca1_79)))
     :weight 0))
(forall ((gca0_84 Int) (gca1_84 Int))
  (! (let ((a!1 (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                     84

                     gca0_84
                     gca1_84)
                   (ite (and (= gca0_84 1) (= gca1_84 2))
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                          79

                          1
                          2)
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                          79

                          gca0_84
                          gca1_84)))))
       (=> b8 a!1))
     :weight 0))
check_unsat (and b8
     (not (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
               79

               1
               2)
             3)))
(=> e2_3 (= v1_3_85 v1_2_67))
(=> b3 (= v1_3_86 v1_3_85))
(=> e3_7 (= v1_7_87 v1_3_86))
(=> e3_4 (= v1_4_88 v1_3_86))
(=> b4 (= v1_4_89 v1_4_88))
(=> e4_5 (= v1_5_90 v1_4_89))
(=> b5 (= v1_5_91 v1_5_90))
(=> e5_6 (= v1_6_92 v1_5_91))
(=> b6 (= v1_6_93 v1_6_92))
(=> e6_7 (= v1_7_87 v1_6_93))
(=> b7 (= v1_7_94 v1_7_87))
(=> e7_8 (= v1_8_95 v1_7_94))
(=> b8 (= v1_8_96 v1_8_95))
check_unsat (and b8
     (not (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
               84

               5
               v1_8_96)
             4)))
(=> e3_7 (= v2_7_97 v2_3_71))
(=> e6_7 (= v2_7_97 v2_6_78))
(=> b7 (= v2_7_98 v2_7_97))
(=> e7_8 (= v2_8_99 v2_7_98))
(=> b8 (= v2_8_100 v2_8_99))
check_unsat (and b8
     (not (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
               84

               v2_8_100
               v1_8_96)
             4)))
(= e8_9 (and b8 b8))
(= b9 e8_9)
=== End function verification trace ===

=== Begin function verification trace ===
(=> b1 (= v0_1_102 1))
(=> b1 (= v1_1_103 2))
=== End function verification trace ===

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

|
|
|
|




<
>



|
|
|

<
>
|
|
|


<
>
|
|




|
|
|
|
|



|


<
>
|
|

<
>
|
|

|

<
>
|
|
|

<
>



<
>
|
|


|


<
>
|
|

<
>
|
|

|

<
>
|
|
|

<
>



<
>
|
|


|


|
|
|

<
>
|
|

|

<
>
|
|
|

|



|
|
|




|





|
|




|


<
>
|
|

<
>
|
|

|


|
|
|

<
>
|
|

|

<
>
|
|
|

|



|
|
|


|


<
>
|
>
>
|
<
<
|
|

|

|
|
|
|

<
>



<
>
|
|


|


<
>
|
|

|
|
|

|

<
>
|
|
|

<
>



<
>
|
|




<
>



|
|
|
|
|
|
|
|
|
|
|
|
|


<
>

|

|
|
|
|
|


<
>
|
|






|
|






















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
28
29
30
31
32
33
34
35
36
37
38
39
40
41

42
43
44
45

46
47
48
49
50
51

52
53
54
55
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136

137
138
139
140

141
142
143
144
145
146
147
148
149
150
151

152
153
154
155
156
157

158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174

175
176
177
178
179


180
181
182
183
184
185
186
187
188
189

190
191
192
193

194
195
196
197
198
199
200
201

202
203
204
205
206
207
208
209
210
211

212
213
214
215
216

217
218
219
220

221
222
223
224
225
226
227

228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246

247
248
249
250
251
252
253
254
255
256
257

258
259
260
261
262
263
264
265
266
267
268
269
270




















=== Begin function verification trace ===
(=> b1 (= v0_1_113 p0))
(=> b1 (= v1_1_114 p1))
(=> b1 (= v2_1_116 u115))
(=> b1 (= v2_1_117 v0_1_113))
(= e1_2 (and b1 b1))
(= b2 e1_2)
(=> b2
    (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

         118
         1
         2)
       3))
(=> e1_2 (= v1_2_119 v1_1_114))
(=> b2 (= v1_2_120 v1_2_119))
(forall ((gca0_121 Int) (gca1_121 Int))
  (! (let ((a!1 (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

                     121
                     gca0_121
                     gca1_121)
                   (ite (and (= gca0_121 5) (= gca1_121 v1_2_120))
                        4
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

                          118
                          gca0_121
                          gca1_121)))))
       (=> b2 a!1))
     :weight 0))
(= e2_3 (and b2 b2))
(= b3 e2_3)
(=> e1_2 (= v2_2_122 v2_1_117))
(=> b2 (= v2_2_123 v2_2_122))
(=> e2_3 (= v2_3_124 v2_2_123))
(=> b3 (= v2_3_125 v2_3_124))
(= e3_4 (and b3 (distinct v2_3_125 5)))
(= b4 e3_4)
(= e4_5 (and b4 b4))
(= b5 e4_5)
(forall ((gca0_128 Int) (gca1_128 Int))
  (! (=> e2_3
         (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

              128
              gca0_128
              gca1_128)
            (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

              121
              gca0_128
              gca1_128)))
     :weight 0))
(forall ((gca0_129 Int) (gca1_129 Int))
  (! (let ((a!1 (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

                     129
                     gca0_129
                     gca1_129)
                   (ite (and (= gca0_129 1) (= gca1_129 2))
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

                          128
                          1
                          2)
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

                          128
                          gca0_129
                          gca1_129)))))
       (=> b3 a!1))
     :weight 0))
(forall ((gca0_127 Int) (gca1_127 Int))
  (! (=> e3_4
         (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

              127
              gca0_127
              gca1_127)
            (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

              129
              gca0_127
              gca1_127)))
     :weight 0))
(forall ((gca0_130 Int) (gca1_130 Int))
  (! (let ((a!1 (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

                     130
                     gca0_130
                     gca1_130)
                   (ite (and (= gca0_130 1) (= gca1_130 2))
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

                          127
                          1
                          2)
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

                          127
                          gca0_130
                          gca1_130)))))
       (=> b4 a!1))
     :weight 0))
(forall ((gca0_126 Int) (gca1_126 Int))
  (! (=> e4_5
         (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
              126
              gca0_126
              gca1_126)
            (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

              130
              gca0_126
              gca1_126)))
     :weight 0))
(forall ((gca0_131 Int) (gca1_131 Int))
  (! (let ((a!1 (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

                     131
                     gca0_131
                     gca1_131)
                   (ite (and (= gca0_131 1) (= gca1_131 2))
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                          126
                          1
                          2)
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                          126
                          gca0_131
                          gca1_131)))))
       (=> b5 a!1))
     :weight 0))
check_unsat (and b5
     (not (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
               126
               1
               2)
             3)))
(= e5_6 (and b5 b5))
(= b6 e5_6)
(=> b6 (= v2_6_132 5))
(= e3_7 (and b3 (not (distinct v2_3_125 5))))
(= e6_7 (and b6 b6))
(= b7 (or e3_7 e6_7))
(= e7_8 (and b7 b7))
(= b8 e7_8)
(forall ((gca0_134 Int) (gca1_134 Int))
  (! (=> e3_7
         (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

              134
              gca0_134
              gca1_134)
            (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

              129
              gca0_134
              gca1_134)))
     :weight 0))
(forall ((gca0_135 Int) (gca1_135 Int))
  (! (=> e5_6
         (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
              135
              gca0_135
              gca1_135)
            (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

              131
              gca0_135
              gca1_135)))
     :weight 0))
(forall ((gca0_136 Int) (gca1_136 Int))
  (! (let ((a!1 (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

                     136
                     gca0_136
                     gca1_136)
                   (ite (and (= gca0_136 1) (= gca1_136 2))
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                          135
                          1
                          2)
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                          135
                          gca0_136
                          gca1_136)))))
       (=> b6 a!1))
     :weight 0))
(forall ((gca0_134 Int) (gca1_134 Int))
  (! (=> e6_7
         (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

              134
              gca0_134
              gca1_134)
            (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
              136


              gca0_134
              gca1_134)))
     :weight 0))
(forall ((gca0_137 Int) (gca1_137 Int))
  (! (let ((a!1 (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
                     137
                     gca0_137
                     gca1_137)
                   (ite (and (= gca0_137 1) (= gca1_137 2))
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

                          134
                          1
                          2)
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

                          134
                          gca0_137
                          gca1_137)))))
       (=> b7 a!1))
     :weight 0))
(forall ((gca0_133 Int) (gca1_133 Int))
  (! (=> e7_8
         (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

              133
              gca0_133
              gca1_133)
            (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
              137
              gca0_133
              gca1_133)))
     :weight 0))
(forall ((gca0_138 Int) (gca1_138 Int))
  (! (let ((a!1 (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

                     138
                     gca0_138
                     gca1_138)
                   (ite (and (= gca0_138 1) (= gca1_138 2))
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

                          133
                          1
                          2)
                        (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

                          133
                          gca0_138
                          gca1_138)))))
       (=> b8 a!1))
     :weight 0))
check_unsat (and b8
     (not (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

               133
               1
               2)
             3)))
(=> e2_3 (= v1_3_139 v1_2_120))
(=> b3 (= v1_3_140 v1_3_139))
(=> e3_7 (= v1_7_141 v1_3_140))
(=> e3_4 (= v1_4_142 v1_3_140))
(=> b4 (= v1_4_143 v1_4_142))
(=> e4_5 (= v1_5_144 v1_4_143))
(=> b5 (= v1_5_145 v1_5_144))
(=> e5_6 (= v1_6_146 v1_5_145))
(=> b6 (= v1_6_147 v1_6_146))
(=> e6_7 (= v1_7_141 v1_6_147))
(=> b7 (= v1_7_148 v1_7_141))
(=> e7_8 (= v1_8_149 v1_7_148))
(=> b8 (= v1_8_150 v1_8_149))
check_unsat (and b8
     (not (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

               138
               5
               v1_8_150)
             4)))
(=> e3_7 (= v2_7_151 v2_3_125))
(=> e6_7 (= v2_7_151 v2_6_132))
(=> b7 (= v2_7_152 v2_7_151))
(=> e7_8 (= v2_8_153 v2_7_152))
(=> b8 (= v2_8_154 v2_8_153))
check_unsat (and b8
     (not (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|

               138
               v2_8_154
               v1_8_150)
             4)))
(= e8_9 (and b8 b8))
(= b9 e8_9)
=== End function verification trace ===

=== Begin function verification trace ===
(=> b1 (= v1_1_155 2))
(=> b1 (= v0_1_156 1))
=== End function verification trace ===

Changes to tests/g0/verification/z3gen/e-z3gen-test-logic-and.txt.
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
=== Checking compilation-time expression ===

=== Begin function verification trace ===
(=> b1 (= v0_1_60 u59))
(=> b1 (= v0_1_61 false))
(=> b1 (= v1_1_63 u62))
(=> b1 (= v1_1_64 true))
(= e1_2 (and b1 v0_1_61))
(= b2 e1_2)
(=> e1_2 (= v1_2_65 v1_1_64))
(=> b2 (= v1_2_66 v1_2_65))
(=> b2 (= v2_2_67 v1_2_66))
(= e1_3 (and b1 (not v0_1_61)))
(= e2_3 (and b2 b2))
(= b3 (or e1_3 e2_3))
(=> e1_3 (= v68 false))
(=> e2_3 (= v2_3_69 v2_2_67))
(=> b3 (= v2_3_70 v2_3_69))
(=> e2_3 (= v68 v2_3_70))
(=> b3 (= v3_3_71 v68))
(=> b3 (= v4_3_73 u72))
(=> b3 (= v4_3_74 v3_3_71))
=== End function verification trace ===

=== Begin function verification trace ===
=== End function verification trace ===

<
<

|
|
|
|
|

|
|
|
|


|
|
|
|
|
|
|







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


=== Begin function verification trace ===
(=> b1 (= v0_1_114 u113))
(=> b1 (= v0_1_115 false))
(=> b1 (= v1_1_117 u116))
(=> b1 (= v1_1_118 true))
(= e1_2 (and b1 v0_1_115))
(= b2 e1_2)
(=> e1_2 (= v1_2_119 v1_1_118))
(=> b2 (= v1_2_120 v1_2_119))
(=> b2 (= v2_2_121 v1_2_120))
(= e1_3 (and b1 (not v0_1_115)))
(= e2_3 (and b2 b2))
(= b3 (or e1_3 e2_3))
(=> e1_3 (= v122 false))
(=> e2_3 (= v2_3_123 v2_2_121))
(=> b3 (= v2_3_124 v2_3_123))
(=> e2_3 (= v122 v2_3_124))
(=> b3 (= v3_3_125 v122))
(=> b3 (= v4_3_127 u126))
(=> b3 (= v4_3_128 v3_3_125))
=== End function verification trace ===

=== Begin function verification trace ===
=== End function verification trace ===

Changes to tests/g0/verification/z3gen/e-z3gen-test-logic-or.txt.
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
=== Checking compilation-time expression ===

=== Begin function verification trace ===
(=> b1 (= v0_1_60 u59))
(=> b1 (= v0_1_61 false))
(=> b1 (= v1_1_63 u62))
(=> b1 (= v1_1_64 true))
(= e1_2 (and b1 (not v0_1_61)))
(= b2 e1_2)
(=> e1_2 (= v1_2_65 v1_1_64))
(=> b2 (= v1_2_66 v1_2_65))
(=> b2 (= v2_2_67 v1_2_66))
(= e1_3 (and b1 v0_1_61))
(= e2_3 (and b2 b2))
(= b3 (or e1_3 e2_3))
(=> e1_3 (= v68 true))
(=> e2_3 (= v2_3_69 v2_2_67))
(=> b3 (= v2_3_70 v2_3_69))
(=> e2_3 (= v68 v2_3_70))
(=> b3 (= v3_3_71 v68))
(=> b3 (= v4_3_73 u72))
(=> b3 (= v4_3_74 v3_3_71))
=== End function verification trace ===

=== Begin function verification trace ===
=== End function verification trace ===

<
<

|
|
|
|
|

|
|
|
|


|
|
|
|
|
|
|







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


=== Begin function verification trace ===
(=> b1 (= v0_1_114 u113))
(=> b1 (= v0_1_115 false))
(=> b1 (= v1_1_117 u116))
(=> b1 (= v1_1_118 true))
(= e1_2 (and b1 (not v0_1_115)))
(= b2 e1_2)
(=> e1_2 (= v1_2_119 v1_1_118))
(=> b2 (= v1_2_120 v1_2_119))
(=> b2 (= v2_2_121 v1_2_120))
(= e1_3 (and b1 v0_1_115))
(= e2_3 (and b2 b2))
(= b3 (or e1_3 e2_3))
(=> e1_3 (= v122 true))
(=> e2_3 (= v2_3_123 v2_2_121))
(=> b3 (= v2_3_124 v2_3_123))
(=> e2_3 (= v122 v2_3_124))
(=> b3 (= v3_3_125 v122))
(=> b3 (= v4_3_127 u126))
(=> b3 (= v4_3_128 v3_3_125))
=== End function verification trace ===

=== Begin function verification trace ===
=== End function verification trace ===