Goose  Artifact [03e2523de9]

Artifact 03e2523de9578e1272dbdaa5f9256342faf8f37dce884fb18ccb582daa17909d:

  • File bs/builtins/operators/logic.cpp — part of check-in [035cf4826c] at 2019-08-06 00:33:17 on branch trunk —
    • Improved the operator parsing rules construction helpers to support both an unary and an infix operators sharing the same identifier.
    • Updated the comma operator implementation to use the same helpers as other operators, which makes it possible to overload it.
    (user: achavasse size: 9408)

#include "builtins/builtins.h"
#include "precedence.h"
#include "helpers.h"

using namespace empathy;
using namespace empathy::ir;
using namespace empathy::llr;
using namespace empathy::parse;

namespace empathy::builtins
{
    void SetupLogicOps( Env& e )
    {
        BuildParseRule( e, "!"_sid,
            PrefixOp( "operator_logical_not"_sid, precedence::UnaryOps,
                ForType< bool >( []( auto&& operand ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        Xor( operand, ToValue( true ) ) );
                } )
            )
        );

        BuildParseRule( e, "~"_sid,
            PrefixOp( "operator_bitwise_not"_sid, precedence::UnaryOps,
                ForType< CustomPattern< RTInteger, RTInteger::Pattern > >( []( auto&& operand ) -> Value
                {
                    auto opTypeVal = *ValueFromIRExpr( operand.type() );
                    auto opType = *FromValue< RTInteger >( opTypeVal );
                    return BuildComputedValue( operand.type(),
                        Xor( operand,
                            BuildComputedValue( operand.type(),
                                LoadConstInt( static_cast< llvm::IntegerType* >( GetLLVMType( opTypeVal ) ),
                                APSInt::getMaxValue( opType.m_numBits, true) )
                            )
                        ) );
                } )
            )
        );

        BuildParseRule( e, "^"_sid,
            LeftAssInfixOp( "operator_xor"_sid, precedence::OrOp,
                // Logical xor
                ForType< bool, Xor >(),

                // ct_int xor
                ForType< BigInt, Xor >(),

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

        BuildParseRule( e, "|"_sid,
            LeftAssInfixOp( "operator_or"_sid, precedence::OrOp,
                // ct_int or
                ForType< BigInt, Or >(),

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

                // bool or
                ForType< bool >( []( auto&& lhs, auto&& rhs ) -> Value
                {
                    // Build a CFG that implements the control flow for
                    // shortcut evaluation.
                    auto cfg = make_shared< CFG >();
                    auto pLhsBB = cfg->entryBB();
                    auto pRhsBB = cfg->createBB();
                    auto pEndBB = cfg->createBB();

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

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

                    auto resultIndex = cfg->getNewTemporaryIndex();

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

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

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

                    pEndBB->emplace_back( move( phi ) );

                    // Build the result val which pulls the temporary created by the
                    // cfg above.
                    auto resultVal = BuildComputedValue( GetValueType< bool >(),
                        GetTemporary( cfg->uniqueId(), resultIndex ) );

                    // Return it
                    pEndBB->setTerminator( Ret( move( resultVal ) ) );

                    // Pachage our cfg in a value with an inline CFG instruction.
                    return BuildComputedValue( GetValueType< bool >(), move( cfg ) );
                } )
            )
        );

        BuildParseRule( e, "&"_sid,
            LeftAssInfixOp( "operator_and"_sid, precedence::AndOp,
                // ct_int and
                ForType< BigInt, And >(),

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

                // bool and
                ForType< bool >( []( auto&& lhs, auto&& rhs ) -> Value
                {
                    // Build a CFG that implements the control flow for
                    // shortcut evaluation.
                    auto cfg = make_shared< CFG >();
                    auto pLhsBB = cfg->entryBB();
                    auto pRhsBB = cfg->createBB();
                    auto pEndBB = cfg->createBB();

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

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

                    auto resultIndex = cfg->getNewTemporaryIndex();

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

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

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

                    pEndBB->emplace_back( move( phi ) );

                    // Build the result val which pulls the temporary created by the
                    // cfg above.
                    auto resultVal = BuildComputedValue( GetValueType< bool >(),
                        GetTemporary( cfg->uniqueId(), resultIndex ) );

                    // Return it
                    pEndBB->setTerminator( Ret( move( resultVal ) ) );

                    // Pachage our cfg in a value with an inline CFG instruction.
                    return BuildComputedValue( GetValueType< bool >(), move( cfg ) );
                } )
            )
        );

        BuildParseRule( e, "<<"_sid,
            LeftAssInfixOp( "operator_shift_left"_sid, precedence::BitShiftOp,
                // ct_int left shift
                ForType< BigInt, Shl >(),

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

        BuildParseRule( e, ">>"_sid,
            LeftAssInfixOp( "operator_shift_right"_sid, precedence::BitShiftOp,
                // ct_int right shift
                ForType< BigInt, AShr >(),

                // runtime signed integer right shift, defined to work for any two integers of same
                // bit size.
                ForType< CustomPattern< RTInteger, RTInteger::PatternSigned > >(
                []( auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( lhs.type(),
                        AShr( lhs, rhs ) );
                } ),

                // runtime unsigned integer right shift, defined to work for any two integers of same
                // bit size.
                ForType< CustomPattern< RTInteger, RTInteger::PatternUnsigned > >(
                []( auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( lhs.type(),
                        LShr( lhs, rhs ) );
                } )
            )
        );
    }
}