#ifndef GOOSE_BUILTINS_BUILDER_CODEBUILDER_H
#define GOOSE_BUILTINS_BUILDER_CODEBUILDER_H
namespace goose::builtins
{
class CodeBuilder
{
public:
template< typename C >
CodeBuilder( C&& cfg ) :
m_cfg( forward< C >( cfg ) )
{}
const auto& cfg() const { return m_cfg; }
void poison()
{
if( m_cfg )
m_cfg->poison();
}
void beginVisibilityScope( Context& c );
void beginLifetimeScope() { ++m_lifetimeScopeLevels; }
void endLifetimeScope( Context& c );
void beginBreakableScope() { ++m_breakableScopeLevels; }
void endBreakableScope() { --m_breakableScopeLevels; }
void beginContinuableScope() { ++m_continuableScopeLevels; }
void endContinuableScope() { --m_continuableScopeLevels; }
uint32_t lifetimeScopeLevels() const { return m_lifetimeScopeLevels; }
uint32_t breakableScopeLevels() const { return m_breakableScopeLevels; }
uint32_t continuableScopeLevels() const { return m_continuableScopeLevels; }
template< typename T >
void declareValue( T&& v, uint32_t index )
{
m_liveValuesList.emplace_front(
forward< T >( v ), index,
m_lifetimeScopeLevels,
m_breakableScopeLevels,
m_continuableScopeLevels
);
}
// Extend the life duration of a live value to the current lifetime scope.
void extendValueLifetime( uint32_t index );
// Destruction
// Destroys an individual value by invoking _DestroyValue, appends any resulting cir
// to the current basic block if necessary.
bool destroyLiveValue( const Context& c, const Value& v,
source_location sloc = source_location::current() );
// Invoke the destroy extension point for every live variable, but leave them on the stack.
// To be called before emitting a return terminator.
bool destroyAllLiveValues( const Context& c );
// Invoke the destroy extension point for every live variable that belong in the specified
// break scope and every child scope, but leave them on the stack.
// To be called before emitting a break terminator.
bool destroyAllLiveValuesFromBreakScope( const Context& c, uint32_t breakScopeLevel );
// Invoke the destroy extension point for every live variable that belong in the specified
// continue scope and every child scope, but leave them on the stack.
// To be called before emitting a continue terminator.
bool destroyAllLiveValuesFromContinueScope( const Context& c, uint32_t continueScopeLevel );
Value emitTypePredicatesCheck( const Context& c, const Value& val, const ptr< Propositions >& props );
private:
// Invoke the destroy extension point for every live variable from the current
// lifetime scope, and pops off the stack. To be called when exiting a lifetime scope.
// Returns false if something goes wrong, in which case the cfg will also be marked
// as poisoned.
// Note: it is called automatically by LifetimeScopeGuard.
void destroyCurrentLifetimeScopeValues( const Context& c );
ptr< cir::CFG > m_cfg;
// The number of nested lifetime scopes.
uint32_t m_lifetimeScopeLevels = 0;
// The number of nested breakable scopes.
uint32_t m_breakableScopeLevels = 0;
// The number of nested continuable scopes.
uint32_t m_continuableScopeLevels = 0;
// The live values stack, used to track when
// to destroy values (such as local variables going out of scope)
struct LiveValue
{
LiveValue() {}
template< typename T >
LiveValue( T&& v, uint32_t index, uint32_t lt, uint32_t b, uint32_t c ) :
m_value( forward< T >( v ) ),
m_index( index ),
m_lifetimeScopeLevel( lt ),
m_breakableScopeLevel( b ),
m_continuableScopeLevel( c )
{}
Value m_value;
uint32_t m_index = 0;
// We keep track of those to determine which values to
// destroy when exiting a scope, emitting a break terminator,
// and emitting a continue terminator, respectively.
uint32_t m_lifetimeScopeLevel = 0;
uint32_t m_breakableScopeLevel = 0;
uint32_t m_continuableScopeLevel = 0;
};
list< LiveValue > m_liveValuesList;
};
}
#endif