#include "builtins/builtins.h"
using namespace empathy;
using namespace empathy::ir;
namespace empathy::builtins
{
void SetupRuntimeTypesUnification( Env& e )
{
auto rtIntTypePattern = Value( TypeType(), VEC( TSID( rt_type ),
ANYTERM( _ ),
TSID( integer ), ANYTERM( _ ), ANYTERM( _ ) ) );
// ct_int type against a IntegerType type:
// return the IntegerType type. We don't care if the
// ct_int fits at this point, this will be dealt with by
// the ct_int value unification rule below.
e.unificationRuleSet()->addSymRule(
ValueToIRExpr( rtIntTypePattern ),
GetValueType< BigInt >(),
[]( const Term& lhs, const Term& rhs, UnificationContext& c ) -> UniGen
{
co_yield HalfUnify( lhs, c );
} );
// Reject the ct_int param and IntegerType arg pattern,
// so that it doesn't fall into the rule above which would be incorrect
// in that case.
e.unificationRuleSet()->addAsymRule(
ValueToIRExpr( ValuePattern(
ANYTERM( _ ),
GetValueType< BigInt >(),
ANYTERM( _ ) ) ),
ValueToIRExpr( ValuePattern(
ANYTERM( _ ),
ValueToIRExpr( rtIntTypePattern ),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, UnificationContext& c ) -> UniGen
{
co_return;
} );
// ct_integer constant unification against a IntegerType:
// Check if the IntegerType is big enough for the constant,
// and emit a LoadConstantInt llr instruction if so.
e.unificationRuleSet()->addAsymRule(
ValueToIRExpr( ValuePattern(
ANYTERM( _ ),
ValueToIRExpr( rtIntTypePattern ),
ANYTERM( _ ) ) ),
ValueToIRExpr( ValuePattern(
ANYTERM( _ ),
GetValueType< BigInt >(),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, UnificationContext& c ) -> UniGen
{
// The rhs might not be constant. It can happen even with ct_int if the rhs is a
// fake argument pattern generated to unify a higher-order function param.
// It means that we are unifying a function parameter against any ct_int value,
// which we can't (because we have to know the value to safely convert it),
// so we reject it.
auto rhsVal = *ValueFromIRExpr( rhs );
if( !rhsVal.isConstant() )
co_return;
// Don't bother during the first pass, the rt integer type pattern
// is likely to contain unresolved holes.
if( !c.secondPass() )
{
co_yield { lhs, c };
co_return;
}
auto ct = FromValue< BigInt >( rhsVal );
if( !ct )
co_return;
auto lhsVal = ValuePatternFromIRExpr( lhs );
if( !lhsVal )
co_return;
auto rttypeVal = ValueFromIRExpr( lhsVal->type() );
if( !rttypeVal )
co_return;
auto rttype = FromValue< IntegerType >( *rttypeVal );
if( !rttype )
co_return;
APSInt valToLoad;
if( rttype->m_signed )
{
if( ct->getMinSignedBits() > rttype->m_numBits )
co_return;
valToLoad = ct->sext( rttype->m_numBits );
valToLoad.setIsSigned( true );
}
else
{
if( ct->isNegative() )
co_return;
if( ct->getActiveBits() > rttype->m_numBits )
co_return;
valToLoad = ct->zext( rttype->m_numBits );
valToLoad.setIsSigned( false );
}
auto* llvmType = static_cast< llvm::IntegerType* >( GetLLVMType( *rttypeVal ) );
co_yield { ValueToIRExpr(
Value( lhsVal->type(), move( valToLoad ) ) ), c };
} );
auto rtInt8TypePattern = Value( TypeType(), VEC( TSID( rt_type ),
ANYTERM( _ ),
TSID( integer ), TERM( 8U ), ANYTERM( _ ) ) );
auto rtInt8PtrTypePattern = Value( TypeType(), VEC( TSID( rt_type ),
ANYTERM( _ ),
TSID( pointer ), ValueToIRExpr( rtInt8TypePattern ) ) );
// ct_int type against a char*:
// return the char* type.
e.unificationRuleSet()->addSymRule(
ValueToIRExpr( rtInt8PtrTypePattern ),
GetValueType< string >(),
[]( const Term& lhs, const Term& rhs, UnificationContext& c ) -> UniGen
{
co_yield HalfUnify( lhs, c );
} );
// Reject the ct_string param and char* arg pattern,
// so that it doesn't fall into the rule above which wouls be incorrect
// in that case.
e.unificationRuleSet()->addAsymRule(
ValueToIRExpr( ValuePattern(
ANYTERM( _ ),
GetValueType< string >(),
ANYTERM( _ ) ) ),
ValueToIRExpr( ValuePattern(
ANYTERM( _ ),
ValueToIRExpr( rtInt8PtrTypePattern ),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, UnificationContext& c ) -> UniGen
{
co_return;
} );
// ct_string constant unification against a pointer to a integer( 8 ):
// Emit a LoadConstantStr llr instruction.
e.unificationRuleSet()->addSymRule(
ValueToIRExpr( Value(
GetValueType< string >(),
ANYTERM( _ ) ) ),
ValueToIRExpr( ValuePattern(
ANYTERM( _ ),
ValueToIRExpr( rtInt8PtrTypePattern ),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, UnificationContext& c ) -> UniGen
{
// Don't bother during the first pass.
if( !c.secondPass() )
{
co_yield { lhs, c };
co_return;
}
auto str = *FromValue< string >( *ValueFromIRExpr( lhs ) );
auto rhsVal = *ValuePatternFromIRExpr( rhs );
co_yield { ValueToIRExpr(
BuildComputedValue( rhsVal.type(), llr::LoadConstStr( str ) ) ), c };
} );
auto ptrTypePattern = Value( TypeType(), VEC( TSID( rt_type ),
ANYTERM( _ ),
TSID( pointer ), ANYTERM( _ ) ) );
// nullptr constant unification against a pointer of any type;
// Yield a value of the given pointer type, with a 0 integer as its content.
// This'll be recognized by codegen to emit a null pointer value of the right type.
e.unificationRuleSet()->addSymRule(
ValueToIRExpr( ValuePattern(
ANYTERM( _ ),
ValueToIRExpr( ptrTypePattern ),
ANYTERM( _ ) ) ),
ValueToIRExpr( ValuePattern(
ANYTERM( _ ),
GetValueType< NullPointer >(),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, UnificationContext& c ) -> UniGen
{
auto lVal = *ValuePatternFromIRExpr( lhs );
co_yield { ValueToIRExpr( Value( lVal.type(), 0U ) ), c };
} );
}
}