From c81edd4b217ce2eac49bb22d78b31c83db71d936 Mon Sep 17 00:00:00 2001 From: rihi <19492038+rihi@users.noreply.github.com> Date: Thu, 12 Oct 2023 15:20:33 +0200 Subject: [PATCH 01/16] Fix incorrect error type in term_order.py --- .../expression_simplification/rules/term_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/term_order.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/term_order.py index 638f4d08a..20b10a596 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/term_order.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/term_order.py @@ -20,7 +20,7 @@ def apply(self, operation: Operation) -> list[tuple[Expression, Expression]]: if operation.operation not in COMMUTATIVE_OPERATIONS: return [] if not isinstance(operation, BinaryOperation): - raise ValueError(f"Expected BinaryOperation, got {operation}") + raise TypeError(f"Expected BinaryOperation, got {operation}") if isinstance(operation.left, Constant) and not isinstance(operation.right, Constant): return [(operation, BinaryOperation(operation.operation, [operation.right, operation.left], operation.type, operation.tags))] From 8c2742b9d79dcc7b282a77bb18109c208d868fe8 Mon Sep 17 00:00:00 2001 From: rihi <19492038+rihi@users.noreply.github.com> Date: Thu, 12 Oct 2023 17:52:21 +0200 Subject: [PATCH 02/16] Improve error handling of constant folding --- .../constant_folding.py | 84 +++--- .../rules/collapse_constants.py | 17 +- .../rules/collapse_nested_constants.py | 11 +- .../rules/term_order.py | 2 +- .../test_constant_folding.py | 247 ++++++++++-------- 5 files changed, 212 insertions(+), 149 deletions(-) diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py index f770ff6b1..946b86184 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py @@ -2,48 +2,80 @@ from functools import partial from typing import Callable, Optional -from decompiler.structures.pseudo import Constant, Integer, OperationType +from decompiler.structures.pseudo import Constant, Integer, OperationType, Type from decompiler.util.integer_util import normalize_int -def constant_fold(operation: OperationType, constants: list[Constant]) -> Constant: +class UnsupportedOperationType(Exception): + """Indicates that the specified Operation is not supported""" + pass + + +class UnsupportedValueType(Exception): + """Indicates that the value type of one constant is not supported.""" + pass + + +class UnsupportedMismatchedSizes(Exception): + """Indicates that folding of different sized constants is not supported for the specified operation.""" + pass + + +def constant_fold(operation: OperationType, constants: list[Constant], result_type: Type) -> Constant: """ Fold operation with constants as operands. :param operation: The operation. :param constants: All constant operands of the operation. + :param result_type: What type the folded constant should have. :return: A constant representing the result of the operation. + :raises: + UnsupportedOperationType: Thrown if the specified operation is not supported. + UnsupportedValueType: Thrown if constants contain value of types not supported. Currently only ints are supported. + UnsupportedMismatchedValueSizes: Thrown if constants types have different sizes and folding of different sized + constants is not supported for the specified operation. """ + if not constants: + raise ValueError(f"Constants list may not be empty") + if operation not in _OPERATION_TO_FOLD_FUNCTION: - raise ValueError(f"Constant folding not implemented for operation '{operation}'.") + raise UnsupportedOperationType(f"Constant folding not implemented for operation '{operation}'.") + + if not all(isinstance(v, int) for v in [c.value for c in constants]): # For now we only support integer value folding + raise UnsupportedValueType(f"Constant folding is not implemented for non int constant values: {[c.value for c in constants]}") - return _OPERATION_TO_FOLD_FUNCTION[operation](constants) + return Constant( + normalize_int( + _OPERATION_TO_FOLD_FUNCTION[operation](constants), + result_type.size, + isinstance(result_type, Integer) and result_type.signed + ), + result_type + ) def _constant_fold_arithmetic_binary( constants: list[Constant], fun: Callable[[int, int], int], norm_sign: Optional[bool] = None -) -> Constant: +) -> int: """ Fold an arithmetic binary operation with constants as operands. - :param constants: A list of exactly 2 constant operands. + :param constants: A list of exactly 2 constant values. :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 + - True: normalize inputs, interpreted as signed values - False: normalize inputs, interpreted as unsigned values - :return: A constant representing the result of the operation. + :return: 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)}.") + if not all(constant.type.size == constants[0].type.size for constant in constants): + raise UnsupportedMismatchedSizes(f"Can not fold constants with different sizes: {[constant.type for constant in constants]}") left, right = constants @@ -53,30 +85,25 @@ def _constant_fold_arithmetic_binary( 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 - ) + return fun(left_value, right_value) -def _constant_fold_arithmetic_unary(constants: list[Constant], fun: Callable[[int], int]) -> Constant: +def _constant_fold_arithmetic_unary(constants: list[Constant], fun: Callable[[int], int]) -> int: """ 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. + :return: 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) + return fun(constants[0].value) -def _constant_fold_shift(constants: list[Constant], fun: Callable[[int, int], int], signed: bool) -> Constant: +def _constant_fold_shift(constants: list[Constant], fun: Callable[[int, int], int], signed: bool) -> int: """ Fold a shift operation with constants as operands. @@ -84,27 +111,21 @@ def _constant_fold_shift(constants: list[Constant], fun: Callable[[int, int], in :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. + :return: 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( + return 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 - ) -_OPERATION_TO_FOLD_FUNCTION: dict[OperationType, Callable[[list[Constant]], Constant]] = { +_OPERATION_TO_FOLD_FUNCTION: dict[OperationType, Callable[[list[Constant]], int]] = { 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), @@ -121,5 +142,4 @@ def _constant_fold_shift(constants: list[Constant], fun: Callable[[int, int], in OperationType.bitwise_not: partial(_constant_fold_arithmetic_unary, fun=operator.inv), } - FOLDABLE_OPERATIONS = _OPERATION_TO_FOLD_FUNCTION.keys() diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_constants.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_constants.py index 03db3c00d..a940a6863 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_constants.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_constants.py @@ -1,4 +1,9 @@ -from decompiler.pipeline.controlflowanalysis.expression_simplification.constant_folding import FOLDABLE_OPERATIONS, constant_fold +from decompiler.pipeline.controlflowanalysis.expression_simplification.constant_folding import ( + UnsupportedMismatchedSizes, + UnsupportedOperationType, + UnsupportedValueType, + constant_fold, +) from decompiler.pipeline.controlflowanalysis.expression_simplification.rules.rule import SimplificationRule from decompiler.structures.pseudo import Constant, Expression, Operation @@ -11,10 +16,10 @@ class CollapseConstants(SimplificationRule): 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: + + try: + folded_constant = constant_fold(operation.operation, operation.operands, operation.type) + except (UnsupportedOperationType, UnsupportedValueType, UnsupportedMismatchedSizes): return [] - return [( - operation, - constant_fold(operation.operation, operation.operands) - )] + return [(operation, folded_constant)] diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py index 2f9346a72..b092416ed 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py @@ -8,6 +8,7 @@ _COLLAPSIBLE_OPERATIONS = COMMUTATIVE_OPERATIONS & FOLDABLE_OPERATIONS + class CollapseNestedConstants(SimplificationRule): """ This rule walks the dafaflow tree and collects and folds constants in commutative operations. @@ -17,8 +18,6 @@ class CollapseNestedConstants(SimplificationRule): def apply(self, operation: Operation) -> list[tuple[Expression, Expression]]: if operation.operation not in _COLLAPSIBLE_OPERATIONS: return [] - if not isinstance(operation, Operation): - raise TypeError(f"Expected Operation, got {type(operation)}") constants = list(_collect_constants(operation)) if len(constants) <= 1: @@ -26,8 +25,10 @@ def apply(self, operation: Operation) -> list[tuple[Expression, Expression]]: first, *rest = constants + # We don't need to catch any errors of 'constant_fold', because '_collect_constants' only returns constants + # which have the same type as 'operation.type' and we check that operation.operation is foldable. folded_constant = reduce( - lambda c0, c1: constant_fold(operation.operation, [c0, c1]), + lambda c0, c1: constant_fold(operation.operation, [c0, c1], operation.type), rest, first ) @@ -56,7 +57,7 @@ def _collect_constants(operation: Operation) -> Iterator[Constant]: current_operation = context_stack.pop() for i, operand in enumerate(current_operation.operands): - if operand.type != operand_type: + if operand.type != operand_type: # This check could potentially be relaxed to only check for equal size continue if isinstance(operand, Operation): @@ -77,6 +78,6 @@ def _identity_constant(operation: OperationType, var_type: Type) -> Constant: case OperationType.multiply | OperationType.multiply_us: return Constant(1, var_type) case OperationType.bitwise_and: - return constant_fold(OperationType.bitwise_not, [Constant(0, var_type)]) + return constant_fold(OperationType.bitwise_not, [Constant(0, var_type)], var_type) case _: raise NotImplementedError() diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/term_order.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/term_order.py index 20b10a596..5af7dd9fe 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/term_order.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/term_order.py @@ -20,7 +20,7 @@ def apply(self, operation: Operation) -> list[tuple[Expression, Expression]]: if operation.operation not in COMMUTATIVE_OPERATIONS: return [] if not isinstance(operation, BinaryOperation): - raise TypeError(f"Expected BinaryOperation, got {operation}") + raise TypeError(f"Expected BinaryOperation, got {type(operation)}") if isinstance(operation.left, Constant) and not isinstance(operation.right, Constant): return [(operation, BinaryOperation(operation.operation, [operation.right, operation.left], operation.type, operation.tags))] diff --git a/tests/pipeline/controlflowanalysis/expression_simplification/test_constant_folding.py b/tests/pipeline/controlflowanalysis/expression_simplification/test_constant_folding.py index 24f88ed93..eb8bad1ed 100644 --- a/tests/pipeline/controlflowanalysis/expression_simplification/test_constant_folding.py +++ b/tests/pipeline/controlflowanalysis/expression_simplification/test_constant_folding.py @@ -1,8 +1,15 @@ from contextlib import nullcontext +from typing import Optional import pytest -from decompiler.pipeline.controlflowanalysis.expression_simplification.constant_folding import FOLDABLE_OPERATIONS, constant_fold -from decompiler.structures.pseudo import Constant, Float, Integer, OperationType +from decompiler.pipeline.controlflowanalysis.expression_simplification.constant_folding import ( + FOLDABLE_OPERATIONS, + UnsupportedMismatchedSizes, + UnsupportedOperationType, + UnsupportedValueType, + constant_fold, +) +from decompiler.structures.pseudo import Constant, Float, Integer, OperationType, Type def _c_i32(value: int) -> Constant: @@ -21,118 +28,148 @@ def _c_float(value: float) -> Constant: return Constant(value, Float.float()) +def test_constant_fold_empty_list(): + with pytest.raises(ValueError): + constant_fold(OperationType.plus, [], Integer.int32_t()) + + @pytest.mark.parametrize( ["operation"], [(operation,) for operation in OperationType if operation not in FOLDABLE_OPERATIONS] ) def test_constant_fold_invalid_operations(operation: OperationType): - with pytest.raises(ValueError): - constant_fold(operation, []) + with pytest.raises(UnsupportedOperationType): + constant_fold(operation, [_c_i32(0)], Integer.int32_t()) + + +@pytest.mark.parametrize( + ["operation", "constants", "result_type", "expected_result", "context"], + [ + (OperationType.plus, [_c_i32(0), _c_i32(0)], Integer.int32_t(), _c_i32(0), nullcontext()), + (OperationType.plus, [_c_float(0.0), _c_float(0.0)], Float.float(), _c_float(0.0), pytest.raises(UnsupportedValueType)), + (OperationType.plus, [_c_i32(0), _c_float(0.0)], Integer.int32_t(), _c_i32(0), pytest.raises(UnsupportedValueType)), + ] +) +def test_constant_fold_invalid_value_type( + operation: OperationType, + constants: list[Constant], + result_type: Type, + expected_result: Optional[Constant], + context +): + with context: + assert constant_fold(operation, constants, result_type) == expected_result @pytest.mark.parametrize( - ["operation", "constants", "result", "context"], + ["operation", "constants", "result_type", "expected_result", "context"], [ - (OperationType.plus, [_c_i32(3), _c_i32(4)], _c_i32(7), nullcontext()), - (OperationType.plus, [_c_i32(2147483647), _c_i32(1)], _c_i32(-2147483648), nullcontext()), - (OperationType.plus, [_c_u32(2147483658), _c_u32(2147483652)], _c_u32(14), nullcontext()), - (OperationType.plus, [_c_u32(3), _c_i32(4)], None, pytest.raises(ValueError)), - (OperationType.plus, [_c_i32(3), _c_i16(4)], None, pytest.raises(ValueError)), - (OperationType.plus, [_c_i32(3)], None, pytest.raises(ValueError)), - (OperationType.plus, [_c_i32(3), _c_i32(3), _c_i32(3)], None, pytest.raises(ValueError)), - - (OperationType.minus, [_c_i32(3), _c_i32(4)], _c_i32(-1), nullcontext()), - (OperationType.minus, [_c_i32(-2147483648), _c_i32(1)], _c_i32(2147483647), nullcontext()), - (OperationType.minus, [_c_u32(3), _c_u32(4)], _c_u32(4294967295), nullcontext()), - (OperationType.minus, [_c_u32(3), _c_i32(4)], None, pytest.raises(ValueError)), - (OperationType.minus, [_c_i32(3), _c_i16(4)], None, pytest.raises(ValueError)), - (OperationType.minus, [_c_i32(3)], None, pytest.raises(ValueError)), - (OperationType.minus, [_c_i32(3), _c_i32(3), _c_i32(3)], None, pytest.raises(ValueError)), - - (OperationType.multiply, [_c_i32(3), _c_i32(4)], _c_i32(12), nullcontext()), - (OperationType.multiply, [_c_i32(-1073741824), _c_i32(2)], _c_i32(-2147483648), nullcontext()), - (OperationType.multiply, [_c_u32(3221225472), _c_u32(2)], _c_u32(2147483648), nullcontext()), - (OperationType.multiply, [_c_u32(3), _c_i32(4)], None, pytest.raises(ValueError)), - (OperationType.multiply, [_c_i32(3), _c_i16(4)], None, pytest.raises(ValueError)), - (OperationType.multiply, [_c_i32(3)], None, pytest.raises(ValueError)), - (OperationType.multiply, [_c_i32(3), _c_i32(3), _c_i32(3)], None, pytest.raises(ValueError)), - - (OperationType.multiply_us, [_c_i32(3), _c_i32(4)], _c_i32(12), nullcontext()), - (OperationType.multiply_us, [_c_i32(-1073741824), _c_i32(2)], _c_i32(-2147483648), nullcontext()), - (OperationType.multiply_us, [_c_u32(3221225472), _c_u32(2)], _c_u32(2147483648), nullcontext()), - (OperationType.multiply_us, [_c_u32(3), _c_i32(4)], None, pytest.raises(ValueError)), - (OperationType.multiply_us, [_c_i32(3), _c_i16(4)], None, pytest.raises(ValueError)), - (OperationType.multiply_us, [_c_i32(3)], None, pytest.raises(ValueError)), - (OperationType.multiply_us, [_c_i32(3), _c_i32(3), _c_i32(3)], None, pytest.raises(ValueError)), - - (OperationType.divide, [_c_i32(12), _c_i32(4)], _c_i32(3), nullcontext()), - (OperationType.divide, [_c_i32(-2147483648), _c_i32(2)], _c_i32(-1073741824), nullcontext()), - (OperationType.divide, [_c_u32(3), _c_i32(4)], None, pytest.raises(ValueError)), - (OperationType.divide, [_c_i32(3), _c_i16(4)], None, pytest.raises(ValueError)), - (OperationType.divide, [_c_i32(3)], None, pytest.raises(ValueError)), - (OperationType.divide, [_c_i32(3), _c_i32(3), _c_i32(3)], None, pytest.raises(ValueError)), - - (OperationType.divide_us, [_c_i32(12), _c_i32(4)], _c_i32(3), nullcontext()), - (OperationType.divide_us, [_c_i32(-2147483648), _c_i32(2)], _c_i32(1073741824), nullcontext()), - (OperationType.divide_us, [_c_u32(3), _c_i32(4)], None, pytest.raises(ValueError)), - (OperationType.divide_us, [_c_i32(3), _c_i16(4)], None, pytest.raises(ValueError)), - (OperationType.divide_us, [_c_i32(3)], None, pytest.raises(ValueError)), - (OperationType.divide_us, [_c_i32(3), _c_i32(3), _c_i32(3)], None, pytest.raises(ValueError)), - - (OperationType.negate, [_c_i32(3)], _c_i32(-3), nullcontext()), - (OperationType.negate, [_c_i32(-2147483648)], _c_i32(-2147483648), nullcontext()), - (OperationType.negate, [], None, pytest.raises(ValueError)), - (OperationType.negate, [_c_i32(3), _c_i32(3)], None, pytest.raises(ValueError)), - - (OperationType.left_shift, [_c_i32(3), _c_i32(4)], _c_i32(48), nullcontext()), - (OperationType.left_shift, [_c_i32(1073741824), _c_i32(1)], _c_i32(-2147483648), nullcontext()), - (OperationType.left_shift, [_c_u32(1073741824), _c_u32(1)], _c_u32(2147483648), nullcontext()), - (OperationType.left_shift, [_c_i32(3)], None, pytest.raises(ValueError)), - (OperationType.left_shift, [_c_i32(3), _c_i32(3), _c_i32(3)], None, pytest.raises(ValueError)), - - (OperationType.right_shift, [_c_i32(32), _c_i32(4)], _c_i32(2), nullcontext()), - (OperationType.right_shift, [_c_i32(-2147483648), _c_i32(1)], _c_i32(-1073741824), nullcontext()), - (OperationType.right_shift, [_c_u32(2147483648), _c_u32(1)], _c_u32(1073741824), nullcontext()), - (OperationType.right_shift, [_c_i32(3)], None, pytest.raises(ValueError)), - (OperationType.right_shift, [_c_i32(3), _c_i32(3), _c_i32(3)], None, pytest.raises(ValueError)), - - (OperationType.right_shift_us, [_c_i32(32), _c_i32(4)], _c_i32(2), nullcontext()), - (OperationType.right_shift_us, [_c_i32(-2147483648), _c_i32(1)], _c_i32(1073741824), nullcontext()), - (OperationType.right_shift_us, [_c_u32(2147483648), _c_u32(1)], _c_u32(1073741824), nullcontext()), - (OperationType.right_shift_us, [_c_i32(3)], None, pytest.raises(ValueError)), - (OperationType.right_shift_us, [_c_i32(3), _c_i32(3), _c_i32(3)], None, pytest.raises(ValueError)), - - (OperationType.bitwise_or, [_c_i32(85), _c_i32(34)], _c_i32(119), nullcontext()), - (OperationType.bitwise_or, [_c_i32(-2147483648), _c_i32(1)], _c_i32(-2147483647), nullcontext()), - (OperationType.bitwise_or, [_c_u32(2147483648), _c_u32(1)], _c_u32(2147483649), nullcontext()), - (OperationType.bitwise_or, [_c_u32(3), _c_i32(4)], None, pytest.raises(ValueError)), - (OperationType.bitwise_or, [_c_i32(3), _c_i16(4)], None, pytest.raises(ValueError)), - (OperationType.bitwise_or, [_c_i32(3)], None, pytest.raises(ValueError)), - (OperationType.bitwise_or, [_c_i32(3), _c_i32(3), _c_i32(3)], None, pytest.raises(ValueError)), - - (OperationType.bitwise_and, [_c_i32(85), _c_i32(51)], _c_i32(17), nullcontext()), - (OperationType.bitwise_and, [_c_i32(-2147483647), _c_i32(3)], _c_i32(1), nullcontext()), - (OperationType.bitwise_and, [_c_u32(2147483649), _c_u32(3)], _c_u32(1), nullcontext()), - (OperationType.bitwise_and, [_c_u32(3), _c_i32(4)], None, pytest.raises(ValueError)), - (OperationType.bitwise_and, [_c_i32(3), _c_i16(4)], None, pytest.raises(ValueError)), - (OperationType.bitwise_and, [_c_i32(3)], None, pytest.raises(ValueError)), - (OperationType.bitwise_and, [_c_i32(3), _c_i32(3), _c_i32(3)], None, pytest.raises(ValueError)), - - (OperationType.bitwise_xor, [_c_i32(85), _c_i32(51)], _c_i32(102), nullcontext()), - (OperationType.bitwise_xor, [_c_i32(-2147483647), _c_i32(-2147483646)], _c_i32(3), nullcontext()), - (OperationType.bitwise_xor, [_c_u32(2147483649), _c_u32(2147483650)], _c_u32(3), nullcontext()), - (OperationType.bitwise_xor, [_c_u32(3), _c_i32(4)], None, pytest.raises(ValueError)), - (OperationType.bitwise_xor, [_c_i32(3), _c_i16(4)], None, pytest.raises(ValueError)), - (OperationType.bitwise_xor, [_c_i32(3)], None, pytest.raises(ValueError)), - (OperationType.bitwise_xor, [_c_i32(3), _c_i32(3), _c_i32(3)], None, pytest.raises(ValueError)), - - (OperationType.bitwise_not, [_c_i32(6)], _c_i32(-7), nullcontext()), - (OperationType.bitwise_not, [_c_i32(-2147483648)], _c_i32(2147483647), nullcontext()), - (OperationType.bitwise_not, [_c_u32(2147483648)], _c_u32(2147483647), nullcontext()), - (OperationType.bitwise_not, [], None, pytest.raises(ValueError)), - (OperationType.bitwise_not, [_c_i32(3), _c_i32(3)], None, pytest.raises(ValueError)), + (OperationType.plus, [_c_i32(3), _c_i32(4)], Integer.int32_t(), _c_i32(7), nullcontext()), + (OperationType.plus, [_c_i32(2147483647), _c_i32(1)], Integer.int32_t(), _c_i32(-2147483648), nullcontext()), + (OperationType.plus, [_c_u32(2147483658), _c_u32(2147483652)], Integer.uint32_t(), _c_u32(14), nullcontext()), + (OperationType.plus, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(7), nullcontext()), + (OperationType.plus, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), + (OperationType.plus, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.plus, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + + (OperationType.minus, [_c_i32(3), _c_i32(4)], Integer.int32_t(), _c_i32(-1), nullcontext()), + (OperationType.minus, [_c_i32(-2147483648), _c_i32(1)], Integer.int32_t(), _c_i32(2147483647), nullcontext()), + (OperationType.minus, [_c_u32(3), _c_u32(4)], Integer.uint32_t(), _c_u32(4294967295), nullcontext()), + (OperationType.minus, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(-1), nullcontext()), + (OperationType.minus, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), + (OperationType.minus, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.minus, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + + (OperationType.multiply, [_c_i32(3), _c_i32(4)], Integer.int32_t(), _c_i32(12), nullcontext()), + (OperationType.multiply, [_c_i32(-1073741824), _c_i32(2)], Integer.int32_t(), _c_i32(-2147483648), nullcontext()), + (OperationType.multiply, [_c_u32(3221225472), _c_u32(2)], Integer.uint32_t(), _c_u32(2147483648), nullcontext()), + (OperationType.multiply, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(12), nullcontext()), + (OperationType.multiply, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), + (OperationType.multiply, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.multiply, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + + (OperationType.multiply_us, [_c_i32(3), _c_i32(4)], Integer.int32_t(), _c_i32(12), nullcontext()), + (OperationType.multiply_us, [_c_i32(-1073741824), _c_i32(2)], Integer.int32_t(), _c_i32(-2147483648), nullcontext()), + (OperationType.multiply_us, [_c_u32(3221225472), _c_u32(2)], Integer.uint32_t(), _c_u32(2147483648), nullcontext()), + (OperationType.multiply_us, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(12), nullcontext()), + (OperationType.multiply_us, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), + (OperationType.multiply_us, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.multiply_us, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + + (OperationType.divide, [_c_i32(12), _c_i32(4)], Integer.int32_t(), _c_i32(3), nullcontext()), + (OperationType.divide, [_c_i32(-2147483648), _c_i32(2)], Integer.int32_t(), _c_i32(-1073741824), nullcontext()), + (OperationType.divide, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(0), nullcontext()), + (OperationType.divide, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), + (OperationType.divide, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.divide, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + + (OperationType.divide_us, [_c_i32(12), _c_i32(4)], Integer.int32_t(), _c_i32(3), nullcontext()), + (OperationType.divide_us, [_c_i32(-2147483648), _c_i32(2)], Integer.int32_t(), _c_i32(1073741824), nullcontext()), + (OperationType.divide_us, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(0), nullcontext()), + (OperationType.divide_us, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), + (OperationType.divide_us, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.divide_us, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + + (OperationType.negate, [_c_i32(3)], Integer.int32_t(), _c_i32(-3), nullcontext()), + (OperationType.negate, [_c_i32(-2147483648)], Integer.int32_t(), _c_i32(-2147483648), nullcontext()), + (OperationType.negate, [], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.negate, [_c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + + (OperationType.left_shift, [_c_i32(3), _c_i32(4)], Integer.int32_t(), _c_i32(48), nullcontext()), + (OperationType.left_shift, [_c_i32(1073741824), _c_i32(1)], Integer.int32_t(), _c_i32(-2147483648), nullcontext()), + (OperationType.left_shift, [_c_u32(1073741824), _c_u32(1)], Integer.uint32_t(), _c_u32(2147483648), nullcontext()), + (OperationType.left_shift, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.left_shift, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + + (OperationType.right_shift, [_c_i32(32), _c_i32(4)], Integer.int32_t(), _c_i32(2), nullcontext()), + (OperationType.right_shift, [_c_i32(-2147483648), _c_i32(1)], Integer.int32_t(), _c_i32(-1073741824), nullcontext()), + (OperationType.right_shift, [_c_u32(2147483648), _c_u32(1)], Integer.uint32_t(), _c_u32(1073741824), nullcontext()), + (OperationType.right_shift, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.right_shift, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + + (OperationType.right_shift_us, [_c_i32(32), _c_i32(4)], Integer.int32_t(), _c_i32(2), nullcontext()), + (OperationType.right_shift_us, [_c_i32(-2147483648), _c_i32(1)], Integer.int32_t(), _c_i32(1073741824), nullcontext()), + (OperationType.right_shift_us, [_c_u32(2147483648), _c_u32(1)], Integer.uint32_t(), _c_u32(1073741824), nullcontext()), + (OperationType.right_shift_us, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.right_shift_us, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + + (OperationType.bitwise_or, [_c_i32(85), _c_i32(34)], Integer.int32_t(), _c_i32(119), nullcontext()), + (OperationType.bitwise_or, [_c_i32(-2147483648), _c_i32(1)], Integer.int32_t(), _c_i32(-2147483647), nullcontext()), + (OperationType.bitwise_or, [_c_u32(2147483648), _c_u32(1)], Integer.uint32_t(), _c_u32(2147483649), nullcontext()), + (OperationType.bitwise_or, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(7), nullcontext()), + (OperationType.bitwise_or, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), + (OperationType.bitwise_or, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.bitwise_or, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + + (OperationType.bitwise_and, [_c_i32(85), _c_i32(51)], Integer.int32_t(), _c_i32(17), nullcontext()), + (OperationType.bitwise_and, [_c_i32(-2147483647), _c_i32(3)], Integer.int32_t(), _c_i32(1), nullcontext()), + (OperationType.bitwise_and, [_c_u32(2147483649), _c_u32(3)], Integer.uint32_t(), _c_u32(1), nullcontext()), + (OperationType.bitwise_and, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(0), nullcontext()), + (OperationType.bitwise_and, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), + (OperationType.bitwise_and, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.bitwise_and, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + + (OperationType.bitwise_xor, [_c_i32(85), _c_i32(51)], Integer.int32_t(), _c_i32(102), nullcontext()), + (OperationType.bitwise_xor, [_c_i32(-2147483647), _c_i32(-2147483646)], Integer.int32_t(), _c_i32(3), nullcontext()), + (OperationType.bitwise_xor, [_c_u32(2147483649), _c_u32(2147483650)], Integer.uint32_t(), _c_u32(3), nullcontext()), + (OperationType.bitwise_xor, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(7), nullcontext()), + (OperationType.bitwise_xor, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), + (OperationType.bitwise_xor, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.bitwise_xor, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + + (OperationType.bitwise_not, [_c_i32(6)], Integer.int32_t(), _c_i32(-7), nullcontext()), + (OperationType.bitwise_not, [_c_i32(-2147483648)], Integer.int32_t(), _c_i32(2147483647), nullcontext()), + (OperationType.bitwise_not, [_c_u32(2147483648)], Integer.uint32_t(), _c_u32(2147483647), nullcontext()), + (OperationType.bitwise_not, [], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.bitwise_not, [_c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), ] ) -def test_constant_fold(operation: OperationType, constants: list[Constant], result: Constant, context): +def test_constant_fold( + operation: OperationType, + constants: list[Constant], + result_type: Type, + expected_result: Optional[Constant], + context +): with context: - assert constant_fold(operation, constants) == result + assert constant_fold(operation, constants, result_type) == expected_result From cc44b6d120bcd61d2bbdc9b75d967939b557611f Mon Sep 17 00:00:00 2001 From: rihi <19492038+rihi@users.noreply.github.com> Date: Thu, 12 Oct 2023 18:07:36 +0200 Subject: [PATCH 03/16] Catch exception caused by simplification rules if in debug --- .../expression_simplification/stages.py | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/stages.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/stages.py index cc05a59ba..495db01fb 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/stages.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/stages.py @@ -25,30 +25,42 @@ from decompiler.task import DecompilerTask +class _SimplificationException(Exception): + pass + + class _ExpressionSimplificationBase(PipelineStage, ABC): def run(self, task: DecompilerTask): max_iterations = task.options.getint("expression-simplification.max_iterations") - self._simplify_instructions(self._get_instructions(task), max_iterations) + debug = task.options.getboolean("pipeline.debug", fallback=False) + + self._simplify_instructions(self._get_instructions(task), max_iterations, debug) @abstractmethod def _get_instructions(self, task: DecompilerTask) -> list[Instruction]: pass @classmethod - def _simplify_instructions(cls, instructions: list[Instruction], max_iterations: int): + def _simplify_instructions(cls, instructions: list[Instruction], max_iterations: int, debug: bool): rule_sets = [ ("pre-rules", _pre_rules), ("rules", _rules), ("post-rules", _post_rules) ] - for rule_name, rule_set in rule_sets: - # max_iterations is counted per rule_set - iteration_count = cls._simplify_instructions_with_rule_set(instructions, rule_set, max_iterations) - if iteration_count <= max_iterations: - logging.info(f"Expression simplification took {iteration_count} iterations for {rule_name}") + try: + for rule_name, rule_set in rule_sets: + # max_iterations is counted per rule_set + iteration_count = cls._simplify_instructions_with_rule_set(instructions, rule_set, max_iterations) + if iteration_count <= max_iterations: + logging.info(f"Expression simplification took {iteration_count} iterations for {rule_name}") + else: + logging.warning(f"Exceeded max iteration count for {rule_name}") + except _SimplificationException as e: + if debug: + raise # re-raises the exception else: - logging.warning(f"Exceeded max iteration count for {rule_name}") + logging.exception(f"An unexpected error occurred while simplifying: {e}") @classmethod def _simplify_instructions_with_rule_set( @@ -91,7 +103,11 @@ def _simplify_instruction_with_rule( if not isinstance(expression, Operation): break - substitutions = rule.apply(expression) + try: + substitutions = rule.apply(expression) + except Exception as e: + raise _SimplificationException(e) + if not substitutions: break From 83252d9a8b594a5ff1c19fb829ddcf2e1a308985 Mon Sep 17 00:00:00 2001 From: rihi <19492038+rihi@users.noreply.github.com> Date: Fri, 13 Oct 2023 17:36:23 +0200 Subject: [PATCH 04/16] Catch empty operands operation in collapse_constants.py --- .../expression_simplification/rules/collapse_constants.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_constants.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_constants.py index a940a6863..b32a84215 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_constants.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_constants.py @@ -14,6 +14,8 @@ class CollapseConstants(SimplificationRule): """ def apply(self, operation: Operation) -> list[tuple[Expression, Expression]]: + if not operation.operands: + return [] # Is this even allowed? if not all(isinstance(o, Constant) for o in operation.operands): return [] From 249a64f9f7cbde3c46141f451f61ce4c3f345942 Mon Sep 17 00:00:00 2001 From: rihi <19492038+rihi@users.noreply.github.com> Date: Wed, 25 Oct 2023 13:41:53 +0200 Subject: [PATCH 05/16] Update documentation of constant_folding.py --- .../constant_folding.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py index 946b86184..3b8a1db30 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py @@ -5,6 +5,11 @@ from decompiler.structures.pseudo import Constant, Integer, OperationType, Type from decompiler.util.integer_util import normalize_int +# Exceptions of these three types indicate that an operation is not suitable for constant folding. +# They do NOT indicate that the input was malformed in any way. +# The idea is that the caller of constant_fold does not need to verify that folding is possible. +# If malformed input was provided, a ValueError will used raised instead. + class UnsupportedOperationType(Exception): """Indicates that the specified Operation is not supported""" @@ -27,13 +32,15 @@ def constant_fold(operation: OperationType, constants: list[Constant], result_ty :param operation: The operation. :param constants: All constant operands of the operation. + Count of operands must be compatible with the specified operation type. :param result_type: What type the folded constant should have. :return: A constant representing the result of the operation. :raises: UnsupportedOperationType: Thrown if the specified operation is not supported. UnsupportedValueType: Thrown if constants contain value of types not supported. Currently only ints are supported. - UnsupportedMismatchedValueSizes: Thrown if constants types have different sizes and folding of different sized + UnsupportedMismatchedSizes: Thrown if constants types have different sizes and folding of different sized constants is not supported for the specified operation. + ValueError: Thrown on malformed input. """ if not constants: @@ -70,6 +77,10 @@ def _constant_fold_arithmetic_binary( - True: normalize inputs, interpreted as signed values - False: normalize inputs, interpreted as unsigned values :return: The result of the operation. + :raises: + UnsupportedMismatchedSizes: Thrown if constants types have different sizes and folding of different sized + constants is not supported for the specified operation. + ValueError: Thrown on malformed input. """ if len(constants) != 2: @@ -95,6 +106,8 @@ def _constant_fold_arithmetic_unary(constants: list[Constant], fun: Callable[[in :param constants: A list containing a single constant operand. :param fun: The unary function to perform on the constant. :return: The result of the operation. + :raises: + ValueError: Thrown on malformed input. """ if len(constants) != 1: @@ -112,6 +125,8 @@ def _constant_fold_shift(constants: list[Constant], fun: Callable[[int, int], in :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: The result of the operation. + :raises: + ValueError: Thrown on malformed input. """ if len(constants) != 2: From ae5e499c0df0e1e71505ddf260d05bb07a70c7e6 Mon Sep 17 00:00:00 2001 From: rihi <19492038+rihi@users.noreply.github.com> Date: Wed, 25 Oct 2023 14:03:59 +0200 Subject: [PATCH 06/16] Create MalformedInput exception --- .../constant_folding.py | 22 ++++--- .../test_constant_folding.py | 64 +++++++++---------- 2 files changed, 42 insertions(+), 44 deletions(-) diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py index 3b8a1db30..3a2d08d0d 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py @@ -26,6 +26,11 @@ class UnsupportedMismatchedSizes(Exception): pass +class MalformedInput(Exception): + """Indicates that the input is malformed in some way.""" + pass + + def constant_fold(operation: OperationType, constants: list[Constant], result_type: Type) -> Constant: """ Fold operation with constants as operands. @@ -40,12 +45,9 @@ def constant_fold(operation: OperationType, constants: list[Constant], result_ty UnsupportedValueType: Thrown if constants contain value of types not supported. Currently only ints are supported. UnsupportedMismatchedSizes: Thrown if constants types have different sizes and folding of different sized constants is not supported for the specified operation. - ValueError: Thrown on malformed input. + MalformedInput: Thrown on malformed input. """ - if not constants: - raise ValueError(f"Constants list may not be empty") - if operation not in _OPERATION_TO_FOLD_FUNCTION: raise UnsupportedOperationType(f"Constant folding not implemented for operation '{operation}'.") @@ -80,11 +82,11 @@ def _constant_fold_arithmetic_binary( :raises: UnsupportedMismatchedSizes: Thrown if constants types have different sizes and folding of different sized constants is not supported for the specified operation. - ValueError: Thrown on malformed input. + MalformedInput: Thrown on malformed input. """ if len(constants) != 2: - raise ValueError(f"Expected exactly 2 constants to fold, got {len(constants)}.") + raise MalformedInput(f"Expected exactly 2 constants to fold, got {len(constants)}.") if not all(constant.type.size == constants[0].type.size for constant in constants): raise UnsupportedMismatchedSizes(f"Can not fold constants with different sizes: {[constant.type for constant in constants]}") @@ -107,11 +109,11 @@ def _constant_fold_arithmetic_unary(constants: list[Constant], fun: Callable[[in :param fun: The unary function to perform on the constant. :return: The result of the operation. :raises: - ValueError: Thrown on malformed input. + MalformedInput: Thrown on malformed input. """ if len(constants) != 1: - raise ValueError("Expected exactly 1 constant to fold") + raise MalformedInput("Expected exactly 1 constant to fold") return fun(constants[0].value) @@ -126,11 +128,11 @@ def _constant_fold_shift(constants: list[Constant], fun: Callable[[int, int], in This is used to normalize the sign of the input constant to simulate unsigned shifts. :return: The result of the operation. :raises: - ValueError: Thrown on malformed input. + MalformedInput: Thrown on malformed input. """ if len(constants) != 2: - raise ValueError("Expected exactly 2 constants to fold") + raise MalformedInput("Expected exactly 2 constants to fold") left, right = constants diff --git a/tests/pipeline/controlflowanalysis/expression_simplification/test_constant_folding.py b/tests/pipeline/controlflowanalysis/expression_simplification/test_constant_folding.py index eb8bad1ed..94f238aea 100644 --- a/tests/pipeline/controlflowanalysis/expression_simplification/test_constant_folding.py +++ b/tests/pipeline/controlflowanalysis/expression_simplification/test_constant_folding.py @@ -4,6 +4,7 @@ import pytest from decompiler.pipeline.controlflowanalysis.expression_simplification.constant_folding import ( FOLDABLE_OPERATIONS, + MalformedInput, UnsupportedMismatchedSizes, UnsupportedOperationType, UnsupportedValueType, @@ -28,18 +29,13 @@ def _c_float(value: float) -> Constant: return Constant(value, Float.float()) -def test_constant_fold_empty_list(): - with pytest.raises(ValueError): - constant_fold(OperationType.plus, [], Integer.int32_t()) - - @pytest.mark.parametrize( ["operation"], [(operation,) for operation in OperationType if operation not in FOLDABLE_OPERATIONS] ) def test_constant_fold_invalid_operations(operation: OperationType): with pytest.raises(UnsupportedOperationType): - constant_fold(operation, [_c_i32(0)], Integer.int32_t()) + constant_fold(operation, [], Integer.int32_t()) @pytest.mark.parametrize( @@ -69,99 +65,99 @@ def test_constant_fold_invalid_value_type( (OperationType.plus, [_c_u32(2147483658), _c_u32(2147483652)], Integer.uint32_t(), _c_u32(14), nullcontext()), (OperationType.plus, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(7), nullcontext()), (OperationType.plus, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), - (OperationType.plus, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), - (OperationType.plus, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.plus, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.plus, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), (OperationType.minus, [_c_i32(3), _c_i32(4)], Integer.int32_t(), _c_i32(-1), nullcontext()), (OperationType.minus, [_c_i32(-2147483648), _c_i32(1)], Integer.int32_t(), _c_i32(2147483647), nullcontext()), (OperationType.minus, [_c_u32(3), _c_u32(4)], Integer.uint32_t(), _c_u32(4294967295), nullcontext()), (OperationType.minus, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(-1), nullcontext()), (OperationType.minus, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), - (OperationType.minus, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), - (OperationType.minus, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.minus, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.minus, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), (OperationType.multiply, [_c_i32(3), _c_i32(4)], Integer.int32_t(), _c_i32(12), nullcontext()), (OperationType.multiply, [_c_i32(-1073741824), _c_i32(2)], Integer.int32_t(), _c_i32(-2147483648), nullcontext()), (OperationType.multiply, [_c_u32(3221225472), _c_u32(2)], Integer.uint32_t(), _c_u32(2147483648), nullcontext()), (OperationType.multiply, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(12), nullcontext()), (OperationType.multiply, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), - (OperationType.multiply, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), - (OperationType.multiply, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.multiply, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.multiply, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), (OperationType.multiply_us, [_c_i32(3), _c_i32(4)], Integer.int32_t(), _c_i32(12), nullcontext()), (OperationType.multiply_us, [_c_i32(-1073741824), _c_i32(2)], Integer.int32_t(), _c_i32(-2147483648), nullcontext()), (OperationType.multiply_us, [_c_u32(3221225472), _c_u32(2)], Integer.uint32_t(), _c_u32(2147483648), nullcontext()), (OperationType.multiply_us, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(12), nullcontext()), (OperationType.multiply_us, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), - (OperationType.multiply_us, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), - (OperationType.multiply_us, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.multiply_us, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.multiply_us, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), (OperationType.divide, [_c_i32(12), _c_i32(4)], Integer.int32_t(), _c_i32(3), nullcontext()), (OperationType.divide, [_c_i32(-2147483648), _c_i32(2)], Integer.int32_t(), _c_i32(-1073741824), nullcontext()), (OperationType.divide, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(0), nullcontext()), (OperationType.divide, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), - (OperationType.divide, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), - (OperationType.divide, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.divide, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.divide, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), (OperationType.divide_us, [_c_i32(12), _c_i32(4)], Integer.int32_t(), _c_i32(3), nullcontext()), (OperationType.divide_us, [_c_i32(-2147483648), _c_i32(2)], Integer.int32_t(), _c_i32(1073741824), nullcontext()), (OperationType.divide_us, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(0), nullcontext()), (OperationType.divide_us, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), - (OperationType.divide_us, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), - (OperationType.divide_us, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.divide_us, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.divide_us, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), (OperationType.negate, [_c_i32(3)], Integer.int32_t(), _c_i32(-3), nullcontext()), (OperationType.negate, [_c_i32(-2147483648)], Integer.int32_t(), _c_i32(-2147483648), nullcontext()), - (OperationType.negate, [], Integer.int32_t(), None, pytest.raises(ValueError)), - (OperationType.negate, [_c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.negate, [], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.negate, [_c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), (OperationType.left_shift, [_c_i32(3), _c_i32(4)], Integer.int32_t(), _c_i32(48), nullcontext()), (OperationType.left_shift, [_c_i32(1073741824), _c_i32(1)], Integer.int32_t(), _c_i32(-2147483648), nullcontext()), (OperationType.left_shift, [_c_u32(1073741824), _c_u32(1)], Integer.uint32_t(), _c_u32(2147483648), nullcontext()), - (OperationType.left_shift, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), - (OperationType.left_shift, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.left_shift, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.left_shift, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), (OperationType.right_shift, [_c_i32(32), _c_i32(4)], Integer.int32_t(), _c_i32(2), nullcontext()), (OperationType.right_shift, [_c_i32(-2147483648), _c_i32(1)], Integer.int32_t(), _c_i32(-1073741824), nullcontext()), (OperationType.right_shift, [_c_u32(2147483648), _c_u32(1)], Integer.uint32_t(), _c_u32(1073741824), nullcontext()), - (OperationType.right_shift, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), - (OperationType.right_shift, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.right_shift, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.right_shift, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), (OperationType.right_shift_us, [_c_i32(32), _c_i32(4)], Integer.int32_t(), _c_i32(2), nullcontext()), (OperationType.right_shift_us, [_c_i32(-2147483648), _c_i32(1)], Integer.int32_t(), _c_i32(1073741824), nullcontext()), (OperationType.right_shift_us, [_c_u32(2147483648), _c_u32(1)], Integer.uint32_t(), _c_u32(1073741824), nullcontext()), - (OperationType.right_shift_us, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), - (OperationType.right_shift_us, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.right_shift_us, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.right_shift_us, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), (OperationType.bitwise_or, [_c_i32(85), _c_i32(34)], Integer.int32_t(), _c_i32(119), nullcontext()), (OperationType.bitwise_or, [_c_i32(-2147483648), _c_i32(1)], Integer.int32_t(), _c_i32(-2147483647), nullcontext()), (OperationType.bitwise_or, [_c_u32(2147483648), _c_u32(1)], Integer.uint32_t(), _c_u32(2147483649), nullcontext()), (OperationType.bitwise_or, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(7), nullcontext()), (OperationType.bitwise_or, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), - (OperationType.bitwise_or, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), - (OperationType.bitwise_or, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.bitwise_or, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.bitwise_or, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), (OperationType.bitwise_and, [_c_i32(85), _c_i32(51)], Integer.int32_t(), _c_i32(17), nullcontext()), (OperationType.bitwise_and, [_c_i32(-2147483647), _c_i32(3)], Integer.int32_t(), _c_i32(1), nullcontext()), (OperationType.bitwise_and, [_c_u32(2147483649), _c_u32(3)], Integer.uint32_t(), _c_u32(1), nullcontext()), (OperationType.bitwise_and, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(0), nullcontext()), (OperationType.bitwise_and, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), - (OperationType.bitwise_and, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), - (OperationType.bitwise_and, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.bitwise_and, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.bitwise_and, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), (OperationType.bitwise_xor, [_c_i32(85), _c_i32(51)], Integer.int32_t(), _c_i32(102), nullcontext()), (OperationType.bitwise_xor, [_c_i32(-2147483647), _c_i32(-2147483646)], Integer.int32_t(), _c_i32(3), nullcontext()), (OperationType.bitwise_xor, [_c_u32(2147483649), _c_u32(2147483650)], Integer.uint32_t(), _c_u32(3), nullcontext()), (OperationType.bitwise_xor, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(7), nullcontext()), (OperationType.bitwise_xor, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), - (OperationType.bitwise_xor, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), - (OperationType.bitwise_xor, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.bitwise_xor, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.bitwise_xor, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), (OperationType.bitwise_not, [_c_i32(6)], Integer.int32_t(), _c_i32(-7), nullcontext()), (OperationType.bitwise_not, [_c_i32(-2147483648)], Integer.int32_t(), _c_i32(2147483647), nullcontext()), (OperationType.bitwise_not, [_c_u32(2147483648)], Integer.uint32_t(), _c_u32(2147483647), nullcontext()), - (OperationType.bitwise_not, [], Integer.int32_t(), None, pytest.raises(ValueError)), - (OperationType.bitwise_not, [_c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(ValueError)), + (OperationType.bitwise_not, [], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.bitwise_not, [_c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), ] ) def test_constant_fold( From e0489846c6199f79272bdcb3cfa85f395773f7e8 Mon Sep 17 00:00:00 2001 From: rihi <19492038+rihi@users.noreply.github.com> Date: Wed, 25 Oct 2023 14:07:34 +0200 Subject: [PATCH 07/16] Handle malformed input in collapse_constants.py --- .../expression_simplification/rules/collapse_constants.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_constants.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_constants.py index b32a84215..839a0a0c5 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_constants.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_constants.py @@ -1,4 +1,7 @@ +import logging + from decompiler.pipeline.controlflowanalysis.expression_simplification.constant_folding import ( + MalformedInput, UnsupportedMismatchedSizes, UnsupportedOperationType, UnsupportedValueType, @@ -23,5 +26,8 @@ def apply(self, operation: Operation) -> list[tuple[Expression, Expression]]: folded_constant = constant_fold(operation.operation, operation.operands, operation.type) except (UnsupportedOperationType, UnsupportedValueType, UnsupportedMismatchedSizes): return [] + except MalformedInput as e: + logging.warning(f"Encountered malformed operation '{operation}': {e}") + return [] return [(operation, folded_constant)] From 6f73d0d21d0ac97513a024b45a42891d430ce551 Mon Sep 17 00:00:00 2001 From: rihi <19492038+rihi@users.noreply.github.com> Date: Wed, 25 Oct 2023 14:24:22 +0200 Subject: [PATCH 08/16] Handle errors in collapse_nested_constants.py --- .../rules/collapse_nested_constants.py | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py index b092416ed..06f62338b 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py @@ -1,7 +1,13 @@ +import logging from functools import reduce from typing import Iterator -from decompiler.pipeline.controlflowanalysis.expression_simplification.constant_folding import FOLDABLE_OPERATIONS, constant_fold +from decompiler.pipeline.controlflowanalysis.expression_simplification.constant_folding import ( + FOLDABLE_OPERATIONS, + MalformedInput, + UnsupportedValueType, + constant_fold, +) from decompiler.pipeline.controlflowanalysis.expression_simplification.rules.rule import SimplificationRule from decompiler.structures.pseudo import Constant, Expression, Operation, OperationType, Type from decompiler.structures.pseudo.operations import COMMUTATIVE_OPERATIONS @@ -25,13 +31,19 @@ def apply(self, operation: Operation) -> list[tuple[Expression, Expression]]: first, *rest = constants - # We don't need to catch any errors of 'constant_fold', because '_collect_constants' only returns constants - # which have the same type as 'operation.type' and we check that operation.operation is foldable. - folded_constant = reduce( - lambda c0, c1: constant_fold(operation.operation, [c0, c1], operation.type), - rest, - first - ) + # We don't need to catch UnsupportedOperationType, because check that operation is in _COLLAPSIBLE_OPERATIONS + # We don't need to catch UnsupportedMismatchedSizes, because '_collect_constants' only returns constants of the same type + try: + folded_constant = reduce( + lambda c0, c1: constant_fold(operation.operation, [c0, c1], operation.type), + rest, + first + ) + except UnsupportedValueType: + return [] + except MalformedInput as e: + logging.warning(f"Encountered malformed operation '{operation}': {e}") + return [] identity_constant = _identity_constant(operation.operation, operation.type) return [ From 106cf2e5bf1da0891da3e459dedb50e885c969b0 Mon Sep 17 00:00:00 2001 From: rihi <19492038+rihi@users.noreply.github.com> Date: Wed, 25 Oct 2023 14:24:36 +0200 Subject: [PATCH 09/16] Add clarifying comment in collapse_nested_constants.py --- .../rules/collapse_nested_constants.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py index 06f62338b..80da72300 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py @@ -90,6 +90,11 @@ def _identity_constant(operation: OperationType, var_type: Type) -> Constant: case OperationType.multiply | OperationType.multiply_us: return Constant(1, var_type) case OperationType.bitwise_and: + # Should not throw any exception because: + # - OperationType.bitwise_not is foldable (UnsupportedOperationType) + # - constant has integer value, which is supported (UnsupportedValueType) + # - with only 1 constant there cant be mismatched sizes (UnsupportedMismatchedSizes) + # - input is not malformed (MalformedInput) return constant_fold(OperationType.bitwise_not, [Constant(0, var_type)], var_type) case _: raise NotImplementedError() From 60a3bd55f34815979ede82eea62ddad070036df9 Mon Sep 17 00:00:00 2001 From: rihi <19492038+rihi@users.noreply.github.com> Date: Wed, 25 Oct 2023 14:30:57 +0200 Subject: [PATCH 10/16] Catch exception in expression simplification earlier --- .../expression_simplification/stages.py | 38 +++++++++---------- .../expression_simplification/test_stage.py | 6 ++- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/stages.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/stages.py index 495db01fb..0d4e42703 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/stages.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/stages.py @@ -25,10 +25,6 @@ from decompiler.task import DecompilerTask -class _SimplificationException(Exception): - pass - - class _ExpressionSimplificationBase(PipelineStage, ABC): def run(self, task: DecompilerTask): @@ -48,26 +44,21 @@ def _simplify_instructions(cls, instructions: list[Instruction], max_iterations: ("rules", _rules), ("post-rules", _post_rules) ] - try: - for rule_name, rule_set in rule_sets: - # max_iterations is counted per rule_set - iteration_count = cls._simplify_instructions_with_rule_set(instructions, rule_set, max_iterations) - if iteration_count <= max_iterations: - logging.info(f"Expression simplification took {iteration_count} iterations for {rule_name}") - else: - logging.warning(f"Exceeded max iteration count for {rule_name}") - except _SimplificationException as e: - if debug: - raise # re-raises the exception + for rule_name, rule_set in rule_sets: + # max_iterations is counted per rule_set + iteration_count = cls._simplify_instructions_with_rule_set(instructions, rule_set, max_iterations, debug) + if iteration_count <= max_iterations: + logging.info(f"Expression simplification took {iteration_count} iterations for {rule_name}") else: - logging.exception(f"An unexpected error occurred while simplifying: {e}") + logging.warning(f"Exceeded max iteration count for {rule_name}") @classmethod def _simplify_instructions_with_rule_set( cls, instructions: list[Instruction], rule_set: list[SimplificationRule], - max_iterations: int + max_iterations: int, + debug: bool ) -> int: iteration_count = 0 @@ -77,7 +68,7 @@ def _simplify_instructions_with_rule_set( for rule in rule_set: for instruction in instructions: - additional_iterations = cls._simplify_instruction_with_rule(instruction, rule, max_iterations - iteration_count) + additional_iterations = cls._simplify_instruction_with_rule(instruction, rule, max_iterations - iteration_count, debug) if additional_iterations > 0: changes = True @@ -92,7 +83,8 @@ def _simplify_instruction_with_rule( cls, instruction: Instruction, rule: SimplificationRule, - max_iterations: int + max_iterations: int, + debug: bool ) -> int: iteration_count = 0 for expression in instruction.subexpressions(): @@ -106,10 +98,14 @@ def _simplify_instruction_with_rule( try: substitutions = rule.apply(expression) except Exception as e: - raise _SimplificationException(e) + if debug: + raise # re-raise the exception + else: + logging.exception(f"An unexpected error occurred while simplifying: {e}") + break # continue with next subexpression if not substitutions: - break + break # continue with next subexpression iteration_count += 1 diff --git a/tests/pipeline/controlflowanalysis/expression_simplification/test_stage.py b/tests/pipeline/controlflowanalysis/expression_simplification/test_stage.py index a9b74833c..54900ce3a 100644 --- a/tests/pipeline/controlflowanalysis/expression_simplification/test_stage.py +++ b/tests/pipeline/controlflowanalysis/expression_simplification/test_stage.py @@ -73,7 +73,8 @@ def test_simplify_instructions_with_rule_set( _ExpressionSimplificationBase._simplify_instructions_with_rule_set( [instruction], rule_set, - 100 + 100, + True ) assert instruction == expected_result @@ -104,6 +105,7 @@ def test_simplify_instructions_with_rule_set_max_iterations( iterations = _ExpressionSimplificationBase._simplify_instructions_with_rule_set( [instruction], rule_set, - max_iterations + max_iterations, + True ) assert (iterations > max_iterations) == expect_exceed_max_iterations From 9ab24727f2c800ef7a58968d1a655e3378584426 Mon Sep 17 00:00:00 2001 From: rihi <19492038+rihi@users.noreply.github.com> Date: Wed, 25 Oct 2023 14:40:08 +0200 Subject: [PATCH 11/16] Fix documentation in constant_folding.py --- .../expression_simplification/constant_folding.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py index 3a2d08d0d..06f9a0b25 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py @@ -8,7 +8,7 @@ # Exceptions of these three types indicate that an operation is not suitable for constant folding. # They do NOT indicate that the input was malformed in any way. # The idea is that the caller of constant_fold does not need to verify that folding is possible. -# If malformed input was provided, a ValueError will used raised instead. +# If malformed input was provided, a MalformedInput will be raised instead. class UnsupportedOperationType(Exception): From 10f17bd2629669b995a301c8e20e93973742b6ed Mon Sep 17 00:00:00 2001 From: rihi <19492038+rihi@users.noreply.github.com> Date: Wed, 25 Oct 2023 15:06:42 +0200 Subject: [PATCH 12/16] Change how exceptions are propagated --- .../constant_folding.py | 21 ++++--- .../rules/collapse_constants.py | 11 ++-- .../rules/collapse_nested_constants.py | 10 ++-- .../expression_simplification/rules/rule.py | 7 +++ .../test_constant_folding.py | 58 +++++++++---------- 5 files changed, 54 insertions(+), 53 deletions(-) diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py index 06f9a0b25..3847d8166 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py @@ -5,10 +5,9 @@ from decompiler.structures.pseudo import Constant, Integer, OperationType, Type from decompiler.util.integer_util import normalize_int -# Exceptions of these three types indicate that an operation is not suitable for constant folding. +# The first three exception types indicate that an operation is not suitable for constant folding. # They do NOT indicate that the input was malformed in any way. # The idea is that the caller of constant_fold does not need to verify that folding is possible. -# If malformed input was provided, a MalformedInput will be raised instead. class UnsupportedOperationType(Exception): @@ -26,8 +25,8 @@ class UnsupportedMismatchedSizes(Exception): pass -class MalformedInput(Exception): - """Indicates that the input is malformed in some way.""" +class IncompatibleOperandCount(Exception): + """Indicates that the specified operation type is not defined for the number of constants specified""" pass @@ -45,7 +44,7 @@ def constant_fold(operation: OperationType, constants: list[Constant], result_ty UnsupportedValueType: Thrown if constants contain value of types not supported. Currently only ints are supported. UnsupportedMismatchedSizes: Thrown if constants types have different sizes and folding of different sized constants is not supported for the specified operation. - MalformedInput: Thrown on malformed input. + IncompatibleOperandCount: Thrown if the specified operation type is not defined for the number of constants in `constants`. """ if operation not in _OPERATION_TO_FOLD_FUNCTION: @@ -82,11 +81,11 @@ def _constant_fold_arithmetic_binary( :raises: UnsupportedMismatchedSizes: Thrown if constants types have different sizes and folding of different sized constants is not supported for the specified operation. - MalformedInput: Thrown on malformed input. + IncompatibleOperandCount: Thrown if the number of constants is not equal to 2. """ if len(constants) != 2: - raise MalformedInput(f"Expected exactly 2 constants to fold, got {len(constants)}.") + raise IncompatibleOperandCount(f"Expected exactly 2 constants to fold, got {len(constants)}.") if not all(constant.type.size == constants[0].type.size for constant in constants): raise UnsupportedMismatchedSizes(f"Can not fold constants with different sizes: {[constant.type for constant in constants]}") @@ -109,11 +108,11 @@ def _constant_fold_arithmetic_unary(constants: list[Constant], fun: Callable[[in :param fun: The unary function to perform on the constant. :return: The result of the operation. :raises: - MalformedInput: Thrown on malformed input. + IncompatibleOperandCount: Thrown if the number of constants is not equal to 1. """ if len(constants) != 1: - raise MalformedInput("Expected exactly 1 constant to fold") + raise IncompatibleOperandCount("Expected exactly 1 constant to fold") return fun(constants[0].value) @@ -128,11 +127,11 @@ def _constant_fold_shift(constants: list[Constant], fun: Callable[[int, int], in This is used to normalize the sign of the input constant to simulate unsigned shifts. :return: The result of the operation. :raises: - MalformedInput: Thrown on malformed input. + IncompatibleOperandCount: Thrown if the number of constants is not equal to 2. """ if len(constants) != 2: - raise MalformedInput("Expected exactly 2 constants to fold") + raise IncompatibleOperandCount("Expected exactly 2 constants to fold") left, right = constants diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_constants.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_constants.py index 839a0a0c5..ffed456b8 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_constants.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_constants.py @@ -1,13 +1,11 @@ -import logging - from decompiler.pipeline.controlflowanalysis.expression_simplification.constant_folding import ( - MalformedInput, + IncompatibleOperandCount, UnsupportedMismatchedSizes, UnsupportedOperationType, UnsupportedValueType, constant_fold, ) -from decompiler.pipeline.controlflowanalysis.expression_simplification.rules.rule import SimplificationRule +from decompiler.pipeline.controlflowanalysis.expression_simplification.rules.rule import MalformedData, SimplificationRule from decompiler.structures.pseudo import Constant, Expression, Operation @@ -26,8 +24,7 @@ def apply(self, operation: Operation) -> list[tuple[Expression, Expression]]: folded_constant = constant_fold(operation.operation, operation.operands, operation.type) except (UnsupportedOperationType, UnsupportedValueType, UnsupportedMismatchedSizes): return [] - except MalformedInput as e: - logging.warning(f"Encountered malformed operation '{operation}': {e}") - return [] + except IncompatibleOperandCount as e: + raise MalformedData() from e return [(operation, folded_constant)] diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py index 80da72300..a60ae776e 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py @@ -1,14 +1,13 @@ -import logging from functools import reduce from typing import Iterator from decompiler.pipeline.controlflowanalysis.expression_simplification.constant_folding import ( FOLDABLE_OPERATIONS, - MalformedInput, + IncompatibleOperandCount, UnsupportedValueType, constant_fold, ) -from decompiler.pipeline.controlflowanalysis.expression_simplification.rules.rule import SimplificationRule +from decompiler.pipeline.controlflowanalysis.expression_simplification.rules.rule import MalformedData, SimplificationRule from decompiler.structures.pseudo import Constant, Expression, Operation, OperationType, Type from decompiler.structures.pseudo.operations import COMMUTATIVE_OPERATIONS @@ -41,9 +40,8 @@ def apply(self, operation: Operation) -> list[tuple[Expression, Expression]]: ) except UnsupportedValueType: return [] - except MalformedInput as e: - logging.warning(f"Encountered malformed operation '{operation}': {e}") - return [] + except IncompatibleOperandCount as e: + raise MalformedData() from e identity_constant = _identity_constant(operation.operation, operation.type) return [ diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/rule.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/rule.py index a4f70334a..6afc904f9 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/rule.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/rule.py @@ -16,5 +16,12 @@ def apply(self, operation: Operation) -> list[tuple[Expression, Expression]]: :param operation: The operation to which the simplification rule should be applied. :return: A list of tuples, each containing a pair of expressions representing the original and simplified versions resulting from applying the simplification rule to the given operation. + :raises: + MalformedData: Thrown inf malformed data, like a dereference operation with two operands, is encountered. """ pass + + +class MalformedData(Exception): + """Used to indicate that malformed data was encountered""" + pass diff --git a/tests/pipeline/controlflowanalysis/expression_simplification/test_constant_folding.py b/tests/pipeline/controlflowanalysis/expression_simplification/test_constant_folding.py index 94f238aea..5d96b9b60 100644 --- a/tests/pipeline/controlflowanalysis/expression_simplification/test_constant_folding.py +++ b/tests/pipeline/controlflowanalysis/expression_simplification/test_constant_folding.py @@ -4,7 +4,7 @@ import pytest from decompiler.pipeline.controlflowanalysis.expression_simplification.constant_folding import ( FOLDABLE_OPERATIONS, - MalformedInput, + IncompatibleOperandCount, UnsupportedMismatchedSizes, UnsupportedOperationType, UnsupportedValueType, @@ -65,99 +65,99 @@ def test_constant_fold_invalid_value_type( (OperationType.plus, [_c_u32(2147483658), _c_u32(2147483652)], Integer.uint32_t(), _c_u32(14), nullcontext()), (OperationType.plus, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(7), nullcontext()), (OperationType.plus, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), - (OperationType.plus, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), - (OperationType.plus, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.plus, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.plus, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.minus, [_c_i32(3), _c_i32(4)], Integer.int32_t(), _c_i32(-1), nullcontext()), (OperationType.minus, [_c_i32(-2147483648), _c_i32(1)], Integer.int32_t(), _c_i32(2147483647), nullcontext()), (OperationType.minus, [_c_u32(3), _c_u32(4)], Integer.uint32_t(), _c_u32(4294967295), nullcontext()), (OperationType.minus, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(-1), nullcontext()), (OperationType.minus, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), - (OperationType.minus, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), - (OperationType.minus, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.minus, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.minus, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.multiply, [_c_i32(3), _c_i32(4)], Integer.int32_t(), _c_i32(12), nullcontext()), (OperationType.multiply, [_c_i32(-1073741824), _c_i32(2)], Integer.int32_t(), _c_i32(-2147483648), nullcontext()), (OperationType.multiply, [_c_u32(3221225472), _c_u32(2)], Integer.uint32_t(), _c_u32(2147483648), nullcontext()), (OperationType.multiply, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(12), nullcontext()), (OperationType.multiply, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), - (OperationType.multiply, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), - (OperationType.multiply, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.multiply, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.multiply, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.multiply_us, [_c_i32(3), _c_i32(4)], Integer.int32_t(), _c_i32(12), nullcontext()), (OperationType.multiply_us, [_c_i32(-1073741824), _c_i32(2)], Integer.int32_t(), _c_i32(-2147483648), nullcontext()), (OperationType.multiply_us, [_c_u32(3221225472), _c_u32(2)], Integer.uint32_t(), _c_u32(2147483648), nullcontext()), (OperationType.multiply_us, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(12), nullcontext()), (OperationType.multiply_us, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), - (OperationType.multiply_us, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), - (OperationType.multiply_us, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.multiply_us, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.multiply_us, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.divide, [_c_i32(12), _c_i32(4)], Integer.int32_t(), _c_i32(3), nullcontext()), (OperationType.divide, [_c_i32(-2147483648), _c_i32(2)], Integer.int32_t(), _c_i32(-1073741824), nullcontext()), (OperationType.divide, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(0), nullcontext()), (OperationType.divide, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), - (OperationType.divide, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), - (OperationType.divide, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.divide, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.divide, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.divide_us, [_c_i32(12), _c_i32(4)], Integer.int32_t(), _c_i32(3), nullcontext()), (OperationType.divide_us, [_c_i32(-2147483648), _c_i32(2)], Integer.int32_t(), _c_i32(1073741824), nullcontext()), (OperationType.divide_us, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(0), nullcontext()), (OperationType.divide_us, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), - (OperationType.divide_us, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), - (OperationType.divide_us, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.divide_us, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.divide_us, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.negate, [_c_i32(3)], Integer.int32_t(), _c_i32(-3), nullcontext()), (OperationType.negate, [_c_i32(-2147483648)], Integer.int32_t(), _c_i32(-2147483648), nullcontext()), - (OperationType.negate, [], Integer.int32_t(), None, pytest.raises(MalformedInput)), - (OperationType.negate, [_c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.negate, [], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.negate, [_c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.left_shift, [_c_i32(3), _c_i32(4)], Integer.int32_t(), _c_i32(48), nullcontext()), (OperationType.left_shift, [_c_i32(1073741824), _c_i32(1)], Integer.int32_t(), _c_i32(-2147483648), nullcontext()), (OperationType.left_shift, [_c_u32(1073741824), _c_u32(1)], Integer.uint32_t(), _c_u32(2147483648), nullcontext()), - (OperationType.left_shift, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), - (OperationType.left_shift, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.left_shift, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.left_shift, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.right_shift, [_c_i32(32), _c_i32(4)], Integer.int32_t(), _c_i32(2), nullcontext()), (OperationType.right_shift, [_c_i32(-2147483648), _c_i32(1)], Integer.int32_t(), _c_i32(-1073741824), nullcontext()), (OperationType.right_shift, [_c_u32(2147483648), _c_u32(1)], Integer.uint32_t(), _c_u32(1073741824), nullcontext()), - (OperationType.right_shift, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), - (OperationType.right_shift, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.right_shift, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.right_shift, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.right_shift_us, [_c_i32(32), _c_i32(4)], Integer.int32_t(), _c_i32(2), nullcontext()), (OperationType.right_shift_us, [_c_i32(-2147483648), _c_i32(1)], Integer.int32_t(), _c_i32(1073741824), nullcontext()), (OperationType.right_shift_us, [_c_u32(2147483648), _c_u32(1)], Integer.uint32_t(), _c_u32(1073741824), nullcontext()), - (OperationType.right_shift_us, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), - (OperationType.right_shift_us, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.right_shift_us, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.right_shift_us, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.bitwise_or, [_c_i32(85), _c_i32(34)], Integer.int32_t(), _c_i32(119), nullcontext()), (OperationType.bitwise_or, [_c_i32(-2147483648), _c_i32(1)], Integer.int32_t(), _c_i32(-2147483647), nullcontext()), (OperationType.bitwise_or, [_c_u32(2147483648), _c_u32(1)], Integer.uint32_t(), _c_u32(2147483649), nullcontext()), (OperationType.bitwise_or, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(7), nullcontext()), (OperationType.bitwise_or, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), - (OperationType.bitwise_or, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), - (OperationType.bitwise_or, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.bitwise_or, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.bitwise_or, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.bitwise_and, [_c_i32(85), _c_i32(51)], Integer.int32_t(), _c_i32(17), nullcontext()), (OperationType.bitwise_and, [_c_i32(-2147483647), _c_i32(3)], Integer.int32_t(), _c_i32(1), nullcontext()), (OperationType.bitwise_and, [_c_u32(2147483649), _c_u32(3)], Integer.uint32_t(), _c_u32(1), nullcontext()), (OperationType.bitwise_and, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(0), nullcontext()), (OperationType.bitwise_and, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), - (OperationType.bitwise_and, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), - (OperationType.bitwise_and, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.bitwise_and, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.bitwise_and, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.bitwise_xor, [_c_i32(85), _c_i32(51)], Integer.int32_t(), _c_i32(102), nullcontext()), (OperationType.bitwise_xor, [_c_i32(-2147483647), _c_i32(-2147483646)], Integer.int32_t(), _c_i32(3), nullcontext()), (OperationType.bitwise_xor, [_c_u32(2147483649), _c_u32(2147483650)], Integer.uint32_t(), _c_u32(3), nullcontext()), (OperationType.bitwise_xor, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(7), nullcontext()), (OperationType.bitwise_xor, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), - (OperationType.bitwise_xor, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), - (OperationType.bitwise_xor, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.bitwise_xor, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.bitwise_xor, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.bitwise_not, [_c_i32(6)], Integer.int32_t(), _c_i32(-7), nullcontext()), (OperationType.bitwise_not, [_c_i32(-2147483648)], Integer.int32_t(), _c_i32(2147483647), nullcontext()), (OperationType.bitwise_not, [_c_u32(2147483648)], Integer.uint32_t(), _c_u32(2147483647), nullcontext()), - (OperationType.bitwise_not, [], Integer.int32_t(), None, pytest.raises(MalformedInput)), - (OperationType.bitwise_not, [_c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(MalformedInput)), + (OperationType.bitwise_not, [], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.bitwise_not, [_c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), ] ) def test_constant_fold( From cebf18268c588183d8df72f649ab564c9b503c80 Mon Sep 17 00:00:00 2001 From: rihi <19492038+rihi@users.noreply.github.com> Date: Wed, 25 Oct 2023 15:12:17 +0200 Subject: [PATCH 13/16] Update outdated comment --- .../rules/collapse_nested_constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py index a60ae776e..49a29cd9d 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py @@ -92,7 +92,7 @@ def _identity_constant(operation: OperationType, var_type: Type) -> Constant: # - OperationType.bitwise_not is foldable (UnsupportedOperationType) # - constant has integer value, which is supported (UnsupportedValueType) # - with only 1 constant there cant be mismatched sizes (UnsupportedMismatchedSizes) - # - input is not malformed (MalformedInput) + # - bitwise_not has exactly one operand (IncompatibleOperandCount) return constant_fold(OperationType.bitwise_not, [Constant(0, var_type)], var_type) case _: raise NotImplementedError() From 878442099ac0cd7a2c20773a6531e0a8b6f67ce5 Mon Sep 17 00:00:00 2001 From: rihi <19492038+rihi@users.noreply.github.com> Date: Wed, 25 Oct 2023 15:26:18 +0200 Subject: [PATCH 14/16] Remove unnecessary check in collapse_constants.py --- .../expression_simplification/rules/collapse_constants.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_constants.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_constants.py index ffed456b8..7e5734686 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_constants.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_constants.py @@ -15,8 +15,6 @@ class CollapseConstants(SimplificationRule): """ def apply(self, operation: Operation) -> list[tuple[Expression, Expression]]: - if not operation.operands: - return [] # Is this even allowed? if not all(isinstance(o, Constant) for o in operation.operands): return [] From e753a102ca8592f533afc0200e0b5b05cb045528 Mon Sep 17 00:00:00 2001 From: Manuel Blatt Date: Wed, 25 Oct 2023 16:50:39 +0200 Subject: [PATCH 15/16] fix broken merge --- .../test_constant_folding.py | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/tests/pipeline/controlflowanalysis/expression_simplification/test_constant_folding.py b/tests/pipeline/controlflowanalysis/expression_simplification/test_constant_folding.py index c0783e44c..74cc07ae5 100644 --- a/tests/pipeline/controlflowanalysis/expression_simplification/test_constant_folding.py +++ b/tests/pipeline/controlflowanalysis/expression_simplification/test_constant_folding.py @@ -29,19 +29,9 @@ def _c_float(value: float) -> Constant: return Constant(value, Float.float()) -@pytest.mark.parametrize(["operation"], [(operation,) for operation in OperationType if operation not in FOLDABLE_OPERATIONS]) -def test_constant_fold_invalid_operations(operation: OperationType): - with pytest.raises(UnsupportedOperationType): - constant_fold(operation, [], Integer.int32_t()) - - @pytest.mark.parametrize( - ["operation", "constants", "result_type", "expected_result", "context"], - [ - (OperationType.plus, [_c_i32(0), _c_i32(0)], Integer.int32_t(), _c_i32(0), nullcontext()), - (OperationType.plus, [_c_float(0.0), _c_float(0.0)], Float.float(), _c_float(0.0), pytest.raises(UnsupportedValueType)), - (OperationType.plus, [_c_i32(0), _c_float(0.0)], Integer.int32_t(), _c_i32(0), pytest.raises(UnsupportedValueType)), - ] + ["operation"], + [(operation,) for operation in OperationType if operation not in FOLDABLE_OPERATIONS] ) def test_constant_fold_invalid_operations(operation: OperationType): with pytest.raises(UnsupportedOperationType): @@ -77,6 +67,7 @@ def test_constant_fold_invalid_value_type( (OperationType.plus, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), (OperationType.plus, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.plus, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.minus, [_c_i32(3), _c_i32(4)], Integer.int32_t(), _c_i32(-1), nullcontext()), (OperationType.minus, [_c_i32(-2147483648), _c_i32(1)], Integer.int32_t(), _c_i32(2147483647), nullcontext()), (OperationType.minus, [_c_u32(3), _c_u32(4)], Integer.uint32_t(), _c_u32(4294967295), nullcontext()), @@ -84,6 +75,7 @@ def test_constant_fold_invalid_value_type( (OperationType.minus, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), (OperationType.minus, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.minus, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.multiply, [_c_i32(3), _c_i32(4)], Integer.int32_t(), _c_i32(12), nullcontext()), (OperationType.multiply, [_c_i32(-1073741824), _c_i32(2)], Integer.int32_t(), _c_i32(-2147483648), nullcontext()), (OperationType.multiply, [_c_u32(3221225472), _c_u32(2)], Integer.uint32_t(), _c_u32(2147483648), nullcontext()), @@ -91,6 +83,7 @@ def test_constant_fold_invalid_value_type( (OperationType.multiply, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), (OperationType.multiply, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.multiply, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.multiply_us, [_c_i32(3), _c_i32(4)], Integer.int32_t(), _c_i32(12), nullcontext()), (OperationType.multiply_us, [_c_i32(-1073741824), _c_i32(2)], Integer.int32_t(), _c_i32(-2147483648), nullcontext()), (OperationType.multiply_us, [_c_u32(3221225472), _c_u32(2)], Integer.uint32_t(), _c_u32(2147483648), nullcontext()), @@ -98,37 +91,44 @@ def test_constant_fold_invalid_value_type( (OperationType.multiply_us, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), (OperationType.multiply_us, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.multiply_us, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.divide, [_c_i32(12), _c_i32(4)], Integer.int32_t(), _c_i32(3), nullcontext()), (OperationType.divide, [_c_i32(-2147483648), _c_i32(2)], Integer.int32_t(), _c_i32(-1073741824), nullcontext()), (OperationType.divide, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(0), nullcontext()), (OperationType.divide, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), (OperationType.divide, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.divide, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.divide_us, [_c_i32(12), _c_i32(4)], Integer.int32_t(), _c_i32(3), nullcontext()), (OperationType.divide_us, [_c_i32(-2147483648), _c_i32(2)], Integer.int32_t(), _c_i32(1073741824), nullcontext()), (OperationType.divide_us, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(0), nullcontext()), (OperationType.divide_us, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), (OperationType.divide_us, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.divide_us, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.negate, [_c_i32(3)], Integer.int32_t(), _c_i32(-3), nullcontext()), (OperationType.negate, [_c_i32(-2147483648)], Integer.int32_t(), _c_i32(-2147483648), nullcontext()), (OperationType.negate, [], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.negate, [_c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.left_shift, [_c_i32(3), _c_i32(4)], Integer.int32_t(), _c_i32(48), nullcontext()), (OperationType.left_shift, [_c_i32(1073741824), _c_i32(1)], Integer.int32_t(), _c_i32(-2147483648), nullcontext()), (OperationType.left_shift, [_c_u32(1073741824), _c_u32(1)], Integer.uint32_t(), _c_u32(2147483648), nullcontext()), (OperationType.left_shift, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.left_shift, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.right_shift, [_c_i32(32), _c_i32(4)], Integer.int32_t(), _c_i32(2), nullcontext()), (OperationType.right_shift, [_c_i32(-2147483648), _c_i32(1)], Integer.int32_t(), _c_i32(-1073741824), nullcontext()), (OperationType.right_shift, [_c_u32(2147483648), _c_u32(1)], Integer.uint32_t(), _c_u32(1073741824), nullcontext()), (OperationType.right_shift, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.right_shift, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.right_shift_us, [_c_i32(32), _c_i32(4)], Integer.int32_t(), _c_i32(2), nullcontext()), (OperationType.right_shift_us, [_c_i32(-2147483648), _c_i32(1)], Integer.int32_t(), _c_i32(1073741824), nullcontext()), (OperationType.right_shift_us, [_c_u32(2147483648), _c_u32(1)], Integer.uint32_t(), _c_u32(1073741824), nullcontext()), (OperationType.right_shift_us, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.right_shift_us, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.bitwise_or, [_c_i32(85), _c_i32(34)], Integer.int32_t(), _c_i32(119), nullcontext()), (OperationType.bitwise_or, [_c_i32(-2147483648), _c_i32(1)], Integer.int32_t(), _c_i32(-2147483647), nullcontext()), (OperationType.bitwise_or, [_c_u32(2147483648), _c_u32(1)], Integer.uint32_t(), _c_u32(2147483649), nullcontext()), @@ -136,6 +136,7 @@ def test_constant_fold_invalid_value_type( (OperationType.bitwise_or, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), (OperationType.bitwise_or, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.bitwise_or, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.bitwise_and, [_c_i32(85), _c_i32(51)], Integer.int32_t(), _c_i32(17), nullcontext()), (OperationType.bitwise_and, [_c_i32(-2147483647), _c_i32(3)], Integer.int32_t(), _c_i32(1), nullcontext()), (OperationType.bitwise_and, [_c_u32(2147483649), _c_u32(3)], Integer.uint32_t(), _c_u32(1), nullcontext()), @@ -143,6 +144,7 @@ def test_constant_fold_invalid_value_type( (OperationType.bitwise_and, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), (OperationType.bitwise_and, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.bitwise_and, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.bitwise_xor, [_c_i32(85), _c_i32(51)], Integer.int32_t(), _c_i32(102), nullcontext()), (OperationType.bitwise_xor, [_c_i32(-2147483647), _c_i32(-2147483646)], Integer.int32_t(), _c_i32(3), nullcontext()), (OperationType.bitwise_xor, [_c_u32(2147483649), _c_u32(2147483650)], Integer.uint32_t(), _c_u32(3), nullcontext()), @@ -150,6 +152,7 @@ def test_constant_fold_invalid_value_type( (OperationType.bitwise_xor, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), (OperationType.bitwise_xor, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.bitwise_xor, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), + (OperationType.bitwise_not, [_c_i32(6)], Integer.int32_t(), _c_i32(-7), nullcontext()), (OperationType.bitwise_not, [_c_i32(-2147483648)], Integer.int32_t(), _c_i32(2147483647), nullcontext()), (OperationType.bitwise_not, [_c_u32(2147483648)], Integer.uint32_t(), _c_u32(2147483647), nullcontext()), From f5cf44e996da987bf916ef5affe0cae61fd9e0e4 Mon Sep 17 00:00:00 2001 From: Manuel Blatt Date: Wed, 25 Oct 2023 17:01:51 +0200 Subject: [PATCH 16/16] fix formatting --- .../constant_folding.py | 22 ++++++------- .../rules/collapse_nested_constants.py | 6 +--- .../expression_simplification/rules/rule.py | 1 + .../expression_simplification/stages.py | 20 ++---------- .../test_constant_folding.py | 32 +++---------------- .../expression_simplification/test_stage.py | 20 ++---------- 6 files changed, 21 insertions(+), 80 deletions(-) diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py index 3847d8166..2706987b0 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/constant_folding.py @@ -12,21 +12,26 @@ class UnsupportedOperationType(Exception): """Indicates that the specified Operation is not supported""" + pass class UnsupportedValueType(Exception): + """Indicates that the value type of one constant is not supported.""" + pass class UnsupportedMismatchedSizes(Exception): """Indicates that folding of different sized constants is not supported for the specified operation.""" + pass class IncompatibleOperandCount(Exception): """Indicates that the specified operation type is not defined for the number of constants specified""" + pass @@ -55,19 +60,13 @@ def constant_fold(operation: OperationType, constants: list[Constant], result_ty return Constant( normalize_int( - _OPERATION_TO_FOLD_FUNCTION[operation](constants), - result_type.size, - isinstance(result_type, Integer) and result_type.signed + _OPERATION_TO_FOLD_FUNCTION[operation](constants), result_type.size, isinstance(result_type, Integer) and result_type.signed ), - result_type + result_type, ) -def _constant_fold_arithmetic_binary( - constants: list[Constant], - fun: Callable[[int, int], int], - norm_sign: Optional[bool] = None -) -> int: +def _constant_fold_arithmetic_binary(constants: list[Constant], fun: Callable[[int, int], int], norm_sign: Optional[bool] = None) -> int: """ Fold an arithmetic binary operation with constants as operands. @@ -135,10 +134,7 @@ def _constant_fold_shift(constants: list[Constant], fun: Callable[[int, int], in left, right = constants - return fun( - normalize_int(left.value, left.type.size, left.type.signed and signed), - right.value - ) + return fun(normalize_int(left.value, left.type.size, left.type.signed and signed), right.value) _OPERATION_TO_FOLD_FUNCTION: dict[OperationType, Callable[[list[Constant]], int]] = { diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py index 9d43dc3b4..62426e6dd 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/collapse_nested_constants.py @@ -34,11 +34,7 @@ def apply(self, operation: Operation) -> list[tuple[Expression, Expression]]: # We don't need to catch UnsupportedOperationType, because check that operation is in _COLLAPSIBLE_OPERATIONS # We don't need to catch UnsupportedMismatchedSizes, because '_collect_constants' only returns constants of the same type try: - folded_constant = reduce( - lambda c0, c1: constant_fold(operation.operation, [c0, c1], operation.type), - rest, - first - ) + folded_constant = reduce(lambda c0, c1: constant_fold(operation.operation, [c0, c1], operation.type), rest, first) except UnsupportedValueType: return [] except IncompatibleOperandCount as e: diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/rule.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/rule.py index 6afc904f9..ef46c9a3d 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/rule.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/rules/rule.py @@ -24,4 +24,5 @@ def apply(self, operation: Operation) -> list[tuple[Expression, Expression]]: class MalformedData(Exception): """Used to indicate that malformed data was encountered""" + pass diff --git a/decompiler/pipeline/controlflowanalysis/expression_simplification/stages.py b/decompiler/pipeline/controlflowanalysis/expression_simplification/stages.py index 575a446a8..708049020 100644 --- a/decompiler/pipeline/controlflowanalysis/expression_simplification/stages.py +++ b/decompiler/pipeline/controlflowanalysis/expression_simplification/stages.py @@ -38,11 +38,7 @@ def _get_instructions(self, task: DecompilerTask) -> list[Instruction]: @classmethod def _simplify_instructions(cls, instructions: list[Instruction], max_iterations: int, debug: bool): - rule_sets = [ - ("pre-rules", _pre_rules), - ("rules", _rules), - ("post-rules", _post_rules) - ] + rule_sets = [("pre-rules", _pre_rules), ("rules", _rules), ("post-rules", _post_rules)] for rule_name, rule_set in rule_sets: # max_iterations is counted per rule_set iteration_count = cls._simplify_instructions_with_rule_set(instructions, rule_set, max_iterations, debug) @@ -53,11 +49,7 @@ def _simplify_instructions(cls, instructions: list[Instruction], max_iterations: @classmethod def _simplify_instructions_with_rule_set( - cls, - instructions: list[Instruction], - rule_set: list[SimplificationRule], - max_iterations: int, - debug: bool + cls, instructions: list[Instruction], rule_set: list[SimplificationRule], max_iterations: int, debug: bool ) -> int: iteration_count = 0 @@ -78,13 +70,7 @@ def _simplify_instructions_with_rule_set( return iteration_count @classmethod - def _simplify_instruction_with_rule( - cls, - instruction: Instruction, - rule: SimplificationRule, - max_iterations: int, - debug: bool - ) -> int: + def _simplify_instruction_with_rule(cls, instruction: Instruction, rule: SimplificationRule, max_iterations: int, debug: bool) -> int: iteration_count = 0 for expression in instruction.subexpressions(): while True: diff --git a/tests/pipeline/controlflowanalysis/expression_simplification/test_constant_folding.py b/tests/pipeline/controlflowanalysis/expression_simplification/test_constant_folding.py index 74cc07ae5..44ff99ea7 100644 --- a/tests/pipeline/controlflowanalysis/expression_simplification/test_constant_folding.py +++ b/tests/pipeline/controlflowanalysis/expression_simplification/test_constant_folding.py @@ -29,10 +29,7 @@ def _c_float(value: float) -> Constant: return Constant(value, Float.float()) -@pytest.mark.parametrize( - ["operation"], - [(operation,) for operation in OperationType if operation not in FOLDABLE_OPERATIONS] -) +@pytest.mark.parametrize(["operation"], [(operation,) for operation in OperationType if operation not in FOLDABLE_OPERATIONS]) def test_constant_fold_invalid_operations(operation: OperationType): with pytest.raises(UnsupportedOperationType): constant_fold(operation, [], Integer.int32_t()) @@ -44,14 +41,10 @@ def test_constant_fold_invalid_operations(operation: OperationType): (OperationType.plus, [_c_i32(0), _c_i32(0)], Integer.int32_t(), _c_i32(0), nullcontext()), (OperationType.plus, [_c_float(0.0), _c_float(0.0)], Float.float(), _c_float(0.0), pytest.raises(UnsupportedValueType)), (OperationType.plus, [_c_i32(0), _c_float(0.0)], Integer.int32_t(), _c_i32(0), pytest.raises(UnsupportedValueType)), - ] + ], ) def test_constant_fold_invalid_value_type( - operation: OperationType, - constants: list[Constant], - result_type: Type, - expected_result: Optional[Constant], - context + operation: OperationType, constants: list[Constant], result_type: Type, expected_result: Optional[Constant], context ): with context: assert constant_fold(operation, constants, result_type) == expected_result @@ -67,7 +60,6 @@ def test_constant_fold_invalid_value_type( (OperationType.plus, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), (OperationType.plus, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.plus, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), - (OperationType.minus, [_c_i32(3), _c_i32(4)], Integer.int32_t(), _c_i32(-1), nullcontext()), (OperationType.minus, [_c_i32(-2147483648), _c_i32(1)], Integer.int32_t(), _c_i32(2147483647), nullcontext()), (OperationType.minus, [_c_u32(3), _c_u32(4)], Integer.uint32_t(), _c_u32(4294967295), nullcontext()), @@ -75,7 +67,6 @@ def test_constant_fold_invalid_value_type( (OperationType.minus, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), (OperationType.minus, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.minus, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), - (OperationType.multiply, [_c_i32(3), _c_i32(4)], Integer.int32_t(), _c_i32(12), nullcontext()), (OperationType.multiply, [_c_i32(-1073741824), _c_i32(2)], Integer.int32_t(), _c_i32(-2147483648), nullcontext()), (OperationType.multiply, [_c_u32(3221225472), _c_u32(2)], Integer.uint32_t(), _c_u32(2147483648), nullcontext()), @@ -83,7 +74,6 @@ def test_constant_fold_invalid_value_type( (OperationType.multiply, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), (OperationType.multiply, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.multiply, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), - (OperationType.multiply_us, [_c_i32(3), _c_i32(4)], Integer.int32_t(), _c_i32(12), nullcontext()), (OperationType.multiply_us, [_c_i32(-1073741824), _c_i32(2)], Integer.int32_t(), _c_i32(-2147483648), nullcontext()), (OperationType.multiply_us, [_c_u32(3221225472), _c_u32(2)], Integer.uint32_t(), _c_u32(2147483648), nullcontext()), @@ -91,44 +81,37 @@ def test_constant_fold_invalid_value_type( (OperationType.multiply_us, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), (OperationType.multiply_us, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.multiply_us, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), - (OperationType.divide, [_c_i32(12), _c_i32(4)], Integer.int32_t(), _c_i32(3), nullcontext()), (OperationType.divide, [_c_i32(-2147483648), _c_i32(2)], Integer.int32_t(), _c_i32(-1073741824), nullcontext()), (OperationType.divide, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(0), nullcontext()), (OperationType.divide, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), (OperationType.divide, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.divide, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), - (OperationType.divide_us, [_c_i32(12), _c_i32(4)], Integer.int32_t(), _c_i32(3), nullcontext()), (OperationType.divide_us, [_c_i32(-2147483648), _c_i32(2)], Integer.int32_t(), _c_i32(1073741824), nullcontext()), (OperationType.divide_us, [_c_u32(3), _c_i32(4)], Integer.int32_t(), _c_i32(0), nullcontext()), (OperationType.divide_us, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), (OperationType.divide_us, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.divide_us, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), - (OperationType.negate, [_c_i32(3)], Integer.int32_t(), _c_i32(-3), nullcontext()), (OperationType.negate, [_c_i32(-2147483648)], Integer.int32_t(), _c_i32(-2147483648), nullcontext()), (OperationType.negate, [], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.negate, [_c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), - (OperationType.left_shift, [_c_i32(3), _c_i32(4)], Integer.int32_t(), _c_i32(48), nullcontext()), (OperationType.left_shift, [_c_i32(1073741824), _c_i32(1)], Integer.int32_t(), _c_i32(-2147483648), nullcontext()), (OperationType.left_shift, [_c_u32(1073741824), _c_u32(1)], Integer.uint32_t(), _c_u32(2147483648), nullcontext()), (OperationType.left_shift, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.left_shift, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), - (OperationType.right_shift, [_c_i32(32), _c_i32(4)], Integer.int32_t(), _c_i32(2), nullcontext()), (OperationType.right_shift, [_c_i32(-2147483648), _c_i32(1)], Integer.int32_t(), _c_i32(-1073741824), nullcontext()), (OperationType.right_shift, [_c_u32(2147483648), _c_u32(1)], Integer.uint32_t(), _c_u32(1073741824), nullcontext()), (OperationType.right_shift, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.right_shift, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), - (OperationType.right_shift_us, [_c_i32(32), _c_i32(4)], Integer.int32_t(), _c_i32(2), nullcontext()), (OperationType.right_shift_us, [_c_i32(-2147483648), _c_i32(1)], Integer.int32_t(), _c_i32(1073741824), nullcontext()), (OperationType.right_shift_us, [_c_u32(2147483648), _c_u32(1)], Integer.uint32_t(), _c_u32(1073741824), nullcontext()), (OperationType.right_shift_us, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.right_shift_us, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), - (OperationType.bitwise_or, [_c_i32(85), _c_i32(34)], Integer.int32_t(), _c_i32(119), nullcontext()), (OperationType.bitwise_or, [_c_i32(-2147483648), _c_i32(1)], Integer.int32_t(), _c_i32(-2147483647), nullcontext()), (OperationType.bitwise_or, [_c_u32(2147483648), _c_u32(1)], Integer.uint32_t(), _c_u32(2147483649), nullcontext()), @@ -136,7 +119,6 @@ def test_constant_fold_invalid_value_type( (OperationType.bitwise_or, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), (OperationType.bitwise_or, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.bitwise_or, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), - (OperationType.bitwise_and, [_c_i32(85), _c_i32(51)], Integer.int32_t(), _c_i32(17), nullcontext()), (OperationType.bitwise_and, [_c_i32(-2147483647), _c_i32(3)], Integer.int32_t(), _c_i32(1), nullcontext()), (OperationType.bitwise_and, [_c_u32(2147483649), _c_u32(3)], Integer.uint32_t(), _c_u32(1), nullcontext()), @@ -144,7 +126,6 @@ def test_constant_fold_invalid_value_type( (OperationType.bitwise_and, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), (OperationType.bitwise_and, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.bitwise_and, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), - (OperationType.bitwise_xor, [_c_i32(85), _c_i32(51)], Integer.int32_t(), _c_i32(102), nullcontext()), (OperationType.bitwise_xor, [_c_i32(-2147483647), _c_i32(-2147483646)], Integer.int32_t(), _c_i32(3), nullcontext()), (OperationType.bitwise_xor, [_c_u32(2147483649), _c_u32(2147483650)], Integer.uint32_t(), _c_u32(3), nullcontext()), @@ -152,7 +133,6 @@ def test_constant_fold_invalid_value_type( (OperationType.bitwise_xor, [_c_i32(3), _c_i16(4)], Integer.int32_t(), None, pytest.raises(UnsupportedMismatchedSizes)), (OperationType.bitwise_xor, [_c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), (OperationType.bitwise_xor, [_c_i32(3), _c_i32(3), _c_i32(3)], Integer.int32_t(), None, pytest.raises(IncompatibleOperandCount)), - (OperationType.bitwise_not, [_c_i32(6)], Integer.int32_t(), _c_i32(-7), nullcontext()), (OperationType.bitwise_not, [_c_i32(-2147483648)], Integer.int32_t(), _c_i32(2147483647), nullcontext()), (OperationType.bitwise_not, [_c_u32(2147483648)], Integer.uint32_t(), _c_u32(2147483647), nullcontext()), @@ -161,11 +141,7 @@ def test_constant_fold_invalid_value_type( ], ) def test_constant_fold( - operation: OperationType, - constants: list[Constant], - result_type: Type, - expected_result: Optional[Constant], - context + operation: OperationType, constants: list[Constant], result_type: Type, expected_result: Optional[Constant], context ): with context: assert constant_fold(operation, constants, result_type) == expected_result diff --git a/tests/pipeline/controlflowanalysis/expression_simplification/test_stage.py b/tests/pipeline/controlflowanalysis/expression_simplification/test_stage.py index 0f1eccc7e..d2b811abe 100644 --- a/tests/pipeline/controlflowanalysis/expression_simplification/test_stage.py +++ b/tests/pipeline/controlflowanalysis/expression_simplification/test_stage.py @@ -57,17 +57,8 @@ def _v_i32(name: str) -> Variable: ), ], ) -def test_simplify_instructions_with_rule_set( - rule_set: list[SimplificationRule], - instruction: Instruction, - expected_result: Instruction -): - _ExpressionSimplificationBase._simplify_instructions_with_rule_set( - [instruction], - rule_set, - 100, - True - ) +def test_simplify_instructions_with_rule_set(rule_set: list[SimplificationRule], instruction: Instruction, expected_result: Instruction): + _ExpressionSimplificationBase._simplify_instructions_with_rule_set([instruction], rule_set, 100, True) assert instruction == expected_result @@ -81,10 +72,5 @@ def test_simplify_instructions_with_rule_set( def test_simplify_instructions_with_rule_set_max_iterations( rule_set: list[SimplificationRule], instruction: Instruction, max_iterations: int, expect_exceed_max_iterations: bool ): - iterations = _ExpressionSimplificationBase._simplify_instructions_with_rule_set( - [instruction], - rule_set, - max_iterations, - True - ) + iterations = _ExpressionSimplificationBase._simplify_instructions_with_rule_set([instruction], rule_set, max_iterations, True) assert (iterations > max_iterations) == expect_exceed_max_iterations