Goose  Check-in [8ddd71f9b2]

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

Overview
Comment:References refactor: references are now values all the way to the llr, where a new "CalcAddress" instruction represents a conversion from a logical address (location + path) into an actual runtime or compilation time address. This is in preparation to allow references to be stored in variables or passed as parameters. (It just took 4.5 months to finish this... Refactoring just sucks)
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 8ddd71f9b2af916c582a4c15856171c749584278e75b30661bfe75769afa97a3
User & Date: achavasse 2020-12-18 01:29:15.640
Original Comment: References refactor: references are now values all the way to the llr, where a new "CalcAddress" instruction represents a conversion from a logical address (location + path) into an actual runtimle or compilation time address. This is in preparation to allow references to be stored in variables or passed as parameters. (It just took 4.5 months to finish this... Refactoring just sucks)
Context
2020-12-26
14:59
Build fix check-in: c8058eaaf9 user: achavasse tags: trunk
2020-12-18
01:29
References refactor: references are now values all the way to the llr, where a new "CalcAddress" instruction represents a conversion from a logical address (location + path) into an actual runtime or compilation time address. This is in preparation to allow references to be stored in variables or passed as parameters. (It just took 4.5 months to finish this... Refactoring just sucks) check-in: 8ddd71f9b2 user: achavasse tags: trunk
2020-08-02
19:26
Execute: use a proper call stack so that we'll be able later on to reference values living on the caller's stack to be able to pass references around at compilation time. check-in: 151e3b4d8c user: achavasse tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to bs/builtins/operators/assignment.cpp.
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
            if( !cb )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( 0, "assignments are not allowed here." );
                return PoisonValue();
            }

            if( auto bb = cb->cfg()->currentBB() )
                bb->emplace_back( Store( ref.address(), rhs ) );

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

        BuildParseRule( e, "="_sid,
            RightAssInfixOp( "operator_assign"_sid, precedence::AssignmentOp,







|







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
            if( !cb )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( 0, "assignments are not allowed here." );
                return PoisonValue();
            }

            if( auto bb = cb->cfg()->currentBB() )
                bb->emplace_back( Store( lhs, rhs ) );

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

        BuildParseRule( e, "="_sid,
            RightAssInfixOp( "operator_assign"_sid, precedence::AssignmentOp,
Changes to bs/builtins/operators/dot.cpp.
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
                        return PoisonValue();
                    }

                    auto tempIndex = cfg->getNewTemporaryIndex();
                    auto loc = Location::CreateSpanningLocation( lhs.locationId(), rhs.locationId() );

                    ReferenceType rt( GetTupleElementType( lhs, index ), TSID( temp ) );
                    auto addr = Address( TemporaryAddress( tempIndex, lhs ) );
                    addr.appendToPath( index );

                    return BuildComputedValue( ValueToIRExpr( ToValue( rt ) ),
                        llr::Load( move( addr ), rt.type() ) )
                        .setLocationId( loc );
                } ),

                // dot operator for a non-temporary tuple (contained in a local var):
                // returns a mutable reference to the specified member.
                ForTypes< CustomPattern< LocalVar, LocalVar::Pattern< TuplePattern > >,
                    CustomPattern< IntegerType, IntegerType::PatternUnsigned32 > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value







|


<
|
<







42
43
44
45
46
47
48
49
50
51

52

53
54
55
56
57
58
59
                        return PoisonValue();
                    }

                    auto tempIndex = cfg->getNewTemporaryIndex();
                    auto loc = Location::CreateSpanningLocation( lhs.locationId(), rhs.locationId() );

                    ReferenceType rt( GetTupleElementType( lhs, index ), TSID( temp ) );
                    auto addr = Reference( rt, TemporaryBaseAddr( tempIndex, lhs ) );
                    addr.appendToPath( index );


                    return ToValue( addr );

                } ),

                // dot operator for a non-temporary tuple (contained in a local var):
                // returns a mutable reference to the specified member.
                ForTypes< CustomPattern< LocalVar, LocalVar::Pattern< TuplePattern > >,
                    CustomPattern< IntegerType, IntegerType::PatternUnsigned32 > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
                            "the index is out of range." );
                        return PoisonValue();
                    }

                    auto loc = Location::CreateSpanningLocation( lhs.locationId(), rhs.locationId() );

                    ReferenceType rt( GetTupleTypeElement( tupType, index ), TSID( mut ) );
                    auto addr = Address( VarAddress( lv.index() ) );
                    addr.appendToPath( index );

                    return BuildComputedValue( ValueToIRExpr( ToValue( rt ) ),
                        Load( move( addr ), rt.type() ) )
                        .setLocationId( loc );
                } )
            )
        );
    }
}







|


<
|
<





75
76
77
78
79
80
81
82
83
84

85

86
87
88
89
90
                            "the index is out of range." );
                        return PoisonValue();
                    }

                    auto loc = Location::CreateSpanningLocation( lhs.locationId(), rhs.locationId() );

                    ReferenceType rt( GetTupleTypeElement( tupType, index ), TSID( mut ) );
                    auto addr = Reference( rt, VarBaseAddr( lv.index() ) );
                    addr.appendToPath( index );


                    return ToValue( addr );

                } )
            )
        );
    }
}
Changes to bs/builtins/types/func/build.cpp.
18
19
20
21
22
23
24


25
26
27
28
29
30
31
32
33
34
                auto decl = *FromValue< Decl >( param );
                tv->append( ParamPat( decl.type() ) );

                // 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( _ ),
                    ValueToIRExpr( BuildComputedValue( decl.type(),
                    llr::Load( llr::VarAddress( varId++ ), decl.type() ) ) ) );
            }
            else if( param.isConstant() )
                tv->append( ValueToIRExpr( param ) );

            return true;
        } );








>
>


|







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
                auto decl = *FromValue< Decl >( param );
                tv->append( ParamPat( decl.type() ) );

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

                Reference argRef( ReferenceType{ decl.type(), TSID( const ) }, llr::VarBaseAddr( varId++ ) );

                c.env()->storeValue( paramVerificationIdentity, ANYTERM( _ ),
                    ValueToIRExpr( BuildComputedValue( decl.type(),
                    llr::Load( ToValue( argRef ), decl.type() ) ) ) );
            }
            else if( param.isConstant() )
                tv->append( ValueToIRExpr( param ) );

            return true;
        } );

Changes to bs/builtins/types/func/compile.cpp.
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
        auto func = FromValue< Func >( f );
        assert( func );

        if( func->isExternal() )
            return f;

        if( ParseFuncBody( c, *func ) )
                return f;

            return PoisonValue();
    }

    bool ParseFuncConstraints( const Context& c, const FuncType& funcType )
    {
        auto& fvi = *funcType.verifInfos();

        if( !fvi.preCondToks() && !fvi.postCondToks() )







|

|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
        auto func = FromValue< Func >( f );
        assert( func );

        if( func->isExternal() )
            return f;

        if( ParseFuncBody( c, *func ) )
            return f;

        return PoisonValue();
    }

    bool ParseFuncConstraints( const Context& c, const FuncType& funcType )
    {
        auto& fvi = *funcType.verifInfos();

        if( !fvi.preCondToks() && !fvi.postCondToks() )
Changes to bs/builtins/types/init.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
            {
                auto ref = *FromValue< Reference >( r );

                if( !ParseTypePredicates( c, c.identity(), *ValueFromIRExpr( ref.type().type() ) ) )
                    return PoisonValue();

                return BuildComputedValue( GetValueType< void >(),
                    Store( ref.address(), initVal ) );
            } );

        // Default initialization for ct_int vars
        RegisterBuiltinFunc< Intrinsic< Value ( CTIntMutRefType ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r )
            {
                auto ref = *FromValue< Reference >( r );

                if( !ParseTypePredicates( c, c.identity(), *ValueFromIRExpr( ref.type().type() ) ) )
                    return PoisonValue();

                return BuildComputedValue( GetValueType< void >(),
                    Store( ref.address(), ToValue( BigInt() ) ) );
            } );

        // Default initialization for ct_string vars
        RegisterBuiltinFunc< Intrinsic< Value ( CTStringMutRefType ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r )
            {
                auto ref = *FromValue< Reference >( r );

                if( !ParseTypePredicates( c, c.identity(), *ValueFromIRExpr( ref.type().type() ) ) )
                    return PoisonValue();

                return BuildComputedValue( GetValueType< void >(),
                    Store( ref.address(), ToValue( ""s ) ) );
            } );
    }
}







|












|












