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
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() );
        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
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 >(),
            cir::Not( forward< V >( val ) ) ).setLocationId( val.locationId() );
            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 >(),
            cir::Or( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
            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 >(),
            cir::And( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
            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 >(),
            cir::Eq( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
            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 >(),
            cir::Neq( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
            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 >(),
            cir::UGT( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
            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 >(),
            cir::UGE( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
            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 >(),
            cir::ULT( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
            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 >(),
            cir::ULE( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
            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 >(),
            cir::SGT( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
            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 >(),
            cir::SGE( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
            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 >(),
            cir::SLT( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
            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 >(),
            cir::SLE( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
            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
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/invoke.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
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 ) );
                        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 >(),
                        Sub( ToValue( BigInt() ), operand ) );
                        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(),
                        Sub( Value( operand.type(), APSInt( opType.m_numBits, false ) ),
                            operand ) );
                        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(),
                        Sub( lhs, rhs ) );
                        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(),
                        Mul( lhs, rhs ) );
                        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
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 ) )
                    cfg->currentBB()->append(
                        move( cond ), cir::Assert( rhs.locationId() )
                    );

                    return BuildComputedValue( lhs.type(),
                        SDiv( lhs, rhs ) );
                        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()->emplace_back(
                        cir::Assert( move( cond ) )
                    cfg->currentBB()->append(
                        move( cond ), cir::Assert( rhs.locationId() )
                    );

                    return BuildComputedValue( lhs.type(),
                        UDiv( lhs, rhs ) );
                        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
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 ) )
                    cfg->currentBB()->append(
                        move( cond ), cir::Assert( rhs.locationId() )
                    );

                    return BuildComputedValue( lhs.type(),
                        SRem( lhs, rhs ) );
                        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()->emplace_back(
                        cir::Assert( move( cond ) )
                    cfg->currentBB()->append(
                        move( cond ), cir::Assert( rhs.locationId() )
                    );

                    return BuildComputedValue( lhs.type(),
                        URem( lhs, rhs ) );
                        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
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() ) );
                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
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, Select( rhs.cir(), index++ ) );
                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
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 ) );
                        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 >(),
                        Neq( lhs, rhs ) );
                        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 >(),
                        SGT( lhs, rhs ) );
                        lhs, rhs, SGT( c.locationId() ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        UGT( lhs, rhs ) );
                        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 >(),
                        SGE( lhs, rhs ) );
                        lhs, rhs, SGE( c.locationId() ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        UGE( lhs, rhs ) );
                        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 >(),
                        SLT( lhs, rhs ) );
                        lhs, rhs, SLT( c.locationId() ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        ULT( lhs, rhs ) );
                        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 >(),
                        SLE( lhs, rhs ) );
                        lhs, rhs, SLE( c.locationId() ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        ULE( lhs, rhs ) );
                        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
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 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 );
                    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
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 ) );
                return BuildComputedValue( GetValueType< RT >(), lhs, rhs, I( ( c.locationId() ) ) );
            };

            pOvlSet->add( e, ToValue< intrinsicType >( move( intrinsicFunc ) ), GetFuncInvocationRule() );
            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 ) ), GetFuncInvocationRule() );
                pOvlSet->add( e, ToValue< intrinsicType >( forward< F >( func ) ), GetBuiltinIntrinsicFuncInvocationRule() );
            }
            else
            {
                using intrinsicType = Intrinsic< Value ( T, T ) >;
                pOvlSet->add( e, ToValue< intrinsicType >( forward< F >( func ) ), GetFuncInvocationRule() );
                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 ), GetFuncInvocationRule() );
            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
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 >(),
                        cir::Not( operand ) );
                        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(),
                        Xor( operand,
                            Value( operand.type(), APSInt::getMaxValue( opType.m_numBits, true ) )
                        ) );
                        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() )
                    {
                        auto loc = Location::CreateSpanningLocation( lhs.locationId(), rhs.locationId() );
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( loc, "bitwise operations between ct_int values are only allowed on constants." );
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( c.locationId(), "bitwise operations between ct_int values are only allowed on constants." );
                        return PoisonValue();
                    }

                    return BuildComputedValue( lhs.type(),
                        Xor( lhs, rhs ) );
                        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(),
                        Xor( lhs, rhs ) );
                        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(),
                        Or( lhs, rhs ) );
                        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(),
                        Or( lhs, rhs ) );
                        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
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 >(), Or( lhs, 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( lhs, pSuccBB, pRhsBB ) );
                predBB->setTerminator( CondBranch( pSuccBB, pRhsBB ) );

                auto rhsIndex = cfg->getNewTemporaryIndex();
                pRhsBB->emplace_back( CreateTemporary( rhsIndex, rhs ) );
                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 );
                    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 ) ) );
                    GetTemporary( GetValueType< bool >(), rhsIndex, c.locationId() ) ) );

                pSuccBB->emplace_back( move( phi ) );
                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 ) );
                    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() )
                    {
                        auto loc = Location::CreateSpanningLocation( lhs.locationId(), rhs.locationId() );
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( loc, "bitwise operations between ct_int values are only allowed on constants." );
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( c.locationId(), "bitwise operations between ct_int values are only allowed on constants." );
                        return PoisonValue();
                    }

                    return BuildComputedValue( lhs.type(),
                        And( lhs, rhs ) );
                        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(),
                        And( lhs, rhs ) );
                        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
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 >(), And( lhs, 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( lhs, pRhsBB, pSuccBB ) );
                predBB->setTerminator( CondBranch( pRhsBB, pSuccBB ) );

                auto rhsIndex = cfg->getNewTemporaryIndex();
                pRhsBB->emplace_back( CreateTemporary( rhsIndex, rhs ) );
                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 );
                    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 ) ) );
                    GetTemporary( GetValueType< bool >(), rhsIndex, c.locationId() ) ) );

                pSuccBB->emplace_back( move( phi ) );
                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 ) );
                    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(),
                        Shl( lhs, rhs ) );
                        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 retreieve its bit size.
                    // 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 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 ) )
                    cfg->currentBB()->append(
                        move( cond ), cir::Assert( rhs.locationId() )
                    );

                    return BuildComputedValue( lhs.type(),
                        Shl( lhs, rhs ) );
                        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() )
                    {
                        auto loc = Location::CreateSpanningLocation( lhs.locationId(), rhs.locationId() );
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( loc, "bitwise operations between ct_int values are only allowed on constants." );
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( c.locationId(), "bitwise operations between ct_int values are only allowed on constants." );
                        return PoisonValue();
                    }

                    return BuildComputedValue( lhs.type(),
                        AShr( lhs, rhs ) );
                        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
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()->emplace_back(
                        cir::Assert( move( cond ) )
                    cfg->currentBB()->append(
                        move( cond ), cir::Assert( rhs.locationId() )
                    );

                    return BuildComputedValue( lhs.type(),
                        AShr( lhs, rhs ) );
                        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 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 ) )
                    cfg->currentBB()->append(
                        move( cond ), cir::Assert( rhs.locationId() )
                    );

                    return BuildComputedValue( lhs.type(),
                        LShr( lhs, rhs ) );
                        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
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() );
                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
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() );
                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
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(
                get< Value >( converted ),
                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
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( p.resolver()->currentLocation(), cir::Ret() );
                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( p.resolver()->currentLocation(), cir::Ret( 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( p.resolver()->currentLocation(), cir::Ret( 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( p.resolver()->currentLocation(), cir::Ret( PoisonValue() ) );
                cfg->emitTerminator( locationId, cir::Ret( locationId ) );
                PoisonBuilder( p.context() );
                return true;
            }

            cfg->emitTerminator( p.resolver()->currentLocation(), cir::Ret( get< Value >( converted ) ) );
            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
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(
                get< Value >( converted ),
                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
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;

                // 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() ) )
                if( !DoesInstrSeqHaveSideEffects( *v.cir() ) )
                    return;

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

                auto bb = cfg->currentBB();
                bb->emplace_back( move( *v.cir() ) );
                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
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 >
    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
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() ) )
        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
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::Load( make_shared< cir::Instruction >( cir::VarAddr( varId++, decl.type() ) ), 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 ) ) ) );
                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
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() );
            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
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
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 );
    }

    Term GetFuncRType( const Value& func )
    optional< Term > GetFuncRType( const Value& func )
    {
        auto funcType = EIRToValue( func.type() );
        assert( funcType );
        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()   // kind
                SubTerm()   // varArg
            )
        );

        assert( typeDecomp );
        if( !typeDecomp )
        auto&& [kind, rtype, ptypes, vinf, varArg] = *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
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 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 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
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
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
                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()   // kind
                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
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( *EIRToValue( a ), *EIRToValue( vp->type() ) ) );
                    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
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 )
                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
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() ) );
                    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 >(),
                    Store( r.cir(), refType.type(), ToValue( BigInt() ), r.locationId() ) );
                    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 >(),
                    Store( r.cir(), refType.type(), ToValue( ""s ), r.locationId() ) );
                    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
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(),
                    cir::Load( GetAddrFromLocalVar( lv ), 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(),
                    cir::Load( GetAddrFromLocalVar( lv ), 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
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 ) );
        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
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 ) );
        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
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
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 << "{ " << *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
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 );
            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
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() ) );
                    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
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.
    ptr< cir::Instruction > GetAddrFromLocalVar( const LocalVar& lv )
    cir::Instruction GetAddrFromLocalVar( const LocalVar& lv )
    {
        // TODO LOC: may need loc in LocalVar
        return make_shared< cir::Instruction >( cir::VarAddr( lv.index(), lv.type() ) );
        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() ) );
        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
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 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
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(),
            cir::Load( refval->cir(), refType->type() ) )
            .setLocationId( refval->locationId() ) );
            *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
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 ) ), TempAddr( tempIndex, rhsVal ) )
                    .setLocationId( rhsVal.locationId() ) );
                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
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(),
                cir::Load( rrefVal.cir(), rrType->type() ) ).setLocationId( rrefVal.locationId() ) );
                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
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() ) );
                    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
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 GetFuncInvocationRule()->resolveInvocation( c, loc, instanceFunc, args );
                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
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 ) ),
                Select( tupRef.cir(), index ) ).setLocationId( elemType.locationId() );
                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
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 ) ),
                        Select( tupRef.cir(), index++ ) ).setLocationId( elemType.locationId() );
                        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
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 argAddr = cir::Select( tupArgRef.cir(), index );

        auto argRef = ValueToEIR( BuildComputedValue( ValueToEIR( ToValue( rt ) ),
            move( argAddr ) ) );
            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
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() } ) ),
                cir::TempAddr( tempIndex, *rtup ) );
                *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
12
13
14
15
16
17
18

19
20
21
22
23
24
25
26







