From 76f67f3fca37884c238c0b9d4059fe38b0107ede Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 30 Aug 2026 21:24:40 +0200 Subject: [PATCH] Add DetectImprovedTypeForForBitOp as a transform step. Previously this happened within the InferType implementation. --- .../Instructions/BinaryNumericInstruction.cs | 12 ++++++- .../IL/Transforms/ExpressionTransforms.cs | 31 +++++++++++++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs index dd280f2ad..fa621f689 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs @@ -78,6 +78,14 @@ namespace ICSharpCode.Decompiler.IL readonly StackType resultType; + /// + /// Result type of this instruction. If not null, InferType() will return this type. + /// Can be used to store an improved C# type for the result of this instruction, + /// e.g. `bit.or.i4(bool1, bool2)` can use CSharpResultType = bool after the + /// ExpressionTransform has determined that the result is always 0 or 1. + /// + public IType? CSharpResultType { get; set; } + public BinaryNumericInstruction(BinaryNumericOperator op, ILInstruction left, ILInstruction right, bool checkForOverflow, Sign sign) : this(op, left, right, left.ResultType, right.ResultType, checkForOverflow, sign) { @@ -123,7 +131,7 @@ namespace ICSharpCode.Decompiler.IL return StackType.Unknown; } - public StackType UnderlyingResultType { get => resultType; } + public StackType UnderlyingResultType => resultType; public sealed override StackType ResultType { get => IsLifted ? StackType.O : resultType; @@ -131,6 +139,8 @@ namespace ICSharpCode.Decompiler.IL public override IType InferType(ICompilation compilation) { + if (CSharpResultType != null) + return CSharpResultType; IType type = compilation.FindType(UnderlyingResultType); if (IsLifted) return NullableType.Create(compilation, type); diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 6103fbd73..e6d7f242d 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -863,8 +863,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms } break; case BinaryNumericOperator.BitAnd: - if (inst.Left.InferType(context.TypeSystem).IsKnownType(KnownTypeCode.Boolean) - && inst.Right.InferType(context.TypeSystem).IsKnownType(KnownTypeCode.Boolean)) + { + IType leftType = inst.Left.InferType(context.TypeSystem); + IType rightType = inst.Right.InferType(context.TypeSystem); + DetectImprovedTypeForForBitOp(inst, leftType, rightType); + if (leftType.IsKnownType(KnownTypeCode.Boolean) + && rightType.IsKnownType(KnownTypeCode.Boolean)) { if (new NullableLiftingTransform(context).Run(inst)) { @@ -872,6 +876,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } break; + } + case BinaryNumericOperator.BitOr: + case BinaryNumericOperator.BitXor: + { + IType leftType = inst.Left.InferType(context.TypeSystem); + IType rightType = inst.Right.InferType(context.TypeSystem); + DetectImprovedTypeForForBitOp(inst, leftType, rightType); + break; + } } bool MatchExpectedShiftSize(ILInstruction rhs) @@ -894,6 +907,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } + // Given bit.or(a, b), promotes the "known to be bool/short" information from the operands to bit.or + void DetectImprovedTypeForForBitOp(BinaryNumericInstruction inst, IType leftType, IType rightType) + { + if (leftType.Equals(rightType) && (leftType.IsCSharpPrimitiveIntegerType() || leftType.IsCSharpNativeIntegerType() || leftType.IsKnownType(KnownTypeCode.Boolean))) + { + if (!leftType.Equals(inst.CSharpResultType)) + { + context.Step("DetectImprovedTypeForForBitOp", inst); + inst.CSharpResultType = leftType; + context.EndStep(inst); + } + } + } + protected internal override void VisitTryCatchHandler(TryCatchHandler inst) { base.VisitTryCatchHandler(inst);