|



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
            {
                auto ref = *FromValue< Reference >( r );

                if( !ParseTypePredicates( c, c.identity(), *ValueFromIRExpr( ref.type().type() ) ) )
                    return PoisonValue();

                return BuildComputedValue( GetValueType< void >(),
                    Store( r, initVal ) );
            } );

        // Default initialization for ct_int vars
        RegisterBuiltinFunc< Intrinsic< Value ( CTIntMutRefType ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r )
            {
                auto ref = *FromValue< Reference >( r );

                if( !ParseTypePredicates( c, c.identity(), *ValueFromIRExpr( ref.type().type() ) ) )
                    return PoisonValue();

                return BuildComputedValue( GetValueType< void >(),
                    Store( r, ToValue( BigInt() ) ) );
            } );

        // Default initialization for ct_string vars
        RegisterBuiltinFunc< Intrinsic< Value ( CTStringMutRefType ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r )
            {
                auto ref = *FromValue< Reference >( r );

                if( !ParseTypePredicates( c, c.identity(), *ValueFromIRExpr( ref.type().type() ) ) )
                    return PoisonValue();

                return BuildComputedValue( GetValueType< void >(),
                    Store( r, ToValue( ""s ) ) );
            } );
    }
}
Changes to bs/builtins/types/localvar/invoke.cpp.
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    {
        public:
            bool canBeInvoked( const Context& c, const Value& callee ) const final
            {
                auto lv = *FromValue< LocalVar >( callee );

                auto val = BuildComputedValue( lv.type(),
                    llr::Load( llr::VarAddress( lv.index() ), lv.type() ) );

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

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

                auto val = BuildComputedValue( lv.type(),
                    llr::Load( llr::VarAddress( lv.index() ), lv.type() ) );

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

    void SetupLocalVarInvocationRule( Env& e )
    {







|









|







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

                auto val = BuildComputedValue( lv.type(),
                    llr::Load( ToValue( BuildLocalVarConstRef( lv ) ), lv.type() ) );

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

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

                auto val = BuildComputedValue( lv.type(),
                    llr::Load( ToValue( BuildLocalVarConstRef( lv ) ), lv.type() ) );

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

    void SetupLocalVarInvocationRule( Env& e )
    {
Changes to bs/builtins/types/localvar/localvar.cpp.
175
176
177
178
179
180
181

182
183






184
185
186
187
188
189
190
        // it will have a meaningful location for the error message to attach itself on.
        uint32_t typeLoc = ValueFromIRExpr( typeTExpr )->locationId();
        LocalVar lv( name, type, index );

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

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

        auto initResult = ResolveInvocation( c, GetInvocationRule( *c.env(), initializerVal ), initializerVal,
            MakeTuple( ToValue( lv ), initVal ) );







        if( !initResult.isPoison() )
        {
            DiagnosticsContext dc2( initResult.locationId(), "When invoking DropValue." );
            InvokeOverloadSet( c, c.env()->extDropValue(),
                MakeTuple( move( initResult ) ) );
        }







>


>
>
>
>
>
>







175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
        // it will have a meaningful location for the error message to attach itself on.
        uint32_t typeLoc = ValueFromIRExpr( typeTExpr )->locationId();
        LocalVar lv( name, type, index );

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

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

        auto initResult = ResolveInvocation( c, GetInvocationRule( *c.env(), initializerVal ), initializerVal,
            MakeTuple( ToValue( lv ), initVal ) );

        if( !initResult.isConstant() && llr::CanValueBeEagerlyEvaluated( initResult ) )
        {
            execute::VM vm;
            initResult = execute::Evaluate( initResult, vm );
        }

        if( !initResult.isPoison() )
        {
            DiagnosticsContext dc2( initResult.locationId(), "When invoking DropValue." );
            InvokeOverloadSet( c, c.env()->extDropValue(),
                MakeTuple( move( initResult ) ) );
        }
Changes to bs/builtins/types/localvar/localvar.h.
1
2
3
4
5
6
7
8
9
10


11
12
13
14
15
16
17
#ifndef GOOSE_BUILTINS_TYPES_LOCALVAR_H
#define GOOSE_BUILTINS_TYPES_LOCALVAR_H

namespace goose::parse
{
    class Parser;
}

namespace goose::builtins
{


    extern void SetupLocalVarInvocationRule( Env& e );
    extern void SetupLocalVarTypeChecking( Env& e );
    extern void SetupLocalVarInitialize( Env& e );
    extern void SetupLocalVarDropValue( Env& e );

    Value DeclareLocalVar( const Context& c, const Term& type, StringId name, const optional< Value >& initializer );
    Value DeclareLocalVarWithTypeInference( Context& c, const Term& typeTExpr, StringId name, const Value& initVal );










>
>







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

namespace goose::parse
{
    class Parser;
}

namespace goose::builtins
{
    class LocalVar;

    extern void SetupLocalVarInvocationRule( Env& e );
    extern void SetupLocalVarTypeChecking( Env& e );
    extern void SetupLocalVarInitialize( Env& e );
    extern void SetupLocalVarDropValue( Env& e );

    Value DeclareLocalVar( const Context& c, const Term& type, StringId name, const optional< Value >& initializer );
    Value DeclareLocalVarWithTypeInference( Context& c, const Term& typeTExpr, StringId name, const Value& initVal );
Changes to bs/builtins/types/reference/reference.cpp.
33
34
35
36
37
38
39










40
41
42
43
44
45
46
    }

    const Term& Reference::PatternAnyMutableOfTypeT::GetPattern()
    {
        static auto pattern = GetValueType< Reference >( HOLE( "T"_sid ), TSID( mut ) );
        return pattern;
    }










}

namespace goose::ir
{
    const Term& Bridge< ReferenceType >::Type()
    {
        return TypeType();







>
>
>
>
>
>
>
>
>
>







33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
    }

    const Term& Reference::PatternAnyMutableOfTypeT::GetPattern()
    {
        static auto pattern = GetValueType< Reference >( HOLE( "T"_sid ), TSID( mut ) );
        return pattern;
    }

    Reference BuildLocalVarConstRef( const LocalVar& lv )
    {
        return { ReferenceType{ lv.type(), TSID( const ) }, llr::VarBaseAddr( lv.index() ) };
    }

    Reference BuildLocalVarMutRef( const LocalVar& lv )
    {
        return { ReferenceType{ lv.type(), TSID( mut ) }, llr::VarBaseAddr( lv.index() ) };
    }
}

namespace goose::ir
{
    const Term& Bridge< ReferenceType >::Type()
    {
        return TypeType();
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
        if( !result )
            return nullopt;

        auto&& [bhv, type] = *result;
        return ReferenceType( move( type ), move( bhv ) );
    }

    Term Bridge< Reference >::Type( const Term& type, const Term& bhv )
    {
        return ValueToIRExpr( ::ToValue( ReferenceType( type, bhv ) ) );
    }

    Value Bridge< Reference >::ToValue( const Reference& ref )
    {
        return BuildComputedValue( ref.type().type(),
            llr::Load( ref.address(), ref.type().type() ) );
    }

    optional< Reference > Bridge< Reference >::FromValue( const Value& v )
    {
        if( v.isConstant() || !v.llr() )




            return nullopt;

        auto t = FromValue< ReferenceType >( *ValueFromIRExpr( v.type() ) );
        if( !t )
            return nullopt;

        return Reference( move( *t ), get< llr::Load >( v.llr()->content() ).addr() );
    }
}







|




|

|
<


|

|
>
>
>
>






|


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
        if( !result )
            return nullopt;

        auto&& [bhv, type] = *result;
        return ReferenceType( move( type ), move( bhv ) );
    }

    Term Bridge< builtins::Reference >::Type( const Term& type, const Term& bhv )
    {
        return ValueToIRExpr( ::ToValue( ReferenceType( type, bhv ) ) );
    }

    Value Bridge< builtins::Reference >::ToValue( const builtins::Reference& ref )
    {
        return BuildComputedValue( ValueToIRExpr( ::ToValue( ref.type() ) ), llr::CalcAddress{ ref.address() } );

    }

    optional< builtins::Reference > Bridge< builtins::Reference >::FromValue( const Value& v )
    {
        if( v.isConstant() )
            return nullopt;

        auto llrRef = get_if< llr::CalcAddress >( &v.llr()->content() );
        if( !llrRef )
            return nullopt;

        auto t = FromValue< ReferenceType >( *ValueFromIRExpr( v.type() ) );
        if( !t )
            return nullopt;

        return builtins::Reference( move( *t ), get< llr::CalcAddress >( v.llr()->content() ) );
    }
}
Changes to bs/builtins/types/reference/reference.h.
36
37
38
39
40
41
42





43
44
45
46
47
48
49
            Reference( T&& type, A&& addr ) :
                m_type( forward< T >( type ) ),
                m_addr( forward< A >( addr ) )
            {}

            const auto& type() const { return m_type; }
            const auto& address() const { return m_addr; }






            struct PatternAny
            {
                static const Term& GetPattern();
            };

            struct PatternAnyMutable







>
>
>
>
>







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
            Reference( T&& type, A&& addr ) :
                m_type( forward< T >( type ) ),
                m_addr( forward< A >( addr ) )
            {}

            const auto& type() const { return m_type; }
            const auto& address() const { return m_addr; }

            void appendToPath( uint32_t index )
            {
                m_addr.appendToPath( index );
            }

            struct PatternAny
            {
                static const Term& GetPattern();
            };

            struct PatternAnyMutable
94
95
96
97
98
99
100
101
102



103
104
105
106
107
108
109
                    static auto pattern = GetValueType< Reference >( PP::GetPattern(), TSID( const ) );
                    return pattern;
                }
            };

        private:
            ReferenceType m_type;
            llr::Address m_addr;
    };



}

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







|

>
>
>







99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
                    static auto pattern = GetValueType< Reference >( PP::GetPattern(), TSID( const ) );
                    return pattern;
                }
            };

        private:
            ReferenceType m_type;
            llr::CalcAddress m_addr;
    };

    extern Reference BuildLocalVarConstRef( const LocalVar& lv );
    extern Reference BuildLocalVarMutRef( const LocalVar& lv );
}

namespace goose::ir
{
    template<>
    struct Bridge< builtins::ReferenceType >
    {
Changes to bs/builtins/types/reference/typecheck.cpp.
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
        auto refTypePatternConstant = ValueToIRExpr(
            Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), TSID( const ), ANYTERM( _ ) ) ) );

        auto refTypePatternMutable = ValueToIRExpr(
            Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), TSID( mut ), ANYTERM( _ ) ) ) );

        auto refTypePatternTemporary = ValueToIRExpr(
            Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), TSID( temporary ), ANYTERM( _ ) ) ) );

        // Reference type checking rule.
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ParamPat( refTypePattern ),

            ValueToIRExpr( ValuePattern(







|







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
        auto refTypePatternConstant = ValueToIRExpr(
            Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), TSID( const ), ANYTERM( _ ) ) ) );

        auto refTypePatternMutable = ValueToIRExpr(
            Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), TSID( mut ), ANYTERM( _ ) ) ) );

        auto refTypePatternTemporary = ValueToIRExpr(
            Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), TSID( temp ), ANYTERM( _ ) ) ) );

        // Reference type checking rule.
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ParamPat( refTypePattern ),

            ValueToIRExpr( ValuePattern(
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
            {
                auto refval = *ValueFromIRExpr( rhs );
                auto ref = FromValue< Reference >( refval );
                if( !ref )
                    co_return;

                auto content = ValueToIRExpr( BuildComputedValue( ref->type().type(),
                    llr::Load( ref->address(), ref->type().type() ) )
                    .setLocationId( refval.locationId() ) );

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

        // LocalVar type checking against a param (implicit referencing):







|







100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
            {
                auto refval = *ValueFromIRExpr( rhs );
                auto ref = FromValue< Reference >( refval );
                if( !ref )
                    co_return;

                auto content = ValueToIRExpr( BuildComputedValue( ref->type().type(),
                    llr::Load( refval, ref->type().type() ) )
                    .setLocationId( refval.locationId() ) );

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

        // LocalVar type checking against a param (implicit referencing):
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
            auto ltype = ValuePatternFromIRExpr( lhs )->type();

            auto lvval = *ValueFromIRExpr( rhs );
            auto locvar = FromValue< LocalVar >( lvval );
            if( !locvar )
                co_return;

            if( IsReferenceType( locvar->type() ) )
            {
                auto ref = ValueToIRExpr( BuildComputedValue( locvar->type(),
                    Load( VarAddress( locvar->index() ), locvar->type() ) )
                    .setLocationId( lvval.locationId() ) );

                co_yield TypeCheck( lhs, ref, tcc );
            }
            else
            {
                ReferenceType rt( locvar->type(), TSID( mut ) );
                auto ref = ValueToIRExpr( BuildComputedValue( ValueToIRExpr( ToValue( rt ) ),
                    Load( VarAddress( locvar->index() ), rt.type() ) )
                    .setLocationId( lvval.locationId() ) );

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

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

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),







<
<
|
<
|

|
<
<
<
<
<
<
<
<
<
<







131
132
133
134
135
136
137


138

139
140
141










142
143
144
145
146
147
148
            auto ltype = ValuePatternFromIRExpr( lhs )->type();

            auto lvval = *ValueFromIRExpr( rhs );
            auto locvar = FromValue< LocalVar >( lvval );
            if( !locvar )
                co_return;



            auto ref = ValueToIRExpr( ToValue( BuildLocalVarMutRef( *locvar ) )

                .setLocationId( lvval.locationId() ) );

            co_yield TypeCheck( lhs, ref, tcc );










        } );

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

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
                        auto&& [ref, rhs] = *result;

                        auto rhsVal = *ValueFromIRExpr( rhs );

                        auto refPat = *ValuePatternFromIRExpr( ref );
                        auto rt = *FromValue< ReferenceType >( *ValueFromIRExpr( refPat.type() ) );

                        return ValueToIRExpr( BuildComputedValue( refPat.type(),
                            Load( TemporaryAddress( tempIndex, rhsVal ), rt.type() ) )
                            .setLocationId( rhsVal.locationId() ) );
                    } );

                    co_yield { move( wrapped ), tcc };
                }
            }
        } );
    }
}