-
+







        SetupPredicatesTypeChecking( e );

        SetupBasicTypes( e );
        SetupBasicTypesPrettyPrinting();

        SetupTupleTypeChecking( e );

        SetupFunctionInvocationRule( 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
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
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
    class AllocVar : public BaseInstr< 0, false >
    {
        public:
            template< typename T >
            AllocVar( T&& type, uint32_t index ) :
            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
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:
            template< typename L, typename R >
            Add( L&& lhs, R&& rhs ) : BinaryOp( forward< L >( lhs ), forward< R >( rhs ) ) {}
            Add( LocationId loc ) :
                BinaryOp( loc )
            {}

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

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

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

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

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

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

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

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

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

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

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

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

            friend ostream& operator<<( ostream& out, const SRem& ins )
            {
                return ins.print( out, "SREM" );
                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
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
    class Assert : public BaseInstr< 1, false >
    {
        public:
            template< typename V >
            Assert( V&& cond ) :
            Assert( LocationId loc ) :
                m_cond( forward< V >( cond ) )
                BaseInstr( loc )
            {}

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

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

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

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

        private:
            eir::Value m_cond;
    };
}

#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
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; }

            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(); }
            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;
            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(); }


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

                return &m_runnableInstructions;
            }
            void reserve( size_t size ) { m_instructions.reserve( size ); }

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

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

            template< typename... T >
            template< typename... I >
            void append( I&&... instrs )
            {
                AppendToInstrSeq( m_instructions, forward< I >( instrs )... );
                m_dirty = true;
            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
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
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:
            vector< Instruction >   m_instructions;
            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
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::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() );
    }

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
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
    class BinaryOp : public BaseInstr< 2, true >
    {
        public:
            template< typename L, typename R >
            BinaryOp( L&& lhs, R&& rhs ) :
                m_lhs( forward< L >( lhs ) ),
            BinaryOp( LocationId loc ) :
                BaseInstr( loc )
                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 canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return true; }
            bool haveSideEffects() const { return false; }

            bool operator<( const BinaryOp& rhs ) const
            {
                if( m_lhs != rhs.m_lhs )
                    return m_lhs < rhs.m_lhs;
                return false;
                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
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
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:
            template< typename L, typename R >
            Shl( L&& lhs, R&& rhs ) : BinaryOp( forward< L >( lhs ), forward< R >( rhs ) ) {}
            Shl( LocationId loc ) :
                BinaryOp( loc )
            {}

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

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

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

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

            friend ostream& operator<<( ostream& out, const AShr& ins )
            {
                return ins.print( out, "ASHR" );
                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
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 V, typename BT, typename BF >
            CondBranch( V&& cond, BT&& trueDest, BF&& falseDest ) :
            template< typename BT, typename BF >
            CondBranch( 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
            bool canBeExecuted() const { return true; }
            {
                return IsValueConstantOrExecutable( m_cond );
            }

            bool canBeEagerlyEvaluated() const
            bool canBeEagerlyEvaluated() const { return true; }
            {
                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
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
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
    class Call : public BaseInstr< 1, true >   // Can pop more depending on arg count
    {
        public:
            template< typename F, typename A >
            Call( F&& func, A&& args ) :
                m_func( forward< F >( func ) ),
                m_args( forward< A >( args ) )
            Call( uint32_t numArgs, LocationId loc ) :
                BaseInstr( loc ),
                m_numArgs( numArgs )
            {}

            const auto& func() const { return m_func; }
            const auto& args() const { return m_args; }
            const auto& numArgs() const { return m_numArgs; }

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

            bool operator<( const Call& rhs ) const
            {
                if( m_func != rhs.m_func )
                    return m_func < rhs.m_func;
                return m_args < rhs.m_args;
                return m_numArgs < rhs.m_numArgs;
            }

            friend ostream& operator<<( ostream& out, const Call& ins );

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

        private:
            eir::Value m_func;
            uint32_t m_numArgs = 0;
            eir::Term m_args;
    };
}

#endif
Changes to bs/cir/cfg.h.
9
10
11
12
13
14
15
16




17
18
19
20
21
22
23
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; }
            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
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 setAddressModifiedByLoop( uint32_t loopId, const eir::Term& type, const Instruction& addrCalculation )
            void setStorageLocationModifiedByLoop( uint32_t loopId, const eir::Term& type, const StorageLocation& sloc )
            {
                m_loopModifiedAddresses.emplace( make_pair( loopId, addrCalculation ), type );
                m_loopModifiedStorageLocations.emplace( make_pair( loopId, sloc ), type );
            }

            template< typename F >
            void forEachAddressModifiedByLoop( uint32_t loopId, F&& func ) const
            void forEachStorageLocationModifiedByLoop( uint32_t loopId, F&& func ) const
            {
                auto begin = m_loopModifiedAddresses.lower_bound( { loopId, {} } );
                auto end = m_loopModifiedAddresses.upper_bound( { loopId, Placeholder( TERM( 0U ), ""_sid ) } );
                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
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 adresses modified
            // 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, Instruction >, eir::Term > m_loopModifiedAddresses;
            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
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
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 )
        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
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
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();
    }
        // TODO also visualize the func and the args
        builder.output() << "Call";

    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";
        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";
        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";
        builder.output() << "Select " << instr.memberIndex();
    }

    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 ) );
        GraphVizBuilder::Color col( builder );

        builder.output() << "CreateTemporary " << instr.index() << ", " << id;
        builder.output() << "CreateTemporary " << instr.index();
        builder.addEdge( builder.currentNodeId(), id );
    }

    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
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 );

        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";
        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
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 );

        /*builder.queueWork( [&]
        {
            CfgViz( builder, instr.cond() );
        } );*/
        auto id = builder.getNodeId( &instr.cond() );
        GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );
        GraphVizBuilder::Color col( builder );

        builder.output() << "Assert " << id;
        builder.output() << "Assert";
        //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 )
    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() << "PHOverride";
        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
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 );
            auto id = builder.getNodeId( &t.cond() );
            GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );
            GraphVizBuilder::Color col( builder );

            builder.output() << "CondBranch " << id;
            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
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
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 PHOverride& 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
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
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
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 BinaryOpSameTypes
    class Eq : public BinaryOp
    {
        public:
            template< typename L, typename R >
            Eq( L&& lhs, R&& rhs ) : BinaryOpSameTypes( forward< L >( lhs ), forward< R >( rhs ) ) {}
            Eq( LocationId loc ) :
                BinaryOp( loc )
            {}

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

    class Neq : public BinaryOpSameTypes
    class Neq : public BinaryOp
    {
        public:
            template< typename L, typename R >
            Neq( L&& lhs, R&& rhs ) : BinaryOpSameTypes( forward< L >( lhs ), forward< R >( rhs ) ) {}
            Neq( LocationId loc ) :
                BinaryOp( loc )
            {}

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

    class UGT : public BinaryOpSameTypes
    class UGT : public BinaryOp
    {
        public:
            template< typename L, typename R >
            UGT( L&& lhs, R&& rhs ) : BinaryOpSameTypes( forward< L >( lhs ), forward< R >( rhs ) ) {}
            UGT( LocationId loc ) :
                BinaryOp( loc )
            {}

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

    class UGE : public BinaryOpSameTypes
    class UGE : public BinaryOp
    {
        public:
            template< typename L, typename R >
            UGE( L&& lhs, R&& rhs ) : BinaryOpSameTypes( forward< L >( lhs ), forward< R >( rhs ) ) {}
            UGE( LocationId loc ) :
                BinaryOp( loc )
            {}

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

    class ULT : public BinaryOpSameTypes
    class ULT : public BinaryOp
    {
        public:
            template< typename L, typename R >
            ULT( L&& lhs, R&& rhs ) : BinaryOpSameTypes( forward< L >( lhs ), forward< R >( rhs ) ) {}
            ULT( LocationId loc ) :
                BinaryOp( loc )
            {}

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

    class ULE : public BinaryOpSameTypes
    class ULE : public BinaryOp
    {
        public:
            template< typename L, typename R >
            ULE( L&& lhs, R&& rhs ) : BinaryOpSameTypes( forward< L >( lhs ), forward< R >( rhs ) ) {}
            ULE( LocationId loc ) :
                BinaryOp( loc )
            {}

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

    class SGT : public BinaryOpSameTypes
    class SGT : public BinaryOp
    {
        public:
            template< typename L, typename R >
            SGT( L&& lhs, R&& rhs ) : BinaryOpSameTypes( forward< L >( lhs ), forward< R >( rhs ) ) {}
            SGT( LocationId loc ) :
                BinaryOp( loc )
            {}

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

    class SGE : public BinaryOpSameTypes
    class SGE : public BinaryOp
    {
        public:
            template< typename L, typename R >
            SGE( L&& lhs, R&& rhs ) : BinaryOpSameTypes( forward< L >( lhs ), forward< R >( rhs ) ) {}
            SGE( LocationId loc ) :
                BinaryOp( loc )
            {}

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

    class SLT : public BinaryOpSameTypes
    class SLT : public BinaryOp
    {
        public:
            template< typename L, typename R >
            SLT( L&& lhs, R&& rhs ) : BinaryOpSameTypes( forward< L >( lhs ), forward< R >( rhs ) ) {}
            SLT( LocationId loc ) :
                BinaryOp( loc )
            {}

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

    class SLE : public BinaryOpSameTypes
    class SLE : public BinaryOp
    {
        public:
            template< typename L, typename R >
            SLE( L&& lhs, R&& rhs ) : BinaryOpSameTypes( forward< L >( lhs ), forward< R >( rhs ) ) {}
            SLE( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const SLE& ins )
            {
                return ins.print( out, "SLE" );
                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
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
    class CreateTemporary : public BaseInstr< 1, false >
    {
        public:
            template< typename V >
            CreateTemporary( uint32_t index, V&& val ) :
                m_index( index ),
            CreateTemporary( uint32_t index, LocationId loc ) :
                BaseInstr( loc ),
                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
            bool canBeExecuted() const { return true; }
            {
                return IsValueConstantOrExecutable( m_value );
            }

            bool canBeEagerlyEvaluated() const
            bool canBeEagerlyEvaluated() const { return true; }
            {
                return CanValueBeEagerlyEvaluated( m_value );
            bool haveSideEffects() const { return true; }
            }

            bool operator<( const CreateTemporary& rhs ) const
            {
                if( m_index != rhs.m_index )
                    return 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 << ')';
                return out << "CREATETEMP(" << ins.m_index << ')';
            }

        private:
            uint32_t m_index = 0;
            eir::Value m_value;
    };
}

#endif
Changes to bs/cir/decorator.cpp.
1
2
3
4
5
6
7
8
9

10
11
12

13
14
15

16
17
1
2
3
4
5
6



7



8



9

10






-
-
-
+
-
-
-
+
-
-
-
+
-

#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.append( move( m_prologue ) );
        bb.emplace_back( move( x ) );

    for( auto&& x : m_operation )
    bb.append( move( m_operation ) );
        bb.emplace_back( move( x ) );

    for( auto&& x : m_epilogue )
    bb.append( move( m_epilogue) );
        bb.emplace_back( move( x ) );
}
Changes to bs/cir/decorator.h.
25
26
27
28
29
30
31
32
33
34



35
36
37
38
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;
            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
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
    class GetTemporary : public BaseInstr< 0, true >
    {
        public:
            template< typename T >
            GetTemporary( T&& type, uint32_t index ) :
            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
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
    class GhostCall : public BaseInstr< 1, true >   // Can pop more depending on arg count
    {
        public:
            template< typename F, typename A >
            GhostCall( F&& func, A&& args ) :
                m_func( forward< F >( func ) ),
                m_args( forward< A >( args ) )
            GhostCall( uint32_t numArgs, LocationId loc ) :
                BaseInstr( loc ),
                m_numArgs( numArgs )
            {}

            const auto& func() const { return m_func; }
            const auto& args() const { return m_args; }
            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
            {
                if( m_func != rhs.m_func )
                    return m_func < rhs.m_func;
                return m_args < rhs.m_args;
                return m_numArgs < rhs.m_numArgs;
            }

            friend ostream& operator<<( ostream& out, const GhostCall& ins )
            {
                return out << "GHOSTCALL(" << ins.m_func << ", " << ins.m_args << ')';
                return out << "GHOSTCALL";
            }

        private:
            eir::Value m_func;
            uint32_t m_numArgs = 0;
            eir::Term m_args;
    };
}

#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
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 llvm::hash_combine( goose::util::ComputeHash( x.func() ), goose::util::ComputeHash( x.args() ) );
        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 llvm::hash_combine( goose::util::ComputeHash( x.tempIndex() ), goose::util::ComputeHash( x.initValue() ) );
        return goose::util::ComputeHash( x.tempIndex() );
    }

    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() ) );
        return 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() ) );
        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 llvm::hash_combine( goose::util::ComputeHash( *x.addr() ), goose::util::ComputeHash( x.type() ) );
        return 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() ) );
        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 goose::util::ComputeHash( x.operand() );
        return 0;
    }

    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() ) );
        return 0;
    }

    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() ) );
        return 0;
    }

    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() ) );
        return 0;
    }

    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() ) );
        return 0;
    }

    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() ) );
        return 0;
    }

    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() ) );
        return 0;
    }

    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() ) );
        return 0;
    }

    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() ) );
        return 0;
    }

    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() ) );
        return 0;
    }

    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() ) );
        return 0;
    }

    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() ) );
        return 0;
    }

    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() ) );
        return 0;
    }

    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() ) );
        return 0;
    }

    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() ) );
        return 0;
    }

    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() ) );
        return 0;
    }

    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() ) );
        return 0;
    }

    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() ) );
        return 0;
    }

    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() ) );
        return 0;
    }

    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() ) );
        return 0;
    }

    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() ) );
        return 0;
    }

    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() ) );
        return 0;
    }

    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() ) );
        return 0;
    }

    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() ) );
        return 0;
    }

    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() ) );
        return 0;
    }

    size_t hash< goose::cir::Assert >::operator()( const goose::cir::Assert& x ) const
    {
        return goose::util::ComputeHash( x.cond() );
        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::PHOverride >::operator()( const goose::cir::PHOverride& x ) const
    size_t hash< goose::cir::PHOverrideSet >::operator()( const goose::cir::PHOverrideSet& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.name() ), goose::util::ComputeHash( x.phVal() ), goose::util::ComputeHash( x.val() ) );
        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 llvm::hash_combine( goose::util::ComputeHash( x.func() ), goose::util::ComputeHash( x.args() ) );
        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
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
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::PHOverride >
    template<> struct hash< goose::cir::PHOverrideSet >
    {
        size_t operator()( const goose::cir::PHOverride& x ) const;
        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
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() )
        {
        return val.cir()->canBeExecuted();
            if( !instr.canBeExecuted() )
                return false;
        }

        return true;
    }

    bool CanValueBeEagerlyEvaluated( const eir::Value& val )
    {
        if( val.isConstant() )
            return true;

        for( const auto& instr : *val.cir() )
        {
        return val.cir()->canBeEagerlyEvaluated();
            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
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 >;

    bool IsValueConstantOrExecutable( const eir::Value& val );
    bool CanValueBeEagerlyEvaluated( const eir::Value& val );
    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&& instr )
    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 ),
            make_shared< Instruction >( forward< I >( instr ) ) );
    }
        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 >
            auto& set( uint32_t index, TT&& x )
            T& set( uint32_t index, TT&& x )
            {
                auto [it, inserted] = m_storage.try_emplace( index );
                it->second = forward< TT >( x );
                return it->second;
                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
            {
                auto it = m_storage.find( index );
                if( it == m_storage.end() )
                    return nullptr;
                if( index < m_storage.size() )
                    return &m_storage[index];
                return nullptr;

                return &it->second;
            }

            T* get( uint32_t index )
            {
                auto it = m_storage.find( index );
                if( it == m_storage.end() )
                    return nullptr;
                if( index < m_storage.size() )
                    return &m_storage[index];
                return nullptr;

                return &it->second;
            }

        private:
            unordered_map< uint32_t, T > m_storage;
            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
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 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 );
    }

    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 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 << ", ";
        out << "SELECT(" << ins.m_memberIndex;
        if( ins.m_baseAddr )
            out << *ins.m_baseAddr;
        else
            out << "null" ;
         return out << ')';
        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 << ')';
        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 << ')';
        return out << "STORE";
    }

    ostream& operator<<( ostream& out, const PHOverride& ins )
    ostream& operator<<( ostream& out, const PHOverrideSet& ins )
    {
        return out << "PHOVERRIDE(" << ins.m_name << ", " << ins.m_phVal << ", " << ins.m_val << ')';
        return out << "PHOVERRIDESET(" << ins.m_name << ')';
    }

    bool Store::canBeEagerlyEvaluated() const
    ostream& operator<<( ostream& out, const PHOverrideClear& ins )
    {
        return m_addr && CanValueBeEagerlyEvaluated( m_val );
        return out << "PHOVERRIDECLEAR(" << ins.m_name << ')';
    }

    bool Select::operator<( const Select& rhs ) const
    {
        if( m_memberIndex != rhs.m_memberIndex )
            return 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_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;
        return m_type < rhs.m_type;
    }

    bool PHOverrideSet::operator<( const PHOverrideSet& rhs ) const
    {
        return m_name < rhs.m_name;
    }

    bool PHOverride::operator<( const PHOverride& rhs ) const
    bool PHOverrideClear::operator<( const PHOverrideClear& rhs ) const
    {
        if( m_name != rhs.m_name )
            return 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;
    }
}
Changes to bs/cir/instruction.h.
21
22
23
24
25
26
27




28
29
30
31
32
33
34
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
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( PHOverride&& 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
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,
                PHOverride,
                PHOverrideClear,
                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;
            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
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
    class Load : public BaseInstr< 1, true >
    {
        public:
            template< typename A, typename T >
            Load( A&& addr, T&& type ) :
                m_addr( forward< A >( addr ) ),
            template< typename T >
            Load( T&& type, LocationId loc ) :
                BaseInstr( loc ),
                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 haveSideEffects() const { return false; }

            bool operator<( const Load& rhs ) const;
            friend ostream& operator<<( ostream& out, const Load& ins );

        private:
            ptr< Instruction > m_addr;
            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
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:
            template< typename L, typename R >
            And( L&& lhs, R&& rhs ) : BinaryOp( forward< L >( lhs ), forward< R >( rhs ) ) {}
            And( LocationId loc ) :
                BinaryOp( loc )
            {}

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

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

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

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

            friend ostream& operator<<( ostream& out, const Xor& ins )
            {
                return ins.print( out, "XOR" );
                return 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 ) ) {}
            Implies( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Implies& ins )
            {
                return ins.print( out, "IMPLIES" );
                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
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 MarkAddrChangedByLoop( const ptr< CFG >& cfg, uint32_t loopId, const eir::Term& type, const Instruction& addrCalculation )
    void MarkSLocChangedByLoop( const ptr< CFG >& cfg, uint32_t loopId, const eir::Term& type, const StorageLocation& sloc )
    {
        cfg->setAddressModifiedByLoop( loopId, type, addrCalculation );
        cfg->setStorageLocationModifiedByLoop( loopId, type, sloc );

        const auto& pHeader = cfg->getBB( loopId );

        if( pHeader->loopId() )
            MarkAddrChangedByLoop( cfg, pHeader->loopId(), type, addrCalculation );
            MarkSLocChangedByLoop( cfg, pHeader->loopId(), type, sloc );
    }

    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;

            EvalStores( *bb, [&]( auto&& type, auto&& sloc )
                auto cir = st->addr();
                if( !cir )
                    continue;

                MarkAddrChangedByLoop( cfg, bb->loopId(), st->val().type(), *cir );
            }
            {
                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
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',
    'binaryop.cpp',
    'terminator.cpp',
    'call.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
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
    class Not : public BaseInstr< 1, true >
    {
        public:
            template< typename T >
            Not( T&& operand ) :
            Not( LocationId loc ) :
                m_operand( forward< T >( operand ) )
                BaseInstr( loc )
            {}

            const auto& operand() const { return m_operand; }

            bool canBeExecuted() const
            bool canBeExecuted() const { return true; }
            {
                return IsValueConstantOrExecutable( m_operand );
            }

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

            bool operator<( const Not& rhs ) const
            {
                return m_operand < rhs.m_operand;
                return false;
            }

            friend ostream& operator<<( ostream& out, const Not& ins )
            {
                return out << "NOT("<< ins.m_operand << ')';
                return out << "NOT";
            }

        private:
            eir::Value m_operand;
    };
}

#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
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
    class Phi : public BaseInstr< 0, false >
    {
        public:
            template< typename T >
            Phi( T&& type, uint32_t numIncomings, uint32_t destIndex ) :
            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
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
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, to be applied to
    // A pseudo instruction that defines an override value for a placeholder
    // its contained value
    class PHOverride
    class PHOverrideSet : public BaseInstr< 1, false >
    {
        public:
            template< typename S, typename P, typename V >
            PHOverride( S&& name, P&& phVal, V&& val ) :
                m_name( forward< S >( name ) ),
            template< typename S >
            PHOverrideSet( S&& name, LocationId loc ) :
                BaseInstr( loc ),
                m_name( forward< S >( name ) )
                m_phVal( forward< P >( phVal ) ),
                m_val( forward< V >( val ) )
            {}

            const auto& name() const { return m_name; }

            // This can't be executed, it's only meant to appear
            // in expressions handled by the verifier.
            const auto& phVal() const { return m_phVal; }
            const auto& val() const { return m_val; }
            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 PHOverride& rhs ) const;
            friend ostream& operator<<( ostream& out, const PHOverride& ins );
            bool operator<( const PHOverrideClear& rhs ) const;
            friend ostream& operator<<( ostream& out, const PHOverrideClear& ins );

        private:
            StringId    m_name;
            eir::Value  m_phVal;
            eir::Value  m_val;
    };
}

#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
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 replacedd with something else.
    // Intended to represent the @ return value paceholder in functions'
    // 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
    class Placeholder : public BaseInstr< 0, true >
    {
        public:
            template< typename T, typename S >
            Placeholder( T&& type, S&& name ) :
            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
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 Ret
    class RetVoid
    {
        public:
            Ret() {}

            RetVoid( LocationId loc ) :
                m_loc( loc )
            template< typename V >
            Ret( V&& val ) :
                m_value( forward< V >( val ) )
            {}

            const auto& value() const { return m_value; }
            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return true; }

            bool canBeExecuted() const
            {
                return !m_value || IsValueConstantOrExecutable( *m_value );
            }
            void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex )  {}

            const auto& locationId() const { return m_loc; }

        private:
            LocationId m_loc;
    };

            bool canBeEagerlyEvaluated() const
            {
    class Ret
    {
                return !m_value || CanValueBeEagerlyEvaluated( *m_value );
            }
        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:
            optional< eir::Value > m_value;
            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
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
    class Select : public BaseInstr< 1, true >
    {
        public:
            template< typename BA >
            Select( BA&& baseAddr, uint32_t memberIndex ) :
                m_baseAddr( forward< BA >( baseAddr ) ),
            Select( uint32_t memberIndex, LocationId loc ) :
                BaseInstr( loc ),
                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 haveSideEffects() 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
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
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 A, typename T, typename V >
            Store( A&& addr, T&& type, V&& val, eir::LocationId destLocId ) :
            template< typename T >
            Store( T&& type, eir::LocationId srcLocId, eir::LocationId destLocId ) :
                m_addr( forward< A >( addr ) ),
                m_type( forward< T >( type ) ),
                m_val( forward< V >( val ) ),
                m_srcLocId( srcLocId ),
                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& srcLocId() const { return m_srcLocId; }
            const auto& destLocId() const { return m_destLocId; }

            bool canBeExecuted() const
            bool canBeExecuted() const { return true; }
            {
                return IsValueConstantOrExecutable( m_val );
            }

            bool canBeEagerlyEvaluated() const;
            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:
            ptr< Instruction > m_addr;
            eir::Term m_type;
            eir::Value m_val;
            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
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
    class TempAddr : public BaseInstr< 1, true >
    {
        public:
            template< typename T >
            TempAddr( uint32_t i, T&& init ) :
            TempAddr( uint32_t i, LocationId loc ) :
                m_initValue( forward< T >( init ) ),
                BaseInstr( loc ),
                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 haveSideEffects() const { return false; }

            bool operator<( const TempAddr& rhs ) const
            {
                if( m_tempIndex != rhs.m_tempIndex )
                    return 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 << ')';
                return out << "TEMPADDR(" << ins.m_tempIndex << ')';
            }

        private:
            eir::Value m_initValue;
            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
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() {}
            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
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
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 ) );
        bb2->setTerminator( CondBranch( bb3, bb4 ) );
        bb3->setTerminator( Branch( bb5 ) );
        bb4->setTerminator( Branch( bb5 ) );
        bb5->setTerminator( CondBranch( PoisonValue(), bb2, bb6 ) );
        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
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 ) );
        bb1->setTerminator( CondBranch( bb2, bb3 ) );

        bb2->setTerminator( CondBranch( PoisonValue(), bb4, bb7 ) );
        bb2->setTerminator( CondBranch( bb4, bb7 ) );
        bb4->setTerminator( Branch( bb5 ) );
        bb5->setTerminator( Branch( bb6 ) );
        bb6->setTerminator( Branch( bb8 ) );
        bb7->setTerminator( Branch( bb8 ) );
        bb8->setTerminator( CondBranch( PoisonValue(), bb7, bbd ) );
        bb8->setTerminator( CondBranch( bb7, bbd ) );

        bb3->setTerminator( CondBranch( PoisonValue(), bb9, bba ) );
        bba->setTerminator( CondBranch( PoisonValue(), bbb, bbc ) );
        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
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_verify,
            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
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
    class VarAddr : public BaseInstr< 0, true >
    {
        public:
            template< typename T >
            VarAddr( uint32_t varIndex, T&& type ) :
            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
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* Module::buildInstruction( Infos& inf, const VarAddr& va )
{
    auto* ppVal = inf.temporaries->get( va.varIndex() );

llvm::Value* codegen::AddressToGEP( llvm::IRBuilder<>& builder, const codegen::Address& addr )
    assert( ppVal );
    if( !ppVal )
        return nullptr;

{
    return *ppVal;
}

    if( addr.path().empty() )
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;
        return addr.originAddr();

    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 )
    for( auto it = addr.path().rbegin(); it != addr.path().rend(); ++it )
        idxs.push_back( llvm::ConstantInt::get( llvm::Type::getInt32Ty( GetLLVMContext() ), *it ) );

    return m_llvmBuilder.CreateGEP( baseAddrVal->getType()->getScalarType()->getPointerElementType(), baseAddrVal, idxs );
    return builder.CreateGEP( addr.originAddr()->getType()->getScalarType()->getPointerElementType(), addr.originAddr(), idxs );
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::GhostCall& gc )
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 nullptr;
    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
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;

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Add& bo )
bool Module::buildInstruction( State& st, const cir::Add& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;
    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    return m_llvmBuilder.CreateAdd( lhs, rhs );
    st.stack.push( m_llvmBuilder.CreateAdd( *lhs, *rhs ) );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Sub& bo )
