#include "builtins/builtins.h"
#include "precedence.h"
#include "helpers.h"
#include "tuple.h"
using namespace goose;
using namespace goose::ir;
using namespace goose::llr;
using namespace goose::parse;
namespace goose::builtins
{
void SetupLogicOps( Env& e )
{
BuildParseRule( e, "!"_sid,
PrefixOp( "operator_logical_not"_sid, precedence::UnaryOps,
BuildGenericTupleOperator(),
ForType< bool >( []( auto&& c, auto&& operand ) -> Value
{
return BuildComputedValue( GetValueType< bool >(),
llr::Not( operand ) );
} )
)
);
BuildParseRule( e, "~"_sid,
PrefixOp( "operator_bitwise_not"_sid, precedence::UnaryOps,
BuildGenericTupleOperator(),
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >( []( auto&& c, auto&& operand ) -> Value
{
auto opTypeVal = *ValueFromIRExpr( operand.type() );
auto opType = *FromValue< IntegerType >( opTypeVal );
return BuildComputedValue( operand.type(),
Xor( operand,
Value( operand.type(), APSInt::getMaxValue( opType.m_numBits, true ) )
) );
} )
)
);
BuildParseRule( e, "^"_sid,
LeftAssInfixOp( "operator_xor"_sid, precedence::OrOp,
BuildGenericTupleOperator(),
// Logical xor
ForType< bool, Xor >(),
// ct_int xor
ForType< BigInt, Xor >(),
// runtime integer xor, defined to work for any two integers of same
// bit size and signedness.
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( lhs.type(),
Xor( lhs, rhs ) );
} )
)
);
BuildParseRule( e, "|"_sid,
LeftAssInfixOp( "operator_or"_sid, precedence::OrOp,
BuildGenericTupleOperator(),
// ct_int or
ForType< BigInt, Or >(),
// runtime integer or, defined to work for any two integers of same
// bit size and signedness.
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( lhs.type(),
Or( lhs, rhs ) );
} ),
// verification expressions bool or
ForVerificationType< bool, Or >(),
// bool or
ForType< bool >( []< typename L, typename R >( auto&& c, L&& lhs, R&& rhs ) -> Value
{
// Handle the case where lhs is constant, so that
// the result gets properly eagerly evaluated (in case
// we are using the expression as a compile time constant)
if( lhs.isConstant() )
{
if( *FromValue< bool >( lhs ) )
return forward< L >( lhs );
return forward< R >( rhs );
}
// Build the control flow for shortcut evaluation.
auto& cb = c.codeBuilder();
assert( cb );
const auto& cfg = cb->cfg();
const auto& predBB = cfg->currentBB();
auto pRhsBB = cfg->createBB();
auto pSuccBB = cfg->createBB();
// If the lhs is true, skip to the end directly.
// Otherwise, jump to the BB that computes rhs.
predBB->setTerminator( CondBranch( lhs, pSuccBB, pRhsBB ) );
auto rhsIndex = cfg->getNewTemporaryIndex();
pRhsBB->emplace_back( CreateTemporary( rhsIndex, rhs ) );
pRhsBB->setTerminator( Branch( pSuccBB ) );
auto resultIndex = cfg->getNewTemporaryIndex();
// Build the Phi instruction that will collect the final result.
auto phi = Phi( *ValueFromIRExpr( GetValueType< bool >() ), 2,
resultIndex );
// If coming directly from the lhs BB, we know the result is true.
phi.setIncoming( predBB, ToValue( true ) );
// Otherwise, the result is whatever was computed by the rhs block.
phi.setIncoming( pRhsBB, BuildComputedValue( GetValueType< bool >(),
GetTemporary( GetValueType< bool >(), rhsIndex ) ) );
pSuccBB->emplace_back( move( phi ) );
cfg->setCurrentBB( pSuccBB );
// Build the result val which pulls the temporary created above.
return BuildComputedValue( GetValueType< bool >(),
GetTemporary( GetValueType< bool >(), resultIndex ) );
} )
)
);
BuildParseRule( e, "&"_sid,
LeftAssInfixOp( "operator_and"_sid, precedence::AndOp,
BuildGenericTupleOperator(),
// ct_int and
ForType< BigInt, And >(),
// runtime integer and, defined to work for any two integers of same
// bit size and signedness.
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( lhs.type(),
And( lhs, rhs ) );
} ),
// verification expressions bool and
ForVerificationType< bool, And >(),
// bool and
ForType< bool >( []< typename L, typename R >( auto&& c, L&& lhs, R&& rhs ) -> Value
{
// Handle the case where lhs is constant, so that
// the result gets properly eagerly evaluated (in case
// we are using the expression as a compile time constant)
if( lhs.isConstant() )
{
if( *FromValue< bool >( lhs ) )
return forward< R >( rhs );
return forward< L >( lhs );
}
// Build the control flow for shortcut evaluation.
auto& cb = c.codeBuilder();
assert( cb );
const auto& cfg = cb->cfg();
const auto& predBB = cfg->currentBB();
auto pRhsBB = cfg->createBB();
auto pSuccBB = cfg->createBB();
// If the lhs is false, skip to the end directly.
// Otherwise, jump to the BB that computes rhs.
predBB->setTerminator( CondBranch( lhs, pRhsBB, pSuccBB ) );
auto rhsIndex = cfg->getNewTemporaryIndex();
pRhsBB->emplace_back( CreateTemporary( rhsIndex, rhs ) );
pRhsBB->setTerminator( Branch( pSuccBB ) );
auto resultIndex = cfg->getNewTemporaryIndex();
// Build the Phi instruction that will collect the final result.
auto phi = Phi( *ValueFromIRExpr( GetValueType< bool >() ), 2,
resultIndex );
// If coming directly from the lhs BB, we know the result is true.
phi.setIncoming( predBB, ToValue( false ) );
// Otherwise, the result is whatever was computed by the rhs block.
phi.setIncoming( pRhsBB, BuildComputedValue( GetValueType< bool >(),
GetTemporary( GetValueType< bool >(), rhsIndex ) ) );
pSuccBB->emplace_back( move( phi ) );
cfg->setCurrentBB( pSuccBB );
// Build the result val which pulls the temporary created above.
return BuildComputedValue( GetValueType< bool >(),
GetTemporary( GetValueType< bool >(), resultIndex ) );
} )
)
);
BuildParseRule( e, "<<"_sid,
LeftAssInfixOp( "operator_shift_left"_sid, precedence::BitShiftOp,
BuildGenericTupleOperator(),
// ct_int left shift
ForTypes< BigInt, uint32_t >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( lhs.type(),
Shl( lhs, rhs ) );
} ),
// runtime integer left shift.
ForTypes< CustomPattern< IntegerType, IntegerType::Pattern >,
CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( lhs.type(),
Shl( lhs, rhs ) );
} )
)
);
BuildParseRule( e, ">>"_sid,
LeftAssInfixOp( "operator_shift_right"_sid, precedence::BitShiftOp,
BuildGenericTupleOperator(),
// ct_int right shift
ForTypes< BigInt, uint32_t >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( lhs.type(),
AShr( lhs, rhs ) );
} ),
// runtime signed integer right shift, defined to work for any two integers of same
// bit size.
ForTypes< CustomPattern< IntegerType, IntegerType::PatternSigned >,
CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( lhs.type(),
AShr( lhs, rhs ) );
} ),
// runtime unsigned integer right shift, defined to work for any two integers of same
// bit size.
ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( lhs.type(),
LShr( lhs, rhs ) );
} )
)
);
}
}