|
<
<








194
195
196
197
198
199
200
201


202
203
204
205
206
207
208
209
                        auto&& [ref, rhs] = *result;

                        auto rhsVal = *ValueFromIRExpr( rhs );

                        auto refPat = *ValuePatternFromIRExpr( ref );
                        auto rt = *FromValue< ReferenceType >( *ValueFromIRExpr( refPat.type() ) );

                        return ValueToIRExpr( ToValue( Reference{ move( rt ), TemporaryBaseAddr( tempIndex, rhsVal ) } ) );


                    } );

                    co_yield { move( wrapped ), tcc };
                }
            }
        } );
    }
}
Changes to bs/builtins/types/runtime/basic.cpp.
227
228
229
230
231
232
233


234
235
236
    Value Bridge< uint64_t >::ToValue( uint64_t x )
    {
        return ir::ToValue( APSInt::getUnsigned( x ) );
    }

    optional< uint64_t > Bridge< uint64_t >::FromValue( const Value& v )
    {


        return FromValue< APSInt >( v )->getLimitedValue();
    }
}







>
>



227
228
229
230
231
232
233
234
235
236
237
238
    Value Bridge< uint64_t >::ToValue( uint64_t x )
    {
        return ir::ToValue( APSInt::getUnsigned( x ) );
    }

    optional< uint64_t > Bridge< uint64_t >::FromValue( const Value& v )
    {
        if( !v.isConstant() )
            return nullopt;
        return FromValue< APSInt >( v )->getLimitedValue();
    }
}
Changes to bs/builtins/types/runtime/init.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

        using IntegerType = CustomPattern< IntegerType, IntegerType::Pattern >;

        // Initialization for integer vars
        RegisterBuiltinFunc< Intrinsic< Value ( IntegerMutRefType, IntegerType ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r, const Value& initVal )
            {
                auto ref = *FromValue< Reference >( r );
                return BuildComputedValue( GetValueType< void >(),
                    Store( ref.address(), initVal ) );
            } );

        // Default initialization for integer vars
        RegisterBuiltinFunc< Intrinsic< Value ( IntegerMutRefType ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r )
            {
                auto ref = *FromValue< Reference >( r );

                auto opTypeVal = *ValueFromIRExpr( ref.type().type() );
                auto opType = *FromValue< IntegerType >( opTypeVal );
                auto initVal = Value( ref.type().type(),
                    APSInt( opType.m_numBits, !opType.m_signed ) );

                return BuildComputedValue( GetValueType< void >(),
                    Store( ref.address(), initVal ) );
            } );
    }
}







<

|














|



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

        using IntegerType = CustomPattern< IntegerType, IntegerType::Pattern >;

        // Initialization for integer vars
        RegisterBuiltinFunc< Intrinsic< Value ( IntegerMutRefType, IntegerType ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r, const Value& initVal )
            {

                return BuildComputedValue( GetValueType< void >(),
                    Store( r, initVal ) );
            } );

        // Default initialization for integer vars
        RegisterBuiltinFunc< Intrinsic< Value ( IntegerMutRefType ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r )
            {
                auto ref = *FromValue< Reference >( r );

                auto opTypeVal = *ValueFromIRExpr( ref.type().type() );
                auto opType = *FromValue< IntegerType >( opTypeVal );
                auto initVal = Value( ref.type().type(),
                    APSInt( opType.m_numBits, !opType.m_signed ) );

                return BuildComputedValue( GetValueType< void >(),
                    Store( r, initVal ) );
            } );
    }
}
Changes to bs/builtins/types/tuple/init.cpp.
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

            // Create a mutable reference to the element to initialize
            ReferenceType rt( t, TSID( mut ) );
            auto addr = lref.address();
            addr.appendToPath( index );

            auto elemRef = BuildComputedValue( ValueToIRExpr( ToValue( rt ) ),
                Load( move( addr ), rt.type() ) )
                .setLocationId( elemType.locationId() );

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

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








<
|







25
26
27
28
29
30
31

32
33
34
35
36
37
38
39

            // Create a mutable reference to the element to initialize
            ReferenceType rt( t, TSID( mut ) );
            auto addr = lref.address();
            addr.appendToPath( index );

            auto elemRef = BuildComputedValue( ValueToIRExpr( ToValue( rt ) ),

                move( addr ) ).setLocationId( elemType.locationId() );

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

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

