mirror of
https://github.com/vtil-project/VTIL-Core
synced 2026-08-17 08:23:03 -04:00
Symbolic VM now uses references, implemented special unique ptr for dirs
This commit is contained in:
parent
5d60e2b759
commit
cb395a2b66
19 changed files with 221 additions and 121 deletions
|
|
@ -55,7 +55,7 @@ namespace vtil::symbolic
|
|||
|
||||
// Declaration of symbolic memory type using sinkhole.
|
||||
//
|
||||
using memory = sinkhole<pointer, expression, pointer::make_weak>;
|
||||
using memory = sinkhole<pointer, expression::reference, pointer::make_weak>;
|
||||
|
||||
// Creates a symbolic memory of the given type.
|
||||
//
|
||||
|
|
@ -64,11 +64,11 @@ namespace vtil::symbolic
|
|||
switch ( type )
|
||||
{
|
||||
case memory_type::free:
|
||||
return { [ ] ( auto& ptr, bitcnt_t size ) -> expression { return make_memory_ex( ptr, size ); } };
|
||||
return { [ ] ( auto& ptr, bitcnt_t size ) -> expression::reference { return make_memory_ex( ptr, size ); } };
|
||||
case memory_type::relaxed:
|
||||
return { [ ] ( auto& ptr, bitcnt_t size ) -> expression { return make_undefined_ex( size ); } };
|
||||
return { [ ] ( auto& ptr, bitcnt_t size ) -> expression::reference { return make_undefined_ex( size ); } };
|
||||
default:
|
||||
return { [ ] ( auto& ptr, bitcnt_t size ) -> expression { unreachable(); return expression{}; } };
|
||||
return { [ ] ( auto& ptr, bitcnt_t size ) -> expression::reference { unreachable(); return nullptr; } };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -498,33 +498,33 @@ namespace vtil
|
|||
// Create a lambda virtual machine and allocate a temporary result.
|
||||
//
|
||||
lambda_vm lvm;
|
||||
symbolic::expression result = {};
|
||||
symbolic::expression::reference result = {};
|
||||
|
||||
lvm.hooks.read_register = [ & ] ( const register_desc& desc )
|
||||
{
|
||||
return trace( { it, desc } );
|
||||
};
|
||||
lvm.hooks.read_memory = [ & ] ( const symbolic::expression& pointer, size_t byte_count )
|
||||
lvm.hooks.read_memory = [ & ] ( const symbolic::expression::reference& pointer, size_t byte_count )
|
||||
{
|
||||
auto exp = trace( symbolic::variable{ it, { pointer, math::narrow_cast<bitcnt_t>( byte_count * 8 ) } } );
|
||||
return exp.is_valid() ? exp.resize( result_bcnt ) : exp;
|
||||
};
|
||||
lvm.hooks.write_register = [ & ] ( const register_desc& desc, symbolic::expression value )
|
||||
lvm.hooks.write_register = [ & ] ( const register_desc& desc, symbolic::expression::reference value )
|
||||
{
|
||||
if ( desc == lookup.reg() )
|
||||
result = std::move( value );
|
||||
};
|
||||
|
||||
lvm.hooks.write_memory = [ & ] ( const symbolic::expression& pointer, symbolic::expression value )
|
||||
lvm.hooks.write_memory = [ & ] ( const symbolic::expression::reference& pointer, symbolic::expression::reference value )
|
||||
{
|
||||
if ( pointer.equals( lookup.mem().decay() ) )
|
||||
if ( pointer->equals( lookup.mem().decay() ) )
|
||||
result = std::move( value );
|
||||
};
|
||||
|
||||
// Step one instruction, if result was successfuly captured, return.
|
||||
//
|
||||
if ( lvm.execute( *it ), result )
|
||||
return result;
|
||||
return *result;
|
||||
|
||||
// If we could not describe the behaviour, increment iterator and return.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ namespace vtil
|
|||
{
|
||||
// Declare a helper to convert operands of current instruction into expressions.
|
||||
//
|
||||
auto cvt_operand = [ & ] ( int i ) -> symbolic::expression
|
||||
auto cvt_operand = [ & ] ( int i ) -> symbolic::expression::reference
|
||||
{
|
||||
const operand& op = ins.operands[ i ];
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ namespace vtil
|
|||
{
|
||||
// Trace the source register.
|
||||
//
|
||||
symbolic::expression result = read_register( op.reg() );
|
||||
symbolic::expression::reference result = read_register( op.reg() );
|
||||
|
||||
// If stack pointer, add the current virtual offset.
|
||||
//
|
||||
|
|
@ -76,7 +76,7 @@ namespace vtil
|
|||
//
|
||||
write_register(
|
||||
ins.operands[ 0 ].reg(),
|
||||
cvt_operand( 1 ).resize( ins.operands[ 0 ].bit_count(), cast_signed )
|
||||
cvt_operand( 1 )->resize( ins.operands[ 0 ].bit_count(), cast_signed )
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -108,13 +108,14 @@ namespace vtil
|
|||
// Read the source operand and byte-align.
|
||||
//
|
||||
auto src = cvt_operand( 2 );
|
||||
src.resize( ( src.size() + 7 ) & ~7 );
|
||||
bitcnt_t bsize = ( src->size() + 7 ) & ~7;
|
||||
if ( src->size() != bsize ) ( +src )->resize( bsize );
|
||||
|
||||
// Query base pointer without using the wrapper to skip SP adjustment and
|
||||
// add offset. Write the source to the pointer.
|
||||
//
|
||||
auto [base, offset] = ins.memory_location();
|
||||
write_memory( read_register( base ) + offset, src );
|
||||
write_memory( read_register( base ) + offset, *src );
|
||||
return true;
|
||||
}
|
||||
// If any symbolic operator:
|
||||
|
|
@ -161,7 +162,7 @@ namespace vtil
|
|||
else if ( ( ins.operands[ 0 ].size() + ins.operands[ 1 ].size() ) <= 8 )
|
||||
{
|
||||
auto op1_low = cvt_operand( 0 );
|
||||
auto op1 = op1_low | ( op1_high.resize( op1_high.size() + op1_low.size() ) << op1_low.size() );
|
||||
auto op1 = op1_low | ( op1_high->resize( op1_high->size() + op1_low->size() ) << op1_low->size() );
|
||||
result = { op1, op_id, cvt_operand( 2 ) };
|
||||
}
|
||||
// If operation is 65 bits or bigger:
|
||||
|
|
|
|||
|
|
@ -41,19 +41,19 @@ namespace vtil
|
|||
|
||||
// Reads from the register.
|
||||
//
|
||||
virtual symbolic::expression read_register( const register_desc& desc ) { unreachable(); return {}; }
|
||||
virtual symbolic::expression::reference read_register( const register_desc& desc ) { unreachable(); return {}; }
|
||||
|
||||
// Reads the given number of bytes from the memory.
|
||||
//
|
||||
virtual symbolic::expression read_memory( const symbolic::expression& pointer, size_t byte_count ) { unreachable(); return {}; }
|
||||
virtual symbolic::expression::reference read_memory( const symbolic::expression::reference& pointer, size_t byte_count ) { unreachable(); return {}; }
|
||||
|
||||
// Writes to the register.
|
||||
//
|
||||
virtual void write_register( const register_desc& desc, symbolic::expression value ) { unreachable(); }
|
||||
virtual void write_register( const register_desc& desc,symbolic::expression::reference value ) { unreachable(); }
|
||||
|
||||
// Writes the given expression to the memory.
|
||||
//
|
||||
virtual void write_memory( const symbolic::expression& pointer, symbolic::expression value ) { unreachable(); }
|
||||
virtual void write_memory( const symbolic::expression::reference& pointer, symbolic::expression::reference value ) { unreachable(); }
|
||||
|
||||
// Runs the given instruction, returns whether it was successful.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -70,25 +70,25 @@ namespace vtil
|
|||
? hooks.size_register( desc )
|
||||
: vm_base::size_register( desc );
|
||||
}
|
||||
symbolic::expression read_register( const register_desc& desc ) override
|
||||
symbolic::expression::reference read_register( const register_desc& desc ) override
|
||||
{
|
||||
return hooks.read_register
|
||||
? hooks.read_register( desc )
|
||||
: vm_base::read_register( desc );
|
||||
}
|
||||
symbolic::expression read_memory( const symbolic::expression& pointer, size_t byte_count ) override
|
||||
symbolic::expression::reference read_memory( const symbolic::expression::reference& pointer, size_t byte_count ) override
|
||||
{
|
||||
return hooks.read_memory
|
||||
? hooks.read_memory( pointer, byte_count )
|
||||
: vm_base::read_memory( pointer, byte_count );
|
||||
}
|
||||
void write_register( const register_desc& desc, symbolic::expression value ) override
|
||||
void write_register( const register_desc& desc, symbolic::expression::reference value ) override
|
||||
{
|
||||
return hooks.write_register
|
||||
? hooks.write_register( desc, std::move( value ) )
|
||||
: vm_base::write_register( desc, std::move( value ) );
|
||||
}
|
||||
void write_memory( const symbolic::expression& pointer, symbolic::expression value ) override
|
||||
void write_memory( const symbolic::expression::reference& pointer, symbolic::expression::reference value ) override
|
||||
{
|
||||
return hooks.write_memory
|
||||
? hooks.write_memory( pointer, std::move( value ) )
|
||||
|
|
|
|||
|
|
@ -31,13 +31,13 @@ namespace vtil
|
|||
{
|
||||
// Reads from the register.
|
||||
//
|
||||
symbolic::expression symbolic_vm::read_register( const register_desc& desc )
|
||||
symbolic::expression::reference symbolic_vm::read_register( const register_desc& desc )
|
||||
{
|
||||
bitcnt_t size = size_register( desc );
|
||||
register_desc full = { desc.flags, desc.local_id, size, 0, desc.architecture };
|
||||
|
||||
auto it = register_state.find( full );
|
||||
symbolic::expression exp = it == register_state.end()
|
||||
auto exp = it == register_state.end()
|
||||
? symbolic::variable{ full }.to_expression( false )
|
||||
: it->second;
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ namespace vtil
|
|||
|
||||
// Writes to the register.
|
||||
//
|
||||
void symbolic_vm::write_register( const register_desc& desc, symbolic::expression value )
|
||||
void symbolic_vm::write_register( const register_desc& desc, symbolic::expression::reference value )
|
||||
{
|
||||
bitcnt_t size = size_register( desc );
|
||||
register_desc full = { desc.flags, desc.local_id, size, 0, desc.architecture };
|
||||
|
|
@ -57,22 +57,22 @@ namespace vtil
|
|||
if ( desc.bit_count == size && desc.bit_offset == 0 )
|
||||
{
|
||||
register_state.erase( desc );
|
||||
register_state.emplace( desc, std::move( value ) );
|
||||
register_state.emplace( desc, value );
|
||||
}
|
||||
else
|
||||
{
|
||||
auto& exp = register_state[ full ];
|
||||
if ( !exp ) exp = symbolic::make_register_ex( full );
|
||||
exp = ( exp & ~desc.get_mask() ) | ( value.resize( desc.bit_count ).resize( size ) << desc.bit_offset );
|
||||
exp = ( std::move( exp ) & ~desc.get_mask() ) | ( value.resize( desc.bit_count ).resize( size ) << desc.bit_offset );
|
||||
}
|
||||
}
|
||||
|
||||
// Reads the given number of bytes from the memory.
|
||||
//
|
||||
symbolic::expression symbolic_vm::read_memory( const symbolic::expression& pointer, size_t byte_count )
|
||||
symbolic::expression::reference symbolic_vm::read_memory( const symbolic::expression::reference& pointer, size_t byte_count )
|
||||
{
|
||||
bitcnt_t bcnt = math::narrow_cast<bitcnt_t>( byte_count * 8 );
|
||||
symbolic::expression exp = memory_state.read_v(
|
||||
symbolic::expression::reference exp = memory_state.read_v(
|
||||
pointer,
|
||||
bcnt
|
||||
);
|
||||
|
|
@ -81,11 +81,11 @@ namespace vtil
|
|||
|
||||
// Writes the given expression to the memory.
|
||||
//
|
||||
void symbolic_vm::write_memory( const symbolic::expression& pointer, symbolic::expression value )
|
||||
void symbolic_vm::write_memory( const symbolic::expression::reference& pointer, symbolic::expression::reference value )
|
||||
{
|
||||
memory_state.write(
|
||||
pointer,
|
||||
value.resize( ( value.size() + 7 ) & ~7 )
|
||||
value.resize( ( value->size() + 7 ) & ~7 )
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ namespace vtil
|
|||
// State of the virtual machine.
|
||||
//
|
||||
symbolic::memory memory_state;
|
||||
std::map<register_desc, symbolic::expression> register_state;
|
||||
std::map<register_desc, symbolic::expression::reference> register_state;
|
||||
|
||||
// Construct from memory type, defaults to free.
|
||||
//
|
||||
|
|
@ -52,19 +52,19 @@ namespace vtil
|
|||
// Reads from the register.
|
||||
// - Value will be unpacked.
|
||||
//
|
||||
symbolic::expression read_register( const register_desc& desc ) override;
|
||||
symbolic::expression::reference read_register( const register_desc& desc ) override;
|
||||
|
||||
// Writes to the register.
|
||||
//
|
||||
void write_register( const register_desc& desc, symbolic::expression value ) override;
|
||||
void write_register( const register_desc& desc, symbolic::expression::reference value ) override;
|
||||
|
||||
// Reads the given number of bytes from the memory.
|
||||
//
|
||||
symbolic::expression read_memory( const symbolic::expression& pointer, size_t byte_count ) override;
|
||||
symbolic::expression::reference read_memory( const symbolic::expression::reference& pointer, size_t byte_count ) override;
|
||||
|
||||
// Writes the given expression to the memory.
|
||||
//
|
||||
void write_memory( const symbolic::expression& pointer, symbolic::expression value ) override;
|
||||
void write_memory( const symbolic::expression::reference& pointer, symbolic::expression::reference value ) override;
|
||||
|
||||
// Override execute to enforce lazyness.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ namespace vtil::optimizer
|
|||
//
|
||||
auto k = pair.first; auto v = pair.second.simplify();
|
||||
symbolic::expression v0 = symbolic::make_register_ex( k );
|
||||
if ( v0.equals( v ) )
|
||||
if ( v->equals( v0 ) )
|
||||
continue;
|
||||
|
||||
// If register value is not used after this instruction, skip from emitted state.
|
||||
|
|
@ -191,11 +191,11 @@ namespace vtil::optimizer
|
|||
|
||||
// Pack registers and the expression.
|
||||
//
|
||||
v = symbolic::variable::pack_all( v.simplify( true ) );
|
||||
symbolic::expression final_value = symbolic::variable::pack_all( ( +v )->simplify( true ) );
|
||||
|
||||
// Buffer a mov instruction.
|
||||
//
|
||||
instruction_buffer.push_back( { &ins::mov, { k, translator << v } } );
|
||||
instruction_buffer.push_back( { &ins::mov, { k, translator << final_value } } );
|
||||
}
|
||||
|
||||
// For each memory state:
|
||||
|
|
@ -208,7 +208,7 @@ namespace vtil::optimizer
|
|||
|
||||
// If value is unchanged, skip.
|
||||
//
|
||||
if ( v.equals( v0 ) )
|
||||
if ( v->equals( v0 ) )
|
||||
continue;
|
||||
|
||||
// Try minimizing expression size.
|
||||
|
|
@ -230,7 +230,7 @@ namespace vtil::optimizer
|
|||
|
||||
// Pack registers and the expression.
|
||||
//
|
||||
v = symbolic::variable::pack_all( v.simplify( true ) );
|
||||
v = symbolic::variable::pack_all( v->simplify( true ) );
|
||||
|
||||
// If pointer can be rewritten as $sp + C:
|
||||
//
|
||||
|
|
@ -242,7 +242,7 @@ namespace vtil::optimizer
|
|||
instruction_buffer.push_back(
|
||||
{
|
||||
&ins::str,
|
||||
{ REG_SP, make_imm<int64_t>( *displacement ), translator << v }
|
||||
{ REG_SP, make_imm<int64_t>( *displacement ), translator << *v }
|
||||
} );
|
||||
}
|
||||
else
|
||||
|
|
@ -283,7 +283,7 @@ namespace vtil::optimizer
|
|||
instruction_buffer.push_back(
|
||||
{
|
||||
&ins::str,
|
||||
{ base, make_imm( offset ), translator << v }
|
||||
{ base, make_imm( offset ), translator << *v }
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,12 +123,12 @@ namespace vtil::optimizer::validation
|
|||
|
||||
// Validate target.
|
||||
//
|
||||
symbolic::expression target_call = ins.operands[ 0 ].is_immediate()
|
||||
? ins.operands[ 0 ].imm().u64
|
||||
auto target_call = ins.operands[ 0 ].is_immediate()
|
||||
? symbolic::expression::reference{ ins.operands[ 0 ].imm().u64 }
|
||||
: vm.read_register( ins.operands[ 0 ].reg() );
|
||||
if ( target_call.value.get() != call.address )
|
||||
if ( target_call->value.get() != call.address )
|
||||
{
|
||||
logger::warning( "Unexpected callee, expected 0x%llx, got [%s].", call.address, target_call );
|
||||
logger::warning( "Unexpected callee, expected 0x%llx, got [%s].", call.address, *target_call );
|
||||
success = false;
|
||||
return false;
|
||||
}
|
||||
|
|
@ -139,7 +139,7 @@ namespace vtil::optimizer::validation
|
|||
auto it = call_conv.param_registers.begin();
|
||||
for ( auto [value, id] : zip( call.parameters, iindices() ) )
|
||||
{
|
||||
symbolic::expression exp;
|
||||
symbolic::expression::reference exp;
|
||||
|
||||
// If we did not reach the end of registers yet:
|
||||
//
|
||||
|
|
@ -163,7 +163,7 @@ namespace vtil::optimizer::validation
|
|||
|
||||
// Fail if value does not match.
|
||||
//
|
||||
if ( exp.value.get() != value )
|
||||
if ( exp->value.get() != value )
|
||||
{
|
||||
logger::warning( "Parameter %d does not match, expected 0x%llx, got [%s].", id, value, exp );
|
||||
success = false;
|
||||
|
|
@ -196,12 +196,12 @@ namespace vtil::optimizer::validation
|
|||
|
||||
// Validate return address.
|
||||
//
|
||||
symbolic::expression sreturn_address = ins.operands[ 0 ].is_immediate()
|
||||
? ins.operands[ 0 ].imm().u64
|
||||
auto sreturn_address = ins.operands[ 0 ].is_immediate()
|
||||
? symbolic::expression::reference{ ins.operands[ 0 ].imm().u64 }
|
||||
: vm.read_register( ins.operands[ 0 ].reg() );
|
||||
if ( sreturn_address.value.get() != return_address )
|
||||
if ( sreturn_address->value.get() != return_address )
|
||||
{
|
||||
logger::warning( "Unexpected return address, expected 0x%llx, got [%s].", return_address, sreturn_address );
|
||||
logger::warning( "Unexpected return address, expected 0x%llx, got [%s].", return_address, *sreturn_address );
|
||||
success = false;
|
||||
return false;
|
||||
}
|
||||
|
|
@ -210,8 +210,8 @@ namespace vtil::optimizer::validation
|
|||
//
|
||||
for ( auto& [reg, value] : exit.register_state )
|
||||
{
|
||||
symbolic::expression exp = vm.read_register( reg );
|
||||
if ( exp.value.get() != value )
|
||||
auto exp = vm.read_register( reg );
|
||||
if ( exp->value.get() != value )
|
||||
{
|
||||
logger::warning( "Return state %s does not match, expected 0x%llx, got [%s].", reg, value, exp );
|
||||
success = false;
|
||||
|
|
@ -227,42 +227,40 @@ namespace vtil::optimizer::validation
|
|||
return vm.symbolic_vm::execute( ins );
|
||||
};
|
||||
|
||||
vm.hooks.read_memory = [ & ] ( const symbolic::expression& pointer, size_t sz )
|
||||
vm.hooks.read_memory = [ & ] ( const symbolic::expression::reference& pointer, size_t sz )
|
||||
{
|
||||
symbolic::expression ptr = pointer.simplify();
|
||||
|
||||
// If action log has a matching read memory on top of the stack:
|
||||
//
|
||||
if ( action_it != action_end && std::get_if<memory_read>( &*action_it ) )
|
||||
{
|
||||
auto& mem = std::get<memory_read>( *action_it );
|
||||
if ( ptr.value.get() == mem.address )
|
||||
if ( pointer->value.get() == mem.address )
|
||||
{
|
||||
// Write fake value to the state and pop the stack.
|
||||
//
|
||||
symbolic::expression value = { mem.fake_value, mem.size };
|
||||
vm.symbolic_vm::write_memory( ptr, value );
|
||||
vm.symbolic_vm::write_memory( pointer, value );
|
||||
++action_it;
|
||||
}
|
||||
}
|
||||
|
||||
return vm.symbolic_vm::read_memory( ptr, sz );
|
||||
return vm.symbolic_vm::read_memory( pointer, sz );
|
||||
};
|
||||
|
||||
vm.hooks.write_memory = [ & ] ( const symbolic::expression& pointer, symbolic::expression exp )
|
||||
vm.hooks.write_memory = [ & ] ( const symbolic::expression::reference& pointer, symbolic::expression::reference exp )
|
||||
{
|
||||
// If action log has a matching write memory on top of the stack:
|
||||
//
|
||||
if ( action_it != action_end && std::get_if<memory_write>( &*action_it ) )
|
||||
{
|
||||
auto& mem = std::get<memory_write>( *action_it );
|
||||
if ( pointer.value.get() == mem.address )
|
||||
if ( pointer->value.get() == mem.address )
|
||||
{
|
||||
// Pop the stack and validate the value.
|
||||
//
|
||||
if ( exp.value.get() != mem.value )
|
||||
if ( exp->value.get() != mem.value )
|
||||
{
|
||||
logger::warning( "Unexpected memory write into 0x%llx, expected 0x%llx, got [%s].", mem.address, mem.value, exp );
|
||||
logger::warning( "Unexpected memory write into 0x%llx, expected 0x%llx, got [%s].", mem.address, mem.value, *exp );
|
||||
success = false;
|
||||
}
|
||||
++action_it;
|
||||
|
|
@ -315,7 +313,7 @@ namespace vtil::optimizer::validation
|
|||
//
|
||||
operand dst = {};
|
||||
if ( lim->base == &ins::js )
|
||||
dst = *vm.read_register( lim->operands[ 0 ].reg() ).get<bool>() ? lim->operands[ 1 ] : lim->operands[ 2 ];
|
||||
dst = *vm.read_register( lim->operands[ 0 ].reg() )->get<bool>() ? lim->operands[ 1 ] : lim->operands[ 2 ];
|
||||
else if ( lim->base == &ins::jmp )
|
||||
dst = lim->operands[ 0 ];
|
||||
|
||||
|
|
@ -326,7 +324,7 @@ namespace vtil::optimizer::validation
|
|||
eit = rtn->explored_blocks.find( dst.imm().u64 );
|
||||
// Otherwise read VM context.
|
||||
//
|
||||
else if ( auto jmp_dst = vm.read_register( dst.reg() ).get() )
|
||||
else if ( auto jmp_dst = vm.read_register( dst.reg() )->get() )
|
||||
eit = rtn->explored_blocks.find( *jmp_dst );
|
||||
|
||||
// If no valid destination, fail.
|
||||
|
|
|
|||
|
|
@ -31,11 +31,13 @@ namespace vtil::symbolic::directive
|
|||
{
|
||||
// Constructor for directive representing the result of an unary operator.
|
||||
//
|
||||
instance::instance( math::operator_id op, const instance& e1 ) : rhs( e1 ), op( op ) {}
|
||||
instance::instance( math::operator_id op, const instance& e1 )
|
||||
: rhs( e1 ), op( op ) {}
|
||||
|
||||
// Constructor for directive representing the result of a binary operator.
|
||||
//
|
||||
instance::instance( const instance& e1, math::operator_id op, const instance& e2 ) : lhs( e1 ), rhs( e2 ), op( op ) {}
|
||||
instance::instance( const instance& e1, math::operator_id op, const instance& e2 )
|
||||
: lhs( e1 ), rhs( e2 ), op( op ) {}
|
||||
|
||||
// Enumerates each unique variable.
|
||||
//
|
||||
|
|
@ -94,4 +96,25 @@ namespace vtil::symbolic::directive
|
|||
else if ( !o.lhs || !lhs->equals( *o.lhs ) ) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Simple copyable unique pointer implementation.
|
||||
//
|
||||
instance::reference::reference( const instance& o ) : ptr( new instance( o ) ) {}
|
||||
instance::reference::reference( instance&& o ) : ptr( new instance( std::move( o ) ) ) {}
|
||||
instance::reference::reference( const reference& o ) : ptr( o ? new instance( *o ) : nullptr ) {}
|
||||
instance::reference::reference( reference&& o ) : ptr( std::exchange( o.ptr, nullptr ) ) {}
|
||||
instance::reference::~reference()
|
||||
{
|
||||
if ( ptr ) delete ptr;
|
||||
}
|
||||
instance::reference& instance::reference::operator=( instance::reference&& o )
|
||||
{
|
||||
ptr = std::exchange( o.ptr, nullptr );
|
||||
return *this;
|
||||
}
|
||||
instance::reference& instance::reference::operator=( const instance::reference& o )
|
||||
{
|
||||
ptr = o ? new instance( *o ) : nullptr;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
|
@ -147,7 +147,42 @@ namespace vtil::symbolic::directive
|
|||
//
|
||||
struct instance : math::operable<instance>
|
||||
{
|
||||
using reference = shared_reference<instance>;
|
||||
// Simple copyable unique pointer implementation.
|
||||
//
|
||||
struct reference
|
||||
{
|
||||
instance* ptr = nullptr;
|
||||
|
||||
// Construct by implicit null or instance value.
|
||||
//
|
||||
reference() {}
|
||||
reference( const instance& i );
|
||||
reference( instance&& i );
|
||||
|
||||
// Copy / Move from another reference.
|
||||
//
|
||||
reference( const reference& o );
|
||||
reference( reference&& o );
|
||||
reference& operator=( reference&& o );
|
||||
reference& operator=( const reference& o );
|
||||
|
||||
// Destructor deletes the value.
|
||||
//
|
||||
~reference();
|
||||
|
||||
// Null check.
|
||||
//
|
||||
explicit operator bool() const { return ptr; }
|
||||
|
||||
// Pointer interface.
|
||||
//
|
||||
operator instance*() { return ptr; }
|
||||
operator const instance*() const { return ptr; }
|
||||
instance& operator*() { return *ptr; }
|
||||
const instance& operator*() const { return *ptr; }
|
||||
instance* operator->() { return ptr; }
|
||||
const instance* operator->() const { return ptr; }
|
||||
};
|
||||
|
||||
// If symbolic variable, the identifier of the variable
|
||||
// and type of expressions it can match.
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ namespace vtil::symbolic::directive
|
|||
|
||||
// Adds the mapping of a variable to an expression.
|
||||
//
|
||||
bool add( const instance::reference& dir, const expression::reference& exp )
|
||||
bool add( const instance* dir, const expression::reference& exp )
|
||||
{
|
||||
// If it's the first time this variable is being used:
|
||||
//
|
||||
|
|
@ -74,7 +74,7 @@ namespace vtil::symbolic::directive
|
|||
|
||||
// Translates a variable to the matching expression.
|
||||
//
|
||||
expression::reference translate( const instance::reference& dir ) const
|
||||
expression::reference translate( const instance* dir ) const
|
||||
{
|
||||
// Assert the looked up type is variable.
|
||||
//
|
||||
|
|
@ -83,7 +83,8 @@ namespace vtil::symbolic::directive
|
|||
// Translate using the lookup table.
|
||||
//
|
||||
return lookup_table[ dir->lookup_index ];
|
||||
}
|
||||
}
|
||||
expression::reference translate( const instance& dir ) const { return translate( &dir ); }
|
||||
};
|
||||
|
||||
// Tries to match the the given expression with the directive and fills the
|
||||
|
|
@ -91,7 +92,7 @@ namespace vtil::symbolic::directive
|
|||
//
|
||||
template<typename T, std::enable_if_t<std::is_same_v<typename T::value_type, symbol_table_t>, int> = 0>
|
||||
static size_t fast_match( T* results,
|
||||
const instance::reference& dir,
|
||||
const instance* dir,
|
||||
const expression::reference& exp,
|
||||
size_t index = 0 )
|
||||
{
|
||||
|
|
@ -196,4 +197,6 @@ namespace vtil::symbolic::directive
|
|||
//
|
||||
return ( results->size() + 1 ) - size_0;
|
||||
}
|
||||
template<typename T, std::enable_if_t<std::is_same_v<typename T::value_type, symbol_table_t>, int> = 0>
|
||||
static size_t fast_match( T* results, const instance& dir, const expression::reference& exp, size_t index = 0 ) { return fast_match( results, &dir, exp, index ); }
|
||||
};
|
||||
|
|
@ -38,7 +38,7 @@ namespace vtil::symbolic
|
|||
// or a null reference if it would fail.
|
||||
//
|
||||
expression::reference translate( const symbol_table_t& sym,
|
||||
const instance::reference& dir,
|
||||
const instance* dir,
|
||||
bitcnt_t bit_cnt,
|
||||
bool speculative_condition,
|
||||
int64_t max_depth )
|
||||
|
|
@ -260,7 +260,7 @@ namespace vtil::symbolic
|
|||
// and returns the first instance that matches query.
|
||||
//
|
||||
expression::reference transform( const expression::reference& exp,
|
||||
const instance::reference& from, const instance::reference& to,
|
||||
const instance* from, const instance* to,
|
||||
const expression_filter_t& filter,
|
||||
int64_t max_depth )
|
||||
{
|
||||
|
|
@ -283,7 +283,7 @@ namespace vtil::symbolic
|
|||
// Log the translation.
|
||||
//
|
||||
log<CON_BLU>( "Translating [%s] => [%s]:\n", *from, *to );
|
||||
from->enum_variables( [ & ] ( const instance& ins )
|
||||
from->enum_variables( [ & ] ( const instance* ins )
|
||||
{
|
||||
log<CON_BLU>( " %s: %s\n", ins.id, *match.translate( ins ) );
|
||||
} );
|
||||
|
|
@ -343,7 +343,7 @@ namespace vtil::symbolic
|
|||
// Log the translation.
|
||||
//
|
||||
log<CON_BLU>( "Translating [%s] => [%s]:\n", *from, *to );
|
||||
from->enum_variables( [ & ] ( const instance& ins )
|
||||
from->enum_variables( [ & ] ( const instance* ins )
|
||||
{
|
||||
log<CON_BLU>( " %s: %s\n", ins.id, *match.translate( ins ) );
|
||||
} );
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ namespace vtil::symbolic
|
|||
// or a null reference if it would fail.
|
||||
//
|
||||
expression::reference translate( const directive::symbol_table_t& sym,
|
||||
const directive::instance::reference& dir,
|
||||
const directive::instance* dir,
|
||||
bitcnt_t bit_cnt,
|
||||
bool speculative_condition,
|
||||
int64_t max_depth );
|
||||
|
|
@ -49,7 +49,7 @@ namespace vtil::symbolic
|
|||
// and returns the first instance that matches query.
|
||||
//
|
||||
expression::reference transform( const expression::reference& exp,
|
||||
const directive::instance::reference& from, const directive::instance::reference& to,
|
||||
const directive::instance* from, const directive::instance* to,
|
||||
const expression_filter_t& filter,
|
||||
int64_t max_depth );
|
||||
};
|
||||
|
|
@ -691,7 +691,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.
|
||||
//
|
||||
auto ref = make_local_reference( this );
|
||||
reference ref = make_local_reference( this );
|
||||
simplify_expression( ref, prettify );
|
||||
|
||||
// Only thing that we should be careful about is the case expression->simplify(),
|
||||
|
|
@ -869,4 +869,44 @@ namespace vtil::symbolic
|
|||
if ( is_variable() ) return uid.to_string();
|
||||
return "null";
|
||||
}
|
||||
|
||||
// Implement some helpers to conditionally copy.
|
||||
//
|
||||
expression_reference& expression_reference::resize( bitcnt_t new_size, bool signed_cast, bool no_explicit )
|
||||
{
|
||||
if ( new_size != get()->size() )
|
||||
own()->resize( new_size, signed_cast, no_explicit );
|
||||
return *this;
|
||||
}
|
||||
expression_reference expression_reference::resize( bitcnt_t new_size, bool signed_cast, bool no_explicit ) const
|
||||
{
|
||||
return expression_reference{ *this }.resize( new_size, signed_cast, no_explicit );
|
||||
}
|
||||
expression_reference& expression_reference::simplify( bool prettify )
|
||||
{
|
||||
if ( prettify || !get()->simplify_hint )
|
||||
own()->simplify( prettify );
|
||||
return *this;
|
||||
}
|
||||
expression_reference expression_reference::simplify( bool prettify ) const
|
||||
{
|
||||
return expression_reference{ *this }.simplify( prettify );
|
||||
}
|
||||
expression_reference& expression_reference::make_lazy()
|
||||
{
|
||||
if ( !get()->is_lazy )
|
||||
own()->is_lazy = true;
|
||||
return *this;
|
||||
}
|
||||
expression_reference expression_reference::make_lazy() const
|
||||
{
|
||||
return expression_reference{ *this }.make_lazy();
|
||||
}
|
||||
|
||||
// Implemented for sinkhole-use.
|
||||
//
|
||||
bitcnt_t expression_reference::size() const
|
||||
{
|
||||
return get()->size();
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
namespace vtil::symbolic::directive
|
||||
{
|
||||
std::vector<std::pair<instance::reference, instance::reference>> boolean_simplifiers
|
||||
std::vector<std::pair<instance, instance>> boolean_simplifiers
|
||||
{
|
||||
// Manually added:
|
||||
//
|
||||
|
|
@ -57,12 +57,12 @@ namespace vtil::symbolic::directive
|
|||
{ A!=-A, 1 },
|
||||
};
|
||||
|
||||
const std::vector<std::pair<instance::reference, instance::reference>>& build_boolean_simplifiers()
|
||||
const std::vector<std::pair<instance, instance>>& build_boolean_simplifiers()
|
||||
{
|
||||
if ( !boolean_simplifiers.empty() )
|
||||
return boolean_simplifiers;
|
||||
#ifndef __INTELLISENSE__
|
||||
#define ADD_DIRECTIVE( ... ) ([ ] () { boolean_simplifiers.push_back( { __VA_ARGS__ } ); })()
|
||||
#define ADD_DIRECTIVE( ... ) ([ ] () { boolean_simplifiers.emplace_back( __VA_ARGS__ ); })()
|
||||
// Boolean Simplifiers
|
||||
//
|
||||
ADD_DIRECTIVE( ((A>B)&(A>C)), __iff((B>C), (A>B)) );
|
||||
|
|
@ -2617,7 +2617,7 @@ namespace vtil::symbolic::directive
|
|||
constexpr auto overflow = [ ] ( auto a, auto b ) { return ((a<0)==(b<0))&((a<0)!=((a+b)<0)); };
|
||||
constexpr auto underflow = [ ] ( auto a, auto b ) { return ((a<0)!=(b<0))&((a<0)!=((a-b)<0)); };
|
||||
|
||||
const std::vector<std::pair<instance::reference, instance::reference>> boolean_joiners =
|
||||
const std::vector<std::pair<instance, instance>> boolean_joiners =
|
||||
{
|
||||
// Manually added:
|
||||
//
|
||||
|
|
|
|||
|
|
@ -32,10 +32,10 @@
|
|||
|
||||
namespace vtil::symbolic::directive
|
||||
{
|
||||
extern std::vector<std::pair<instance::reference, instance::reference>> boolean_simplifiers;
|
||||
extern const std::vector<std::pair<instance::reference, instance::reference>> boolean_joiners;
|
||||
extern std::vector<std::pair<instance, instance>> boolean_simplifiers;
|
||||
extern const std::vector<std::pair<instance, instance>> boolean_joiners;
|
||||
|
||||
const std::vector<std::pair<instance::reference, instance::reference>>& build_boolean_simplifiers();
|
||||
const std::vector<std::pair<instance, instance>>& build_boolean_simplifiers();
|
||||
|
||||
/*
|
||||
Auto generated using:
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ namespace vtil::symbolic::directive
|
|||
// List of universal simplifiers, they have to reduce complexity or keep it equal
|
||||
// at the very least to not cause an infinity loop.
|
||||
//
|
||||
static const std::pair<instance::reference, instance::reference> universal_simplifiers[] =
|
||||
static const std::pair<instance, instance> universal_simplifiers[] =
|
||||
{
|
||||
// TODO: Arithmetic operators, */% etc.
|
||||
//
|
||||
|
|
@ -235,7 +235,7 @@ namespace vtil::symbolic::directive
|
|||
// Describes the way operands of two operators join each other.
|
||||
// - Has no obligation to produce simple output, should be checked.
|
||||
//
|
||||
static const std::pair<instance::reference, instance::reference> join_descriptors[] =
|
||||
static const std::pair<instance, instance> join_descriptors[] =
|
||||
{
|
||||
// TODO: Arithmetic operators, */% etc.
|
||||
// TODO: Should we add ADD and SUB to bitwise despite the partial evaluator?
|
||||
|
|
@ -391,7 +391,7 @@ namespace vtil::symbolic::directive
|
|||
|
||||
// Grouping of simple representations into more complex directives.
|
||||
//
|
||||
static const std::pair<instance::reference, instance::reference> pack_descriptors[] =
|
||||
static const std::pair<instance, instance> pack_descriptors[] =
|
||||
{
|
||||
{ __ucast(A>>B, 0x1), __bt(A, B) },
|
||||
{ (A>>B)&1, __ucast(__bt(A,B),__bcnt(A)) },
|
||||
|
|
@ -413,7 +413,7 @@ namespace vtil::symbolic::directive
|
|||
|
||||
// Conversion from more complex directives into simple representations.
|
||||
//
|
||||
static const std::pair<instance::reference, instance::reference> unpack_descriptors[] =
|
||||
static const std::pair<instance, instance> unpack_descriptors[] =
|
||||
{
|
||||
{ __bt(A,B), __ucast((A&(1<<B))>>B,1) },
|
||||
{ __min(A,B), __if(A<=B,A)|__if(A>B,B) },
|
||||
|
|
|
|||
|
|
@ -44,8 +44,8 @@ namespace vtil::symbolic
|
|||
|
||||
// Implement lookup-table based dynamic tables.
|
||||
//
|
||||
using static_directive_table_entry = std::pair<directive::instance::reference, directive::instance::reference>;
|
||||
using dynamic_directive_table_entry = std::pair<const directive::instance*, const directive::instance*>;
|
||||
using static_directive_table_entry = std::pair<directive::instance, directive::instance>;
|
||||
using dynamic_directive_table_entry = std::pair<const directive::instance*, const directive::instance*>;
|
||||
|
||||
using dynamic_directive_table = std::vector<dynamic_directive_table_entry>;
|
||||
using organized_directive_table = std::array<dynamic_directive_table, ( size_t ) math::operator_id::max>;
|
||||
|
|
@ -56,8 +56,8 @@ namespace vtil::symbolic
|
|||
organized_directive_table table;
|
||||
for ( auto [table, op] : zip( table, iindices() ) )
|
||||
for( auto& directive : container )
|
||||
if ( directive.first->op == ( math::operator_id ) op )
|
||||
table.emplace_back( directive.first.get(), directive.second.get() );
|
||||
if ( directive.first.op == ( math::operator_id ) op )
|
||||
table.emplace_back( &directive.first, &directive.second );
|
||||
return table;
|
||||
};
|
||||
|
||||
|
|
@ -122,7 +122,7 @@ namespace vtil::symbolic
|
|||
{
|
||||
// If we can transform the expression by the directive set:
|
||||
//
|
||||
if ( auto exp_new = transform( exp, *dir_src, *dir_dst, {}, -1 ) )
|
||||
if ( auto exp_new = transform( exp, dir_src, dir_dst, {}, -1 ) )
|
||||
{
|
||||
#if VTIL_SYMEX_SIMPLIFY_VERBOSE
|
||||
log<CON_PRP>( "[Pack] %s => %s\n", *dir_src, *dir_dst );
|
||||
|
|
@ -141,7 +141,7 @@ namespace vtil::symbolic
|
|||
|
||||
// Checks if the expression can be interpreted as a vector-boolean expression.
|
||||
//
|
||||
static std::pair<bool, const expression*> match_boolean_expression( const expression::reference& exp )
|
||||
static std::pair<bool, expression::reference> match_boolean_expression( const expression::reference& exp )
|
||||
{
|
||||
switch ( exp->op )
|
||||
{
|
||||
|
|
@ -149,7 +149,7 @@ namespace vtil::symbolic
|
|||
//
|
||||
case math::operator_id::invalid:
|
||||
{
|
||||
if ( exp->is_variable() ) return { true, &*exp };
|
||||
if ( exp->is_variable() ) return { true, exp };
|
||||
else return { true, nullptr };
|
||||
}
|
||||
|
||||
|
|
@ -170,11 +170,11 @@ namespace vtil::symbolic
|
|||
auto [m2, p2] = match_boolean_expression( exp->rhs );
|
||||
if ( !m2 ) return { false, nullptr };
|
||||
|
||||
if ( !p2 ) return { true, p1 };
|
||||
if ( !p1 ) return { true, p2 };
|
||||
if ( !p2 ) return { true, std::move( p1 ) };
|
||||
if ( !p1 ) return { true, std::move( p2 ) };
|
||||
|
||||
if ( p1->uid == p2->uid )
|
||||
return { true, p1 };
|
||||
return { true, std::move( p1 ) };
|
||||
else
|
||||
return { false, nullptr };
|
||||
}
|
||||
|
|
@ -208,18 +208,18 @@ namespace vtil::symbolic
|
|||
|
||||
// Apply each mask if not no-op.
|
||||
//
|
||||
expression exp_new = uid_base->clone();
|
||||
expression::reference& exp_new = uid_base;
|
||||
if ( and_mask != ~0ull ) exp_new = exp_new & expression{ and_mask, exp->size() };
|
||||
if ( xor_mask ) exp_new = exp_new ^ expression{ xor_mask, exp->size() };
|
||||
if ( or_mask ) exp_new = exp_new | expression{ or_mask, exp->size() };
|
||||
|
||||
// If complexity was higher or equal, fail.
|
||||
//
|
||||
if ( exp_new.complexity >= exp->complexity ) return false;
|
||||
if ( exp_new->complexity >= exp->complexity ) return false;
|
||||
|
||||
// Apply and return.
|
||||
//
|
||||
*+exp = exp_new;
|
||||
exp = std::move( exp_new );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -304,7 +304,7 @@ namespace vtil::symbolic
|
|||
|
||||
// Invoke resize with failure on explicit cast:
|
||||
//
|
||||
( +exp_new )->resize( new_size, exp->op == math::operator_id::cast, true );
|
||||
exp_new.resize( new_size, exp->op == math::operator_id::cast, true );
|
||||
|
||||
// If implicit resize failed:
|
||||
//
|
||||
|
|
@ -330,7 +330,7 @@ namespace vtil::symbolic
|
|||
}
|
||||
}
|
||||
|
||||
( +exp )->simplify_hint = true;
|
||||
exp->simplify_hint = true;
|
||||
cache_entry = *exp;
|
||||
return success_flag;
|
||||
}
|
||||
|
|
@ -342,7 +342,7 @@ namespace vtil::symbolic
|
|||
// Recurse, and indicate success.
|
||||
//
|
||||
simplify_expression( exp, pretty, max_depth - 1 );
|
||||
( +exp )->simplify_hint = true;
|
||||
exp->simplify_hint = true;
|
||||
cache_entry = *exp;
|
||||
success_flag = true;
|
||||
return true;
|
||||
|
|
@ -374,7 +374,7 @@ namespace vtil::symbolic
|
|||
// Recurse, and indicate success.
|
||||
//
|
||||
simplify_expression( exp, pretty, max_depth - 1 );
|
||||
( +exp )->simplify_hint = true;
|
||||
exp->simplify_hint = true;
|
||||
cache_entry = *exp;
|
||||
success_flag = true;
|
||||
return true;
|
||||
|
|
@ -462,7 +462,7 @@ namespace vtil::symbolic
|
|||
{
|
||||
// If we can transform the expression by the directive set:
|
||||
//
|
||||
if ( auto exp_new = transform( exp, *dir_src, *dir_dst, {}, max_depth ) )
|
||||
if ( auto exp_new = transform( exp, dir_src, dir_dst, {}, max_depth ) )
|
||||
{
|
||||
#if VTIL_SYMEX_SIMPLIFY_VERBOSE
|
||||
log<CON_GRN>( "[Simplify] %s => %s\n", *dir_src, *dir_dst );
|
||||
|
|
@ -471,7 +471,7 @@ namespace vtil::symbolic
|
|||
// Recurse, set the hint and return the simplified instance.
|
||||
//
|
||||
simplify_expression( exp_new, pretty, max_depth );
|
||||
( +exp_new )->simplify_hint = true;
|
||||
exp_new->simplify_hint = true;
|
||||
cache_entry = exp_new;
|
||||
success_flag = !exp->is_identical( *exp_new );
|
||||
exp = exp_new;
|
||||
|
|
@ -489,7 +489,7 @@ namespace vtil::symbolic
|
|||
{
|
||||
// If we can transform the expression by the directive set:
|
||||
//
|
||||
if ( auto exp_new = transform( exp, *dir_src, *dir_dst, {}, max_depth ) )
|
||||
if ( auto exp_new = transform( exp, dir_src, dir_dst, {}, max_depth ) )
|
||||
{
|
||||
#if VTIL_SYMEX_SIMPLIFY_VERBOSE
|
||||
log<CON_GRN>( "[Simplify] %s => %s\n", *dir_src, *dir_dst );
|
||||
|
|
@ -498,7 +498,7 @@ namespace vtil::symbolic
|
|||
// Recurse, set the hint and return the simplified instance.
|
||||
//
|
||||
simplify_expression( exp_new, pretty, max_depth );
|
||||
( +exp_new )->simplify_hint = true;
|
||||
exp_new->simplify_hint = true;
|
||||
cache_entry = exp_new;
|
||||
success_flag = !exp->is_identical( *exp_new );
|
||||
exp = exp_new;
|
||||
|
|
@ -513,7 +513,7 @@ namespace vtil::symbolic
|
|||
{
|
||||
// If we can transform the expression by the directive set:
|
||||
//
|
||||
if ( auto exp_new = transform( exp, *dir_src, *dir_dst, filter, max_depth ) )
|
||||
if ( auto exp_new = transform( exp, dir_src, dir_dst, filter, max_depth ) )
|
||||
{
|
||||
#if VTIL_SYMEX_SIMPLIFY_VERBOSE
|
||||
log<CON_GRN>( "[Join] %s => %s\n", *dir_src, *dir_dst );
|
||||
|
|
@ -523,7 +523,7 @@ namespace vtil::symbolic
|
|||
// Recurse, set the hint and return the simplified instance.
|
||||
//
|
||||
simplify_expression( exp_new, pretty, max_depth - 1 );
|
||||
( +exp_new )->simplify_hint = true;
|
||||
exp_new->simplify_hint = true;
|
||||
cache_entry = exp_new;
|
||||
success_flag = !exp->is_identical( *exp_new );
|
||||
exp = exp_new;
|
||||
|
|
@ -541,7 +541,7 @@ namespace vtil::symbolic
|
|||
{
|
||||
// If we can transform the expression by the directive set:
|
||||
//
|
||||
if ( auto exp_new = transform( exp, *dir_src, *dir_dst, filter, max_depth ) )
|
||||
if ( auto exp_new = transform( exp, dir_src, dir_dst, filter, max_depth ) )
|
||||
{
|
||||
#if VTIL_SYMEX_SIMPLIFY_VERBOSE
|
||||
log<CON_GRN>( "[Join] %s => %s\n", *dir_src, *dir_dst );
|
||||
|
|
@ -551,7 +551,7 @@ namespace vtil::symbolic
|
|||
// Recurse, set the hint and return the simplified instance.
|
||||
//
|
||||
simplify_expression( exp_new, pretty, max_depth - 1 );
|
||||
( +exp_new )->simplify_hint = true;
|
||||
exp_new->simplify_hint = true;
|
||||
cache_entry = exp_new;
|
||||
success_flag = !exp->is_identical( *exp_new );
|
||||
exp = exp_new;
|
||||
|
|
@ -570,7 +570,7 @@ namespace vtil::symbolic
|
|||
{
|
||||
// If we can transform the expression by the directive set:
|
||||
//
|
||||
if ( auto exp_new = transform( exp, *dir_src, *dir_dst,
|
||||
if ( auto exp_new = transform( exp, dir_src, dir_dst,
|
||||
[ & ] ( auto& exp_new ) { simplify_expression( exp_new, true, max_depth - 1 ); return exp_new->complexity < exp->complexity; }, max_depth ) )
|
||||
{
|
||||
#if VTIL_SYMEX_SIMPLIFY_VERBOSE
|
||||
|
|
@ -580,7 +580,7 @@ namespace vtil::symbolic
|
|||
|
||||
// Set the hint and return the simplified instance.
|
||||
//
|
||||
( +exp_new )->simplify_hint = true;
|
||||
exp_new->simplify_hint = true;
|
||||
cache_entry = exp_new;
|
||||
success_flag = !exp->is_identical( *exp_new );
|
||||
exp = exp_new;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue