Artifact 73f39948b2ccc2056bf17e198b7b344d6942907f0ddaa89f8cae7699a9547f2b:
- File
bs/builtins/operators/logic.cpp
— part of check-in
[7a7e54ac04]
at
2020-07-18 16:25:47
on branch llr-stack-language
— Computed values now carry lists of instructions around, instead of expression trees. Also absolutely nothing works anymore.
Update: closing this branch as I will finally not pursue this. (user: achavasse size: 14378)
#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 ) ); } ), // 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 ); } // When building verification clauses we have bo code builder and no cfg, // and we just want to generate a plain or operation. if( !c.codeBuilder() ) return BuildComputedValue( GetValueType< bool >(), Or( lhs, rhs ) ); // Build the control flow for shortcut evaluation. auto& cb = c.codeBuilder(); 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->append( 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->append( 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 ) ); } ), // 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 ); } // When building verification clauses we have bo code builder and no cfg, // and we just want to generate a plain and operation. if( !c.codeBuilder() ) return BuildComputedValue( GetValueType< bool >(), And( lhs, rhs ) ); // Build the control flow for shortcut evaluation. auto& cb = c.codeBuilder(); 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->append( 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->append( 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 { using namespace goose::builtins::exprbuilder; auto cb = c.codeBuilder(); auto cfg = cb->cfg(); assert( cfg ); // Shifting for a number of bits equal or larger than the bitsize // of lhs is an undefined behavior, so we require verification that // it won't happen. // Extract the integer type of lhs to retreieve its bit size. auto lhsType = *FromValue< IntegerType >( *ValueFromIRExpr( lhs.type() ) ); auto bitSizeValue = Value( rhs.type(), APSInt::get( lhsType.m_numBits) ); auto cond = ULT( rhs, bitSizeValue ); DiagnosticsManager::GetInstance().defineCustomDiagnostic( cond.locationId(), "assert"_sid, "the shift amount may be equal or greater than the bitsize." ); cfg->currentBB()->append( llr::Assert( move( cond ) ) ); return BuildComputedValue( lhs.type(), Shl( lhs, rhs ) ); } ) ) ); BuildParseRule( e, ">>"_sid, LeftAssInfixOp( "operator_shift_right"_sid, precedence::BitShiftOp, BuildGenericTupleOperator(), // ct_int right shift ForTypes< BigInt, uint32_t >( []( auto&& c, auto&& lhs, auto&& rhs ) -> Value { 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 { using namespace goose::builtins::exprbuilder; auto cb = c.codeBuilder(); auto cfg = cb->cfg(); assert( cfg ); // Shifting for a number of bits equal or larger than the bitsize // of lhs is an undefined behavior, so we require verification that // it won't happen. // Extract the integer type of lhs to retreieve its bit size. auto lhsType = *FromValue< IntegerType >( *ValueFromIRExpr( lhs.type() ) ); auto bitSizeValue = Value( rhs.type(), APSInt::get( lhsType.m_numBits) ); auto cond = ULT( rhs, bitSizeValue ); DiagnosticsManager::GetInstance().defineCustomDiagnostic( cond.locationId(), "assert"_sid, "the shift amount may be equal or greater than the bitsize." ); cfg->currentBB()->append( llr::Assert( move( cond ) ) ); return BuildComputedValue( lhs.type(), AShr( lhs, rhs ) ); } ), // runtime unsigned integer right shift, defined to work for any two integers of same // bit size. ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >( []( auto&& c, auto&& lhs, auto&& rhs ) -> Value { using namespace goose::builtins::exprbuilder; auto cb = c.codeBuilder(); auto cfg = cb->cfg(); assert( cfg ); // Shifting for a number of bits equal or larger than the bitsize // of lhs is an undefined behavior, so we require verification that // it won't happen. // Extract the integer type of lhs to retreieve its bit size. auto lhsType = *FromValue< IntegerType >( *ValueFromIRExpr( lhs.type() ) ); auto bitSizeValue = Value( rhs.type(), APSInt::get( lhsType.m_numBits) ); auto cond = ULT( rhs, bitSizeValue ); DiagnosticsManager::GetInstance().defineCustomDiagnostic( cond.locationId(), "assert"_sid, "the shift amount may be equal or greater than the bitsize." ); cfg->currentBB()->append( llr::Assert( move( cond ) ) ); return BuildComputedValue( lhs.type(), LShr( lhs, rhs ) ); } ) ) ); } }