62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
                ForEachInTupleType( tupType, [&]( auto&& t )
                {
                    auto elemType = *ValueFromIRExpr( t );

                    // Create a mutable reference to the element to initialize
                    ReferenceType rt( t, TSID( mut ) );
                    auto addr = ref.address();
                    addr.appendToPath( index++ );

                    auto elemRef = BuildComputedValue( ValueToIRExpr( ToValue( rt ) ),
                        Load( move( addr ), rt.type() ) )
                        .setLocationId( elemType.locationId() );

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

                    DiagnosticsContext dc2( elemType.locationId(), "When invoking DropValue." );







|


|







61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
                ForEachInTupleType( tupType, [&]( auto&& t )
                {
                    auto elemType = *ValueFromIRExpr( t );

                    // Create a mutable reference to the element to initialize
                    ReferenceType rt( t, TSID( mut ) );
                    auto addr = ref.address();
                    addr.path().back() = index++;

                    auto elemRef = BuildComputedValue( ValueToIRExpr( ToValue( rt ) ),
                        Load( ToValue( ref ), rt.type() ) )
                        .setLocationId( elemType.locationId() );

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

                    DiagnosticsContext dc2( elemType.locationId(), "When invoking DropValue." );
Changes to bs/builtins/types/tuple/typecheck.cpp.
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
            if( index == ( tupSize - 1 ) )
                co_yield { ValueToIRExpr( newOut ), tcc };
            else
                co_yield TypeCheckConstantTuple( tcc, tupType, tupArg, index + 1, newOut );
        }
    }

    TCGen TypeCheckComputedTuple( const TypeCheckingContext& tcc, const Value& tupType, const Value& tupArg, const llr::Address& tupAddr, uint32_t index, const Value& out )
    {
        auto param = ParamPat( GetTupleTypeElement( tupType, index ) );

        auto argType = GetTupleElementType( tupArg, index );
        ReferenceType rt( argType, TSID( const ) );
        auto argAddr = tupAddr;
        argAddr.appendToPath( index );

        auto argRef = ValueToIRExpr( BuildComputedValue( ValueToIRExpr( ToValue( rt ) ),
            llr::Load( move( argAddr ), argType ) ) );

        auto tupSize = TupleTypeSize( tupType );

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







|









|







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
            if( index == ( tupSize - 1 ) )
                co_yield { ValueToIRExpr( newOut ), tcc };
            else
                co_yield TypeCheckConstantTuple( tcc, tupType, tupArg, index + 1, newOut );
        }
    }

    TCGen TypeCheckComputedTuple( const TypeCheckingContext& tcc, const Value& tupType, const Value& tupArg, const llr::CalcAddress& tupAddr, uint32_t index, const Value& out )
    {
        auto param = ParamPat( GetTupleTypeElement( tupType, index ) );

        auto argType = GetTupleElementType( tupArg, index );
        ReferenceType rt( argType, TSID( const ) );
        auto argAddr = tupAddr;
        argAddr.appendToPath( index );

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

        auto tupSize = TupleTypeSize( tupType );

        for( auto&& [s,tcc] : TypeCheck( param, argRef, tcc ) )
        {
            auto val = ValuePatternFromIRExpr( s );
            assert( val );
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
            if( !ltup || !rtup || !tcc.context().codeBuilder() )
                co_return;

            auto tupType = *ValueFromIRExpr( ltup->type() );
            assert( TupleTypeSize( tupType ) == TupleTypeSize( *ValueFromIRExpr( rtup->type() ) ) );

            auto tempIndex = tcc.context().codeBuilder()->cfg()->getNewTemporaryIndex();
            llr::Address addr( llr::TemporaryAddress( tempIndex, *rtup ) );

            co_yield TypeCheckComputedTuple( tcc, tupType, *rtup, addr, 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,







|







102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
            if( !ltup || !rtup || !tcc.context().codeBuilder() )
                co_return;

            auto tupType = *ValueFromIRExpr( ltup->type() );
            assert( TupleTypeSize( tupType ) == TupleTypeSize( *ValueFromIRExpr( rtup->type() ) ) );

            auto tempIndex = tcc.context().codeBuilder()->cfg()->getNewTemporaryIndex();
            llr::CalcAddress addr( llr::TemporaryBaseAddr( tempIndex, *rtup ) );

            co_yield TypeCheckComputedTuple( tcc, tupType, *rtup, addr, 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/codegen/address.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;

llvm::Value* Module::buildAddress( Infos& inf, const Address& addr )
{
    auto* baseAddrVal = buildAddress( inf, addr.baseAddr() );
    if( !baseAddrVal )
        return nullptr;

    if( addr.path().empty() )
        return baseAddrVal;







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#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 CalcAddress& addr )
{
    auto* baseAddrVal = buildAddress( inf, addr.baseAddr() );
    if( !baseAddrVal )
        return nullptr;

    if( addr.path().empty() )
        return baseAddrVal;
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
{
    return visit( [&]( auto&& ba )
    {
        return buildAddress( inf, ba );
    }, baseAddr );
}

llvm::Value* Module::buildAddress( Infos& inf, const TemporaryAddress& ta )
{
    auto* ppVal = inf.temporaries->get( ta.index );

    if( !ppVal )
    {
        auto* pValue = buildValue( inf, ta.m_initValue );
        if( !pValue )







|







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
{
    return visit( [&]( auto&& ba )
    {
        return buildAddress( inf, ba );
    }, baseAddr );
}

llvm::Value* Module::buildAddress( Infos& inf, const TemporaryBaseAddr& ta )
{
    auto* ppVal = inf.temporaries->get( ta.index );

    if( !ppVal )
    {
        auto* pValue = buildValue( inf, ta.m_initValue );
        if( !pValue )
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

    auto* pAlloca = buildAlloca( inf, ( *ppVal )->getType() );
    auto* pStore = m_llvmBuilder.CreateStore( *ppVal, pAlloca );
    inf.temporaries->set( ta.index, pAlloca );
    return pAlloca;
}

llvm::Value* Module::buildAddress( Infos& inf, const VarAddress& va )
{
    auto* ppVal = inf.temporaries->get( va.index );

    assert( ppVal );
    if( !ppVal )
        return nullptr;

    return *ppVal;
}







|









50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

    auto* pAlloca = buildAlloca( inf, ( *ppVal )->getType() );
    auto* pStore = m_llvmBuilder.CreateStore( *ppVal, pAlloca );
    inf.temporaries->set( ta.index, pAlloca );
    return pAlloca;
}

llvm::Value* Module::buildAddress( Infos& inf, const VarBaseAddr& va )
{
    auto* ppVal = inf.temporaries->get( va.index );

    assert( ppVal );
    if( !ppVal )
        return nullptr;

    return *ppVal;
}
Changes to bs/codegen/instructions.cpp.
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
    auto type = LowerTypeForRuntime( inf.context, *ValueFromIRExpr( load.type() ) );
    if( !type )
        return nullptr;

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

    auto* ptrVal = buildAddress( inf, load.addr() );
    return m_llvmBuilder.CreateLoad( llvmType, ptrVal );
}

llvm::Value* Module::buildInstruction( Infos& inf, const llr::Store& store )
{
    auto* pValue = buildValue( inf, store.val() );
    if( !pValue )
        return nullptr;

    auto* ptrVal = buildAddress( inf, store.addr() );

    if( llvm::isa< llvm::ConstantAggregate >( pValue ) )
    {
        stringstream name;
        name << ".constaggr" << hex << m_nextAggregateID++;

        auto pGlob = new llvm::GlobalVariable( m_llvmModule,







|









|







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
    auto type = LowerTypeForRuntime( inf.context, *ValueFromIRExpr( load.type() ) );
    if( !type )
        return nullptr;

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

    auto* ptrVal = buildValue(inf, load.addr() );//  buildAddress( inf, load.addr() );
    return m_llvmBuilder.CreateLoad( llvmType, ptrVal );
}

llvm::Value* Module::buildInstruction( Infos& inf, const llr::Store& store )
{
    auto* pValue = buildValue( inf, store.val() );
    if( !pValue )
        return nullptr;

    auto* ptrVal = buildValue(inf, store.addr() );

    if( llvm::isa< llvm::ConstantAggregate >( pValue ) )
    {
        stringstream name;
        name << ".constaggr" << hex << m_nextAggregateID++;

        auto pGlob = new llvm::GlobalVariable( m_llvmModule,
Changes to bs/codegen/module.h.
50
51
52
53
54
55
56

57
58
59
60
61
62
63
            llvm::BasicBlock* buildBasicBlock( Infos& inf, const ptr< llr::BasicBlock >& pBB );

            llvm::Value* buildValue( Infos& inf, const Value& val );
            llvm::Constant* buildConstant( Infos& inf, const Value& val );

            llvm::Value* buildInstruction( Infos& inf, const llr::Instruction& instr );
            llvm::Value* buildInstruction( Infos& inf, const llr::Call& call );

            llvm::Value* buildInstruction( Infos& inf, const llr::CreateTemporary& ct );
            llvm::Value* buildInstruction( Infos& inf, const llr::GetTemporary& gt );
            llvm::Value* buildInstruction( Infos& inf, const llr::AllocVar& av );
            llvm::Value* buildInstruction( Infos& inf, const llr::Load& load );
            llvm::Value* buildInstruction( Infos& inf, const llr::Store& store );
            llvm::Value* buildInstruction( Infos& inf, const llr::Phi& p );
            llvm::Value* buildInstruction( Infos& inf, const llr::LoadConstStr& lcs );







>







50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
            llvm::BasicBlock* buildBasicBlock( Infos& inf, const ptr< llr::BasicBlock >& pBB );

            llvm::Value* buildValue( Infos& inf, const Value& val );
            llvm::Constant* buildConstant( Infos& inf, const Value& val );

            llvm::Value* buildInstruction( Infos& inf, const llr::Instruction& instr );
            llvm::Value* buildInstruction( Infos& inf, const llr::Call& call );
            llvm::Value* buildInstruction( Infos& inf, const llr::CalcAddress& ct );
            llvm::Value* buildInstruction( Infos& inf, const llr::CreateTemporary& ct );
            llvm::Value* buildInstruction( Infos& inf, const llr::GetTemporary& gt );
            llvm::Value* buildInstruction( Infos& inf, const llr::AllocVar& av );
            llvm::Value* buildInstruction( Infos& inf, const llr::Load& load );
            llvm::Value* buildInstruction( Infos& inf, const llr::Store& store );
            llvm::Value* buildInstruction( Infos& inf, const llr::Phi& p );
            llvm::Value* buildInstruction( Infos& inf, const llr::LoadConstStr& lcs );
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116

            template< typename T >
            bool buildTerminator( Infos& inf, const T& t )
            {
                return false;
            }

            llvm::Value* buildAddress( Infos& inf, const Address& addr );
            llvm::Value* buildAddress( Infos& inf, const BaseAddress& baseAddr );
            llvm::Value* buildAddress( Infos& inf, const TemporaryAddress& ta );
            llvm::Value* buildAddress( Infos& inf, const VarAddress& va );

            llvm::Value* buildAlloca( Infos& inf, llvm::Type* type );
            llvm::Value* createTemporary( Infos& inf, uint32_t index, llvm::Value* pValue ) const;

            llvm::Module m_llvmModule;
            llvm::DataLayout m_dataLayout;
            llvm::IRBuilder<> m_llvmBuilder;







<

|
|







100
101
102
103
104
105
106

107
108
109
110
111
112
113
114
115
116

            template< typename T >
            bool buildTerminator( Infos& inf, const T& t )
            {
                return false;
            }


            llvm::Value* buildAddress( Infos& inf, const BaseAddress& baseAddr );
            llvm::Value* buildAddress( Infos& inf, const TemporaryBaseAddr& ta );
            llvm::Value* buildAddress( Infos& inf, const VarBaseAddr& va );

            llvm::Value* buildAlloca( Infos& inf, llvm::Type* type );
            llvm::Value* createTemporary( Infos& inf, uint32_t index, llvm::Value* pValue ) const;

            llvm::Module m_llvmModule;
            llvm::DataLayout m_dataLayout;
            llvm::IRBuilder<> m_llvmBuilder;
Changes to bs/compile/compiler.cpp.
59
60
61
62
63
64
65



66

67
68
69
70
71
72
73
    {
        auto result = LoadAndExecuteFile( m_pEnv, filename, builtins::RootIdentity(), GetValueType< uint32_t >(), ToValue< uint32_t >( 1 ) );

        if( DiagnosticsManager::GetInstance().errorsWereEmitted() )
            return 1;

        if( !result )



            return 0;


        if( result->isPoison() )
            return 1;

        return *FromValue< uint32_t >( *result );
    }








>
>
>
|
>







59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
    {
        auto result = LoadAndExecuteFile( m_pEnv, filename, builtins::RootIdentity(), GetValueType< uint32_t >(), ToValue< uint32_t >( 1 ) );

        if( DiagnosticsManager::GetInstance().errorsWereEmitted() )
            return 1;

        if( !result )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( 0,
                format( "{}: coult not be executed.", filename ) );
            return 1;
        }

        if( result->isPoison() )
            return 1;

        return *FromValue< uint32_t >( *result );
    }

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
#ifndef GOOSE_EXECUTE_BINARYOPS_INL
#define GOOSE_EXECUTE_BINARYOPS_INL

namespace goose::execute
{
    template< typename F >
    Value VM::executeEqualityBinOp( const llr::BinaryOp& bo, F&& func )
    {
        if( bo.lhs().isPoison() || bo.rhs().isPoison() )
            return PoisonValue();

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

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



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



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

        if( auto lint = FromValue< APSInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );






|









|

>
>


|

>
>







1
2
3
4
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_EXECUTE_BINARYOPS_INL
#define GOOSE_EXECUTE_BINARYOPS_INL

namespace goose::execute
{
    template< typename F >
    optional< Value > VM::executeEqualityBinOp( const llr::BinaryOp& bo, F&& func )
    {
        if( bo.lhs().isPoison() || bo.rhs().isPoison() )
            return PoisonValue();

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

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

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

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

        if( auto lint = FromValue< APSInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );
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
        }

        assert( false );
        return PoisonValue();
    }

    template< typename F >
    Value VM::executeLogicBinOp( const llr::BinaryOp& bo, F&& func )
    {
        if( bo.lhs().isPoison() || bo.rhs().isPoison() )
            return PoisonValue();

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

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



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



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

        if( auto lint = FromValue< APSInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );







|









|

>
>


|

>
>







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
        }

        assert( false );
        return PoisonValue();
    }

    template< typename F >
    optional< Value > VM::executeLogicBinOp( const llr::BinaryOp& bo, F&& func )
    {
        if( bo.lhs().isPoison() || bo.rhs().isPoison() )
            return PoisonValue();

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

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

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

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

        if( auto lint = FromValue< APSInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );
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
        }

        assert( false );
        return PoisonValue();
    }

    template< typename F >
    Value VM::executeBinOp( const llr::BinaryOp& bo, F&& func )
    {
        if( bo.lhs().isPoison() || bo.rhs().isPoison() )
            return PoisonValue();

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

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



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



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

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

        assert( false );
        return PoisonValue();
    }

    template< typename F >
    Value VM::executeShiftBinOp( const llr::BinaryOp& bo, F&& func )
    {
        if( bo.lhs().isPoison() || bo.rhs().isPoison() )
            return PoisonValue();

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



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



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








|









|

>
>


|

>
>


















|





|

>
>


|

>
>







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
        }

        assert( false );
        return PoisonValue();
    }

    template< typename F >
    optional< Value > VM::executeBinOp( const llr::BinaryOp& bo, F&& func )
    {
        if( bo.lhs().isPoison() || bo.rhs().isPoison() )
            return PoisonValue();

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

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

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

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

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

        assert( false );
        return PoisonValue();
    }

    template< typename F >
    optional< Value > VM::executeShiftBinOp( const llr::BinaryOp& bo, F&& func )
    {
        if( bo.lhs().isPoison() || bo.rhs().isPoison() )
            return PoisonValue();

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

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

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

Changes to bs/execute/vm.cpp.
84
85
86
87
88
89
90


91

92
93
94
95
96
97
98
            assert( val );

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

            if( !newVal.isConstant() )


                return ValueToIRExpr( PoisonValue() );


            return ValueToIRExpr( newVal );
        } );

        if( poisoned )
            return PoisonValue();








>
>

>







84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
            assert( val );

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

            if( !newVal.isConstant() )
            {
                poisoned = true;
                return ValueToIRExpr( PoisonValue() );
            }

            return ValueToIRExpr( newVal );
        } );

        if( poisoned )
            return PoisonValue();

139
140
141
142
143
144
145









146
147
148
149
150
151
152
}

optional< Value > VM::ExecuteBuiltinFuncCall( const Value& func, const Term& args )
{
    const auto& f = GetBuiltinFuncWrapper( func );
    return f( args );
}










optional< Value > VM::execute( const llr::CreateTemporary& ct )
{
    auto stackIndex = m_currentFrameStart + ct.index();
    if( m_stack.size() <= stackIndex )
        m_stack.resize( stackIndex + 1, TSID( UNINITIALIZED ) );








>
>
>
>
>
>
>
>
>







142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
}

optional< Value > VM::ExecuteBuiltinFuncCall( const Value& func, const Term& args )
{
    const auto& f = GetBuiltinFuncWrapper( func );
    return f( args );
}

optional< Value > VM::execute( const llr::CalcAddress& ref )
{
    auto* pAddr = calcAddress( ref );
    if( !pAddr )
        return nullopt;

    return ToValue( reinterpret_cast< uintptr_t >( pAddr ) );
}

optional< Value > VM::execute( const llr::CreateTemporary& ct )
{
    auto stackIndex = m_currentFrameStart + ct.index();
    if( m_stack.size() <= stackIndex )
        m_stack.resize( stackIndex + 1, TSID( UNINITIALIZED ) );

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

    m_stack[stackIndex] = BuildUninitializedValue( av.type() );
    return nullopt;
}

optional< Value > VM::execute( const llr::Load& l )
{
    const auto* addr = calcAddress( l.addr() );
    if( !addr )
        return nullopt;


    return ValueFromIRExpr( *addr );
}

optional< Value > VM::execute( const llr::Store& s )
{
    auto* addr = calcAddress( s.addr() );
    if( !addr )
        return nullopt;



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

    *addr = ValueToIRExpr( result );
    return nullopt;







|
|


>





|
|

>
>







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

    m_stack[stackIndex] = BuildUninitializedValue( av.type() );
    return nullopt;
}

optional< Value > VM::execute( const llr::Load& l )
{
    auto addrInt = FromValue< uintptr_t >( Evaluate( l.addr(), *this ) );
    if( !addrInt )
        return nullopt;

    auto addr = reinterpret_cast< const Term* >( *addrInt );
    return ValueFromIRExpr( *addr );
}

optional< Value > VM::execute( const llr::Store& s )
{
    auto addrInt = FromValue< uintptr_t >( Evaluate( s.addr(), *this ) );
    if( !addrInt )
        return nullopt;

    auto addr = reinterpret_cast< Term* >( *addrInt );

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

    *addr = ValueToIRExpr( result );
    return nullopt;
219
220
221
222
223
224
225
226
227


228
229
230
231
232
233
234

optional< Value > VM::execute( const llr::Not& uo )
{
    if( uo.operand().isPoison() )
        return PoisonValue();

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



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

    return ToValue( !*boolVal );
}







|

>
>







234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251

optional< Value > VM::execute( const llr::Not& uo )
{
    if( uo.operand().isPoison() )
        return PoisonValue();

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

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

    return ToValue( !*boolVal );
}
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303

    if( *FromValue< bool >( cond ) )
        return cb.trueDest().lock();

    return cb.falseDest().lock();
}

Term* VM::calcAddress( const Address& addr )
{
    auto* pTerm = calcAddress( addr.baseAddr() );
    if( !pTerm )
        return nullptr;

    for( auto&& index : addr.path() )
    {







|







306
307
308
309
310
311
312
313
314
315
316
317
318
319
320

    if( *FromValue< bool >( cond ) )
        return cb.trueDest().lock();

    return cb.falseDest().lock();
}

Term* VM::calcAddress( const CalcAddress& addr )
{
    auto* pTerm = calcAddress( addr.baseAddr() );
    if( !pTerm )
        return nullptr;

    for( auto&& index : addr.path() )
    {
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
{
    return visit( [&]( auto&& ba )
    {
        return calcAddress( ba );
    }, baseAddr );
}

Term* VM::calcAddress( const llr::TemporaryAddress& ta )
{
    auto stackIndex = m_currentFrameStart + ta.index;
    if( m_stack.size() <= stackIndex )
        m_stack.resize( stackIndex + 1, TSID( UNINITIALIZED ) );

    if( stackIndex >= m_stack.size() )
        return nullptr;

    if( m_stack[stackIndex] == TSID( UNINITIALIZED ) )
        m_stack[stackIndex] = ValueToIRExpr( Evaluate( ta.m_initValue, *this ) );

    return &m_stack[stackIndex];
}

Term* VM::calcAddress( const llr::VarAddress& va )
{
    auto stackIndex = m_currentFrameStart + va.index;
    if( stackIndex >= m_stack.size() )
        return nullptr;

    return &m_stack[stackIndex];
}







|














|







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
{
    return visit( [&]( auto&& ba )
    {
        return calcAddress( ba );
    }, baseAddr );
}

Term* VM::calcAddress( const llr::TemporaryBaseAddr& ta )
{
    auto stackIndex = m_currentFrameStart + ta.index;
    if( m_stack.size() <= stackIndex )
        m_stack.resize( stackIndex + 1, TSID( UNINITIALIZED ) );

    if( stackIndex >= m_stack.size() )
        return nullptr;

    if( m_stack[stackIndex] == TSID( UNINITIALIZED ) )
        m_stack[stackIndex] = ValueToIRExpr( Evaluate( ta.m_initValue, *this ) );

    return &m_stack[stackIndex];
}

Term* VM::calcAddress( const llr::VarBaseAddr& va )
{
    auto stackIndex = m_currentFrameStart + va.index;
    if( stackIndex >= m_stack.size() )
        return nullptr;

    return &m_stack[stackIndex];
}
Changes to bs/execute/vm.h.
14
15
16
17
18
19
20

21
22
23
24
25
26
27
            }

            optional< Value > execute( CFG& cfg );
            optional< Value > execute( ptr< BasicBlock > bb );

            optional< Value > execute( const llr::Instruction& instr );
            optional< Value > execute( const llr::Call& call );

            optional< Value > execute( const llr::CreateTemporary& ct );
            optional< Value > execute( const llr::GetTemporary& gt );
            optional< Value > execute( const llr::AllocVar& av );
            optional< Value > execute( const llr::Load& l );
            optional< Value > execute( const llr::Store& s );
            optional< Value > execute( const llr::Phi& p );








>







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

            optional< Value > execute( CFG& cfg );
            optional< Value > execute( ptr< BasicBlock > bb );

            optional< Value > execute( const llr::Instruction& instr );
            optional< Value > execute( const llr::Call& call );
            optional< Value > execute( const llr::CalcAddress& ref );
            optional< Value > execute( const llr::CreateTemporary& ct );
            optional< Value > execute( const llr::GetTemporary& gt );
            optional< Value > execute( const llr::AllocVar& av );
            optional< Value > execute( const llr::Load& l );
            optional< Value > execute( const llr::Store& s );
            optional< Value > execute( const llr::Phi& p );

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
            }

        private:
            static optional< Value > ExecuteBuiltinFuncCall( const Value& func, const Term& args );
            static Term BuildUninitializedValue( const Value& type );

            template< typename F >
            Value executeEqualityBinOp( const llr::BinaryOp& bo, F&& func );

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

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

            template< typename F >
            Value executeShiftBinOp( const llr::BinaryOp& bo, F&& func );

            Term* calcAddress( const Address& addr );
            Term* calcAddress( const BaseAddress& baseAddr );
            Term* calcAddress( const TemporaryAddress& va );
            Term* calcAddress( const VarAddress& va );

            llvm::SmallVector< Term, 8 > m_stack;
            optional< Value > m_retVal;
            size_t m_currentFrameStart = 0;

            // Used to interpret Phi instructions.
            ptr< BasicBlock > m_pPreviousBB;







|


|


|


|

|

|
|







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
            }

        private:
            static optional< Value > ExecuteBuiltinFuncCall( const Value& func, const Term& args );
            static Term BuildUninitializedValue( const Value& type );

            template< typename F >
            optional< Value > executeEqualityBinOp( const llr::BinaryOp& bo, F&& func );

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

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

            template< typename F >
            optional< Value > executeShiftBinOp( const llr::BinaryOp& bo, F&& func );

            Term* calcAddress( const CalcAddress& addr );
            Term* calcAddress( const BaseAddress& baseAddr );
            Term* calcAddress( const TemporaryBaseAddr& va );
            Term* calcAddress( const VarBaseAddr& va );

            llvm::SmallVector< Term, 8 > m_stack;
            optional< Value > m_retVal;
            size_t m_currentFrameStart = 0;

            // Used to interpret Phi instructions.
            ptr< BasicBlock > m_pPreviousBB;
Name change from bs/llr/address.h to bs/llr/calcaddr.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#ifndef GOOSE_LLR_ADDRESS_H
#define GOOSE_LLR_ADDRESS_H

namespace goose::llr
{
    struct TemporaryAddress
    {
        template< typename T >
        TemporaryAddress( uint32_t i, T&& init ) :
            m_initValue( forward< T >( init ) ),
            index( i )
        {}

        auto operator<=>( const TemporaryAddress& rhs ) const
        {
            return index <=> rhs.index;
        }

        ir::Value m_initValue;
        uint32_t index = 0;
    };

    struct VarAddress
    {
        VarAddress( uint32_t i ) : index( i ) {}

        auto operator<=>( const VarAddress& ) const = default;

        uint32_t index = 0;
    };

    // TODO: at some point we'll want to be able to use an integer Value as the base address.
    using BaseAddress = variant
    <
        TemporaryAddress,
        VarAddress
    >;

    static inline strong_ordering operator<=>( const BaseAddress& lhs, const BaseAddress& rhs )
    {
        return visit( [&]< typename L, typename R >( const L& l, const R& r )
        {
            if constexpr( is_same_v< L, R > )
|
|



|


|




|








|

|

|







|
|







1
2
3
4
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_LLR_CALCADDR_H
#define GOOSE_LLR_CALCADDR_H

namespace goose::llr
{
    struct TemporaryBaseAddr
    {
        template< typename T >
        TemporaryBaseAddr( uint32_t i, T&& init ) :
            m_initValue( forward< T >( init ) ),
            index( i )
        {}

        auto operator<=>( const TemporaryBaseAddr& rhs ) const
        {
            return index <=> rhs.index;
        }

        ir::Value m_initValue;
        uint32_t index = 0;
    };

    struct VarBaseAddr
    {
        VarBaseAddr( uint32_t i ) : index( i ) {}

        auto operator<=>( const VarBaseAddr& ) const = default;

        uint32_t index = 0;
    };

    // TODO: at some point we'll want to be able to use an integer Value as the base address.
    using BaseAddress = variant
    <
        TemporaryBaseAddr,
        VarBaseAddr
    >;

    static inline strong_ordering operator<=>( const BaseAddress& lhs, const BaseAddress& rhs )
    {
        return visit( [&]< typename L, typename R >( const L& l, const R& r )
        {
            if constexpr( is_same_v< L, R > )
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
            if( auto cmp = lhs[i] <=> rhs[i]; cmp != 0 )
                return cmp;
        }

        return strong_ordering::equal;
    }

    // This represents an address as stored and manipulated in the llr.
    // This is really a representation of an addres calculation.
    class Address
    {
        public:



            template< typename T >
            Address( T&& baseAddr ) :
                m_baseAddr( forward< T >( baseAddr ) )
            {}

            const auto& baseAddr() const
            {
                return m_baseAddr;
            }

            void appendToPath( uint32_t index )
            {
                m_path.push_back( index );
            }

            const auto& path() const
            {
                return m_path;
            }






            auto operator<=>( const Address& ) const = default;









        private:
            BaseAddress m_baseAddr;

            // Indices of tuple or array elements to follow to reach into nested
            // values, interpreted depending on the types encountered. This is similar
            // in spirit to the indices passed to llvm's GEP instruction.
            AddressPath m_path;
    };

    static bool CanAddressBeEagerlyComputed( const Address& addr )
    {
        return holds_alternative< TemporaryAddress >( addr.baseAddr() );
    }
}

#endif







<
<
|


>
>
>
|
|
|

















>
>
>
>
>
|
>
>
>
>
>
>
>
>









<
<
<
<
<



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
            if( auto cmp = lhs[i] <=> rhs[i]; cmp != 0 )
                return cmp;
        }

        return strong_ordering::equal;
    }



    class CalcAddress
    {
        public:
            CalcAddress( BaseAddress&& baseAddr ) :
                m_baseAddr( move( baseAddr ) )
            {}

            CalcAddress( const BaseAddress& baseAddr ) :
                m_baseAddr( baseAddr )
            {}

            const auto& baseAddr() const
            {
                return m_baseAddr;
            }

            void appendToPath( uint32_t index )
            {
                m_path.push_back( index );
            }

            const auto& path() const
            {
                return m_path;
            }

            auto& path()
            {
                return m_path;
            }

            auto operator<=>( const CalcAddress& ) const = default;

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

            bool isTempRef() const
            {
                return holds_alternative< TemporaryBaseAddr >( baseAddr() );
            }

        private:
            BaseAddress m_baseAddr;

            // Indices of tuple or array elements to follow to reach into nested
            // values, interpreted depending on the types encountered. This is similar
            // in spirit to the indices passed to llvm's GEP instruction.
            AddressPath m_path;
    };





}

#endif
Changes to bs/llr/cfg.h.
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
            template< typename F >
            void forEachBB( F&& func )
            {
                for( auto&& bb : m_basicBlocks )
                    func( bb );
            }

            void setAddressModifiedByLoop( uint32_t loopId, const ir::Term& type, const Address& addr )
            {
                m_loopModifiedAddresses.emplace( make_pair( loopId, addr ), type );
            }

            template< typename F >
            void forEachAddressModifiedInLoop( uint32_t loopId, F&& func ) const
            {
                auto begin = m_loopModifiedAddresses.lower_bound( { loopId, 0U } );
                auto end = m_loopModifiedAddresses.upper_bound( { loopId, ~0U } );

                for( auto it = begin; it != end; ++it )
                    func( it->second, it->first.second );
            }

        private:
            vector< ptr< BasicBlock > > m_basicBlocks;
            ptr< BasicBlock > m_currentBB;

            // All the edges of the CFG in SrcBBIndex, DestBBIndex form
            unordered_multimap< uint32_t, uint32_t > m_edges;

            // For each BB, store the index of its immediate dominator.
            // May be be empty if it has not (yet) been computed.
            vector< uint32_t > m_idoms;

            // For each loop, store all of the adresses modified
            // during that loop.
            // May be be empty if it has not (yet) been computed.
            multimap< pair< uint32_t, Address >, ir::Term > m_loopModifiedAddresses;

            // The number of temporary indices used by this CFG.
            uint32_t m_temporariesCount = 0;

            uint32_t m_loopCount = 0;

            bool m_poisoned = false;







|







|
|



















|







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
            template< typename F >
            void forEachBB( F&& func )
            {
                for( auto&& bb : m_basicBlocks )
                    func( bb );
            }

            void setAddressModifiedByLoop( uint32_t loopId, const ir::Term& type, const CalcAddress& addr )
            {
                m_loopModifiedAddresses.emplace( make_pair( loopId, addr ), type );
            }

            template< typename F >
            void forEachAddressModifiedInLoop( uint32_t loopId, F&& func ) const
            {
                auto begin = m_loopModifiedAddresses.lower_bound( { loopId, CalcAddress( 0U ) } );
                auto end = m_loopModifiedAddresses.upper_bound( { loopId, CalcAddress( ~0U ) } );

                for( auto it = begin; it != end; ++it )
                    func( it->second, it->first.second );
            }

        private:
            vector< ptr< BasicBlock > > m_basicBlocks;
            ptr< BasicBlock > m_currentBB;

            // All the edges of the CFG in SrcBBIndex, DestBBIndex form
            unordered_multimap< uint32_t, uint32_t > m_edges;

            // For each BB, store the index of its immediate dominator.
            // May be be empty if it has not (yet) been computed.
            vector< uint32_t > m_idoms;

            // For each loop, store all of the adresses modified
            // during that loop.
            // May be be empty if it has not (yet) been computed.
            multimap< pair< uint32_t, CalcAddress >, ir::Term > m_loopModifiedAddresses;

            // The number of temporary indices used by this CFG.
            uint32_t m_temporariesCount = 0;

            uint32_t m_loopCount = 0;

            bool m_poisoned = false;
Changes to bs/llr/cfgviz.cpp.
70
71
72
73
74
75
76









77
78
79
80
81
82
83
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder );

        // TODO also visualize the func and the args
        builder.output() << "Call";
    }










    void CfgViz( GraphVizBuilder& builder, const CreateTemporary& instr )
    {
        builder.queueWork( [&]
        {
            CfgViz( builder, instr.value() );
        } );







>
>
>
>
>
>
>
>
>







70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder );

        // TODO also visualize the func and the args
        builder.output() << "Call";
    }

    void CfgViz( GraphVizBuilder& builder, const Reference& instr )
    {
        GraphVizBuilder::Row row( builder );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder );

        builder.output() << "Reference";
    }

    void CfgViz( GraphVizBuilder& builder, const CreateTemporary& instr )
    {
        builder.queueWork( [&]
        {
            CfgViz( builder, instr.value() );
        } );
Changes to bs/llr/cfgviz.h.
15
16
17
18
19
20
21

22
23
24
25
26
27
28
    extern void CfgViz( GraphVizBuilder& builder, const ptr< BasicBlock >& bb );
    extern void CfgViz( GraphVizBuilder& builder, const ir::Value& val );

    extern void CfgViz( GraphVizBuilder& builder, const Instruction& instr );
    extern void CfgViz( GraphVizBuilder& builder, const Terminator& t );

    extern void CfgViz( GraphVizBuilder& builder, const Call& 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 LoadConstStr& instr );







>







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    extern void CfgViz( GraphVizBuilder& builder, const ptr< BasicBlock >& bb );
    extern void CfgViz( GraphVizBuilder& builder, const ir::Value& val );

    extern void CfgViz( GraphVizBuilder& builder, const Instruction& instr );
    extern void CfgViz( GraphVizBuilder& builder, const Terminator& t );

    extern void CfgViz( GraphVizBuilder& builder, const Call& instr );
    extern void CfgViz( GraphVizBuilder& builder, const CalcAddress& 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 LoadConstStr& instr );
Changes to bs/llr/helpers.cpp.
13
14
15
16
17
18
19









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

        return val.llr()->canBeEagerlyEvaluated();
    }









}







>
>
>
>
>
>
>
>
>

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    bool CanValueBeEagerlyEvaluated( const ir::Value& val )
    {
        if( val.isConstant() )
            return true;

        return val.llr()->canBeEagerlyEvaluated();
    }

    bool IsCompileTimeTempRef( const ir::Value& val )
    {
        if( !val.isConstant() )
            return false;

        const auto* pRefInst = get_if< CalcAddress >( &val.llr()->content() );
        return pRefInst && pRefInst->isTempRef();
    }
}
Changes to bs/llr/helpers.h.
1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
#ifndef GOOSE_LLR_HELPERS_H
#define GOOSE_LLR_HELPERS_H

namespace goose::llr
{
    class Instruction;

    bool IsValueConstantOrExecutable( const ir::Value& val );
    bool CanValueBeEagerlyEvaluated( const ir::Value& val );


    template< typename T, typename I >
    auto BuildComputedValue( T&& type, I&& instr )
    {
        return ir::Value( forward< T >( type ),
            make_shared< Instruction >( forward< I >( instr ) ) );
    }









>







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

namespace goose::llr
{
    class Instruction;

    bool IsValueConstantOrExecutable( const ir::Value& val );
    bool CanValueBeEagerlyEvaluated( const ir::Value& val );
    bool IsCompileTimeTempRef( const ir::Value& val );

    template< typename T, typename I >
    auto BuildComputedValue( T&& type, I&& instr )
    {
        return ir::Value( forward< T >( type ),
            make_shared< Instruction >( forward< I >( instr ) ) );
    }
Changes to bs/llr/instruction.h.
1
2
3
4
5
6
7
8
9
10
11
12
13




14
15
16
17
18
19
20
#ifndef GOOSE_LLR_INSTRUCTION_H
#define GOOSE_LLR_INSTRUCTION_H

namespace goose::llr
{
    class CFG;

    class Instruction
    {
        public:
            Instruction( Call&& c ) :
                m_content( move( c ) )
            {}





            Instruction( CreateTemporary&& ct ) :
                m_content( move( ct ) )
            {}

            Instruction( GetTemporary&& gt ) :
                m_content( move( gt ) )













>
>
>
>







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_LLR_INSTRUCTION_H
#define GOOSE_LLR_INSTRUCTION_H

namespace goose::llr
{
    class CFG;

    class Instruction
    {
        public:
            Instruction( Call&& c ) :
                m_content( move( c ) )
            {}

            Instruction( CalcAddress&& cad ) :
                m_content( move( cad ) )
            {}

            Instruction( CreateTemporary&& ct ) :
                m_content( move( ct ) )
            {}

            Instruction( GetTemporary&& gt ) :
                m_content( move( gt ) )
143
144
145
146
147
148
149



150
151
152
153
154
155
156
            Instruction( Placeholder&& x ) :
                m_content( move( x ) )
            {}

            using Content = variant
            <
                Call,



                CreateTemporary,
                GetTemporary,
                AllocVar,
                Load,
                Store,
                Phi,
                LoadConstStr,







>
>
>







147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
            Instruction( Placeholder&& x ) :
                m_content( move( x ) )
            {}

            using Content = variant
            <
                Call,

                CalcAddress,

                CreateTemporary,
                GetTemporary,
                AllocVar,
                Load,
                Store,
                Phi,
                LoadConstStr,
Changes to bs/llr/llr.h.
11
12
13
14
15
16
17

18
19
20
21
22
23
24
    static constexpr uint32_t InvalidVarId = numeric_limits< uint32_t >::max();

    class CFG;
}

#include "helpers.h"
#include "address.h"

#include "call.h"
#include "loadconst.h"
#include "createtemporary.h"
#include "gettemporary.h"
#include "allocvar.h"
#include "load.h"
#include "store.h"







>







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    static constexpr uint32_t InvalidVarId = numeric_limits< uint32_t >::max();

    class CFG;
}

#include "helpers.h"
#include "address.h"
#include "calcaddr.h"
#include "call.h"
#include "loadconst.h"
#include "createtemporary.h"
#include "gettemporary.h"
#include "allocvar.h"
#include "load.h"
#include "store.h"
Changes to bs/llr/load.h.
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

            const auto& addr() const { return m_addr; }
            const auto& type() const { return m_type; }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const
            {
                return CanAddressBeEagerlyComputed( m_addr );
            }

        private:
            Address m_addr;
            ir::Term m_type;
    };
}

#endif







|



|





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

            const auto& addr() const { return m_addr; }
            const auto& type() const { return m_type; }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const
            {
                return IsCompileTimeTempRef( m_addr );
            }

        private:
            ir::Value m_addr;
            ir::Term m_type;
    };
}

#endif
Changes to bs/llr/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
#include "llr.h"

namespace goose::llr
{
    void MarkAddrChangedByLoop( const ptr< CFG >& cfg, uint32_t loopId, const ir::Term& type, const Address& addr )
    {
        cfg->setAddressModifiedByLoop( loopId, type, addr );

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

        if( pHeader->loopId() )
            MarkAddrChangedByLoop( cfg, pHeader->loopId(), type, addr );
    }

    void ComputeLoopModifiedAddrs( const ptr< CFG >& cfg )
    {
        cfg->forEachBB( [&]( auto&& bb )
        {
            for( auto&& instr : *bb )
            {
                if( auto st = get_if< Store >( &instr.content() ) )











                    MarkAddrChangedByLoop( cfg, bb->index(), st->val().type(), st->addr() );
            }
        } );
    }
}




|















|
>
>
>
>
>
>
>
>
>
>
>
|




1
2
3
4
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
#include "llr.h"

namespace goose::llr
{
    void MarkAddrChangedByLoop( const ptr< CFG >& cfg, uint32_t loopId, const ir::Term& type, const CalcAddress& addr )
    {
        cfg->setAddressModifiedByLoop( loopId, type, addr );

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

        if( pHeader->loopId() )
            MarkAddrChangedByLoop( cfg, pHeader->loopId(), type, addr );
    }

    void ComputeLoopModifiedAddrs( const ptr< CFG >& cfg )
    {
        cfg->forEachBB( [&]( auto&& bb )
        {
            for( auto&& instr : *bb )
            {
                auto st = get_if< Store >( &instr.content() );
                if( !st )
                    continue;

                auto llr = st->addr().llr();
                if( !llr )
                    continue;

                auto ref = get_if< CalcAddress >( &llr->content() );
                if( !ref )
                    continue;

                MarkAddrChangedByLoop( cfg, bb->index(), st->val().type(), *ref );
            }
        } );
    }
}
Changes to bs/llr/store.h.
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
            bool canBeExecuted() const
            {
                return IsValueConstantOrExecutable( m_val );
            }

            bool canBeEagerlyEvaluated() const
            {
                return CanAddressBeEagerlyComputed( m_addr )
                    && CanValueBeEagerlyEvaluated( m_val );
            }

        private:
            Address m_addr;
            ir::Value m_val;
    };
}

#endif







|




|





18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
            bool canBeExecuted() const
            {
                return IsValueConstantOrExecutable( m_val );
            }

            bool canBeEagerlyEvaluated() const
            {
                return IsCompileTimeTempRef( m_addr )
                    && CanValueBeEagerlyEvaluated( m_val );
            }

        private:
            ir::Value m_addr;
            ir::Value m_val;
    };
}

