Majorly optimized refs for overwrite, fixed some of the messy parts.

This commit is contained in:
Can Bölük 2020-09-08 09:07:22 +02:00
parent 2bac0c4e9b
commit a1e8a013fb
21 changed files with 251 additions and 231 deletions

View file

@ -66,7 +66,7 @@ namespace vtil::symbolic
// Construct from symbolic expression.
//
pointer( const expression::reference& base );
pointer( const expression& base ) : pointer( ( ( expression::reference&& ) make_local_reference( &base ) ) ) {}
pointer( const expression& base ) : pointer( make_local_reference( &base ) ) {}
// Default copy/move.
//

View file

@ -47,9 +47,9 @@ namespace vtil
return tbl;
}();
fassert( lookup_table.size() > ( size_t ) op );
dassert( lookup_table.size() > ( size_t ) op );
const instruction_desc* desc = lookup_table[ ( size_t ) op ];
fassert( desc );
dassert( desc );
return desc;
}

View file

@ -274,7 +274,7 @@ namespace vtil
ref.simplify();
return false;
}
ref = {};
ref.reset();
return *result;
}
@ -414,7 +414,7 @@ namespace vtil
{
symbolic::variable&& var = std::move( ( +exp )->uid.get<symbolic::variable>() );
var.is_branch_dependant = true;
*+exp = { var, exp->size() };
exp = symbolic::expression{ var, exp->size() };
}
}, true, false );
}

View file

@ -61,7 +61,7 @@ namespace vtil
//
else
{
fassert( op.is_immediate() );
dassert( op.is_immediate() );
return { op.imm().i64, op.imm().bit_count };
}
};
@ -186,7 +186,7 @@ namespace vtil
// Operand 0 should always be the result for this class.
//
fassert( ins.base->operand_types[ 0 ] >= operand_type::write );
dassert( ins.base->operand_types[ 0 ] >= operand_type::write );
return vm_exit_reason::none;
}
// If NOP:

View file