bool Module::buildInstruction( State& st, const cir::Sub& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;
    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    return m_llvmBuilder.CreateSub( lhs, rhs );
    st.stack.push( m_llvmBuilder.CreateSub( *lhs, *rhs ) );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Mul& bo )
bool Module::buildInstruction( State& st, const cir::Mul& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;
    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    return m_llvmBuilder.CreateMul( lhs, rhs );
    st.stack.push( m_llvmBuilder.CreateMul( *lhs, *rhs ) );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::UDiv& bo )
bool Module::buildInstruction( State& st, const cir::UDiv& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;
    if( !lhs )
        return false;

    return m_llvmBuilder.CreateUDiv( lhs, rhs );
    st.stack.push( m_llvmBuilder.CreateUDiv( *lhs, *rhs ) );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::SDiv& bo )
bool Module::buildInstruction( State& st, const cir::SDiv& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;
    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    return m_llvmBuilder.CreateSDiv( lhs, rhs );
    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 );
llvm::Value* Module::buildInstruction( Infos& inf, const cir::URem& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;
    if( !rhs )
        return false;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;
    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    return m_llvmBuilder.CreateURem( lhs, rhs );
    st.stack.push( m_llvmBuilder.CreateURem( *lhs, *rhs ) );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::SRem& bo )
bool Module::buildInstruction( State& st, const cir::SRem& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;
    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    return m_llvmBuilder.CreateSRem( lhs, rhs );
    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
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::buildBasicBlock( Infos& inf, const ptr< BasicBlock >& pBB )
llvm::BasicBlock* Module::GetOrCreateLLVMBB( State& st, const ptr< cir::BasicBlock >& pBB )
{
    if( pBB->llvmBB()->getTerminator() || pBB->codeGenStarted() )
        return pBB->llvmBB();

    pBB->setCodeGenStarted();
    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( pBB->llvmBB() );
    m_llvmBuilder.SetInsertPoint( pLLVMBB );

    auto pCurrentBB = pBB;
    for( auto&& instr : *pBB )

    for(;;)
    {
        pCurrentBB->setCodeGenStarted();
        if( !buildInstruction( inf, instr ) )

        const auto* pInstrs = pCurrentBB->runnableInstructions();
        if( !pInstrs )
            return nullptr;
    }

    if( !buildTerminator( inf, *pBB->terminator() ) )
        return nullptr;

    return pBB->llvmBB();

        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( Infos& inf, const cir::Terminator& terminator )
bool Module::buildTerminator( State& st, const cir::Terminator& terminator )
{
    return visit( [&]( auto&& e )
    {
        return buildTerminator( inf, e );
        return buildTerminator( st, e );
    }, terminator.content() );
}

bool Module::buildTerminator( Infos& inf, const cir::Ret& r )
bool Module::buildTerminator( State& st, const cir::RetVoid& r )
{
    if( !r.value() )
    {
        m_llvmBuilder.CreateRetVoid();
        return true;
    }
{
    m_llvmBuilder.CreateRetVoid();
    return true;
}

bool Module::buildTerminator( State& st, const cir::Ret& r )
{
    auto* pRetVal = buildValue( inf, *r.value() );
    if( !pRetVal )
    auto retVal = st.stack.pop( m_llvmBuilder );
    if( !retVal )
        return false;

    m_llvmBuilder.CreateRet( pRetVal );
    m_llvmBuilder.CreateRet( *retVal );
    return true;
}

bool Module::buildTerminator( Infos& inf, const cir::Branch& b )
bool Module::buildTerminator( State& st, const cir::Branch& b )
{
    auto pLLVMBB = GetOrCreateLLVMBB( st, b.dest().lock() );
    m_llvmBuilder.CreateBr( b.dest().lock()->llvmBB() );
    m_llvmBuilder.CreateBr( pLLVMBB );

    llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );

    if( !buildBasicBlock( inf, b.dest().lock() ) )
    if( !buildBasicBlock( st, b.dest().lock() ) )
        return false;

    return true;
}

bool Module::buildTerminator( Infos& inf, const cir::CondBranch& cb )
bool Module::buildTerminator( State& st, const cir::CondBranch& cb )
{
    auto* pCondVal = buildValue( inf, cb.cond() );
    if( !pCondVal )
    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( pCondVal,
    m_llvmBuilder.CreateCondBr( *condVal, pTrueLLVMBB, pFalseLLVMBB );
        cb.trueDest().lock()->llvmBB(), cb.falseDest().lock()->llvmBB() );

    llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );

    if( !buildBasicBlock( inf, cb.trueDest().lock() ) )
    if( !buildBasicBlock( st, cb.trueDest().lock() ) )
        return false;

    if( !buildBasicBlock( inf, cb.falseDest().lock() ) )
    if( !buildBasicBlock( st, cb.falseDest().lock() ) )
        return false;

    return true;
}

bool Module::buildTerminator( Infos& inf, const cir::GhostBranch& gb )
bool Module::buildTerminator( State& st, const cir::GhostBranch& gb )
{
    auto pLLVMBB = GetOrCreateLLVMBB( st, gb.continuation().lock() );
    m_llvmBuilder.CreateBr( gb.continuation().lock()->llvmBB() );
    m_llvmBuilder.CreateBr( pLLVMBB );

    llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );

    if( !buildBasicBlock( inf, gb.continuation().lock() ) )
    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
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
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;

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Eq& bo )
bool Module::buildInstruction( State& st, const cir::Eq& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;
    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    return m_llvmBuilder.CreateICmpEQ( lhs, rhs );
    st.stack.push( m_llvmBuilder.CreateICmpEQ( *lhs, *rhs ) );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Neq& bo )
bool Module::buildInstruction( State& st, const cir::Neq& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;
    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    return m_llvmBuilder.CreateICmpNE( lhs, rhs );
    st.stack.push( m_llvmBuilder.CreateICmpNE( *lhs, *rhs ) );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::UGT& bo )
bool Module::buildInstruction( State& st, const cir::UGT& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;
    if( !lhs )
        return false;

    return m_llvmBuilder.CreateICmpUGT( lhs, rhs );
    st.stack.push( m_llvmBuilder.CreateICmpUGT( *lhs, *rhs ) );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::UGE& bo )
bool Module::buildInstruction( State& st, const cir::UGE& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;
    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    return m_llvmBuilder.CreateICmpUGE( lhs, rhs );
    st.stack.push( m_llvmBuilder.CreateICmpUGE( *lhs, *rhs ) );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::ULT& bo )
bool Module::buildInstruction( State& st, const cir::ULT& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;
    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    return m_llvmBuilder.CreateICmpULT( lhs, rhs );
    st.stack.push( m_llvmBuilder.CreateICmpULT( *lhs, *rhs ) );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::ULE& bo )
bool Module::buildInstruction( State& st, const cir::ULE& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;
    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    return m_llvmBuilder.CreateICmpULE( lhs, rhs );
    st.stack.push( m_llvmBuilder.CreateICmpULE( *lhs, *rhs ) );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::SGT& bo )
bool Module::buildInstruction( State& st, const cir::SGT& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;
    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    return m_llvmBuilder.CreateICmpSGT( lhs, rhs );
    st.stack.push( m_llvmBuilder.CreateICmpSGT( *lhs, *rhs ) );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::SGE& bo )
bool Module::buildInstruction( State& st, const cir::SGE& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;
    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    return m_llvmBuilder.CreateICmpSGE( lhs, rhs );
    st.stack.push( m_llvmBuilder.CreateICmpSGE( *lhs, *rhs ) );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::SLT& bo )
bool Module::buildInstruction( State& st, const cir::SLT& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;
    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    return m_llvmBuilder.CreateICmpSLT( lhs, rhs );
    st.stack.push( m_llvmBuilder.CreateICmpSLT( *lhs, *rhs ) );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::SLE& bo )
bool Module::buildInstruction( State& st, const cir::SLE& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;
    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    return m_llvmBuilder.CreateICmpSLE( lhs, rhs );
    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
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();
    Infos inf( c );

    State st( c, cfg->temporariesCount(), pllvmFunc );

    // Generate allocas and stores for the args
    auto pEntryBB = func.cir()->body()->entryBB();
    auto pEntryBB = cfg->entryBB();
    if( !pEntryBB->llvmBB() )
        pEntryBB->setLLVMBB( llvm::BasicBlock::Create( GetLLVMContext(), "", pllvmFunc ) );

    inf.allocaBasicBlock = pEntryBB->llvmBB();
    st.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 );
        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] );
        inf.temporaries->set( i, args[i] );
        st.temporaries->set( i, args[i] );
        ++i;
    }

    if( !buildCFG( inf, pllvmFunc, func.cir()->body() ) )
    if( !buildCFG( st, 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 )
llvm::BasicBlock* Module::buildCFG( State& st, 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() );
    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
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;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Instruction& instr )
bool Module::buildInstruction( State& st, const cir::Instruction& instr )
{
    return visit( [&]< typename T >( T&& inst )
    {
        return buildInstruction( inf, inst );
        return buildInstruction( st, inst );
    }, instr.content() );
}

llvm::Value* Module::buildInstruction( Infos& inf, const monostate& )
bool Module::buildInstruction( State& st, const monostate& )
{
    return nullptr;
    return false;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Call& call )
bool Module::buildInstruction( State& st, const cir::Call& call )
{
    const auto& callee = call.func();
    if( IsBuiltinFunc( callee ) || IsBuiltinIntrinsicFunc( callee ) )
    {
    auto callee = st.stack.pop< llvm::Function* >( m_llvmBuilder );
    if( !callee )
        return false;

        DiagnosticsManager::GetInstance().emitErrorMessage(
            callee.locationId(), "builtin functions can't be called at runtime." );
    }

    DiagnosticsContext dc( callee.locationId(), "When calling this.", true );
    llvm::SmallVector< llvm::Value*, 8 > args;
    args.resize( call.numArgs() );

    llvm::FunctionCallee llvmCallee;

    for( uint32_t i = 0; i < call.numArgs(); ++i )
    // 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;
        auto parg = st.stack.pop( m_llvmBuilder );
        if( !parg )
            return false;

        llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );
        llvmCallee = llvm::FunctionCallee( getOrCreateFunc( inf.context, *f ) );
        args[call.numArgs() - i - 1] = *parg;
    }
    else
    {
        // TODO: function pointers

    st.stack.push( m_llvmBuilder.CreateCall( llvm::FunctionCallee( *callee ), args ) );
        assert( false );
        return nullptr;
    }
    return true;
}

bool Module::buildInstruction( State& st, const cir::Constant& ct )
{
    auto val = buildValue( st, ct.value() );
    if( !llvmCallee )
        return nullptr;
    if( !val )
        return false;

    const auto& vec = *get< pvec >( call.args() );
    vector< llvm::Value* > args;
    args.reserve( vec.terms().size() );

    st.stack.push( val );
    for( auto&& a : vec.terms() )
    {
        auto* parg = buildValue( inf, *EIRToValue( a ) );
        if( !parg )
            return nullptr;

    return true;
}
        args.emplace_back( parg );
    }

    return m_llvmBuilder.CreateCall( llvm::FunctionCallee( llvmCallee ), args );

bool Module::buildInstruction( State& st, const cir::CreateTemporary& ct )
{
    auto initVal = st.stack.pop( m_llvmBuilder );
}

    if( !initVal )
llvm::Value* Module::buildInstruction( Infos& inf, const cir::CreateTemporary& ct )
{
    return createTemporary( inf, ct.index(), buildValue( inf, ct.value() ) );
        return false;

    createTemporary( st, ct.index(), *initVal );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::GetTemporary& gt )
bool Module::buildInstruction( State& st, const cir::GetTemporary& gt )
{
    auto* ppVal = inf.temporaries->get( gt.index() );
    auto* ppVal = st.temporaries->get( gt.index() );
    assert( ppVal );
    if( !ppVal )
        return nullptr;
    return *ppVal;
        return false;

    st.stack.push( *ppVal );
    return true;
}

llvm::Value* Module::createTemporary( Infos& inf, uint32_t index, llvm::Value* pValue ) const
llvm::Value* Module::createTemporary( State& st, uint32_t index, llvm::Value* pValue ) const
{
    inf.temporaries->set( index, pValue );
    st.temporaries->set( index, pValue );
    return pValue;
}

llvm::Value* Module::buildAlloca( Infos& inf, llvm::Type* type )
llvm::Value* Module::buildAlloca( State& st, llvm::Type* type )
{
    llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );

    if( inf.lastEmittedAlloca && inf.lastEmittedAlloca->getNextNode() )
        m_llvmBuilder.SetInsertPoint( inf.lastEmittedAlloca->getNextNode() );
    if( st.lastEmittedAlloca && st.lastEmittedAlloca->getNextNode() )
        m_llvmBuilder.SetInsertPoint( st.lastEmittedAlloca->getNextNode() );
    else
        m_llvmBuilder.SetInsertPoint( inf.allocaBasicBlock, inf.allocaBasicBlock->begin() );
        m_llvmBuilder.SetInsertPoint( st.allocaBasicBlock, st.allocaBasicBlock->begin() );

    inf.lastEmittedAlloca = m_llvmBuilder.CreateAlloca( type );
    st.lastEmittedAlloca = m_llvmBuilder.CreateAlloca( type );

    return inf.lastEmittedAlloca;
    return st.lastEmittedAlloca;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::AllocVar& av )