#endif
Changes to bs/sema/invocation.cpp.
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
        auto loc = Location::CreateSpanningLocation( callee.locationId(), args.locationId() );
        if( loc == ~0 )
            DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );

        auto result = pInvRule->resolveInvocation( c, loc, callee, args.val() ).setLocationId( loc );

        // If the result is non-void, register it for destruction.
        // TODO probably just always store it as a numbered temporary
        // since we'll need this later to implement the dot operator
        // (as it will need to create a reference to the temporary)
        if( c.codeBuilder() && result.type() != GetValueType< void >() )
        {
            if( auto cfg = c.codeBuilder()->cfg() )
                c.codeBuilder()->pushLiveValue( result, cfg->getNewTemporaryIndex() );
        }

        return result;







<
<
<







51
52
53
54
55
56
57



58
59
60
61
62
63
64
        auto loc = Location::CreateSpanningLocation( callee.locationId(), args.locationId() );
        if( loc == ~0 )
            DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );

        auto result = pInvRule->resolveInvocation( c, loc, callee, args.val() ).setLocationId( loc );

        // If the result is non-void, register it for destruction.



        if( c.codeBuilder() && result.type() != GetValueType< void >() )
        {
            if( auto cfg = c.codeBuilder()->cfg() )
                c.codeBuilder()->pushLiveValue( result, cfg->getNewTemporaryIndex() );
        }

        return result;