@ -160,7 +160,7 @@ namespace vtil::format
template<typename T>
static auto as_string( const T& x );
template<typename T>
concept StringConvertible = requires( T v ) { !is_specialization_v<type_tag, decltype( as_string( v ) )>; };
concept StringConvertible = requires( std::string res, T v ) { res = as_string( v ); };
template<typename T>
__forceinline static auto as_string( const T& x )
@ -190,7 +190,7 @@ namespace vtil::format
}
else if constexpr ( std::is_same_v<base_type, bool> )
{
return std::string{ x ? "true" : "false" };
return x ? "true"s : "false"s;
}
else if constexpr ( StdStringConvertible<T> )
{
@ -248,7 +248,7 @@ namespace vtil::format
}();
if constexpr ( std::tuple_size_v<base_type> == 0 )
return "{}";
return "{}"s;
else if constexpr ( is_tuple_str_cvtable )
{
std::string res = std::apply( [ ] ( auto&&... args ) {
@ -265,7 +265,7 @@ namespace vtil::format
if ( x.has_value() )
return as_string( x.value() );
else
return std::string{ "nullopt" };
return "nullopt"s;
}
else return type_tag<T>{};
}
@ -277,11 +277,11 @@ namespace vtil::format
}
else if constexpr ( Iterable<T> )
{
if constexpr ( StringConvertible<decltype( *std::begin( x ) )> )
if constexpr ( StringConvertible<iterator_value_type_t<T>> )
{
std::string items = {};
for ( auto&& entry : x )
items += as_string( entry ) + ", ";
items += as_string( entry ) + ", "s;
if ( !items.empty() ) items.resize( items.size() - 2 );
return "{" + items + "}";
}

View file

@ -34,6 +34,7 @@
#include <mutex>
#include <thread>
#include <functional>
#include <cstdarg>
#include "formatting.hpp"
#include "../util/intrinsics.hpp"
#include "../util/literals.hpp"
@ -176,68 +177,84 @@ namespace vtil::logger
// Main function used when logging.
//
namespace impl
{
template<bool has_args>
static int log_w( FILE* dst, console_color color, const char* fmt, ... )
{
// Hold the lock for the critical section guarding ::log.
//
std::lock_guard g( logger_state );
// Do not execute if logs are disabled.
//
if ( logger_state.mute ) return 0;
// If we should pad this output:
//
int out_cnt = 0;
if ( logger_state.padding > 0 )
{
// If it was not carried from previous:
//
if ( int pad_by = logger_state.padding - logger_state.padding_carry )
{
for ( int i = 0; i < pad_by; i++ )
{
if ( ( i + 1 ) == pad_by )
{
out_cnt += fprintf( dst, "%*c", log_padding_step - 1, ' ' );
if ( fmt[ 0 ] == ' ' ) putchar( log_padding_c );
}
else
{
out_cnt += fprintf( dst, "%*c%c", log_padding_step - 1, ' ', log_padding_c );
}
}
}
// Set or clear the carry for next.
//
if ( fmt[ strlen( fmt ) - 1 ] == '\n' )
logger_state.padding_carry = 0;
else
logger_state.padding_carry = logger_state.padding;
}
// Set to requested color and redirect to printf.
//
set_color( color );
// If string literal with no parameters, use puts instead.
//
if ( has_args )
{
va_list args;
va_start( args, fmt );
out_cnt += vfprintf( dst, fmt, args );
va_end( args );
}
else
{
out_cnt += fputs( fmt, dst );
}
// Reset to defualt color.
//
set_color( CON_DEF );
return out_cnt;
}
};
template<typename... params>
static int log( console_color color, const char* fmt, params&&... ps )
{
// Hold the lock for the critical section guarding ::log.
//
std::lock_guard g( logger_state );
// Do not execute if logs are disabled.
//
if ( logger_state.mute ) return 0;
// If we should pad this output:
//
int out_cnt = 0;
if ( logger_state.padding > 0 )
{
// If it was not carried from previous:
//
if ( int pad_by = logger_state.padding - logger_state.padding_carry )
{
for ( int i = 0; i < pad_by; i++ )
{
if ( ( i + 1 ) == pad_by )
{
out_cnt += fprintf( VTIL_LOGGER_DST, "%*c", log_padding_step - 1, ' ' );
if ( fmt[ 0 ] == ' ' ) putchar( log_padding_c );
}
else
{
out_cnt += fprintf( VTIL_LOGGER_DST, "%*c%c", log_padding_step - 1, ' ', log_padding_c );
}
}
}
// Set or clear the carry for next.
//
if ( fmt[ strlen( fmt ) - 1 ] == '\n' )
logger_state.padding_carry = 0;
else
logger_state.padding_carry = logger_state.padding;
}
// Set to requested color and redirect to printf.
//
set_color( color );
// If string literal with no parameters, use puts instead.
//
if ( sizeof...( ps ) == 0 )
out_cnt += fputs( fmt, VTIL_LOGGER_DST );
else
out_cnt += fprintf( VTIL_LOGGER_DST, fmt, format::fix_parameter<params>( std::forward<params>( ps ) )... );
// Reset to defualt color.
//
set_color( CON_DEF );
return out_cnt;
return impl::log_w<sizeof...( params ) != 0>( VTIL_LOGGER_DST, color, fmt, format::fix_parameter<params>( std::forward<params>( ps ) )... );
}
template<console_color color = CON_DEF, typename... params>
static int log( const char* fmt, params&&... ps )
{
return log( color, fmt, std::forward<params>( ps )... );
return impl::log_w<sizeof...( params ) != 0>( VTIL_LOGGER_DST, color, fmt, format::fix_parameter<params>( std::forward<params>( ps ) )... );
}
// Prints a warning message.

View file

@ -42,33 +42,6 @@ namespace vtil
{
namespace impl
{
template<typename... params> struct first_of { using type = std::tuple_element_t<0, std::tuple<params...>>; };
template<> struct first_of<> { using type = void; };
template<typename... params>
using first_of_t = typename first_of<params...>::type;
template<typename T, typename... params>
static constexpr bool should_invoke_constructor()
{
// Constructor should be always invoked if we have more than one parameter and
// never if we have zero parameters.
//
if constexpr ( sizeof...( params ) != 1 )
{
return sizeof...( params ) != 0;
}
else
{
// Invoke if not equal to the reference type.
//
return !std::is_base_of_v<T, std::remove_cvref_t<first_of_t<params...>>>;
}
}
template<typename T, typename... params>
using enable_if_constructor = typename std::enable_if_t<should_invoke_constructor<T, params...>(), int>;
template<typename T>
inline static T* reloc_const( const T* ptr, const void* src, void* dst )
{
@ -87,7 +60,7 @@ namespace vtil
// Used to implement shared and extremely fast Copy-on-Write memory.
//
template<typename T>
struct shared_reference
struct base_shared_reference
{
// Declare the object entry and its pool.
//
@ -138,25 +111,24 @@ namespace vtil
// Null reference construction.
//
constexpr shared_reference() : combined_value( 0 ) {}
constexpr shared_reference( std::nullptr_t ) : shared_reference() {}
constexpr shared_reference( std::nullopt_t ) : shared_reference() {}
constexpr base_shared_reference() : combined_value( 0 ) {}
constexpr base_shared_reference( std::nullptr_t ) : base_shared_reference() {}
// Owning reference constructor.
//
template<typename... params, impl::enable_if_constructor<shared_reference<T>, params...> = 0>
shared_reference( params&&... p )
template<typename... Tx> requires ( Constructable<T, Tx...> && sizeof...( Tx ) > 0 )
base_shared_reference( Tx&&... p )
{
combined_value = ( uint64_t ) object_pool::construct
(
/*Object itself*/ T( std::forward<params>( p )... ),
/*Object itself*/ T( std::forward<Tx>( p )... ),
/*Reference counter*/ 1
);
}
// Shared reference constructor.
//
shared_reference( const shared_reference& ref )
base_shared_reference( const base_shared_reference& ref )
: combined_value( ref.combined_value )
{
// If object is null, return.
@ -173,7 +145,7 @@ namespace vtil
else
inc_ref( get_entry() );
}
shared_reference& operator=( const shared_reference& o )
base_shared_reference& operator=( const base_shared_reference& o )
{
// If object is null, reset and return.
//
@ -225,15 +197,44 @@ namespace vtil
// Construction and assignment operator for rvalue references.
//
shared_reference( shared_reference&& ref )
base_shared_reference( base_shared_reference&& ref )
: combined_value( std::exchange( ref.combined_value, 0 ) ) {}
shared_reference& operator=( shared_reference&& o )
base_shared_reference& operator=( base_shared_reference&& o )
{
uint64_t value = std::exchange( o.combined_value, 0 );
reset().combined_value = value;
return *this;
}
// Assignment of value.
//
template<typename Tv> requires Constructable<T, Tv>
base_shared_reference& operator=( Tv&& value )
{
// If we have valid memory:
//
if ( combined_value )
{
// If it's unique memory, move over it and return, otherwise dereference.
//
if ( is_temporary() || get_ref( get_entry() ) == 1 )
{
*( T* ) pointer = std::forward<Tv>( value );
return *this;
}
dec_ref( get_entry() );
}
// Construct a new object and return.
//
combined_value = ( uint64_t ) object_pool::construct
(
/*Object itself*/ std::forward<Tv>( value ),
/*Reference counter*/ 1
);
return *this;
}
// Gets object entry.
//
constexpr object_entry* get_entry() const { dassert( !is_temporary() ); return _entry; }
@ -292,8 +293,8 @@ namespace vtil
// Basic comparison operators are redirected to the pointer type.
//
constexpr bool operator==( const shared_reference& o ) const { return combined_value == o.combined_value; }
constexpr bool operator<( const shared_reference& o ) const { return combined_value < o.combined_value; }
constexpr bool operator==( const base_shared_reference& o ) const { return combined_value == o.combined_value; }
constexpr bool operator<( const base_shared_reference& o ) const { return combined_value < o.combined_value; }
// Redirect pointer and dereferencing operator to the reference and cast to const-qualified equivalent.
//
@ -312,7 +313,7 @@ namespace vtil
// Resets the reference to nullptr.
//
__forceinline shared_reference& reset()
__forceinline base_shared_reference& reset()
{
// If non-temporary and non-null, decrement reference count, if
// it reaches 0, destroy the object and deallocate.
@ -328,9 +329,16 @@ namespace vtil
// Constructor invokes reset.
//
~shared_reference() { reset(); }
~base_shared_reference() { reset(); }
};
// Can be overloaded to implement customization.
//
template<typename T, typename = void>
struct specialized_shared_reference { using type = base_shared_reference<T>; };
template<typename T>
using shared_reference = typename specialized_shared_reference<T>::type;
// Weak references are used to store shared references without implying
// ownership. This class should not be used together with temporaries.
//

View file

@ -123,12 +123,17 @@ namespace vtil
//
else if constexpr ( Iterable<const T&> )
{
hash_t hash = {};
size_t i = 0;
for ( const auto& entry : value )
hash = combine_hash( hash, hasher<std::decay_t<decltype( entry )>>{}( entry ) ), i++;
hash.add_bytes( sizeof( T ) + i );
return hash;
using value_type = std::decay_t<iterator_value_type_t<const T&>>;
if constexpr ( !std::is_void_v<decltype( hasher<value_type>{}( std::declval<value_type&>() ) ) > )
{
hash_t hash = {};
size_t i = 0;
for ( const auto& entry : value )
hash = combine_hash( hash, hasher<value_type>{}( entry ) ), i++;
hash.add_bytes( sizeof( T ) + i );
return hash;
}
}
// If hash, combine with default seed.
//

View file

@ -73,34 +73,6 @@
#pragma warning(disable: 4305)
namespace vtil
{
namespace impl
{
// Applies type modifier over each element in pair/tuple.
//
template<template<typename> typename F, typename T>
struct apply_each { using type = F<T>; };
template<template<typename> typename F, typename... T>
struct apply_each<F, std::pair<T...>> { using type = std::pair<F<T>...>; };
template<template<typename> typename F, typename... T>
struct apply_each<F, std::tuple<T...>> { using type = std::tuple<F<T>...>; };
template<template<typename> typename F, typename T>
using apply_each_t = typename apply_each<F, T>::type;
};
// Mask of requested reducable auto declarations.
//
enum reducable_auto_decl_id : uint8_t
{
reducable_none = 0x00,
reducable_equ = 1 << 0,
reducable_nequ = 1 << 1,
reducable_leq = 1 << 2,
reducable_greq = 1 << 3,
reducable_less = 1 << 4,
reducable_greater = 1 << 5,
reducable_all = 0xFF,
};
// Reducable tag let's us check if a type is reducable without having
// to template for proxied type or the auto-decl flags.
//
@ -113,7 +85,7 @@ namespace vtil
// The main definition of the helper:
//
template<typename T, uint8_t flags = reducable_all>
template<typename T>
struct reducable : reducable_tag_t
{
protected:
@ -132,18 +104,9 @@ namespace vtil
public:
// Define basic comparison operators using std::tuple.
//
template<std::enable_if_t<( flags & reducable_equ ) != 0, int> = 0>
__forceinline constexpr auto operator==( const T& other ) const { return &other == this || reduce_proxy( ( T& ) *this ) == reduce_proxy( other ); }
template<std::enable_if_t<( flags & reducable_nequ ) != 0, int> = 0>
__forceinline constexpr auto operator!=( const T& other ) const { return &other != this && reduce_proxy( ( T& ) *this ) != reduce_proxy( other ); }
template<std::enable_if_t<( flags & reducable_leq ) != 0, int> = 0>
__forceinline constexpr auto operator<=( const T& other ) const { return &other == this || reduce_proxy( ( T& ) *this ) <= reduce_proxy( other ); }
template<std::enable_if_t<( flags & reducable_greq ) != 0, int> = 0>
__forceinline constexpr auto operator>=( const T& other ) const { return &other == this || reduce_proxy( ( T& ) *this ) >= reduce_proxy( other ); }
template<std::enable_if_t<( flags & reducable_less ) != 0, int> = 0>
__forceinline constexpr auto operator< ( const T& other ) const { return &other != this && reduce_proxy( ( T& ) *this ) < reduce_proxy( other ); }
template<std::enable_if_t<( flags & reducable_greater ) != 0, int> = 0>
__forceinline constexpr auto operator> ( const T& other ) const { return &other != this && reduce_proxy( ( T& ) *this ) > reduce_proxy( other ); }
__forceinline constexpr auto operator==( const T& other ) const { return &other == this || reduce_proxy( ( const T& ) *this ) == reduce_proxy( other ); }
__forceinline constexpr auto operator!=( const T& other ) const { return &other != this && reduce_proxy( ( const T& ) *this ) != reduce_proxy( other ); }
__forceinline constexpr auto operator< ( const T& other ) const { return &other != this && reduce_proxy( ( const T& ) *this ) > reduce_proxy( other ); }
// Define VTIL hash using a simple VTIL tuple hasher.
//

View file

@ -149,10 +149,20 @@ namespace vtil
template <template<typename...> typename Tmp, typename T>
concept Specialization = is_specialization_v<Tmp, T>;
template<typename T, typename... Args>
concept Constructable = requires { T( std::declval<Args>()... ); };
concept Constructable = requires( Args&&... a ) { T( a... ); };
template<typename T, typename X>
concept Assignable = requires( T r, X v ) { r = v; };
// Comparison traits.
//
template<typename T, typename O> concept ThreeWayComparable = requires( T&& a, O&& b ) { a <=> b; };
template<typename T, typename O> concept LessEqualComparable = requires( T&& a, O&& b ) { a <= b; };
template<typename T, typename O> concept LessComparable = requires( T&& a, O&& b ) { a < b; };
template<typename T, typename O> concept EqualComparable = requires( T&& a, O&& b ) { a == b; };
template<typename T, typename O> concept NotEqualComparable = requires( T&& a, O&& b ) { a != b; };
template<typename T, typename O> concept GreatComparable = requires( T&& a, O&& b ) { a > b; };
template<typename T, typename O> concept GreatEqualComparable = requires( T&& a, O&& b ) { a >= b; };
// Functor traits.
//
template<typename T, typename Ret, typename... Args>
@ -207,7 +217,7 @@ namespace vtil
// Atomicity-related traits.
//
template<typename T>
concept Lockable = requires( T & x ) { x.lock(); x.unlock(); };
concept Lockable = requires( T& x ) { x.lock(); x.unlock(); };
template<typename T>
concept Atomic = is_specialization_v<std::atomic, T>;

View file

@ -144,7 +144,7 @@ namespace vtil::analysis
branch_targets.emplace_back( cvt_operand( 0 ) );
is_branch_real = true;
is_branch_exiting = ins.base == &ins::vexit;
branch_cc = nullptr;
branch_cc.reset();
return vm_exit_reason::stream_end;
}
// If unconditional jump:
@ -153,7 +153,7 @@ namespace vtil::analysis
{
branch_targets.emplace_back( cvt_operand( 0 ) );
is_branch_real = false;
branch_cc = nullptr;
branch_cc.reset();
return vm_exit_reason::stream_end;
}
// If conditional jump:
@ -351,12 +351,12 @@ namespace vtil::analysis
if ( exp_approx == approx )
{
if ( pexp->equals( ccexp ) )
*+pexp = { expected_value, 1 };
pexp = symbolic::expression{ expected_value, 1 };
}
else if ( inv_approx == approx )
{
if ( pexp->equals( inv_cc ) )
*+pexp = { !expected_value, 1 };
pexp = symbolic::expression{ !expected_value, 1 };
}
}
};

View file

@ -342,7 +342,7 @@ namespace vtil::optimizer::aux
{
auto& var = ex->uid.get<symbolic::variable>();
if ( var.is_register() && var.reg() == REG_IMGBASE )
*+ex = { 0, ex->size() };
ex = symbolic::expression{ 0, ex->size() };
}
}, true, false ).simplify( true );
@ -393,12 +393,12 @@ namespace vtil::optimizer::aux
{
if ( exp->is_identical( *cnd_out ) )
{
*+exp = symbolic::expression{ state, exp->size() };
exp = symbolic::expression{ state, exp->size() };
confirmed |= !state;
}
else if ( exp->is_identical( ~cnd_out ) )
{
*+exp = symbolic::expression{ state ^ 1, exp->size() };
exp = symbolic::expression{ state ^ 1, exp->size() };
confirmed |= !state;
}
}
@ -420,7 +420,7 @@ namespace vtil::optimizer::aux
dst->enumerate( explore_cc_space );
if ( cnd_out ) dst.transform( transform_cc );
if ( !confirmed ) cnd_out = {};
if ( !confirmed ) cnd_out.reset();
};
symbolic::expression::reference cc = {};

View file

@ -198,7 +198,7 @@ namespace vtil::optimizer
if ( !( math::fill( vctx.linear_store[ n ].size(), n ) & read_mask ) )
{
math::bit_reset( vctx.bitmap, n );
vctx.linear_store[ n ] = nullptr;
vctx.linear_store[ n ].reset();
cnt++;
}
// Otherwise, or with value mask.

View file

@ -178,7 +178,7 @@ namespace vtil::symbolic::directive
// and type of expressions it can match.
//
const char* id = nullptr;
int lookup_index = 0;
uint32_t lookup_index = 0xFFFFFFFF;
matching_type mtype = match_any;
// Priority hint for transformer.
@ -215,7 +215,7 @@ namespace vtil::symbolic::directive
for ( auto [out, idx] : zip( signatures, iindices ) )
out = { make_copy( value ).resize( math::narrow_cast<bitcnt_t>( idx + 1 ) ) };
}
instance( const char* id, int lookup_index, matching_type mtype = match_any ) :
instance( const char* id, uint32_t lookup_index, matching_type mtype = match_any ) :
id( id ), lookup_index( lookup_index ), mtype( mtype ), num_nodes( 1 ) {}
// Constructor for directive representing the result of an unary operator.
@ -292,9 +292,9 @@ namespace vtil::symbolic::directive
// Special variables, one per type:
//
static const instance V = { "Π", 7, match_variable };
static const instance U = { "Σ", 8, match_constant };
static const instance Q = { "Ω", 9, match_expression };
static const instance V = { "Π", 7, match_variable };
static const instance U = { "Σ", 8, match_constant };
static const instance Q = { "Ω", 9, match_expression };
static const instance W = { "Ψ", 10, match_non_constant };
static const instance X = { "Θ", 11, match_non_expression };

View file

@ -41,7 +41,11 @@ namespace vtil::symbolic::directive
// Adds the mapping of a variable to an expression.
//
bool add( const instance* dir, expression::weak_reference exp )
{
{
// Assert the looked up type is variable.
//
dassert( dir->op == math::operator_id::invalid && !dir->is_constant() );
// If it's the first time this variable is being used:
//
if ( !lookup_table[ dir->lookup_index ] )
@ -78,7 +82,7 @@ namespace vtil::symbolic::directive
{
// Assert the looked up type is variable.
//
fassert( dir->op == math::operator_id::invalid && !dir->is_constant() );
dassert( dir->op == math::operator_id::invalid && !dir->is_constant() );
// Translate using the lookup table.
//

View file

@ -195,7 +195,7 @@ namespace vtil::symbolic
// Return the unknown mask.
//
return ( *+exp = expression{ exp->unknown_mask(), exp->size() }, exp );
return exp = expression{ exp->unknown_mask(), exp->size() };
}
break;
}
@ -207,7 +207,7 @@ namespace vtil::symbolic
{
// Return the unknown mask.
//
return ( *+exp = expression{ exp->known_one(), exp->size() }, exp );
return exp = expression{ exp->known_one(), exp->size() };
}
break;
}
@ -219,7 +219,7 @@ namespace vtil::symbolic
{
// Return the unknown mask.
//
return ( *+exp = expression{ exp->known_zero(), exp->size() }, exp );
return exp = expression{ exp->known_zero(), exp->size() };
}
break;
}

