-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into assert-cfg-no-duplication
- Loading branch information
Showing
41 changed files
with
3,573 additions
and
2,085 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
from .expression_simplification import ExpressionSimplification | ||
from .expression_simplification.stages import ExpressionSimplificationAst, ExpressionSimplificationCfg | ||
from .instruction_length_handler import InstructionLengthHandler | ||
from .loop_name_generator import LoopNameGenerator | ||
from .readability_based_refinement import ReadabilityBasedRefinement | ||
from .variable_name_generation import VariableNameGeneration |
142 changes: 0 additions & 142 deletions
142
decompiler/pipeline/controlflowanalysis/expression_simplification.py
This file was deleted.
Oops, something went wrong.
145 changes: 145 additions & 0 deletions
145
decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import operator | ||
from functools import partial | ||
from typing import Callable, Optional | ||
|
||
from decompiler.structures.pseudo import Constant, Integer, OperationType | ||
|
||
|
||
def constant_fold(operation: OperationType, constants: list[Constant]) -> Constant: | ||
""" | ||
Fold operation with constants as operands. | ||
:param operation: The operation. | ||
:param constants: All constant operands of the operation. | ||
:return: A constant representing the result of the operation. | ||
""" | ||
|
||
if operation not in _OPERATION_TO_FOLD_FUNCTION: | ||
raise ValueError(f"Constant folding not implemented for operation '{operation}'.") | ||
|
||
return _OPERATION_TO_FOLD_FUNCTION[operation](constants) | ||
|
||
|
||
def _constant_fold_arithmetic_binary( | ||
constants: list[Constant], | ||
fun: Callable[[int, int], int], | ||
norm_sign: Optional[bool] = None | ||
) -> Constant: | ||
""" | ||
Fold an arithmetic binary operation with constants as operands. | ||
:param constants: A list of exactly 2 constant operands. | ||
:param fun: The binary function to perform on the constants. | ||
:param norm_sign: Optional boolean flag to indicate if/how to normalize the input constants to 'fun': | ||
- None (default): no normalization | ||
- True: normalize inputs, interpreted as signed values | ||
- False: normalize inputs, interpreted as unsigned values | ||
:return: A constant representing the result of the operation. | ||
""" | ||
|
||
if len(constants) != 2: | ||
raise ValueError(f"Expected exactly 2 constants to fold, got {len(constants)}.") | ||
if not all(constant.type == constants[0].type for constant in constants): | ||
raise ValueError(f"Can not fold constants with different types: {(constant.type for constant in constants)}") | ||
if not all(isinstance(constant.type, Integer) for constant in constants): | ||
raise ValueError(f"All constants must be integers, got {list(constant.type for constant in constants)}.") | ||
|
||
left, right = constants | ||
|
||
left_value = left.value | ||
right_value = right.value | ||
if norm_sign is not None: | ||
left_value = normalize_int(left_value, left.type.size, norm_sign) | ||
right_value = normalize_int(right_value, right.type.size, norm_sign) | ||
|
||
return Constant( | ||
normalize_int(fun(left_value, right_value), left.type.size, left.type.signed), | ||
left.type | ||
) | ||
|
||
|
||
def _constant_fold_arithmetic_unary(constants: list[Constant], fun: Callable[[int], int]) -> Constant: | ||
""" | ||
Fold an arithmetic unary operation with a constant operand. | ||
:param constants: A list containing a single constant operand. | ||
:param fun: The unary function to perform on the constant. | ||
:return: A constant representing the result of the operation. | ||
""" | ||
|
||
if len(constants) != 1: | ||
raise ValueError("Expected exactly 1 constant to fold") | ||
if not isinstance(constants[0].type, Integer): | ||
raise ValueError(f"Constant must be of type integer: {constants[0].type}") | ||
|
||
return Constant(normalize_int(fun(constants[0].value), constants[0].type.size, constants[0].type.signed), constants[0].type) | ||
|
||
|
||
def _constant_fold_shift(constants: list[Constant], fun: Callable[[int, int], int], signed: bool) -> Constant: | ||
""" | ||
Fold a shift operation with constants as operands. | ||
:param constants: A list of exactly 2 constant operands. | ||
:param fun: The shift function to perform on the constants. | ||
:param signed: Boolean flag indicating whether the shift is signed. | ||
This is used to normalize the sign of the input constant to simulate unsigned shifts. | ||
:return: A constant representing the result of the operation. | ||
""" | ||
|
||
if len(constants) != 2: | ||
raise ValueError("Expected exactly 2 constants to fold") | ||
if not all(isinstance(constant.type, Integer) for constant in constants): | ||
raise ValueError("All constants must be integers") | ||
|
||
left, right = constants | ||
|
||
shifted_value = fun( | ||
normalize_int(left.value, left.type.size, left.type.signed and signed), | ||
right.value | ||
) | ||
return Constant( | ||
normalize_int(shifted_value, left.type.size, left.type.signed), | ||
left.type | ||
) | ||
|
||
|
||
def normalize_int(v: int, size: int, signed: bool) -> int: | ||
""" | ||
Normalizes an integer value to a specific size and signedness. | ||
This function takes an integer value 'v' and normalizes it to fit within | ||
the specified 'size' in bits by discarding overflowing bits. If 'signed' is | ||
true, the value is treated as a signed integer, i.e. interpreted as a two's complement. | ||
Therefore the return value will be negative iff 'signed' is true and the most-significant bit is set. | ||
:param v: The value to be normalized. | ||
:param size: The desired bit size for the normalized integer. | ||
:param signed: True if the integer should be treated as signed. | ||
:return: The normalized integer value. | ||
""" | ||
value = v & ((1 << size) - 1) | ||
if signed and value & (1 << (size - 1)): | ||
return value - (1 << size) | ||
else: | ||
return value | ||
|
||
|
||
_OPERATION_TO_FOLD_FUNCTION: dict[OperationType, Callable[[list[Constant]], Constant]] = { | ||
OperationType.minus: partial(_constant_fold_arithmetic_binary, fun=operator.sub), | ||
OperationType.plus: partial(_constant_fold_arithmetic_binary, fun=operator.add), | ||
OperationType.multiply: partial(_constant_fold_arithmetic_binary, fun=operator.mul, norm_sign=True), | ||
OperationType.multiply_us: partial(_constant_fold_arithmetic_binary, fun=operator.mul, norm_sign=False), | ||
OperationType.divide: partial(_constant_fold_arithmetic_binary, fun=operator.floordiv, norm_sign=True), | ||
OperationType.divide_us: partial(_constant_fold_arithmetic_binary, fun=operator.floordiv, norm_sign=False), | ||
OperationType.negate: partial(_constant_fold_arithmetic_unary, fun=operator.neg), | ||
OperationType.left_shift: partial(_constant_fold_shift, fun=operator.lshift, signed=True), | ||
OperationType.right_shift: partial(_constant_fold_shift, fun=operator.rshift, signed=True), | ||
OperationType.right_shift_us: partial(_constant_fold_shift, fun=operator.rshift, signed=False), | ||
OperationType.bitwise_or: partial(_constant_fold_arithmetic_binary, fun=operator.or_), | ||
OperationType.bitwise_and: partial(_constant_fold_arithmetic_binary, fun=operator.and_), | ||
OperationType.bitwise_xor: partial(_constant_fold_arithmetic_binary, fun=operator.xor), | ||
OperationType.bitwise_not: partial(_constant_fold_arithmetic_unary, fun=operator.inv), | ||
} | ||
|
||
|
||
FOLDABLE_OPERATIONS = _OPERATION_TO_FOLD_FUNCTION.keys() |
30 changes: 30 additions & 0 deletions
30
decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_add_neg.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
from decompiler.pipeline.controlflowanalysis.expression_simplification.rules.rule import SimplificationRule | ||
from decompiler.structures.pseudo import BinaryOperation, Expression, Operation, OperationType, UnaryOperation | ||
|
||
|
||
class CollapseAddNeg(SimplificationRule): | ||
""" | ||
Simplifies additions/subtraction with negated expression. | ||
- `e0 + -(e1) -> e0 - e1` | ||
- `e0 - -(e1) -> e0 + e1` | ||
""" | ||
|
||
def apply(self, operation: Operation) -> list[tuple[Expression, Expression]]: | ||
if operation.operation not in [OperationType.plus, OperationType.minus]: | ||
return [] | ||
if not isinstance(operation, BinaryOperation): | ||
raise TypeError(f"Expected BinaryOperation, got {type(operation)}") | ||
|
||
right = operation.right | ||
if not isinstance(right, UnaryOperation) or right.operation != OperationType.negate: | ||
return [] | ||
|
||
return [( | ||
operation, | ||
BinaryOperation( | ||
OperationType.minus if operation.operation == OperationType.plus else OperationType.plus, | ||
[operation.left, right.operand], | ||
operation.type | ||
) | ||
)] |
20 changes: 20 additions & 0 deletions
20
...mpiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_constants.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
from decompiler.pipeline.controlflowanalysis.expression_simplification.constant_folding import FOLDABLE_OPERATIONS, constant_fold | ||
from decompiler.pipeline.controlflowanalysis.expression_simplification.rules.rule import SimplificationRule | ||
from decompiler.structures.pseudo import Constant, Expression, Operation | ||
|
||
|
||
class CollapseConstants(SimplificationRule): | ||
""" | ||
Fold operations with only constants as operands: | ||
""" | ||
|
||
def apply(self, operation: Operation) -> list[tuple[Expression, Expression]]: | ||
if not all(isinstance(o, Constant) for o in operation.operands): | ||
return [] | ||
if operation.operation not in FOLDABLE_OPERATIONS: | ||
return [] | ||
|
||
return [( | ||
operation, | ||
constant_fold(operation.operation, operation.operands) | ||
)] |
Oops, something went wrong.