bool Module::buildInstruction( State& st, const cir::AllocVar& av )
{
    auto type = LowerTypeForRuntime( inf.context, av.type() );
    auto type = LowerTypeForRuntime( st.context, av.type() );
    if( !type )
        return nullptr;
        return false;

    auto* pAlloca = buildAlloca( inf, GetLLVMType( *type ) );
    createTemporary( inf, av.index(), pAlloca );
    return pAlloca;
    auto* pAlloca = buildAlloca( st, GetLLVMType( *type ) );
    createTemporary( st, av.index(), pAlloca );

    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Load& load )
bool Module::buildInstruction( State& st, const cir::Load& load )
{
    auto ptrVal = st.stack.pop( m_llvmBuilder );
    if( !ptrVal )
        return false;

    auto type = LowerTypeForRuntime( inf.context, *EIRToValue( load.type() ) );
    auto type = LowerTypeForRuntime( st.context, *EIRToValue( load.type() ) );
    if( !type )
        return nullptr;
        return false;

    auto* llvmType = GetLLVMType( *type );
    assert( llvmType );

    auto* ptrVal = buildInstruction( inf, *load.addr() );
    return m_llvmBuilder.CreateLoad( llvmType, ptrVal );
    st.stack.push( m_llvmBuilder.CreateLoad( llvmType, *ptrVal ) );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Store& store )
bool Module::buildInstruction( State& st, const cir::Store& store )
{
    auto* pValue = buildValue( inf, store.val() );
    if( !pValue )
        return nullptr;
    auto val = st.stack.pop( m_llvmBuilder );
    if( !val )
        return false;

    auto ptrVal = st.stack.pop( m_llvmBuilder );
    auto* ptrVal = buildInstruction( inf, *store.addr() );
    if( !ptrVal )
        return false;

    if( llvm::isa< llvm::ConstantAggregate >( pValue ) )
    if( llvm::isa< llvm::ConstantAggregate >( *val ) )
    {
        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() );
            ( *val )->getType(), true, llvm::GlobalValue::LinkageTypes::PrivateLinkage,
            static_cast< llvm::Constant* >( *val ), name.str() );

        auto size = m_dataLayout.getTypeAllocSize( pValue->getType() );
        auto size = m_dataLayout.getTypeAllocSize( ( *val )->getType() );

        return m_llvmBuilder.CreateMemCpy( ptrVal, llvm::None, pGlob, llvm::None,
        m_llvmBuilder.CreateMemCpy( *ptrVal, llvm::None, pGlob, llvm::None,
            llvm::ConstantInt::get( llvm::Type::getInt32Ty( GetLLVMContext() ), size ) );
        return true;
    }

    return m_llvmBuilder.CreateStore( pValue, ptrVal );
    m_llvmBuilder.CreateStore( *val, *ptrVal );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Phi& p )
bool Module::buildInstruction( State& st, const cir::Phi& p )
{
    auto type = LowerTypeForRuntime( inf.context, p.type() );
    auto type = LowerTypeForRuntime( st.context, p.type() );
    if( !type )
        return nullptr;
        return false;

    auto* pPHI = m_llvmBuilder.CreatePHI( GetLLVMType( *type ), p.numIncomings() );
    if( !pPHI )
        return nullptr;
        return false;

    {
        llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );

        p.forAllIncomings( [&]( auto&& bb, auto&& val )
        {
            auto pIncomingBB = buildBasicBlock( inf, bb );
            auto pIncomingBB = buildBasicBlock( st, bb );
            if( !pIncomingBB )
            {
                pPHI = nullptr;
                return false;
            }

            auto* pVal = buildValue( inf, val );
            auto* pVal = buildValue( st, val );
            if( !pVal )
            {
                pPHI = nullptr;
                return false;
            }

            pPHI->addIncoming( pVal, pIncomingBB );
            return true;
        } );
    }

    if( !pPHI )
        return nullptr;
        return false;

    return createTemporary( inf, p.destIndex(), pPHI );
    createTemporary( st, p.destIndex(), pPHI );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Assert& ass )
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.
    // 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() );
    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;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Placeholder& ph )
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 nullptr;
    return false;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Implies& bo )
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 nullptr;
    return false;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::PHOverride& pho )
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 buildValue( inf, pho.val() );
    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
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;

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Not& uo )
bool Module::buildInstruction( State& st, const cir::Not& uo )
{
    auto operand = buildValue( inf, uo.operand() );
    auto operand = st.stack.pop( m_llvmBuilder );
    if( !operand )
        return nullptr;
        return false;

    return m_llvmBuilder.CreateXor( operand, llvm::ConstantInt::getTrue( GetLLVMContext() ) );
    st.stack.push( m_llvmBuilder.CreateXor( *operand, llvm::ConstantInt::getTrue( GetLLVMContext() ) ) );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::And& bo )
bool Module::buildInstruction( State& st, const cir::And& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;
    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    return m_llvmBuilder.CreateAnd( lhs, rhs );
    st.stack.push( m_llvmBuilder.CreateAnd( *lhs, *rhs ) );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Or& bo )
bool Module::buildInstruction( State& st, const cir::Or& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;
    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    return m_llvmBuilder.CreateOr( lhs, rhs );
    st.stack.push( m_llvmBuilder.CreateOr( *lhs, *rhs ) );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Xor& bo )
bool Module::buildInstruction( State& st, const cir::Xor& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;
    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    return m_llvmBuilder.CreateXor( lhs, rhs );
    st.stack.push( m_llvmBuilder.CreateXor( *lhs, *rhs ) );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::Shl& bo )
bool Module::buildInstruction( State& st, const cir::Shl& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;
    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    return m_llvmBuilder.CreateShl( lhs, rhs );
    st.stack.push( m_llvmBuilder.CreateShl( *lhs, *rhs ) );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::LShr& bo )
bool Module::buildInstruction( State& st, const cir::LShr& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;

    auto rhs = st.stack.pop( m_llvmBuilder );
    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;
        return false;

    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    return m_llvmBuilder.CreateLShr( lhs, rhs );
    st.stack.push( m_llvmBuilder.CreateLShr( *lhs, *rhs ) );
    return true;
}

llvm::Value* Module::buildInstruction( Infos& inf, const cir::AShr& bo )
bool Module::buildInstruction( State& st, const cir::AShr& bo )
{
    auto lhs = buildValue( inf, bo.lhs() );
    if( !lhs )
        return nullptr;
    auto rhs = st.stack.pop( m_llvmBuilder );
    if( !rhs )
        return false;

    auto rhs = buildValue( inf, bo.rhs() );
    if( !rhs )
        return nullptr;
    auto lhs = st.stack.pop( m_llvmBuilder );
    if( !lhs )
        return false;

    return m_llvmBuilder.CreateAShr( lhs, rhs );
    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
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 Infos
            struct State
            {
                State( const Context& c, size_t numVars, llvm::Function* f ) :
                Infos( const Context& c ) : context( c ) {}
                    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 = make_shared< storage_type >();
                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( Infos& inf, llvm::Function* llvmFunc, const ptr< cir::CFG >& pCFG );
            llvm::BasicBlock* buildBasicBlock( Infos& inf, const ptr< cir::BasicBlock >& pBB );
            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( 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* 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 );
            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 );
            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 );

            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 );
            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 );

            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 );
            bool buildInstruction( State& st, const cir::Shl& bo );
            bool buildInstruction( State& st, const cir::LShr& bo );
            bool buildInstruction( State& st, 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 );
            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 );

            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 );
            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 );

            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 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( 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 );
            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( Infos& inf, const T& t )
            bool buildTerminator( State& st, 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::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
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( Infos& inf, const Value& val )
llvm::Value* Module::buildValue( State& st, const Value& val )
{
    if( val.isPoison() )
        return nullptr;

    if( val.isConstant() )
        return buildConstant( inf, val );
        return buildConstant( st, val );

    return buildInstruction( inf, *val.cir() );
}
    if( !buildInstruction( st, *val.cir() ) )
        return nullptr;

    auto result = st.stack.pop( m_llvmBuilder );
    if( !result )
        return nullptr;

    return *result;
}

llvm::Constant* Module::buildConstant( Infos& inf, const Value& v )
llvm::Constant* Module::buildConstant( State& st, const Value& v )
{
    assert( v.isConstant() );

    auto val = LowerConstantForRuntime( inf.context, v );
    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( inf, *ptType, *val );
        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( inf, *EIRToValue( 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() ) ) )

    DiagnosticsManager::GetInstance().emitErrorMessage(
        v.locationId(), "constants with compile time types are not supported.", 0 );
    {
        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( Infos& inf, const PointerType& ptType, const Value& v )
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
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 )
        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->entryBB() );
        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
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() );
                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
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( 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
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
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() );
        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
1
2
3










4
5
6
7
8
9
10



-
-
-
-
-
-
-
-
-
-







#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;
    };

Changes to bs/eir/value.cpp.
33
34
35
36
37
38
39
40

41
42
43
44
45
46
47
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();
        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
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 );
        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
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
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::Instruction > cir() const
            ptr< cir::InstrSeq > cir() const
            {
                const auto* ppCir = get_if< ptr< cir::Instruction > >( &m_valOrCIR );
                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
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::Instruction > > m_valOrCIR;
            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
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
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
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 )
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;
        }
    } );
}

optional< Value > VM::execute( const cir::Or& bo )
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;
        }
    } );
}

optional< Value > VM::execute( const cir::Xor& bo )
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;
        }
    } );
}

optional< Value > VM::execute( const cir::Shl& bo )
bool 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 )
bool 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 )
bool 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 )
bool VM::execute( const cir::Add& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs + rhs;
    } );
}

optional< Value > VM::execute( const cir::Sub& bo )
bool VM::execute( const cir::Sub& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs - rhs;
    } );
}

optional< Value > VM::execute( const cir::Mul& bo )
bool VM::execute( const cir::Mul& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs * rhs;
    } );
}

optional< Value > VM::execute( const cir::UDiv& bo )
bool VM::execute( const cir::UDiv& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs / rhs;
    } );
}

optional< Value > VM::execute( const cir::SDiv& bo )
bool VM::execute( const cir::SDiv& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs / rhs;
    } );
}

optional< Value > VM::execute( const cir::URem& bo )
bool VM::execute( const cir::URem& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs % rhs;
    } );
}

optional< Value > VM::execute( const cir::SRem& bo )
bool VM::execute( const cir::SRem& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs % rhs;
    } );
}

optional< Value > VM::execute( const cir::Eq& bo )
bool VM::execute( const cir::Eq& bo )
{
    return executeEqualityBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs == rhs;
    } );
}

optional< Value > VM::execute( const cir::Neq& bo )
bool VM::execute( const cir::Neq& bo )
{
    return executeEqualityBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs != rhs;
    } );
}

optional< Value > VM::execute( const cir::UGT& bo )
bool VM::execute( const cir::UGT& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs > rhs;
    } );
}

optional< Value > VM::execute( const cir::UGE& bo )
bool VM::execute( const cir::UGE& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs >= rhs;
    } );
}

optional< Value > VM::execute( const cir::ULT& bo )
bool VM::execute( const cir::ULT& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs < rhs;
    } );
}

optional< Value > VM::execute( const cir::ULE& bo )
bool VM::execute( const cir::ULE& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs <= rhs;
    } );
}

optional< Value > VM::execute( const cir::SGT& bo )
bool VM::execute( const cir::SGT& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs > rhs;
    } );
}

optional< Value > VM::execute( const cir::SGE& bo )
bool VM::execute( const cir::SGE& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs >= rhs;
    } );
}

optional< Value > VM::execute( const cir::SLT& bo )
bool VM::execute( const cir::SLT& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs < rhs;
    } );
}

optional< Value > VM::execute( const cir::SLE& bo )
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
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 >
    optional< Value > VM::executeEqualityBinOp( const cir::BinaryOp& bo, F&& func )
    bool VM::executeEqualityBinOp( const cir::BinaryOp& bo, F&& func )
    {
        auto rhs = pop();
        auto lhs = pop();
        if( !lhs || !rhs )
            return false;
        if( bo.lhs().isPoison() || bo.rhs().isPoison() )
            return PoisonValue();
        if( lhs->isPoison() || rhs->isPoison() )
            return false;

        assert( bo.lhs().type() == bo.rhs().type() );
        if( bo.lhs().type() != bo.rhs().type() )
            return PoisonValue();
        assert( lhs->type() == rhs->type() );
        if( lhs->type() != rhs->type() )
            return false;

        auto lval = Evaluate( bo.lhs(), *this );
        auto lval = Evaluate( *lhs, *this );
        if( lval.isPoison() )
            return PoisonValue();
            return false;
        if( !lval.isConstant() )
            return nullopt;
            return false;

        auto rval = Evaluate( bo.rhs(), *this );
        auto rval = Evaluate( *rhs, *this );
        if( rval.isPoison()  )
            return PoisonValue();
            return false;
        if( !rval.isConstant() )
            return nullopt;
            return false;

        if( auto lbool = FromValue< bool >( lval ) )
        {
            return ToValue( func( *lbool, *FromValue< bool >( rval ) ) );

            push( ToValue( func( *lbool, *FromValue< bool >( rval ) ) ) );
            return true;
        }

        if( auto lint = FromValue< APSInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );
            return ToValue( func( *lint, move( rint ) ) );
            push( ToValue( func( *lint, move( rint ) ) ) );
            return true;
        }

        if( auto lint = FromValue< BigInt >( lval ) )
        {
            auto rint = *FromValue< BigInt >( rval );
            return ToValue( func( *lint, move( rint ) ) );
            push( ToValue( func( *lint, move( rint ) ) ) );
            return true;
        }

        if( auto lc = FromValue< char32_t >( lval ) )
        {
            auto rc = *FromValue< char32_t >( rval );
            return ToValue( func( *lc, rc ) );
            push( ToValue( func( *lc, rc ) ) );
            return true;
        }

        if( auto lstr = FromValue< string >( lval ) )
        {
            const auto& rstr = *FromValue< string >( rval );
            return ToValue( func( *lstr, rstr ) );
            push( ToValue( func( *lstr, rstr ) ) );
            return true;
        }

        assert( false );
        return PoisonValue();
        G_TRACE_VAL( lval )
        G_TRACE_VAL( rval )
        G_ERROR( "binop execution failure")
        return false;
    }

    template< typename F >
    optional< Value > VM::executeLogicBinOp( const cir::BinaryOp& bo, F&& func )
    bool VM::executeLogicBinOp( const cir::BinaryOp& bo, F&& func )
    {
        auto rhs = pop();
        auto lhs = pop();
        if( !lhs || !rhs )
            return false;
        if( bo.lhs().isPoison() || bo.rhs().isPoison() )
            return PoisonValue();
        if( lhs->isPoison() || rhs->isPoison() )
            return false;

        assert( bo.lhs().type() == bo.rhs().type() );
        if( bo.lhs().type() != bo.rhs().type() )
            return PoisonValue();
        assert( lhs->type() == rhs->type() );
        if( lhs->type() != rhs->type() )
            return false;

        auto lval = Evaluate( bo.lhs(), *this );
        auto lval = Evaluate( *lhs, *this );
        if( lval.isPoison()  )
            return PoisonValue();
            return false;
        if( !lval.isConstant() )
            return nullopt;
            return false;

        auto rval = Evaluate( bo.rhs(), *this );
        auto rval = Evaluate( *rhs, *this );
        if( rval.isPoison()  )
            return PoisonValue();
            return false;
        if( !rval.isConstant() )
            return nullopt;
            return false;

        if( auto lbool = FromValue< bool >( lval ) )
        {
            return ToValue( func( *lbool, *FromValue< bool >( rval ) ) );

            push( ToValue( func( *lbool, *FromValue< bool >( rval ) ) ) );
            return true;
        }

        if( auto lint = FromValue< APSInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );
            return ToValue( func( *lint, move( rint ) ) );
            push( ToValue( func( *lint, move( rint ) ) ) );
            return true;
        }

        if( auto lint = FromValue< BigInt >( lval ) )
        {
            auto rint = *FromValue< BigInt >( rval );
            return ToValue( func( *lint, move( rint ) ) );
            push( ToValue( func( *lint, move( rint ) ) ) );
            return true;
        }

        assert( false );
        return PoisonValue();
        G_TRACE_VAL( lval )
        G_TRACE_VAL( rval )
        G_ERROR( "binop execution failure")
        return false;
    }

    template< typename F >
    optional< Value > VM::executeBinOp( const cir::BinaryOp& bo, F&& func )
    bool VM::executeBinOp( const cir::BinaryOp& bo, F&& func )
    {
        auto rhs = pop();
        auto lhs = pop();
        if( !lhs || !rhs )
            return false;
        if( bo.lhs().isPoison() || bo.rhs().isPoison() )
            return PoisonValue();
        if( lhs->isPoison() || rhs->isPoison() )
            return false;

        assert( bo.lhs().type() == bo.rhs().type() );
        if( bo.lhs().type() != bo.rhs().type() )
            return PoisonValue();
        assert( lhs->type() == rhs->type() );
        if( lhs->type() != rhs->type() )
            return false;

        auto lval = Evaluate( bo.lhs(), *this );
        auto lval = Evaluate( *lhs, *this );
        if( lval.isPoison()  )
            return PoisonValue();
            return false;
        if( !lval.isConstant() )
            return nullopt;
            return false;

        auto rval = Evaluate( bo.rhs(), *this );
        auto rval = Evaluate( *rhs, *this );
        if( rval.isPoison()  )
            return PoisonValue();
            return false;
        if( !rval.isConstant() )
            return nullopt;
            return false;

        if( auto lint = FromValue< APSInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );
            return ToValue( func( *lint, move( rint ) ) );
            push( ToValue( func( *lint, move( rint ) ) ) );
            return true;
        }

        if( auto lint = FromValue< BigInt >( lval ) )
        {
            const auto& rint = *FromValue< BigInt >( rval );
            return ToValue( func( *lint, rint ) );
            push( ToValue( func( *lint, rint ) ) );
            return true;
        }

        assert( false );
        return PoisonValue();
        G_TRACE_VAL( lval )
        G_TRACE_VAL( rval )
        G_ERROR( "binop execution failure")
        return false;
    }

    template< typename F >
    optional< Value > VM::executeShiftBinOp( const cir::BinaryOp& bo, F&& func )
    bool VM::executeShiftBinOp( const cir::BinaryOp& bo, F&& func )
    {
        auto rhs = pop();
        auto lhs = pop();
        if( !lhs || !rhs )
            return false;
        if( bo.lhs().isPoison() || bo.rhs().isPoison() )
            return PoisonValue();
        if( lhs->isPoison() || rhs->isPoison() )
            return false;

        auto lval = Evaluate( bo.lhs(), *this );
        auto lval = Evaluate( *lhs, *this );
         if( lval.isPoison()  )
            return PoisonValue();
            return false;
        if( !lval.isConstant() )
            return nullopt;
            return false;

        auto rval = Evaluate( bo.rhs(), *this );
        auto rval = Evaluate( *rhs, *this );
        if( rval.isPoison()  )
            return PoisonValue();
            return false;
        if( !rval.isConstant() )
            return nullopt;
            return false;

        if( auto lint = FromValue< APSInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );
            return ToValue( APSInt( func( *lint, move( rint ) ), lint->isUnsigned() ) );
            push( ToValue( APSInt( func( *lint, move( rint ) ), lint->isUnsigned() ) ) );
            return true;
        }

        if( auto lint = FromValue< BigInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );
            return ToValue( BigInt( func( *lint, move( rint ) ) ) );
            push( ToValue( BigInt( func( *lint, move( rint ) ) ) ) );
            return true;
        }

        assert( false );
        return PoisonValue();
        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
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();

            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.
            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
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 );
    return execute( cfg.entryBB() );
    if( !execute( cfg.entryBB() ) )
        return nullopt;
    return pop();
}

