/// <summary> /// /// </summary> /// <typeparam name="Instruction"></typeparam> /// <param name="instruction"></param> /// <param name="mappings"></param> /// <param name="canonicalizer"></param> /// <returns></returns> internal static Instruction SimplifyUnary <Instruction>(Instruction instruction, ValueMappings <Instruction> mappings, ExpressionCanonicalizer <Instruction> canonicalizer) where Instruction : Microsoft.Cci.Analysis.Instruction, new() { Contract.Requires(instruction != null); Contract.Requires(mappings != null); Contract.Requires(canonicalizer != null); Contract.Ensures(Contract.Result <Instruction>() != null); var operation = instruction.Operation; Contract.Assume(instruction.Operand1 is Instruction); var operand1 = (Instruction)instruction.Operand1; var operand = Simplify(operand1, mappings, canonicalizer); var compileTimeConstant = mappings.GetCompileTimeConstantValueFor(operand); if (compileTimeConstant != null) { var constantResult = Evaluator.Evaluate(instruction.Operation, compileTimeConstant); if (constantResult != null) { return(canonicalizer.GetAsCanonicalizedLoadConstant(constantResult, instruction)); } } switch (operation.OperationCode) { case OperationCode.Neg: if (operand.Operation.OperationCode == OperationCode.Neg) { Contract.Assume(operand.Operand1 is Instruction); return((Instruction)operand.Operand1); } //TODO: if the operand is a binary operation with arithmetic operands where one of them is a Neg //distribute the neg over the binary operation, if doing so is safe w.r.t. overflow. break; case OperationCode.Not: var simplerInverse = TryToGetSimplerLogicalInverse(operand); if (simplerInverse != null) { return(simplerInverse); } if (operand != operand1) { var operation1 = operand1.Operation; switch (operation1.OperationCode) { case OperationCode.Bne_Un: case OperationCode.Bne_Un_S: case OperationCode.Beq: case OperationCode.Beq_S: OperationCode newOpcode = GetInverse(operation1.OperationCode, operand1.Type.TypeCode == PrimitiveTypeCode.Float32 || operand1.Type.TypeCode == PrimitiveTypeCode.Float64); return(new Instruction() { Operation = new Operation() { OperationCode = newOpcode, Offset = operation.Offset, Location = operation.Location }, Operand1 = operand1.Operand1, Operand2 = operand1.Operand2 }); } } return(new Instruction() { Operation = operation, Operand1 = operand }); } return(instruction); }
/// <summary> /// Uses Arithmetic and Boolean laws to simplify expressions. /// </summary> /// <typeparam name="Instruction"></typeparam> /// <param name="instruction"></param> /// <param name="mappings"></param> /// <param name="canonicalizer"></param> /// <returns></returns> internal static Instruction SimplifyBinary <Instruction>(Instruction instruction, ValueMappings <Instruction> mappings, ExpressionCanonicalizer <Instruction> canonicalizer) where Instruction : Microsoft.Cci.Analysis.Instruction, new() { Contract.Requires(instruction != null); Contract.Requires(mappings != null); Contract.Requires(canonicalizer != null); Contract.Ensures(Contract.Result <Instruction>() != null); var operation = instruction.Operation; Contract.Assume(instruction.Operand1 is Instruction); var operand1 = (Instruction)instruction.Operand1; Contract.Assume(instruction.Operand2 is Instruction); var operand2 = (Instruction)instruction.Operand2; IMetadataConstant constantResult = null; var compileTimeConstant1 = mappings.GetCompileTimeConstantValueFor(operand1); var compileTimeConstant2 = mappings.GetCompileTimeConstantValueFor(operand2); if (compileTimeConstant1 != null) { if (compileTimeConstant2 != null) { constantResult = Evaluator.Evaluate(instruction.Operation, compileTimeConstant1, compileTimeConstant2); } else { constantResult = Evaluator.Evaluate(instruction.Operation, compileTimeConstant1, operand2, mappings); } } else if (compileTimeConstant2 != null) { constantResult = Evaluator.Evaluate(instruction.Operation, operand1, compileTimeConstant2, mappings); } else { constantResult = Evaluator.Evaluate(instruction.Operation, operand1, operand2, mappings); } if (constantResult != null) { return(canonicalizer.GetAsCanonicalizedLoadConstant(constantResult, instruction)); } //If we get here, the instruction does not simplify to a constant, but it could still simplify to a simpler expression. bool operand1IsZero = compileTimeConstant1 != null && MetadataExpressionHelper.IsIntegralZero(compileTimeConstant1); bool operand1IsOne = compileTimeConstant1 != null && (operand1IsZero ? false : MetadataExpressionHelper.IsIntegralOne(compileTimeConstant1)); bool operand1IsMinusOne = compileTimeConstant1 != null && ((operand1IsZero || operand1IsOne) ? false : MetadataExpressionHelper.IsIntegralMinusOne(compileTimeConstant1)); bool operand2IsZero = compileTimeConstant2 != null && MetadataExpressionHelper.IsIntegralZero(compileTimeConstant2); bool operand2IsOne = compileTimeConstant2 != null && (operand1IsZero ? false : MetadataExpressionHelper.IsIntegralOne(compileTimeConstant2)); bool operand2IsMinusOne = compileTimeConstant2 != null && ((operand2IsZero || operand2IsOne) ? false : MetadataExpressionHelper.IsIntegralMinusOne(compileTimeConstant2)); operand1 = Simplify(operand1, mappings, canonicalizer); operand2 = Simplify(operand2, mappings, canonicalizer); switch (operation.OperationCode) { case OperationCode.Add: case OperationCode.Add_Ovf: case OperationCode.Add_Ovf_Un: if (operand1IsZero) { return(operand2); } if (operand2IsZero) { return(operand1); } //TODO: factor out common mults/divs/etc (subject to overflow checks). break; case OperationCode.And: if (operand1IsZero) { return(operand1); } if (operand2IsZero) { return(operand2); } if (operand1IsMinusOne) { return(operand2); } if (operand2IsMinusOne) { return(operand1); } if (operand1.Operation.OperationCode == OperationCode.Not && operand2.Operation.OperationCode == OperationCode.Not) { var opnd11 = operand1.Operand1 as Instruction; var opnd21 = operand2.Operand1 as Instruction; Contract.Assume(opnd11 != null && opnd21 != null); var or = new Operation() { OperationCode = OperationCode.Or, Location = operation.Location, Offset = operation.Offset }; var orInst = new Instruction() { Operation = or, Operand1 = opnd11, Operand2 = opnd21, Type = instruction.Type }; var not = new Operation { OperationCode = OperationCode.Not, Location = operation.Location, Offset = operation.Offset }; return(new Instruction() { Operation = not, Operand1 = orInst, Type = instruction.Type }); } break; case OperationCode.Ceq: //If one of the operands is const 0 and the other is a boolean expression, invert the boolean expression if (operand2IsZero && operand1.Type.TypeCode == PrimitiveTypeCode.Boolean) { var not = new Operation() { Location = instruction.Operation.Location, Offset = instruction.Operation.Offset, OperationCode = OperationCode.Not }; instruction = new Instruction() { Operation = not, Operand1 = operand1, Type = instruction.Type }; return(SimplifyUnary(instruction, mappings, canonicalizer)); } else if (operand1IsZero && operand2.Type.TypeCode == PrimitiveTypeCode.Boolean) { var not = new Operation() { Location = instruction.Operation.Location, Offset = instruction.Operation.Offset, OperationCode = OperationCode.Not }; instruction = new Instruction() { Operation = not, Operand1 = operand2, Type = instruction.Type }; return(SimplifyUnary(instruction, mappings, canonicalizer)); } else { operation = new Operation() { Location = instruction.Operation.Location, Offset = instruction.Operation.Offset, OperationCode = OperationCode.Beq }; } break; case OperationCode.Cgt: operation = new Operation() { Location = instruction.Operation.Location, Offset = instruction.Operation.Offset, OperationCode = OperationCode.Bgt }; break; case OperationCode.Cgt_Un: operation = new Operation() { Location = instruction.Operation.Location, Offset = instruction.Operation.Offset, OperationCode = OperationCode.Bgt_Un }; break; case OperationCode.Clt: operation = new Operation() { Location = instruction.Operation.Location, Offset = instruction.Operation.Offset, OperationCode = OperationCode.Blt }; break; case OperationCode.Clt_Un: operation = new Operation() { Location = instruction.Operation.Location, Offset = instruction.Operation.Offset, OperationCode = OperationCode.Blt_Un }; break; case OperationCode.Div: case OperationCode.Div_Un: if (operand2IsOne) { return(operand1); } break; case OperationCode.Mul: case OperationCode.Mul_Ovf: case OperationCode.Mul_Ovf_Un: if (operand1IsOne) { return(operand2); } if (operand2IsOne) { return(operand1); } break; case OperationCode.Or: if (operand1IsZero) { return(operand2); } if (operand2IsZero) { return(operand1); } if (operand1.Operation.OperationCode == OperationCode.Not && operand2.Operation.OperationCode == OperationCode.Not) { var opnd11 = operand1.Operand1 as Instruction; var opnd21 = operand2.Operand1 as Instruction; Contract.Assume(opnd11 != null && opnd21 != null); var and = new Operation() { OperationCode = OperationCode.And, Location = operation.Location, Offset = operation.Offset }; var andInst = new Instruction() { Operation = and, Operand1 = opnd11, Operand2 = opnd21, Type = instruction.Type }; var not = new Operation { OperationCode = OperationCode.Not, Location = operation.Location, Offset = operation.Offset }; return(new Instruction() { Operation = not, Operand1 = andInst, Type = instruction.Type }); } if (operand1.Operand1 == operand2.Operand1 && operand1.Operand2 == operand2.Operand2 && operand1.Operation.OperationCode != operand2.Operation.OperationCode && operand2.Operand1 != null && operand1.Operation.OperationCode == GetInverse(operand2.Operation.OperationCode, operand2.Operand1.Type.TypeCode == PrimitiveTypeCode.Float32 || operand2.Operand1.Type.TypeCode == PrimitiveTypeCode.Float64)) { return(canonicalizer.GetAsCanonicalizedLoadConstant(new MetadataConstant() { Value = true, Type = instruction.Type }, instruction)); } break; case OperationCode.Rem: case OperationCode.Rem_Un: break; case OperationCode.Shl: case OperationCode.Shr: case OperationCode.Shr_Un: if (operand2IsZero) { return(operand1); } break; case OperationCode.Sub: case OperationCode.Sub_Ovf: case OperationCode.Sub_Ovf_Un: if (operand2IsZero) { return(operand1); } break; case OperationCode.Xor: break; case OperationCode.Beq: case OperationCode.Beq_S: if (operand1IsZero && operand2.Type.TypeCode == PrimitiveTypeCode.Boolean) { var operand2inv = TryToGetSimplerLogicalInverse(operand2); if (operand2inv != null) { return(operand2inv); } } else if (operand2IsZero && operand1.Type.TypeCode == PrimitiveTypeCode.Boolean) { var operand1inv = TryToGetSimplerLogicalInverse(operand1); if (operand1inv != null) { return(operand1inv); } } goto case OperationCode.Bge_S; case OperationCode.Bne_Un: case OperationCode.Bne_Un_S: if (operand1IsZero && operand2.Type.TypeCode == PrimitiveTypeCode.Boolean) { return(operand2); } if (operand2IsZero && operand1.Type.TypeCode == PrimitiveTypeCode.Boolean) { return(operand1); } goto case OperationCode.Bge_S; case OperationCode.Bge_S: case OperationCode.Bge_Un_S: case OperationCode.Bgt_S: case OperationCode.Bgt_Un_S: case OperationCode.Ble_S: case OperationCode.Ble_Un_S: case OperationCode.Blt_S: case OperationCode.Blt_Un_S: operation = new Operation() { Location = operation.Location, Offset = operation.Offset, OperationCode = LongVersionOf(operation.OperationCode), Value = operation.Value }; break; } if (operation != instruction.Operation || operand1 != instruction.Operand1 || operand2 != instruction.Operand2) { return new Instruction() { Operation = operation, Operand1 = operand1, Operand2 = operand2, Type = instruction.Type } } ; return(instruction); }