From 5d75b255a82d2af95b0931f92ff394623065b5b7 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 26 Feb 2026 13:02:42 +0000 Subject: [PATCH] C++: Remove IR re-evaluation. --- .../cpp/ir/dataflow/internal/ExprNodes.qll | 42 +++------- .../code/cpp/ir/dataflow/internal/SsaImpl.qll | 7 +- .../ir/dataflow/internal/SsaImplCommon.qll | 83 +++++++++--------- .../raw/internal/IRConstruction.qll | 84 +++++++++++++++++++ 4 files changed, 139 insertions(+), 77 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ExprNodes.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ExprNodes.qll index 2e4c114a522..927d2ea9028 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ExprNodes.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ExprNodes.qll @@ -7,8 +7,7 @@ private import semmle.code.cpp.ir.IR private import DataFlowUtil private import DataFlowPrivate private import DataFlowNodes -private import semmle.code.cpp.ir.implementation.raw.internal.TranslatedExpr -private import semmle.code.cpp.ir.implementation.raw.internal.InstructionTag +private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction cached private module Cached { @@ -74,17 +73,9 @@ private module Cached { // a result for `getConvertedResultExpression`. We remap this here so that // this `ConvertInstruction` maps to the result of the expression that // represents the extent. - exists(TranslatedNonConstantAllocationSize tas | - result = tas.getExtent().getExpr() and - instr = tas.getInstruction(AllocationExtentConvertTag()) - ) + result = IRConstruction::Raw::getAllocationExtentConvertExpr(instr) or - // There's no instruction that returns `ParenthesisExpr`, but some queries - // expect this - exists(TranslatedTransparentConversion ttc | - result = ttc.getExpr().(ParenthesisExpr) and - instr = ttc.getResult() - ) + result = IRConstruction::Raw::getTransparentConversionParenthesisExpr(instr) or // Certain expressions generate `CopyValueInstruction`s only when they // are needed. Examples of this include crement operations and compound @@ -113,10 +104,10 @@ private module Cached { // needed, and in that case the only value that will propagate forward in // the program is the value that's been updated. So in those cases we just // use the result of `node.asDefinition()` as the result of `node.asExpr()`. - exists(TranslatedCoreExpr tco | - tco.getInstruction(_) = instr and - tco.producesExprResult() and - result = asDefinitionImpl0(instr) + exists(StoreInstruction store | + store = instr and + IRConstruction::Raw::instructionProducesExprResult(store) and + result = asDefinitionImpl0(store) ) or // IR construction breaks an array aggregate literal `{1, 2, 3}` into a @@ -146,18 +137,9 @@ private module Cached { // For an expression such as `i += 2` we pretend that the generated // `StoreInstruction` contains the result of the expression even though // this isn't totally aligned with the C/C++ standard. - exists(TranslatedAssignOperation tao | - store = tao.getInstruction(AssignmentStoreTag()) and - result = tao.getExpr() - ) + result = IRConstruction::Raw::getAssignOperationStoreExpr(store) or - // Similarly for `i++` and `++i` we pretend that the generated - // `StoreInstruction` contains the result of the expression even though - // this isn't totally aligned with the C/C++ standard. - exists(TranslatedCrementOperation tco | - store = tco.getInstruction(CrementStoreTag()) and - result = tco.getExpr() - ) + result = IRConstruction::Raw::getCrementOperationStoreExpr(store) } /** @@ -167,11 +149,7 @@ private module Cached { */ private predicate excludeAsDefinitionResult(StoreInstruction store) { // Exclude the store to the temporary generated by a ternary expression. - exists(TranslatedConditionalExpr tce | - store = tce.getInstruction(ConditionValueFalseStoreTag()) - or - store = tce.getInstruction(ConditionValueTrueStoreTag()) - ) + IRConstruction::Raw::isConditionalExprTempStore(store) } /** diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll index bbc34a63134..f1bdd6b8c52 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll @@ -10,7 +10,7 @@ private import semmle.code.cpp.models.interfaces.PartialFlow as PartialFlow private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs as FIO private import semmle.code.cpp.ir.internal.IRCppLanguage private import semmle.code.cpp.ir.dataflow.internal.ModelUtil -private import semmle.code.cpp.ir.implementation.raw.internal.TranslatedInitialization +private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction private import DataFlowPrivate private import DataFlowNodes import SsaImplCommon @@ -439,10 +439,7 @@ private predicate sourceVariableHasBaseAndIndex(SourceVariable v, BaseSourceVari * initialize `v`. */ private Instruction getInitializationTargetAddress(IRVariable v) { - exists(TranslatedVariableInitialization init | - init.getIRVariable() = v and - result = init.getTargetAddress() - ) + result = IRConstruction::Raw::getInitializationTargetAddress(v) } /** An initial definition of an SSA variable address. */ diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll index 425657a1cf0..45a6755356b 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll @@ -10,42 +10,6 @@ private import DataFlowPrivate private import TypeFlow private import semmle.code.cpp.ir.ValueNumbering -/** - * Holds if `operand` is an operand that is not used by the dataflow library. - * Ignored operands are not recognized as uses by SSA, and they don't have a - * corresponding `(Indirect)OperandNode`. - */ -predicate ignoreOperand(Operand operand) { - operand = any(Instruction instr | ignoreInstruction(instr)).getAnOperand() or - operand = any(Instruction instr | ignoreInstruction(instr)).getAUse() or - operand instanceof MemoryOperand -} - -/** - * Holds if `instr` is an instruction that is not used by the dataflow library. - * Ignored instructions are not recognized as reads/writes by SSA, and they - * don't have a corresponding `(Indirect)InstructionNode`. - */ -predicate ignoreInstruction(Instruction instr) { - DataFlowImplCommon::forceCachingInSameStage() and - ( - instr instanceof CallSideEffectInstruction or - instr instanceof CallReadSideEffectInstruction or - instr instanceof ExitFunctionInstruction or - instr instanceof EnterFunctionInstruction or - instr instanceof WriteSideEffectInstruction or - instr instanceof PhiInstruction or - instr instanceof ReadSideEffectInstruction or - instr instanceof ChiInstruction or - instr instanceof InitializeIndirectionInstruction or - instr instanceof AliasedDefinitionInstruction or - instr instanceof AliasedUseInstruction or - instr instanceof InitializeNonLocalInstruction or - instr instanceof ReturnIndirectionInstruction or - instr instanceof UninitializedGroupInstruction - ) -} - /** * Gets the C++ type of `this` in the member function `f`. * The result is a glvalue if `isGLValue` is true, and @@ -328,10 +292,6 @@ predicate isWrite(Node0Impl value, Operand address, boolean certain) { ) } -predicate isAdditionalConversionFlow(Operand opFrom, Instruction instrTo) { - any(Indirection ind).isAdditionalConversionFlow(opFrom, instrTo) -} - newtype TBaseSourceVariable = // Each IR variable gets its own source variable TBaseIRVariable(IRVariable var) or @@ -553,6 +513,49 @@ private class BaseCallInstruction extends BaseSourceVariableInstruction, CallIns cached private module Cached { + /** + * Holds if `operand` is an operand that is not used by the dataflow library. + * Ignored operands are not recognized as uses by SSA, and they don't have a + * corresponding `(Indirect)OperandNode`. + */ + cached + predicate ignoreOperand(Operand operand) { + operand = any(Instruction instr | ignoreInstruction(instr)).getAnOperand() or + operand = any(Instruction instr | ignoreInstruction(instr)).getAUse() or + operand instanceof MemoryOperand + } + + /** + * Holds if `instr` is an instruction that is not used by the dataflow library. + * Ignored instructions are not recognized as reads/writes by SSA, and they + * don't have a corresponding `(Indirect)InstructionNode`. + */ + cached + predicate ignoreInstruction(Instruction instr) { + DataFlowImplCommon::forceCachingInSameStage() and + ( + instr instanceof CallSideEffectInstruction or + instr instanceof CallReadSideEffectInstruction or + instr instanceof ExitFunctionInstruction or + instr instanceof EnterFunctionInstruction or + instr instanceof WriteSideEffectInstruction or + instr instanceof PhiInstruction or + instr instanceof ReadSideEffectInstruction or + instr instanceof ChiInstruction or + instr instanceof InitializeIndirectionInstruction or + instr instanceof AliasedDefinitionInstruction or + instr instanceof AliasedUseInstruction or + instr instanceof InitializeNonLocalInstruction or + instr instanceof ReturnIndirectionInstruction or + instr instanceof UninitializedGroupInstruction + ) + } + + cached + predicate isAdditionalConversionFlow(Operand opFrom, Instruction instrTo) { + any(Indirection ind).isAdditionalConversionFlow(opFrom, instrTo) + } + /** * Gets the C++ type of the instruction `i`. * diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll index 594e37b668d..9e9a47a5b4f 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll @@ -15,6 +15,7 @@ private import TranslatedCall private import TranslatedStmt private import TranslatedFunction private import TranslatedGlobalVar +private import TranslatedInitialization TranslatedElement getInstructionTranslatedElement(Instruction instruction) { instruction = TRawInstruction(result, _) @@ -194,6 +195,89 @@ module Raw { Expr getInstructionUnconvertedResultExpression(Instruction instruction) { result = getInstructionConvertedResultExpression(instruction).getUnconverted() } + + /** + * Gets the expression associated with the instruction `instr` that computes + * the `Convert` instruction on the extent expression of an allocation. + */ + cached + Expr getAllocationExtentConvertExpr(Instruction instr) { + exists(TranslatedNonConstantAllocationSize tas | + instr = tas.getInstruction(AllocationExtentConvertTag()) and + result = tas.getExtent().getExpr() + ) + } + + /** + * Gets the `ParenthesisExpr` associated with a transparent conversion + * instruction, if any. + */ + cached + ParenthesisExpr getTransparentConversionParenthesisExpr(Instruction instr) { + exists(TranslatedTransparentConversion ttc | + result = ttc.getExpr() and + instr = ttc.getResult() + ) + } + + /** + * Holds if `instr` belongs to a `TranslatedCoreExpr` that produces an + * expression result. This indicates that the instruction represents a + * definition whose result should be mapped back to the expression. + */ + cached + predicate instructionProducesExprResult(Instruction instr) { + exists(TranslatedCoreExpr tco | + tco.getInstruction(_) = instr and + tco.producesExprResult() + ) + } + + /** + * Gets the expression associated with a `StoreInstruction` generated + * by an `TranslatedAssignOperation`. + */ + cached + Expr getAssignOperationStoreExpr(StoreInstruction store) { + exists(TranslatedAssignOperation tao | + store = tao.getInstruction(AssignmentStoreTag()) and + result = tao.getExpr() + ) + } + + /** + * Gets the expression associated with a `StoreInstruction` generated + * by an `TranslatedCrementOperation`. + */ + cached + Expr getCrementOperationStoreExpr(StoreInstruction store) { + exists(TranslatedCrementOperation tco | + store = tco.getInstruction(CrementStoreTag()) and + result = tco.getExpr() + ) + } + + /** + * Holds if `store` is a `StoreInstruction` that defines the temporary + * `IRVariable` generated as part of the translation of a ternary expression. + */ + cached + predicate isConditionalExprTempStore(StoreInstruction store) { + exists(TranslatedConditionalExpr tce | + store = tce.getInstruction(ConditionValueFalseStoreTag()) + or + store = tce.getInstruction(ConditionValueTrueStoreTag()) + ) + } + + /** Gets the instruction that computes the address used to initialize `v`. */ + cached + Instruction getInitializationTargetAddress(IRVariable v) { + exists(TranslatedVariableInitialization init | + init.getIRVariable() = v and + result = init.getTargetAddress() + ) + } } class TStageInstruction = TRawInstruction or TRawUnreachedInstruction;