optional< Value > VM::execute( ptr< BasicBlock > bb )
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 : *bb )
            execute( instr );
        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 m_retVal;
    return true;
}

bool VM::execute( const cir::InstrSeq& is )
{
    for( const auto& instr : is )
    {
        if( !execute( instr ) )
            return false;
    }

    return true;
}

optional< Value > VM::execute( const cir::Instruction& instr )
bool VM::execute( const cir::Instruction& instr )
{
    return visit( [&]( auto&& e )
    {
        return execute( e );
    }, instr.content() );
}

optional< Value > VM::execute( const cir::Call& call )
bool VM::execute( const cir::Call& call )
{
    if( !( ms_remainingBranchInstExecutions ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "execute: compilation time execution budget exceeded." );
        return PoisonValue();
        return false;
    }

    --ms_remainingBranchInstExecutions;

    auto callFunc = pop();
    if( !callFunc )
        return false;

    if( call.func().isPoison() )
    if( callFunc->isPoison() )
        DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );

    auto func = Evaluate( call.func(), *this );
    auto func = Evaluate( *callFunc, *this );
    if( func.isPoison() )
        return PoisonValue();
        return false;

    if( !func.isConstant() )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "execute: function evaluation failed." );
        return PoisonValue();
        return false;
    }

    if( IsExternalFunc( func ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "execute: can't call external functions." );
        return PoisonValue();
    }

        return false;

    auto argCount = call.numArgs();
    bool poisoned = false;
    const auto& vec = *get< pvec >( call.args() );

    if( IsBuiltinFunc( func ) )
    {
        auto newVec = make_shared< Vector >();
        newVec->resize( argCount );
        auto newVec = vec.transform( [&]( auto&& x ) -> optional< Term >

        for( uint32_t argIndex = 0; argIndex < argCount; ++argIndex )
        {
            auto val = EIRToValue( x );
            assert( val );
            auto val = pop();
            if( !val )
                return false;

            auto newVal = Evaluate( *val, *this );
            if( newVal.isPoison() )
                poisoned = true;
                return false;

            // 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 false;

            return ValueToEIR( newVal );
            newVec->set( argCount - argIndex - 1, ValueToEIR( newVal ) );
        } );

        }
        if( poisoned )
            return PoisonValue();

        auto result = ExecuteBuiltinFuncCall( func, TERM( newVec ) );
        if( !newVec )
            return nullopt;
        if( result )
            push( *result );
        return true;

        return ExecuteBuiltinFuncCall( func, TERM( newVec ) );
    }

    const auto* pFunc = GetFuncCIR( func );

    if( !pFunc || !pFunc->isValid() )
        return PoisonValue();
        return false;

    auto savedStackSize = m_stack.size();
    // Pop and eval all args before creating our stackframe
    llvm::SmallVector< Term, 8 > evaluatedArgs( argCount, {} );

    for( auto&& a : vec.terms() )
    for( uint32_t argIndex = 0; argIndex < argCount; ++argIndex )
    {
        auto val = EIRToValue( a );
        assert( val );
        auto val = pop();
        if( !val )
            return false;

        auto newVal = Evaluate( *val, *this );
        if( newVal.isPoison()  )
            return false;
        {
            m_stack.resize( savedStackSize );
            return PoisonValue();
        }


        if( !newVal.isConstant() )
            return false;

        evaluatedArgs[argCount - argIndex - 1] = ValueToEIR( newVal );
    }
        if( !newVal.isConstant() )
        {

            m_stack.resize( savedStackSize );
            return nullopt;
        }

        m_stack.emplace_back( make_shared< Term >( 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();
    swap( m_currentFrameStart, savedStackSize );
    auto result = execute( *pFunc->body() );
    swap( m_currentFrameStart, savedStackSize );

    m_stack.resize( savedStackSize );
    return result;

        // 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;
}

optional< Value > VM::execute( const cir::VarAddr& va )
bool VM::execute( const cir::VarAddr& va )
{
    auto stackIndex = m_currentFrameStart + va.varIndex();
    if( stackIndex >= m_stack.size() )
        return nullopt;
        return false;

    if( !m_stack[stackIndex] )
        return nullopt;
        return false;

    return ToValue( m_stack[stackIndex].get() );
    push( ToValue( m_stack[stackIndex].get() ) );
    return true;
}

optional< Value > VM::execute( const cir::TempAddr& ta )
bool VM::execute( const cir::TempAddr& ta )
{
    auto initVal = pop();
    if( !initVal )
        return false;

    auto stackIndex = m_currentFrameStart + ta.tempIndex();
    if( m_stack.size() <= stackIndex )
        m_stack.resize( stackIndex + 1 );

    if( stackIndex >= m_stack.size() )
        return nullopt;
        return false;

    if( !m_stack[stackIndex] )
        m_stack[stackIndex] = make_shared< Term >( ValueToEIR( Evaluate( ta.initValue(), *this ) ) );
        m_stack[stackIndex] = make_shared< Term >( ValueToEIR( Evaluate( *initVal, *this ) ) );

    return ToValue( m_stack[stackIndex].get() );
    push( ToValue( m_stack[stackIndex].get() ) );
    return true;
}

optional< Value > VM::execute( const cir::Select& s )
bool VM::execute( const cir::Select& s )
{
    auto baseAddr = execute( *s.baseAddr() );
    if( !baseAddr || baseAddr->isPoison() )
        return nullopt;
    auto baseAddr = pop();
    if( !baseAddr )
        return false;

    auto pvoid = FromValue< Term* >( *baseAddr );
    if( !pvoid )
        return nullopt;
        return false;

    auto val = EIRToValue( *static_cast< Term* >( pvoid ) );
    if( !val )
        return nullopt;
        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 ) )
    {
        cout << "execute: select: value is not a tuple\n";
        return nullopt;
        G_VAL_ERROR( *val, "execute: select: value is not a tuple" );
        return false;
    }

    assert( val->isConstant() );

    return ToValue( &GetTupleElement( *val, s.memberIndex() ) );
    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
optional< Value > VM::execute( const cir::CreateTemporary& ct )
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( ct.value(), *this ) ) );
    return nullopt;
    m_stack[stackIndex] = make_shared< Term >( ValueToEIR( Evaluate( *val, *this ) ) );
    return true;
}

optional< Value > VM::execute( const cir::GetTemporary& gt )
bool VM::execute( const cir::GetTemporary& gt )
{
    auto stackIndex = gt.index() + m_currentFrameStart;
    if( stackIndex >= m_stack.size() )
        return nullopt;
        return false;

    return EIRToValue( *m_stack[stackIndex] );
    push( *EIRToValue( *m_stack[stackIndex] ) );
    return true;
}

optional< Value > VM::execute( const cir::AllocVar& av )
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 nullopt;
    return true;
}

optional< Value > VM::execute( const cir::Load& l )
bool VM::execute( const cir::Load& l )
{
    auto baseAddr = execute( *l.addr() );
    if( !baseAddr || baseAddr->isPoison() )
        return nullopt;
    auto baseAddr = pop();
    if( !baseAddr )
        return false;

    auto addr = FromValue< Term* >( *baseAddr );
    if( !addr )
        return nullopt;
        return false;

    return EIRToValue( *addr );
}

optional< Value > VM::execute( const cir::Store& s )
    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 = execute( *s.addr() );
    if( !baseAddr || baseAddr->isPoison() )
        return nullopt;
    auto baseAddr = pop();
    if( !baseAddr )
        return false;

    auto addr = FromValue< Term* >( *baseAddr );
    if( !addr )
        return nullopt;
        return false;

    auto result = Evaluate( s.val(), *this );
    if( !result.isConstant() )
        return PoisonValue();

    *addr = ValueToEIR( result );
    return nullopt;
    return true;
}

optional< Value > VM::execute( const cir::Phi& p )
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 PoisonValue();
    return success;
}