View file

@ -96,7 +96,7 @@ namespace vtil::symbolic
//
if ( exp_new->is_constant() )
{
exp_new = { *exp_new->value.get(), exp->size() };
exp_new = expression{ *exp_new->value.get(), exp->size() };
}
else
{

View file

@ -351,7 +351,7 @@ namespace vtil::symbolic
//
else
{
*+rhs = new_size;
rhs = expression{ new_size, 8 };
return update( false );
}
break;
@ -361,7 +361,7 @@ namespace vtil::symbolic
case math::operator_id::cast:
// Signed cast should not be used to shrink.
//
fassert( lhs->size() <= rhs->get().value() );
dassert( lhs->size() <= rhs->get().value() );
// If sizes match, escape cast operator.
//
@ -373,7 +373,7 @@ namespace vtil::symbolic
//
else if ( signed_cast )
{
*+rhs = new_size;
rhs = expression{ new_size, 8 };
return update( false );
}
// Else, convert to unsigned cast since top bits will be zero.
@ -490,7 +490,7 @@ namespace vtil::symbolic
//
if ( ( is_lazy || auto_simplify ) && value.is_known() )
{
lhs = {}; rhs = {};
lhs.reset(); rhs.reset();
op = math::operator_id::invalid;
is_lazy = false;
return update( false );
@ -531,7 +531,7 @@ namespace vtil::symbolic
//
if ( ( is_lazy || auto_simplify ) && value.is_known() )
{
lhs = {}; rhs = {};
lhs.reset(); rhs.reset();
op = math::operator_id::invalid;
is_lazy = false;
return update( false );
@ -718,7 +718,7 @@ namespace vtil::symbolic
// this way and additionally we avoid copying where an operand is being simplified
// as that can be replaced by a simple swap of shared references.
//
reference ref = ( reference&& ) make_local_reference( this );
reference ref = make_local_reference( this );
simplify_expression( ref, prettify );
// Set the simplifier hint to indicate skipping further calls to simplify_expression.
@ -816,8 +816,8 @@ namespace vtil::symbolic
// Simplify both expressions.
//
expression::reference a = make_local_reference( this );
expression::reference b = make_local_reference( &other );
reference a = make_local_reference( this );
reference b = make_local_reference( &other );
a.simplify();
b.simplify();

View file

@ -44,41 +44,25 @@
// Allow expression::reference to be used with expression type directly as operable.
//
namespace vtil::symbolic { struct expression; struct expression_reference; };
namespace vtil::symbolic { struct expression; struct expression_reference; struct expression_delegate; };
namespace vtil::math { template<> struct resolve_alias<symbolic::expression_reference> { using type = symbolic::expression; }; };
// Specialize reference.
//
namespace vtil
{
template<>
struct specialized_shared_reference<symbolic::expression, void>
{
using type = symbolic::expression_reference;
};
};
namespace vtil::symbolic
{
struct expression;
// Expression delegates used to implement copyless write detection
// in case the reference is already owning.
//
struct expression_delegate
{
shared_reference<expression>& ref;
bool dirty;
expression_delegate( shared_reference<expression>& ref ) : ref( ref ), dirty( false ) {}
expression_delegate( const expression_delegate& ) = delete;
expression_delegate& operator=( const expression_delegate& ) = delete;
template<typename T, std::enable_if_t<!std::is_same_v<std::decay_t<T>, expression_delegate>, int> = 0>
expression_delegate& operator=( T&& value )
{
ref = std::forward<T>( value );
dirty = true;
return *this;
}
const expression* operator->() const { return ref.get(); }
const expression& operator*() const { return *ref.get(); }
expression* operator+() { dirty = 1; return ref.own(); }
};
// Expression references.
//
struct expression_reference : shared_reference<expression>
struct expression_reference : base_shared_reference<expression>
{
// Declare hasher and equivalence checker.
//
@ -99,21 +83,25 @@ namespace vtil::symbolic
// Forward operators and constructor.
//
template<typename... Tx>
expression_reference( Tx&&... args )
: shared_reference( std::forward<Tx>( args )...) {}
template<typename... Tx> requires ( Constructable<expression, Tx...> && sizeof...(Tx) > 0 )
expression_reference( Tx&&... args ) : base_shared_reference( std::forward<Tx>( args )... ) {}
constexpr expression_reference() {}
constexpr expression_reference( std::nullptr_t ) {}
using shared_reference::operator bool;
using shared_reference::operator*;
using shared_reference::operator+;
using shared_reference::operator->;
expression_reference( expression_reference&& o ) : base_shared_reference( std::move( o ) ) {}
expression_reference( const expression_reference& o ) : base_shared_reference( o ) {}
expression_reference& operator=( expression_reference&& o ) { base_shared_reference::operator=( std::move( o ) ); return *this; }
expression_reference& operator=( const expression_reference& o ) { base_shared_reference::operator=( o ); return *this; }
// Basic comparison operators are redirected to the pointer type.
//
bool operator<( const shared_reference& o ) const { return combined_value < o.combined_value; }
bool operator==( const shared_reference& o ) const { return combined_value == o.combined_value; }
bool operator<( const expression_reference& o ) const { return combined_value < o.combined_value; }
bool operator==( const expression_reference& o ) const { return combined_value == o.combined_value; }
template<typename Tv> requires Constructable<expression, Tv>
expression_reference& operator=( Tv&& o ) { base_shared_reference::operator=( std::forward<Tv>( o ) ); return *this; }
using base_shared_reference::operator bool;
using base_shared_reference::operator*;
using base_shared_reference::operator+;
using base_shared_reference::operator->;
using base_shared_reference::operator<;
using base_shared_reference::operator==;
// Implement some helpers to conditionally copy.
//
@ -178,6 +166,31 @@ namespace vtil::symbolic
}
};
// Expression delegates used to implement copyless write detection
// in case the reference is already owning.
//
struct expression_delegate
{
expression_reference& ref;
bool dirty;
expression_delegate( expression_reference& ref ) : ref( ref ), dirty( false ) {}
expression_delegate( const expression_delegate& ) = delete;
expression_delegate& operator=( const expression_delegate& ) = delete;
template<typename T, std::enable_if_t<!std::is_same_v<std::decay_t<T>, expression_delegate>, int> = 0>
expression_delegate& operator=( T&& value )
{
ref = std::forward<T>( value );
dirty = true;
return *this;
}
const expression* operator->() const { return ref.get(); }
const expression& operator*() const { return *ref.get(); }
expression* operator+() { dirty = 1; return ref.own(); }
};
// Expression descriptor.
//
struct expression : math::operable<expression>

View file

@ -515,7 +515,7 @@ namespace vtil::symbolic
//
if ( exp->value.is_known() )
{
*+exp = expression{ exp->value.known_one(), exp->value.size() };
exp = expression{ exp->value.known_one(), exp->value.size() };
#if VTIL_SYMEX_SIMPLIFY_VERBOSE
log<CON_CYN>( "= %s [By evaluation]\n", *exp );
#endif

View file

@ -49,7 +49,7 @@
#define VTIL_SYMEX_LRU_CACHE_SIZE 0x18000
#endif
#ifndef VTIL_SYMEX_LRU_PRUNE_COEFF
#define VTIL_SYMEX_LRU_PRUNE_COEFF 0.2
#define VTIL_SYMEX_LRU_PRUNE_COEFF 0.2f
#endif
#ifndef VTIL_SYMEX_HASH_COLLISION_MAX
#define VTIL_SYMEX_HASH_COLLISION_MAX 8