Changes to bs/sema/typecheck.cpp.
70
71
72
73
74
75
76






77
78
79
80
81
82
83

        // Let's not really worry about how to properly report this for now.
        if( ambiguous )
            throw runtime_error( "ambiguous type checking rule" );

        if( !bestRule )
            co_return;







        co_yield bestRule->func( lhs, rhs, context );
    }

    TCGen Unify( const Term& lhs, const Term& rhs, const TypeCheckingContext& context )
    {
        const auto& rules = context.rules()->uniRules();







>
>
>
>
>
>







70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

        // Let's not really worry about how to properly report this for now.
        if( ambiguous )
            throw runtime_error( "ambiguous type checking rule" );

        if( !bestRule )
            co_return;

#ifdef TCRULES_DEBUG
        cout << "calling typecheck rule " << bestRule->infos.pFilename << ':' << bestRule->infos.line << endl;
        cout << "  lhs " << lhs << endl;
        cout << "  rhs " << rhs << endl;
#endif

        co_yield bestRule->func( lhs, rhs, context );
    }

    TCGen Unify( const Term& lhs, const Term& rhs, const TypeCheckingContext& context )
    {
        const auto& rules = context.rules()->uniRules();
Changes to bs/verify/call.cpp.
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
                )
            );

            if( !result )
                return true;

            auto&& [type, val, locId] = *result;
            auto paramVal = BuildComputedValue( type, llr::Load( llr::VarAddress( varId++ ), type ) );

            if( auto zv = BuildZ3ExprFromValue( cb, paramVal ) )
            {
                ForEachPredicate( cb, type, zv->expr, [&]( auto&& z3expr, auto locId )
                {
                    DiagnosticsContext dc( instr.func().locationId(), "At this call." );
                    b.checkAssertion( z3expr, locId );







|







55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
                )
            );

            if( !result )
                return true;

            auto&& [type, val, locId] = *result;
            auto paramVal = BuildComputedValue( type, CalcAddress( llr::VarBaseAddr( varId++ ) ) );

            if( auto zv = BuildZ3ExprFromValue( cb, paramVal ) )
            {
                ForEachPredicate( cb, type, zv->expr, [&]( auto&& z3expr, auto locId )
                {
                    DiagnosticsContext dc( instr.func().locationId(), "At this call." );
                    b.checkAssertion( z3expr, locId );
Changes to bs/verify/comptime.cpp.
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
    bool VerifyCompTimeExpr( Builder& b, const Call& instr );

    template< typename T >
    bool VerifyCompTimeExpr( Builder& b, const T& instr )
    {
        assert( false );
        return false;
    }

    bool VerifyCompTimeExpr( const sema::Context& c, const Value& val )
    {
        if( val.isConstant() || val.isPoison() )
            return true;








<







8
9
10
11
12
13
14

15
16
17
18
19
20
21
{
    bool VerifyCompTimeExpr( Builder& b, const Call& instr );

    template< typename T >
    bool VerifyCompTimeExpr( Builder& b, const T& instr )
    {
        assert( false );

    }

    bool VerifyCompTimeExpr( const sema::Context& c, const Value& val )
    {
        if( val.isConstant() || val.isPoison() )
            return true;

141
142
143
144
145
146
147

148
149
150
151
152
153
154
155
                )
            );

            if( !result )
                return true;

            auto&& [type, val, locId] = *result;

            auto paramVal = BuildComputedValue( type, llr::Load( llr::VarAddress( varId++ ), type ) );

            if( auto zv = BuildZ3ExprFromValue( b, paramVal ) )
            {
                ForEachPredicate( b, type, zv->expr, [&]( auto&& z3expr, auto locId )
                {
                    DiagnosticsContext dc( instr.func().locationId(), "At this compilation-time call." );
                    b.checkAssertion( z3expr, locId );







>
|







140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
                )
            );

            if( !result )
                return true;

            auto&& [type, val, locId] = *result;
            builtins::Reference argRef( builtins::ReferenceType{ type, TSID( const ) }, llr::VarBaseAddr( varId++ ) );
            auto paramVal = BuildComputedValue( type, llr::Load( ToValue( argRef ), type ) );

            if( auto zv = BuildZ3ExprFromValue( b, paramVal ) )
            {
                ForEachPredicate( b, type, zv->expr, [&]( auto&& z3expr, auto locId )
                {
                    DiagnosticsContext dc( instr.func().locationId(), "At this compilation-time call." );
                    b.checkAssertion( z3expr, locId );
Changes to bs/verify/func.cpp.
67
68
69
70
71
72
73

74
75
76
77
78
79
80
81
                        )
                    );

                    if( !result )
                        return true;

                    auto&& [type, val, locId] = *result;

                    auto paramVal = BuildComputedValue( type, llr::Load( llr::VarAddress( varId ), type ) );

                    // Initialize every parameter containing variable with an freshly named constant of the right type.
                    if( auto paramInit = BuildZ3ConstantFromType( m_builder, type, format( "p{}", varId ) ) )
                        m_builder.setVar( varId, move( *paramInit ) );

                    ++varId;








>
|







67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
                        )
                    );

                    if( !result )
                        return true;

                    auto&& [type, val, locId] = *result;
                    builtins::Reference argRef( builtins::ReferenceType{ type, TSID( const ) }, llr::VarBaseAddr( varId ) );
                    auto paramVal = BuildComputedValue( type, llr::Load( ToValue( argRef ), type ) );

                    // Initialize every parameter containing variable with an freshly named constant of the right type.
                    if( auto paramInit = BuildZ3ConstantFromType( m_builder, type, format( "p{}", varId ) ) )
                        m_builder.setVar( varId, move( *paramInit ) );

                    ++varId;

106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
            bool result = buildZ3Expressions( *m_cfg->entryBB(), nullptr );

            if( ms_TraceMode )
                cout << "=== End function verification trace ===\n\n";

            return result;
        }
        catch( z3::exception e )
        {
            cerr << "Func: z3 exception: " << e << endl;

            // rethrow so we get a stack trace dump to know where the error happened
            throw;
        }
    }







|







107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
            bool result = buildZ3Expressions( *m_cfg->entryBB(), nullptr );

            if( ms_TraceMode )
                cout << "=== End function verification trace ===\n\n";

            return result;
        }
        catch( const z3::exception& e )
        {
            cerr << "Func: z3 exception: " << e << endl;

            // rethrow so we get a stack trace dump to know where the error happened
            throw;
        }
    }
Changes to bs/verify/storage.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "verify.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::builtins;

namespace goose::verify
{
    optional< Z3Val > LoadFromAddress( Builder& b, const Address& addr )
    {
        auto val = LoadFromAddress( b, addr.baseAddr() );
        if( !val )
            return nullopt;

        for( auto&& index : addr.path() )
        {








|







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

using namespace goose;
using namespace goose::builtins;

namespace goose::verify
{
    optional< Z3Val > LoadFromAddress( Builder& b, const CalcAddress& addr )
    {
        auto val = LoadFromAddress( b, addr.baseAddr() );
        if( !val )
            return nullopt;

        for( auto&& index : addr.path() )
        {
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
    {
        return visit( [&]( auto&& ba )
        {
            return LoadFromAddress( b, ba );
        }, baseAddr );
    }

    optional< Z3Val > LoadFromAddress( Builder& b, const llr::TemporaryAddress& ta )
    {
        auto zv = b.retrieveVar( ta.index );
        if( zv )
            return zv;

        return b.setVar( ta.index, ta.m_initValue );
    }

    optional< Z3Val > LoadFromAddress( Builder& b, const llr::VarAddress& va )
    {
        return b.retrieveVar( va.index );
    }

    optional< z3::expr > ModifyAggregate( Builder& b, const Z3Val& aggregate, const AddressPath& path, uint32_t index, Z3Val&& valToStore )
    {
        auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), ValueToIRExpr( aggregate.type ) );







|








|







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
    {
        return visit( [&]( auto&& ba )
        {
            return LoadFromAddress( b, ba );
        }, baseAddr );
    }

    optional< Z3Val > LoadFromAddress( Builder& b, const llr::TemporaryBaseAddr& ta )
    {
        auto zv = b.retrieveVar( ta.index );
        if( zv )
            return zv;

        return b.setVar( ta.index, ta.m_initValue );
    }

    optional< Z3Val > LoadFromAddress( Builder& b, const llr::VarBaseAddr& va )
    {
        return b.retrieveVar( va.index );
    }

    optional< z3::expr > ModifyAggregate( Builder& b, const Z3Val& aggregate, const AddressPath& path, uint32_t index, Z3Val&& valToStore )
    {
        auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), ValueToIRExpr( aggregate.type ) );
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
            else
                args.push_back( move( elemExpr ) );
        }

        return tinfo->ctor( args );
    }

    void StoreToAddress( Builder& b, const Address& addr, Z3Val&& valToStore )
    {
        if( addr.path().empty() )
        {
            StoreToAddress( b, addr.baseAddr(), move( valToStore ) );
            return;
        }








|







91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
            else
                args.push_back( move( elemExpr ) );
        }

        return tinfo->ctor( args );
    }

    void StoreToAddress( Builder& b, const CalcAddress& addr, Z3Val&& valToStore )
    {
        if( addr.path().empty() )
        {
            StoreToAddress( b, addr.baseAddr(), move( valToStore ) );
            return;
        }

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
    {
        return visit( [&]( auto&& ba )
        {
            return StoreToAddress( b, ba, move( val ) );
        }, baseAddr );
    }

    void StoreToAddress( Builder& b, const llr::TemporaryAddress& ta, Z3Val&& val )
    {
        b.setVar( ta.index, move( val ) );
    }

    void StoreToAddress( Builder& b, const llr::VarAddress& va, Z3Val&& val )
    {
        b.setVar( va.index, move( val ) );
    }

    void HavocAddress( Builder& b, uint32_t bbIndex, const Term& type, const Address& addr )
    {
        auto valToStore = BuildZ3ConstantFromType( b, type, format( "v{}", b.newUniqueId() ) );
        if( !valToStore )
            return;

        StoreToAddress( b, addr, move( *valToStore ) );
    }
}







|




|




|








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
    {
        return visit( [&]( auto&& ba )
        {
            return StoreToAddress( b, ba, move( val ) );
        }, baseAddr );
    }

    void StoreToAddress( Builder& b, const llr::TemporaryBaseAddr& ta, Z3Val&& val )
    {
        b.setVar( ta.index, move( val ) );
    }

    void StoreToAddress( Builder& b, const llr::VarBaseAddr& va, Z3Val&& val )
    {
        b.setVar( va.index, move( val ) );
    }

    void HavocAddress( Builder& b, uint32_t bbIndex, const Term& type, const CalcAddress& addr )
    {
        auto valToStore = BuildZ3ConstantFromType( b, type, format( "v{}", b.newUniqueId() ) );
        if( !valToStore )
            return;

        StoreToAddress( b, addr, move( *valToStore ) );
    }
}
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
#ifndef GOOSE_VERIFY_STORAGE_H
#define GOOSE_VERIFY_STORAGE_H

namespace goose::verify
{
    extern optional< Z3Val > LoadFromAddress( Builder& b, const Address& addr );
    extern optional< Z3Val > LoadFromAddress( Builder& b, const BaseAddress& baseAddr );
    extern optional< Z3Val > LoadFromAddress( Builder& b, const llr::TemporaryAddress& ta );
    extern optional< Z3Val > LoadFromAddress( Builder& b, const llr::VarAddress& va );

    extern void StoreToAddress( Builder& b, const Address& addr, Z3Val&& val );
    extern void StoreToAddress( Builder& b, const BaseAddress& baseAddr, Z3Val&& val );
    extern void StoreToAddress( Builder& b, const llr::TemporaryAddress& ta, Z3Val&& val );
    extern void StoreToAddress( Builder& b, const llr::VarAddress& va, Z3Val&& val );

    extern void HavocAddress( Builder& b, uint32_t bbIndex, const Term& type, const Address& addr );
}

#endif





|

|
|

|

|
|

|



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

namespace goose::verify
{
    extern optional< Z3Val > LoadFromAddress( Builder& b, const llr::CalcAddress& addr );
    extern optional< Z3Val > LoadFromAddress( Builder& b, const BaseAddress& baseAddr );
    extern optional< Z3Val > LoadFromAddress( Builder& b, const llr::TemporaryBaseAddr& ta );
    extern optional< Z3Val > LoadFromAddress( Builder& b, const llr::VarBaseAddr& va );

    extern void StoreToAddress( Builder& b, const llr::CalcAddress& addr, Z3Val&& val );
    extern void StoreToAddress( Builder& b, const BaseAddress& baseAddr, Z3Val&& val );
    extern void StoreToAddress( Builder& b, const llr::TemporaryBaseAddr& ta, Z3Val&& val );
    extern void StoreToAddress( Builder& b, const llr::VarBaseAddr& va, Z3Val&& val );

    extern void HavocAddress( Builder& b, uint32_t bbIndex, const Term& type, const llr::CalcAddress& addr );
}

#endif
Changes to bs/verify/value.cpp.
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
    optional< Z3Val > BuildZ3Op( Builder& b, const T& instr )
    {
        return nullopt;
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Load& instr )
    {
        return LoadFromAddress( b, instr.addr() );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Store& instr )
    {
        if( auto zv = BuildZ3ExprFromValue( b, instr.val() ) )
            StoreToAddress( b, instr.addr(), move( *zv ) );
        return nullopt;
    }

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

    optional< Z3Val > BuildZ3Op( Builder& b, const CreateTemporary& instr )







|





|







150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
    optional< Z3Val > BuildZ3Op( Builder& b, const T& instr )
    {
        return nullopt;
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Load& instr )
    {
        return LoadFromAddress( b, FromValue< builtins::Reference >( instr.addr() )->address() );
    }

    optional< Z3Val > BuildZ3Op( Builder& b, const Store& instr )
    {
        if( auto zv = BuildZ3ExprFromValue( b, instr.val() ) )
            StoreToAddress( b, FromValue< builtins::Reference >( instr.addr() )->address(), move( *zv ) );
        return nullopt;
    }

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

    optional< Z3Val > BuildZ3Op( Builder& b, const CreateTemporary& instr )
Changes to tests/noprelude/verify/z3out-test-4.txt.
51
52
53
54
55
56
57
58
59
60
61
62
63
64
(= b1 true)
check_unsat (not (distinct v80 v81))
assume (> r79 0)
check_unsat (not (distinct r79 0))
=== End function verification trace ===

=== Checking compilation-time call ===
assume (= v82 5)
check_unsat (not (distinct v82 0))

=== Begin function verification trace ===
(= b1 true)
=== End function verification trace ===








|
|





51
52
53
54
55
56
57
58
59
60
61
62
63
64
(= b1 true)
check_unsat (not (distinct v80 v81))
assume (> r79 0)
check_unsat (not (distinct r79 0))
=== End function verification trace ===

=== Checking compilation-time call ===
assume (= v84 5)
check_unsat (not (distinct v84 0))

=== Begin function verification trace ===
(= b1 true)
=== End function verification trace ===