optional< Value > VM::execute( const cir::Not& uo )
bool VM::execute( const cir::Not& uo )
{
    auto operand = pop();
    if( uo.operand().isPoison() )
        return PoisonValue();
    if( !operand || operand->isPoison() )
        return false;

    auto opval = Evaluate( uo.operand(), *this );
    auto opval = Evaluate( *operand, *this );
    if( opval.isPoison() )
        return PoisonValue();
        return false;
    if( !opval.isConstant() )
        return nullopt;
        return false;

    auto boolVal = FromValue< bool >( opval );
    if( !boolVal )
        return PoisonValue();
        return false;

    return ToValue( !*boolVal );
    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( r.value() )
        m_retVal = Evaluate( *r.value(), *this );
    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
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( cb.cond(), *this );
    auto cond = Evaluate( *condVal, *this );
    if( cond.isPoison() )
        return nullptr;

    if( !cond.isConstant() )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( cb.cond().locationId(),
        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
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 );

            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 );
            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 );

            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 );
            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 );

            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 );
            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 );

            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 );
            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 >
            optional< Value > execute( const T& )
            bool execute( const T& )
            {
                return PoisonValue();
                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 >
            optional< Value > executeEqualityBinOp( const cir::BinaryOp& bo, F&& func );
            bool executeEqualityBinOp( const cir::BinaryOp& bo, F&& func );

            template< typename F >
            optional< Value > executeLogicBinOp( const cir::BinaryOp& bo, F&& func );
            bool executeLogicBinOp( const cir::BinaryOp& bo, F&& func );

            template< typename F >
            optional< Value > executeBinOp( const cir::BinaryOp& bo, F&& func );
            bool executeBinOp( const cir::BinaryOp& bo, F&& func );

            template< typename F >
            optional< Value > executeShiftBinOp( const cir::BinaryOp& bo, F&& func );
            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
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
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 > >
            []( 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 TypeWrapper< ptr< C > >& instr, TermRef< tup_type >& out )
            [&]( 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
84
85
86
87
88
89
90

91
92
93
94
95
96
97
98
99







-
+
+







        SGT,
        SGE,
        SLT,
        SLE,

        Assert,
        Placeholder,
        PHOverride
        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
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::lhs, &T::rhs );
        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
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, "InstrOpCodePHOverride"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::PHOverride ) ) ) );
        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 TypeWrapper< ptr< Instruction > >& t )
            []( 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::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 );
        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
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::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 );
        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 );

        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;

        RegisterTermiOverloads< RetVoid, TermiOpCode::RetVoid >( e, MkTermi, UnpackTermi, &RetVoid::locationId );
                out = *pRet->value();
                return true;
            } );

        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::cond, &CondBranch::trueDest, &CondBranch::falseDest );
        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 TypeWrapper< ptr< BasicBlock > >& pBB ) -> TypeWrapper< LocationId >
            []( const auto& 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 )
            []( const auto& pBB, const auto& loc )
            {
                pBB->setLocationId( loc );
            } );

        RegisterBuiltinFunc< uint32_t ( TypeWrapper< ptr< BasicBlock > > ) >( e, "GetBasicBlockLoopId"_sid,
            []( const TypeWrapper< ptr< BasicBlock > >& pBB )
            []( const auto& pBB )
            {
                return pBB->loopId();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< BasicBlock > > ) >( e, "IsBasicBlockLoopHeader"_sid,
            []( const TypeWrapper< ptr< BasicBlock > >& pBB )
            []( const auto& 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& 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 TypeWrapper< ptr< BasicBlock > >& pBB, const TypeWrapper< ptr< Terminator > >& termi )
            []( const auto& pBB, const auto& termi )
            {
                pBB->setTerminator( *termi.get() );
            } );

        RegisterBuiltinFunc< uint32_t ( TypeWrapper< ptr< BasicBlock > > ) >( e, "GetBasicBlockInstrCount"_sid,
            []( const TypeWrapper< ptr< BasicBlock > >& pBB )
            {
                return pBB->size();
            } );

        // 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< 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 )
            []( const auto& pBB, auto& instr )
            {
                auto& bb = *pBB.get();
                bb.emplace_back( *instr.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 TypeWrapper< ptr< CFG > >& pCFG )
            []( const auto& pCFG )
            {
                return pCFG->isPoisoned();
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< CFG > > ) >( e, "PoisonCFG"_sid,
            []( const TypeWrapper< ptr< CFG > >& pCFG )
            []( const auto& pCFG )
            {
                pCFG->poison();
            } );

        RegisterBuiltinFunc< uint32_t ( TypeWrapper< ptr< CFG > > ) >( e, "GetCFGBasicBlockCount"_sid,
            []( const TypeWrapper< ptr< CFG > >& pCFG )
            []( 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 TypeWrapper< ptr< CFG > >& pCFG, uint32_t index, TermRef< TypeWrapper< ptr< BasicBlock > > >& out )
            []( 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 TypeWrapper< ptr< CFG > >& pCFG, TermRef< TypeWrapper< ptr< BasicBlock > > >& out )
            []( 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 TypeWrapper< ptr< CFG > >& pCFG, TermRef< TypeWrapper< ptr< BasicBlock > > >& out )
            []( 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 TypeWrapper< ptr< CFG > >& pCFG, TermRef< TypeWrapper< ptr< BasicBlock > > >& out )
            []( 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 TypeWrapper< ptr< CFG > >& pCFG, TypeWrapper< ptr< BasicBlock > >& pBB )
            []( const auto& pCFG, auto& 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 > >
            []( const auto& 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 )
            []( const auto& 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 )
            []( 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 TypeWrapper< ptr< Decorator > >& pBB, TypeWrapper< ptr< Instruction > >& instr )
            []( 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 TypeWrapper< ptr< Decorator > >& pBB, TypeWrapper< ptr< Instruction > >& instr )
            []( 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
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 )
            []( 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
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 )
            []( 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
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& 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
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 >
            []( 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 TypeWrapper< StringId >& name ) -> TypeWrapper< Hole >
            []( const auto& 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 >
            []( const auto& name, const auto& kind ) -> TypeWrapper< Hole >
            {
                return Hole( name, kind.get() );
            } );

        RegisterBuiltinFunc< TypeWrapper< StringId > ( TypeWrapper< Hole > ) >( e, "GetHoleName"_sid,
            []( const TypeWrapper< Hole >& h ) -> TypeWrapper< StringId >
            []( const auto& h ) -> TypeWrapper< StringId >
            {
                return h.get().name();
            } );

        RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< Hole > ) >( e, "GetHoleKind"_sid,
            []( const TypeWrapper< Hole >& h ) -> TypeWrapper< Term >
            []( const auto& h ) -> TypeWrapper< Term >
            {
                return h.get().kind();
            } );

        ////////////////////////////
        // AnyTerm
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< StringId > ( TypeWrapper< AnyTerm > ) >( e, "GetAnyTermVarName"_sid,
            []( const TypeWrapper< AnyTerm >& at ) -> TypeWrapper< StringId >
            []( const auto& at ) -> TypeWrapper< StringId >
            {
                return at.get().varName();
            } );

        RegisterBuiltinFunc< TypeWrapper< AnyTerm > ( TypeWrapper< StringId > ) >( e, "MkAnyTerm"_sid,
            []( const TypeWrapper< StringId >& name ) -> TypeWrapper< AnyTerm >
            []( const auto& name ) -> TypeWrapper< AnyTerm >
            {
                return AnyTerm( name );
            } );

        ////////////////////////////
        // VecOfLength
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< StringId > ( TypeWrapper< VecOfLength > ) >( e, "GetVecOfLengthVarName"_sid,
            []( const TypeWrapper< VecOfLength >& at ) -> TypeWrapper< StringId >
            []( const auto& at ) -> TypeWrapper< StringId >
            {
                return at.get().varName();
            } );

        RegisterBuiltinFunc< TypeWrapper< VecOfLength > ( TypeWrapper< StringId > ) >( e, "MkVecOfLength"_sid,
            []( const TypeWrapper< StringId >& name ) -> TypeWrapper< VecOfLength >
            []( 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 TypeWrapper< pvec >& vec1, const TypeWrapper< pvec >& vec2 )
            []( 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 TypeWrapper< pvec >& vec, uint32_t index, const TypeWrapper< Term >& t )
            []( const auto& vec, uint32_t index, const auto& t )
            {
                vec->terms()[index] = t.get();
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< pvec >, int32_t ) >( e, "SetVecWeight"_sid,
            []( const TypeWrapper< pvec >& vec, int32_t w )
            []( const auto& vec, int32_t w )
            {
                vec->setWeight( w );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< pvec >, int32_t ) >( e, "SetVecWeightOverride"_sid,
            []( const TypeWrapper< pvec >& vec, int32_t w )
            []( const auto& vec, int32_t w )
            {
                vec->setWeightOverride( w );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< pvec >, TypeWrapper< Term > ) >( e, "VecAppend"_sid,
            []( const TypeWrapper< pvec >& vec, const TypeWrapper< Term >& t )
            []( const auto& vec, const auto& t )
            {
                vec->append( t.get() );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< pvec >, TypeWrapper< Term > ) >( e, "VecSetRepetition"_sid,
            []( const TypeWrapper< pvec >& vec, const TypeWrapper< Term >& t )
            []( const auto& vec, const auto& t )
            {
                vec->setRepetitionTerm( t.get() );
            } );

        RegisterBuiltinFunc< tuple< uint32_t, bool > ( TypeWrapper< pvec > ) >( e, "GetVecLength"_sid,
            []( const TypeWrapper< pvec >& vec )
            []( 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 TypeWrapper< pvec >& vec, uint32_t index, TermRef< TypeWrapper< Term > >& out )
            []( 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 TypeWrapper< pvec >& vec )
            []( const auto& vec )
            {
                return vec->weight();
            } );

        RegisterBuiltinFunc< int32_t ( TypeWrapper< pvec > ) >( e, "GetVecWeightOverride"_sid,
            []( const TypeWrapper< pvec >& vec )
            []( const auto& 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& vec, auto& 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 )
            []( const auto& vec )
            {
                return vec->empty();
            } );

        ////////////////////////////
        // Helpers
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< Term >, TypeWrapper< Term > ) >( e, "AppendToVectorTerm"_sid,
            []( const TypeWrapper< Term >& vec, TypeWrapper< Term > t ) -> TypeWrapper< Term >
            []( 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
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 )
            []( 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 TypeWrapper< ptr< Context > >& c, const TypeWrapper< Value >& type, TermRef< TypeWrapper< ptr< Propositions > > >& out )
            []( 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 TypeWrapper< ptr< Propositions > >& preds )
            []( const auto& 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 )
            []( 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
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 TypeWrapper< Term >& type, const TypeWrapper< Term >& val ) -> TypeWrapper< Value >
            []( const auto& type, const auto& 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 >
        RegisterBuiltinFunc< TypeWrapper< Value > ( TypeWrapper< Term > ) >( e, MkValue,
            []( const auto& type ) -> TypeWrapper< Value >
            {
                return Value( type.get(), cir.get() );
                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 TypeWrapper< Value >& val ) -> TypeWrapper< Term >
            []( const auto& 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
            []( const auto& val, auto& out )
            {
                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
        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 TypeWrapper< Value >& val ) -> TypeWrapper< LocationId >
            []( const auto& val ) -> TypeWrapper< LocationId >
            {
                return val.get().locationId();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< Value > ) >( e, "IsPoisonValue"_sid,
            []( const TypeWrapper< Value >& val )
            []( const auto& val )
            {
                return val.get().isPoison();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< Value > ) >( e, "IsConstantValue"_sid,
            []( const TypeWrapper< Value >& val )
            []( const auto& val )
            {
                return val.get().isConstant();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< Value > ) >( e, "IsTypeValue"_sid,
            []( const TypeWrapper< Value >& val )
            []( const auto& 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 >
            []( const auto& v, const auto& loc ) -> TypeWrapper< Value >
            {
                Value val( v );
                return val.setLocationId( loc );
            } );

        RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< Value > ) >( e, "ValueToEIR"_sid,
            []( const TypeWrapper< Value >& val ) -> TypeWrapper< Term >
            []( const auto& 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 )
            []( 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 Value& v ) -> TypeWrapper< Value >
            []( 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
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
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
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 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
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();

        if( val.isConstant() || !cir::CanValueBeEagerlyEvaluated( val ) )
        {
            m_lastValue = forward< V >( val );
        m_lastValue = forward< V >( val );
            return;
        }


        if( !context().locationId().invalid() )
        if( !verify::VerifyCompTimeExpr( context(), val ) )
        {
            m_lastValue = PoisonValue();
            return;
        }

            m_lastValue->setLocationId( context().locationId() );
        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 >() )
        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
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 >
    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 >
Changes to bs/parse/rule-helpers.inl.
35
36
37
38
39
40
41


42
43

44
45
46
47
48
49
50
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 ).setLocationId( loc ) );
            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
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 ).setLocationId( loc ) );
                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
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 ).setLocationId( loc ) );
                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
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 ).setLocationId( loc ) );
                    p.pushValue( move( result ) );
                    return true;
                }

                auto result = func( p, move( *leftVal ), move( *rightVal ) );
                p.pushValue( move( result ).setLocationId( loc ) );
                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
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 ).setLocationId( loc ) );
                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
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 ).setLocationId( loc ) );
                    p.pushValue( move( result ) );
                    return true;
                }

                auto result = func( p, move( *leftVal ), move( *rightVal ) );
                p.pushValue( move( result ).setLocationId( loc ) );
                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
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
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() ) ) )
        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
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"
#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" ) );

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
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"
#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 );
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
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"
#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 )
    {
Changes to bs/util/util.h.
74
75
76
77
78
79
80







81
82
83
84
85
86
87
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
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 )
            BuildZ3Op( m_builder, x );

        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 !m_builder.hasCheckFailed();
            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
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, const Value& val )
optional< Z3Val > Builder::setVar( uint32_t index, Z3Val&& v )
{
    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 )
optional< Z3Val > Builder::retrieveGFC( const GhostFuncApplication& gfa, uint32_t bbIndex )
{
    return m_gfcTracker.retrieve( *this, bbIndex, gc );
    return m_gfcTracker.retrieve( *this, bbIndex, gfa );
}

void Builder::setGFC( const cir::GhostCall& gc, Z3Val&& v )
void Builder::setGFC( const GhostFuncApplication& gfa, Z3Val&& v )
{
    return m_gfcTracker.set( *this, gc, move( 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
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, Remapper* remapper = nullptr ) :
            Builder( const sema::Context& c, z3::solver& solver, size_t numVars, Remapper* remapper = nullptr ) :
                m_context( &c ),
                m_solver( &solver ),
                m_remapper( remapper )
                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
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, const Value& val );
            optional< Z3Val > setVar( uint32_t index, Z3Val&& v );
            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 );
            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
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
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
{
    optional< Z3Val > BuildZ3Op( Builder& b, const Call& instr )
    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 >( instr.func() );
        auto func = FromValue< builtins::Func >( *fVal );
        if( !func )
        {
            if( rt == GetValueType< void >() )
            return nullopt;
                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 nullopt;
            return false;

        optional< Z3Val > retExpr;
        if( rt != GetValueType< void >() )
        auto retExpr = BuildZ3ConstantFromType( b, ft.returnType(), format( "r{}", b.newUniqueId() ) );
            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() );
        cb.setLoadMustAssume( false );
        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 index = 0;
        uint32_t argCount = instr.numArgs();

        ForEachInVectorTerm( instr.args(), [&]( auto&& t )
        for( uint32_t argIndex = 0; argIndex < argCount; ++argIndex )
        {
            auto arg = *EIRToValue( t );
            auto zv = b.pop();
            if( !zv )
                return false;

            if( auto zv = BuildZ3ExprFromValue( b, arg ) )
                cb.setVar( index++, move( *zv ) );

            cb.setVar( argCount - argIndex - 1, 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 ) );
            auto paramVal = BuildComputedValue( type, VarAddr( varId++, type, locId ), Load( type, locId ) );

            if( auto zv = BuildZ3ExprFromValue( cb, paramVal ) )
            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 );
                } );
            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 = BuildZ3ExprFromValue( cb, val ) )
            if( auto zv = TryBuildZ3ExprFromValue( cb, val ) )
            {
                DiagnosticsContext dc( instr.func().locationId(), "At this call." );
                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 = BuildZ3ExprFromValue( cb, val ) )
            if( auto zv = TryBuildZ3ExprFromValue( cb, val ) )
                b.assume( zv->expr );
        } );

        if( retExpr )
            b.push( move( *retExpr ) );
        return retExpr;
        return true;
    }
}
Changes to bs/verify/cfg.cpp.
9
10
11
12
13
14
15
16

17
18
19
20
21
22
23
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() )
        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
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
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_remapper )
        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_remapper )
        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
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, Load( make_shared< Instruction >( VarAddr( varId, type ) ), type ) );
                    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.
                    if( auto paramInit = BuildZ3ConstantFromType( m_builder, type, format( "p{}", varId ) ) )
                        m_builder.setVar( varId, move( *paramInit ) );
                    auto paramInit = BuildZ3ConstantFromType( m_builder, type, format( "p{}", varId ) );
                    m_builder.setVar( varId, move( paramInit ) );

                    ++varId;

                    if( auto zv = BuildZ3ExprFromValue( m_builder, paramVal ) )
                    auto zv = BuildZ3ExprFromValue( m_builder, paramVal );
                    {
                        ForEachPredicate( m_builder, type, zv->expr, [&]( auto&& z3expr, auto locId )
                        {
                    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 = BuildZ3ExprFromValue( m_builder, val ) )
                    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
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
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 GFCTracker::getGFIndex( const GhostFuncApplication& gfa )
{
    uint32_t gfIndex = 0;
    auto it = m_ghostFuncIndices.find( gc.func() );
    auto it = m_ghostFuncIndices.find( gfa.func() );
    if( it != m_ghostFuncIndices.end() )
        gfIndex = it->second;
    else
    {
        gfIndex = m_ghostFuncIndices.size();
        m_ghostFuncIndices.emplace( gc.func(), gfIndex );
        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
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 );
        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 )
z3::expr GFCTracker::buildFunctionApplication( Builder& b, uint32_t uid, const GhostFuncDeclCache::GFDecl& gfDecl, const GhostFuncApplication& gfa )
{
    llvm::SmallVector< z3::expr, 8 > z3args;
    z3args.reserve( VecSize( gc.args() ) + 1 );
    z3args.reserve( gfa.args().size() + 1 );
    z3args.emplace_back( GetZ3Context().int_val( uid ) );

    ForEachInVectorTerm( gc.args(), [&]( auto&& t )
    for( auto&& zv : gfa.args() )
    {
        z3args.emplace_back( BuildZ3ExprFromValue( b, *EIRToValue( t ) )->expr );
        z3args.emplace_back( zv.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 );
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
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 );
        gfcs = &m_gfcStorage.set( gfIndex, move( newGFCS ) );
        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 cir::GhostCall& gc,
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, gc );
        b.add( z3::implies( predicate, apply == Coerce( expr, { apply, gfDecl.returnType } ) ) );
        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 = VecSize( gc.args() );
        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 );

        ForEachInVectorTerm( gc.args(), [&]( auto&& t )
        for( auto&& arg : gfa.args() )
        {
            auto arg = *EIRToValue( t );

            auto argConst = BuildZ3ConstantFromType( b, arg.type(), format( "gca{}_{}", argIndex, newUid ) )->expr;
            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 );
            }
            auto comp = argConst == arg.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 } ),
            Coerce( expr, { newApply, gfDecl.returnType, gfa.locationId() } ),
            previousApply
        ) ) ) );
    }

    setCurrentUidForBasicBlock( bbIndex, gfIndex, newUid );
}

optional< Z3Val > GFCTracker::retrieve( Builder& b, uint32_t bbIndex, const cir::GhostCall& gc )
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(), gc.func() );
    auto gfDecl = GhostFuncDeclCache::GetInstance()->getDecl( b.context(), gfa.func() );
    if( !gfDecl )
        return nullopt;

    auto gft = *FromValue< builtins::FuncType >( *EIRToValue( gc.func().type() ) );
    auto gft = *FromValue< builtins::FuncType >( *EIRToValue( gfa.func().type() ) );

    uint32_t gfIndex = getGFIndex( gc );
    uint32_t gfIndex = getGFIndex( gfa );
    auto uid = getCurrentUidForBasicBlock( bbIndex, gfIndex );
    if( uid )
        return Z3Val{ buildFunctionApplication( b, uid, *gfDecl, gc ), *EIRToValue( gft.returnType() ) };
        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, gc ) )
            if( auto predVal = retrieve( b, predIndex, gfa ) )
            {
                if( !lhsExpr )
                    lhsExpr = buildFunctionApplication( b, uid, *gfDecl, gc );
                    lhsExpr = buildFunctionApplication( b, uid, *gfDecl, gfa );

                if( z3args.empty() )
                {
                    auto argCount = VecSize( gc.args() );
                    auto argCount = gfa.args().size();

                    z3args.reserve( argCount + 1 );
                    z3args.emplace_back( c.int_val( uid ) );

                    z3argsVars.resize( argCount );

                    uint32_t argIndex = 0;

                    ForEachInVectorTerm( gc.args(), [&]( auto&& t )
                    for( auto&& arg : gfa.args() )
                    {
                        auto arg = *EIRToValue( t );

                        auto argConst = BuildZ3ConstantFromType( b, arg.type(), format( "gca{}_{}", argIndex, uid ) )->expr;
                        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() ) };
    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 cir::GhostCall& gc, Z3Val&& val )
void GFCTracker::set( Builder& b, const GhostFuncApplication& gfa, Z3Val&& val )
{
    auto gfDecl = GhostFuncDeclCache::GetInstance()->getDecl( b.context(), gc.func() );
    auto gfDecl = GhostFuncDeclCache::GetInstance()->getDecl( b.context(), gfa.func() );
    if( !gfDecl )
        return;

    uint32_t gfIndex = getGFIndex( gc );
    return setForBasicBlock( b, b.currentBBIndex(), gc, gfIndex, *gfDecl, move( val.expr ) );
    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
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 cir::GhostCall& gc );
            void set( Builder& b, const cir::GhostCall& gc, Z3Val&& val );
            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 cir::GhostCall& gc );
            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 cir::GhostCall& gc );
            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 cir::GhostCall& gc,
            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
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
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() );
            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
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;
                cout << "  == Havocing modified storage locations for loop " << header.index() << endl;

            m_cfg->forEachAddressModifiedByLoop( header.index(), [&]( auto&& type, auto&& addr )
            m_cfg->forEachStorageLocationModifiedByLoop( header.index(), [&]( auto&& type, auto&& sloc )
            {
                HavocAddress( m_builder, firstInductionIterationHeaderIndex, type, addr );
                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
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',
    'comptime.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
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
{
    optional< Z3Val > BuildZ3Op( Builder& b, const Phi& instr )
    bool BuildZ3Op( Builder& b, const Phi& instr )
    {
        const auto* remapper = b.remapper();
        if( !remapper )
            return nullopt;
            return false;

        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 ) ) );
                    newVar.expr == Coerce( zv, newVar ) ) );

                return true;
            } );
        } );

        b.setVar( instr.destIndex(), Z3Val( *newVar ) );
        return *newVar;
        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
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 )
    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 = BuildZ3ExprFromValue( m_builder, val );
        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
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 );
    }
    template< typename T >
    optional< Z3Val > LoadFromAddress( Builder& b, const T& )

    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 nullopt;
        return result;
    }

    optional< Z3Val > LoadFromAddress( Builder& b, const cir::Instruction& addr )
    optional< Z3Val > LoadFromBaseAddress( Builder& b, const Address& addr )
    {
        optional< Z3Val > val;
        return visit( [&]( auto&& instr )

        switch( addr.origin() )
        {
            return LoadFromAddress( b, instr );
        }, addr.content() );
    }

            case Address::Origin::Stack:
                val = LoadVar( b, addr.path().front() );
                break;

            case Address::Origin::Heap:
                // TODO
                break;
        }
    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 val;
    }

    optional< Z3Val > LoadFromBaseAddress( Builder& b, const Address& addr, const Term& type )
            return nullopt;

    {
        return b.setVar( va.varIndex(), move( *v ) );
    }

    optional< Z3Val > LoadFromAddress( Builder& b, const cir::TempAddr& ta )
    {
        auto zv = b.retrieveVar( ta.tempIndex() );
        optional< Z3Val > val;

        switch( addr.origin() )
        {
            case Address::Origin::Stack:
                val = LoadVar( b, addr.path().front(), type );
                break;

            case Address::Origin::Heap:
                // TODO
                break;
        }

        if( zv )
            return zv;

        return val;
    }
        return b.setVar( ta.tempIndex(), ta.initValue() );
    }

    optional< Z3Val > LoadFromAddress( Builder& b, const cir::Select& s )
    {
        auto val = LoadFromAddress( b, *s.baseAddr() );

    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;
            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 ) };
    }
            // 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 cir::GhostCall& gc )
    optional< Z3Val > LoadFromAddress( Builder& b, const GhostFuncApplication& gfa )
    {
        return b.retrieveGFC( gc );
        return b.retrieveGFC( gfa );
    }

    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.
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
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 )
                if( --index > 0 )
                {
                    auto newElem = ModifyAggregate( b, Z3Val{ move( elemExpr ), *EIRToValue( elemType ) }, path, --index, move( valToStore ) );
                    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 );
    }

    template< typename T >
    void StoreToAddress( Builder& b, const T&, Z3Val&& val )
    void StoreToBaseAddress( Builder& b, const Address& addr, Z3Val&& val )
    {}

    {
    void StoreToAddress( Builder& b, const cir::Instruction& addr, Z3Val&& val )
    {
        visit( [&]( auto&& instr )
        switch( addr.origin() )
        {
            StoreToAddress( b, instr, move( val ) );
        }, addr.content() );
            case Address::Origin::Stack:
                b.setVar( addr.path().front(), move( val ) );
    }

                break;
    void StoreToAddress( Builder& b, const cir::VarAddr& va, Z3Val&& val )
    {

        b.setVar( va.varIndex(), move( val ) );
    }

            case Address::Origin::Heap:
                // TODO
                break;
        }
    }
    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 )
    void StoreToAddress( Builder& b, const Address& addr, Z3Val&& val )
    {
        SelectPath path;
        path.emplace_back( s.memberIndex() );

        if( addr.path().size() == 1 )
        auto pBaseAddr = s.baseAddr();
        for(;;)
        {
            const auto* pSel = get_if< cir::Select >( &pBaseAddr->content() );
            StoreToBaseAddress( b, addr, move( val ) );
            if( !pSel )
                break;

            return;
        }
            path.emplace_back( pSel->memberIndex() );
            pBaseAddr = pSel->baseAddr();
        }


        // If the address contains a path (a series of nested Select cir instructions),
        // 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 = LoadFromAddress( b, *pBaseAddr );
        auto aggregate = LoadFromBaseAddress( b, addr );
        if( !aggregate )
            return;

        if( auto newAggregateToStore = ModifyAggregate( b, *aggregate, path, 0, move( val ) ) )
            StoreToAddress( b, *pBaseAddr, Z3Val{ *newAggregateToStore, aggregate->type } );
        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 cir::GhostCall& gc, Z3Val&& val )
    void StoreToAddress( Builder& b, const GhostFuncApplication& gfa, Z3Val&& val )
    {
        b.setGFC( gc, move( 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 HavocAddress( Builder& b, uint32_t bbIndex, const Term& type, const cir::Instruction& addr )
    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 > )
            {
        if( !valToStore )
                StoreToAddress( b, x, move( valToStore ) );
            return;

        StoreToAddress( b, addr, 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
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
    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 );
    // 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 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 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 HavocAddress( Builder& b, uint32_t bbIndex, const Term& type, const cir::Instruction& 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
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." );
        // 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 );
        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 )
                {
            // 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 );
                } );
                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 ) )
            if( auto zv = TryBuildZ3ExprFromValue( 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() );
                bool succ = m_builder.checkAssertion( zv->expr, cond.locationId() );
                }
                else
                    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
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 = BuildZ3ExprFromValue( m_builder, t.cond() ) )
        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
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_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 );
    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 sema::Context& c, const Value& typeVal )
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
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( auto result = BuildZ3Op( b, *val.cir() ) )
                    return result->expr;
                if( !BuildZ3Op( b, *val.cir() ) )
                    return GetZ3Context().constant( format( "ml{}", b.newUniqueId() ).c_str(), m_memLocSort );

                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 )
optional< TypeInfo > TypeCache::createTypeInfo( const sema::Context& c, const Term& type )
{
    auto typeVal = *EIRToValue( type );

    auto tinfo = CreateTypeInfo( c, typeVal );
    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( c, *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
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 sema::Context& c, const Value& typeVal );
            optional< TypeInfo > CreateTypeInfo( const sema::Context& c, const Term& type );
            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
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
{
    optional< Z3Val > BuildZ3ValFromConstant( Builder& b, const Value& val )
    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() ) };
        return Z3Val { move( zexpr ), *EIRToValue( val.type() ), val.locationId() };
    }

    optional< Z3Val > BuildZ3ConstantFromType( Builder& b, const Value& type, const string& name )
    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 };
        return Z3Val { GetZ3Context().constant( name.c_str(), *tinfo->sort ), type, type.locationId() };
    }

    optional< Z3Val > BuildZ3ConstantFromType( Builder& b, const Term& type, const string& name )
    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 ), *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
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 >
    optional< Z3Val > BuildZ3UnaryExpr( Builder& b, const I& instr, F&& func )
    bool BuildZ3UnaryExpr( Builder& b, const I& instr, F&& func )
    {
        auto operand = BuildZ3ExprFromValue( b, instr.operand() );
        auto operand = b.pop();
        if( !operand )
            return nullopt;
            return false;

        return Z3Val{ func( operand->expr ), operand->type };
        b.push( Z3Val{ func( operand->expr ), operand->type, instr.locationId() } );
        return true;
    }

    template< typename I, typename F >
    optional< Z3Val > BuildZ3BinExpr( Builder& b, const I& instr, F&& func )
    bool BuildZ3BinExpr( Builder& b, const I& instr, F&& func )
    {
        auto lhs = BuildZ3ExprFromValue( b, instr.lhs() );
        if( !lhs )
            return nullopt;
        auto rhs = b.pop();
        if( !rhs )
            return false;

        auto rhs = BuildZ3ExprFromValue( b, instr.rhs() );
        if( !rhs )
            return nullopt;
        auto lhs = b.pop();
        if( !lhs )
            return false;

        if( lhs->expr.get_sort().sort_kind() == rhs->expr.get_sort().sort_kind() )
        {
            return Z3Val{ func( lhs->expr, rhs->expr, lhs->type ), lhs->type };
            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() );
            return Z3Val{ func( lhs->expr, GetAsBitVec( *rhs ), lhs->type ), lhs->type };
            b.push( Z3Val{ func( lhs->expr, GetAsBitVec( *rhs ), lhs->type ), lhs->type, instr.locationId() } );
            return true;
        }
        else
        {
            assert( lhs->expr.is_int() );
            return Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ), lhs->type };
            b.push( Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ), lhs->type, instr.locationId() } );
            return true;
        }

        return nullopt;
        return false;
    }

    template< typename I, typename F >
    optional< Z3Val > BuildZ3BinBitwiseExpr( Builder& b, const I& instr, F&& func )
    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( instr.lhs().type() == GetValueType< BigInt >() )
        if( ValueToEIR( lhs->type ) == GetValueType< BigInt >() )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( instr.lhs().locationId(), "verifier error: bitwise operands can't be ct_int." );
            return nullopt;
            DiagnosticsManager::GetInstance().emitErrorMessage( 0, "verifier error: bitwise operands can't be ct_int." );
            return false;
        }

        if( instr.rhs().type() == GetValueType< BigInt >() )
        if( ValueToEIR( rhs->type ) == GetValueType< BigInt >() )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( instr.rhs().locationId(), "verifier error: bitwise operands can't be ct_int." );
            return nullopt;
            DiagnosticsManager::GetInstance().emitErrorMessage( 0, "verifier error: bitwise operands can't be ct_int." );
            return false;
        }

        auto lhs = BuildZ3ExprFromValue( b, instr.lhs() );
        if( !lhs )
        if( lhs->expr.get_sort().sort_kind() == rhs->expr.get_sort().sort_kind() )
            return nullopt;

        {
        auto rhs = BuildZ3ExprFromValue( b, instr.rhs() );
        if( !rhs )
            return nullopt;

            b.push( Z3Val{ func( lhs->expr, rhs->expr, lhs->type ), lhs->type, instr.locationId() } );
            return true;
        }
        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 };
            b.push( Z3Val{ func( lhs->expr, GetAsBitVec( *rhs ), lhs->type ), lhs->type, instr.locationId() } );
            return true;
        }
        else
        {
            assert( lhs->expr.is_int() );
            return Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ), lhs->type };
            b.push( Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ), lhs->type, instr.locationId() } );
            return true;
        }

        return nullopt;
        return false;
    }

    template< typename I, typename F >
    optional< Z3Val > BuildZ3BinBoolExpr( Builder& b, const I& instr, F&& func )
    bool BuildZ3BinBoolExpr( Builder& b, const I& instr, F&& func )
    {
        auto lhs = BuildZ3ExprFromValue( b, instr.lhs() );
        if( !lhs )
            return nullopt;
        auto rhs = b.pop();
        if( !rhs )
            return false;

        auto rhs = BuildZ3ExprFromValue( b, instr.rhs() );
        if( !rhs )
            return nullopt;
        auto lhs = b.pop();
        if( !lhs )
            return false;

        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 >() ) };
            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() );
            return Z3Val{ func( lhs->expr, GetAsBitVec( *rhs ), lhs->type ), *EIRToValue( GetValueType< bool >() ) };
            b.push( Z3Val{ func( lhs->expr, GetAsBitVec( *rhs ), lhs->type ), *EIRToValue( GetValueType< bool >() ), instr.locationId() } );
            return true;
        }
        else
        {
            assert( lhs->expr.is_int() );
            return Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ), *EIRToValue( GetValueType< bool >() ) };
            b.push( Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ), *EIRToValue( GetValueType< bool >() ), instr.locationId() } );
            return true;
        }

        return nullopt;
        return false;
    }

    template< typename I, typename F >
    optional< Z3Val > BuildZ3BinLogicExpr( Builder& b, const I& instr, F&& func )
    bool BuildZ3BinLogicExpr( Builder& b, const I& instr, F&& func )
    {
        auto lhs = BuildZ3ExprFromValue( b, instr.lhs() );
        if( !lhs )
            return nullopt;
        auto rhs = b.pop();
        if( !rhs )
            return false;

        auto rhs = BuildZ3ExprFromValue( b, instr.rhs() );
        if( !rhs )
            return nullopt;
        auto lhs = b.pop();
        if( !lhs )
            return false;

        if( !lhs->expr.is_bool() )
            return nullopt;
            return false;

        if( !rhs->expr.is_bool() )
            return nullopt;
            return false;

        return Z3Val{ func( lhs->expr, rhs->expr, lhs->type ), *EIRToValue( GetValueType< bool >() ) };
        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 >
    optional< Z3Val > BuildZ3Op( Builder& b, const T& instr )
    bool BuildZ3Op( Builder& b, const T& instr )
    {
        return nullopt;
        return false;
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Load& instr )
    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 >() )
        auto zv = LoadFromAddress( b, *instr.addr() );
            zv = LoadFromAddress( b, *gfa );
        else if( auto symAddr = b.pop< Z3Val >() )
            zv = BuildZ3ConstantFromType( b, instr.type(), format( "val{}", b.newUniqueId() ) );

        if( !zv )
            return nullopt;
            return false;

        ForEachPredicate( b, instr.type(), zv->expr, [&]( auto&& z3expr, auto locId )
        {
            if( b.mustLoadAssume() )
                b.assume( z3expr );
        if( b.mustLoadAssume() )
            ForEachPredicate( b, instr.type(), zv->expr, [&]( auto&& z3expr, auto locId )
        } );

        return zv;
            {
                b.assume( z3expr );
            } );

        zv->loc = instr.locationId();
        b.push( move( *zv ) );
        return true;
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Store& instr )
    bool BuildZ3Op( Builder& b, const Store& instr )
    {
        auto zv = BuildZ3ExprFromValue( b, instr.val() );
        auto zv = b.pop();
        if( !zv )
            return nullopt;
            return false;

        ForEachPredicate( b, instr.type(), zv->expr, [&]( auto&& z3expr, auto locId )
        {
            if( !instr.destLocId().invalid() && !instr.val().locationId().invalid() )
            if( !instr.srcLocId().invalid() && !instr.destLocId().invalid() )
            {
                DiagnosticsContext dc( instr.destLocId(), "...to this." );
                DiagnosticsContext dc2( instr.val().locationId(), "When assigning 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, *instr.addr(), move( *zv ) );
        return nullopt;
            StoreToAddress( b, *gfa, move( *zv ) );
        else
            b.pop();

        return true;
    }

    // Implemented in call.cpp
    extern optional< Z3Val > BuildZ3Op( Builder& b, const Call& instr );
    extern bool BuildZ3Op( Builder& b, const Call& instr );

    optional< Z3Val > BuildZ3Op( Builder& b, const CreateTemporary& instr )
    bool BuildZ3Op( Builder& b, const CreateTemporary& instr )
    {
        auto zv = b.pop();
        if( !zv )
            return false;

        b.setVar( instr.index(), instr.value() );
        return nullopt;
        b.setVar( instr.index(), move( *zv ) );
        return true;
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const GetTemporary& instr )
    bool BuildZ3Op( Builder& b, const GetTemporary& instr )
    {
        auto zv = b.retrieveVar( instr.index() );
        if( zv )
        {
            b.push( move( *zv ) );
            return zv;

        return BuildZ3ConstantFromType( b, instr.type(), format( "v{}", instr.index() ) );
            return true;
        }

        auto result = BuildZ3ConstantFromType( b, instr.type(), format( "v{}", instr.index() ) );
        b.push( move( result ) );
        return true;
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const AllocVar& instr )
    bool BuildZ3Op( Builder& b, const AllocVar& instr )
    {
        auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), ValueToEIR( instr.type() ) );
        if( !tinfo )
            return nullopt;
            tinfo = TypeCache::GetInstance()->getUninterpretedTypeInfo();

        b.setVar( instr.index(), Z3Val{ tinfo->undefined( b ), instr.type() } );
        return nullopt;
        b.setVar( instr.index(), Z3Val{ tinfo->undefined( b ), instr.type(), instr.locationId() } );
        return true;
    }

    // Implemented in phi.cpp
    optional< Z3Val > BuildZ3Op( Builder& b, const Phi& instr );
    bool BuildZ3Op( Builder& b, const Phi& instr );

    // TODO: LoadConstStr. Build a z3 str value.

    optional< Z3Val > BuildZ3Op( Builder& b, const Not& instr )
    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;
        } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const And& instr )
    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;
        } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Or& instr )
    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;
        } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Xor& instr )
    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;
        } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Implies& instr )
    bool 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 )
    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 );
        } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const LShr& instr )
    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 );
        } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const AShr& instr )
    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 );
        } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Add& instr )
    bool 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 )
    bool 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 )
    bool 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 )
    bool 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 )
    bool 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 )
    bool 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 )
    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 );
        } );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Eq& instr )
    bool 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 )
    bool 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 )
    bool 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 )
    bool 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 )
    bool 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 )
    bool 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 )
    bool 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 )
    bool 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 )
    bool 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 )
    bool 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 )
    bool 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;
        auto cond = b.pop();
        if( !cond )
            return false;

        b.checkAssertion( cond->expr, cond->loc );
        return true;
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Placeholder& instr )
    bool 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() ) );
            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;
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const PHOverride& instr )
    bool BuildZ3Op( Builder& b, const PHOverrideSet& instr )
    {
        const auto* savedExpr = b.retrievePlaceholder( instr.name() );
        auto phExpr = b.pop();
        if( !phExpr )
            return false;

        auto newExpr = BuildZ3ExprFromValue( b, instr.phVal() );
        if( newExpr )
            b.setPlaceholder( instr.name(), newExpr->expr );
        else
        b.setPlaceholder( instr.name(), phExpr->expr );
        return true;
            b.unsetPlaceholder( instr.name() );

        auto result = BuildZ3ExprFromValue( b, instr.val() );

    }

    bool BuildZ3Op( Builder& b, const PHOverrideClear& instr )
    {
        if( savedExpr )
            b.setPlaceholder( instr.name(), *savedExpr );
        else
            b.unsetPlaceholder( instr.name() );
        b.unsetPlaceholder( instr.name() );

        return result;
        return true;
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const VarAddr& instr )
    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 Z3Val
        {
            GetZ3Context().int_val( instr.varIndex() ).unit(),
        return true;
    }

    bool BuildZ3Op( Builder& b, const VarAddr& instr )
    {
        b.push( Address( Address::Origin::Stack, instr.varIndex(), instr.locationId() ) );
            *EIRToValue( GetValueType< builtins::MemLoc >() )
        };
        return true;
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const TempAddr& instr )
    bool BuildZ3Op( Builder& b, const TempAddr& instr )
    {
        auto initVal = b.pop();
        if( !initVal )
        return Z3Val
        {
            GetZ3Context().int_val( instr.tempIndex() ).unit(),
            return false;

        b.push( Address( Address::Origin::Stack, instr.tempIndex(), instr.locationId() ) );
        return !!b.setVar( instr.tempIndex(), move( *initVal ) );
            *EIRToValue( GetValueType< builtins::MemLoc >() )
        };
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Select& instr )
    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 baseLoc = BuildZ3Op( b, *instr.baseAddr() );
        if( !baseLoc )
            return nullopt;

        auto symBaseLoc = b.pop();
        if( !symBaseLoc )
            return false;

        return Z3Val
        b.push( Z3Val
        {
            z3::concat( baseLoc->expr, GetZ3Context().int_val( instr.memberIndex() ).unit() ),
            *EIRToValue( GetValueType< builtins::MemLoc >() )
        };
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const cir::Instruction& instr )
            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() );
    }

    optional< Z3Val > BuildZ3ExprFromValue( Builder& b, const Value& val )
    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 BuildZ3ValFromConstant( b, val );
            return TryBuildZ3ValFromConstant( b, val );

        if( auto expr = BuildZ3Op( b, *val.cir() )  )
            return expr;
        if( BuildZ3Op( b, *val.cir() ) )
            return b.pop();

        return BuildZ3ConstantFromType( b, val.type(), format( "val{}", b.newUniqueId() ) );
        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
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 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 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
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 ];
}

Z3Val& VarTracker::setForBasicBlock( Builder& b, uint32_t bbIndex, uint32_t index, Z3Val&& val )
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 );
        vs = &m_varStorage.set( index, move( newVS ) );
        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() ) );
    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
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() ) );
                    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 )
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
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 );
            const Z3Val& set( Builder& b, uint32_t index, Z3Val&& val );
            optional< 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 );
            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
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 );
    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 "remapper.h"
#include "ghostfunc.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
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 )
        result = MkValue( ValueToEIR( WrapValue( bool ) ), MkInstr( InstrOpCodeAnd, 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
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 )
    var cond = MkValue( ValueToEIR( WrapValue( bool ) ), MkInstr( InstrOpCodeEq, lhsExpr, rhsExpr ) )
    AppendBasicBlockInstr( bb, MkInstr( InstrOpCodeEq, argLoc ) )
    cond = SetValueLocation( cond, argLoc )
    AppendBasicBlockInstr( bb, MkInstr( InstrOpCodeAssert, cond ) )
    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
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

    Instruction refCIR
    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 argDeref = MkValue( referedType, MkInstr( InstrOpCodeLoad, refCIR, referedType ) )
    SetValueLocation( argDeref, GetValueLocation( arg ) )
    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 )
        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 ) )
        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
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
    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
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
  %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
  %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
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
    var v1 = a ^ b
    var v2 = c ^ d

    c ^ 1337
    1337 ^ c
    var v3 = c ^ 1337
    var v4 = 1337 ^ c

    ~d
    var v5 = ~d

    a | b
    a & b
    var v6 = a | b
    var v7 = a & b

    a << 4
    a >> 4
    c << 4
    c >> 4
    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
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
  %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
  %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
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

    a == b
    a != b
    a > b
    a < b
    a >= b
    a <= b
    c > d
    c < d
    c >= d
    c <= d
    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
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
  %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
  %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
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
  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 %9, label %10
  br i1 %8, label %10, label %11

9:                                                ; preds = %6
  call void @puts([7 x i8]* @.str0)
  br label %11
9:                                                ; preds = %2
  call void @puts([4 x i8]* @.str3)
  br label %13

10:                                               ; preds = %6
  call void @puts([5 x i8]* @.str2)
  br label %11
  call void @puts([7 x i8]* @.str0)
  br label %12

11:                                               ; preds = %10, %9
  br label %13
11:                                               ; preds = %6
  call void @puts([5 x i8]* @.str2)
  br label %12

12:                                               ; preds = %2
12:                                               ; preds = %11, %10
  call void @puts([4 x i8]* @.str3)
  br label %13

13:                                               ; preds = %12, %11
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
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
  br i1 %5, label %6, label %8

6:                                                ; preds = %2
  %7 = load i1, i1* %4, align 1
  br label %8
6:                                                ; preds = %8, %2
  %7 = phi i1 [ true, %2 ], [ %9, %8 ]
  ret i1 %7

8:                                                ; preds = %6, %2
  %9 = phi i1 [ true, %2 ], [ %7, %6 ]
  ret i1 %9
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
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
4:                                                ; preds = %17, %10, %0
  %5 = load i32, i32* %1, align 4
  %6 = icmp ult i32 %5, 10
  br i1 %6, label %7, label %23
  br i1 %6, label %7, label %9

7:                                                ; preds = %4
  %8 = load i1, i1* %2, align 1
  br i1 %8, label %9, label %10
  br i1 %8, label %10, label %11

9:                                                ; preds = %7
9:                                                ; preds = %4
  ret void

10:                                               ; preds = %7
  br label %4

10:                                               ; preds = %7
  br label %11
11:                                               ; preds = %7
  br label %12

11:                                               ; preds = %17, %10
  %12 = load i32, i32* %1, align 4
  %13 = icmp ugt i32 %12, 50
  br i1 %13, label %14, label %20
12:                                               ; preds = %21, %11
  %13 = load i32, i32* %1, align 4
  %14 = icmp ugt i32 %13, 50
  br i1 %14, label %15, label %17

14:                                               ; preds = %11
  %15 = load i1, i1* %3, align 1
15:                                               ; preds = %12
  %16 = load i1, i1* %3, align 1
  br i1 %15, label %16, label %17

16:                                               ; preds = %14
  br label %20
  br i1 %16, label %20, label %21

17:                                               ; preds = %14
17:                                               ; preds = %20, %12
  %18 = load i32, i32* %1, align 4
  %19 = add i32 %18, 33
  %19 = sub i32 %18, 2
  store i32 %19, i32* %1, align 4
  br label %11
  br label %4

20:                                               ; preds = %16, %11
20:                                               ; preds = %15
  %21 = load i32, i32* %1, align 4
  %22 = sub i32 %21, 2
  store i32 %22, i32* %1, align 4
  br label %4
  br label %17

23:                                               ; preds = %4
  ret void
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

1







2

3

4

-
-
-
-
-
-
-

-
+
-
+
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.
      | 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









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

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.
      |                  Error: this condition may not be met.
     ...
   21 | testFunc2( 0 )
      | ~~~~~~~~~~~~~~
      | At this compilation-time call.
      | At this call.
Changes to tests/g0/verification/misc/c-fail-mandelbrot-bad-3.txt.
1
2
3
4

5
6
7
8

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.
      |                        Error: this condition may not be met.
     ...
   17 | using minU = IntToFixedWidthDec( -20000 )   // Intentional error
      |              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |             At this compilation-time call.
      |                     At this call.
Changes to tests/g0/verification/misc/c-fail-mandelbrot-bad-4.txt.
1
2
3
4

5
6
7
8

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.
      |               Error: this condition may not be met.
     ...
   18 | using maxU = IntToFixedWidthDec( 100000 )   // Intentional error
      |              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |             At this compilation-time call.
      |                     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






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

-
-
-
-
+
+
+
+

-
-
+
+




-
-
-
-
-
-
+
+
+
+
+
+





-
+


=== 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)))
(=> 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_63 5))
(= e1_3 (and b1 (not (distinct v1_1_62 5))))
(=> 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_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)))
(=> 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_69 1))
(=> 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














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

-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+




-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+





-
-
-
+
+
+


=== 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)))
(=> 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_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))))
(=> 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_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)))
(=> 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 (= v0_1_98 2))
(=> b1 (= v1_1_99 1))
check_unsat (not (distinct v0_1_98 v1_1_99))
(=> 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
















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

-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+


-
-
-
-
-
-
-
+
+
+
+
+
+
+


-
-
-
-
-
+
+
+
+
+



=== 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)))
(=> 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_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))))
(=> 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_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)))
(=> 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 compilation-time expression ===
(=> b1 (= v0_1_93 2))
(=> b1 (= v1_1_94 1))
check_unsat (not (distinct v0_1_93 v1_1_94))

=== 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


























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

-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+


-
-
-
-
-
-
-
+
+
+
+
+
+
+


-
-

-
-
-
+
+
+
+
-
-
-
-
+
+
+


-
-
-
-
+
+
+
+



=== 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)))
(=> 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_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))))
(=> 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_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)))
(=> 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 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 (= 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))
(=> 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))
check_unsat (not (distinct v0_1_146 v1_1_145))
assume (> r144 0)
check_unsat (not (distinct r144 0))
=== End function verification trace ===

=== Checking compilation-time expression ===
(=> b1 (= v0_1_99 5))
check_unsat (not (distinct v0_1_99 0))

=== 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


















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

-
-
-
-
-
-
-
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+





-
-
-
-
-
-
+
+
+
+
+
+





-
-
-
+
+
+


=== 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))))
(=> 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_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))))))
(=> 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_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))))
(=> 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 (= v0_1_85 2))
(=> b1 (= v1_1_86 1))
check_unsat (not (distinct v0_1_85 v1_1_86))
(=> 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
















1


2
3
4
5






6
7
8
9
10
11
12
13
14
15
16


17
18
19
20
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

-
-
+
+


-
-
-
-
-
-
+
+
+
+
+
+





-
-
+
+


=== 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 (= v0_1_113 p0))
(=> b1 (= v1_1_114 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))))
(=> 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 (= v0_1_66 1))
(=> b1 (= v1_1_67 2))
(=> 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
































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 ===

=== 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 (= 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_67 u66))
(=> b1 (= v5_1_68 #x0000abcd))
(let ((a!1 (- (bv2int (bvor ((_ int2bv 32) v0_1_59) ((_ int2bv 32) v1_1_60)))
(=> 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_59) ((_ int2bv 32) v1_1_60))
(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_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)))
                (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_69) ((_ int2bv 32) v1_1_60))
(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_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)))
                (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_71) ((_ int2bv 32) v1_1_60))
(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_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)
                (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_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)
                (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_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)))
                (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 (= v0_1_80 0))
(=> b1 (= v1_1_81 0))
(=> b1 (= v2_1_82 #x00000000))
(=> b1 (= v3_1_83 #x00000000))
(=> 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




















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

-
-
-
-
+
+
+
+




-
+



-
-
-
+
+
+

-
-
-
-
+
+
+
+


-
-
-
+
+
+




-
-
-
-
-
+
+
+
+
+



-
+


-
-
-
+
+
+

-
-
-
+
+
+

-
+

-
-
-
-
+
+
+
+

-
+



-
-
-
+
+
+


-
+


-
-
-
+
+
+

-
-
-
+
+
+

-
+

-
-
-
-
+
+
+
+

-
+



-
-
-
+
+
+


-
+


-
-
-
+
+
+

-
-
-
+
+
+

-
+

-
-
-
-
+
+
+
+

-
+



-
-
-
+
+
+




-
+





-
-
+
+




-
+


-
-
-
+
+
+

-
-
-
+
+
+

-
+


-
-
-
+
+
+

-
-
-
+
+
+

-
+

-
-
-
-
+
+
+
+

-
+



-
-
-
+
+
+


-
+


-
-
-
+
+
+
+
+
-
-
-
-
+
+

-
+

-
-
-
-
+
+
+
+

-
+



-
-
-
+
+
+


-
+


-
-
-
+
+
+

-
-
-
+
+
+

-
+

-
-
-
-
+
+
+
+

-
+



-
-
-
+
+
+




-
+



-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+

-
+

-
-
-
-
-
+
+
+
+
+


-
-
-
+
+
+






-
-
+
+


=== 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))
(=> 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|
         64
         118
         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))
(=> 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|
                     65
                     gca0_65
                     gca1_65)
                   (ite (and (= gca0_65 5) (= gca1_65 v1_2_67))
                     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|
                          64
                          gca0_65
                          gca1_65)))))
                          118
                          gca0_121
                          gca1_121)))))
       (=> 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)))
(=> 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_74 Int) (gca1_74 Int))
(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|
              74
              gca0_74
              gca1_74)
              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|
              65
              gca0_74
              gca1_74)))
              121
              gca0_128
              gca1_128)))
     :weight 0))
(forall ((gca0_75 Int) (gca1_75 Int))
(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|
                     75
                     gca0_75
                     gca1_75)
                   (ite (and (= gca0_75 1) (= gca1_75 2))
                     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|
                          74
                          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|
                          74
                          gca0_75
                          gca1_75)))))
                          128
                          gca0_129
                          gca1_129)))))
       (=> b3 a!1))
     :weight 0))
(forall ((gca0_73 Int) (gca1_73 Int))
(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|
              73
              gca0_73
              gca1_73)
              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|
              75
              gca0_73
              gca1_73)))
              129
              gca0_127
              gca1_127)))
     :weight 0))
(forall ((gca0_76 Int) (gca1_76 Int))
(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|
                     76
                     gca0_76
                     gca1_76)
                   (ite (and (= gca0_76 1) (= gca1_76 2))
                     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|
                          73
                          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|
                          73
                          gca0_76
                          gca1_76)))))
                          127
                          gca0_130
                          gca1_130)))))
       (=> b4 a!1))
     :weight 0))
(forall ((gca0_72 Int) (gca1_72 Int))
(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|
              72
              gca0_72
              gca1_72)
              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|
              76
              gca0_72
              gca1_72)))
              130
              gca0_126
              gca1_126)))
     :weight 0))
(forall ((gca0_77 Int) (gca1_77 Int))
(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|
                     77
                     gca0_77
                     gca1_77)
                   (ite (and (= gca0_77 1) (= gca1_77 2))
                     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|
                          72
                          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|
                          72
                          gca0_77
                          gca1_77)))))
                          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|
               72
               126
               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))))
(=> 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_80 Int) (gca1_80 Int))
(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|
              80
              gca0_80
              gca1_80)
              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|
              75
              gca0_80
              gca1_80)))
              129
              gca0_134
              gca1_134)))
     :weight 0))
(forall ((gca0_81 Int) (gca1_81 Int))
(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|
              81
              gca0_81
              gca1_81)
              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|
              77
              gca0_81
              gca1_81)))
              131
              gca0_135
              gca1_135)))
     :weight 0))
(forall ((gca0_82 Int) (gca1_82 Int))
(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|
                     82
                     gca0_82
                     gca1_82)
                   (ite (and (= gca0_82 1) (= gca1_82 2))
                     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|
                          81
                          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|
                          81
                          gca0_82
                          gca1_82)))))
                          135
                          gca0_136
                          gca1_136)))))
       (=> b6 a!1))
     :weight 0))
(forall ((gca0_80 Int) (gca1_80 Int))
(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|
              80
              gca0_80
              gca1_80)
              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
            (|_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)))
              gca0_134
              gca1_134)))
     :weight 0))
(forall ((gca0_83 Int) (gca1_83 Int))
(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|
                     83
                     gca0_83
                     gca1_83)
                   (ite (and (= gca0_83 1) (= gca1_83 2))
                     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|
                          80
                          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|
                          80
                          gca0_83
                          gca1_83)))))
                          134
                          gca0_137
                          gca1_137)))))
       (=> b7 a!1))
     :weight 0))
(forall ((gca0_79 Int) (gca1_79 Int))
(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|
              79
              gca0_79
              gca1_79)
              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|
              83
              gca0_79
              gca1_79)))
              137
              gca0_133
              gca1_133)))
     :weight 0))
(forall ((gca0_84 Int) (gca1_84 Int))
(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|
                     84
                     gca0_84
                     gca1_84)
                   (ite (and (= gca0_84 1) (= gca1_84 2))
                     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|
                          79
                          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|
                          79
                          gca0_84
                          gca1_84)))))
                          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|
               79
               133
               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))
(=> 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|
               84
               138
               5
               v1_8_96)
               v1_8_150)
             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))
(=> 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|
               84
               v2_8_100
               v1_8_96)
               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 (= v0_1_102 1))
(=> b1 (= v1_1_103 2))
(=> 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


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

-
-
-
-
-
+
+
+
+
+

-
-
-
-
+
+
+
+


-
-
-
-
-
-
-
+
+
+
+
+
+
+





=== 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))
(=> 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_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)))
(=> 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 (= 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))
(=> 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


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

-
-
-
-
-
+
+
+
+
+

-
-
-
-
+
+
+
+


-
-
-
-
-
-
-
+
+
+
+
+
+
+





=== 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)))
(=> 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_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))
(=> 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 (= 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))
(=> 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 ===