@@ -62,12 +62,11 @@ abstract class TranslatedExpr extends TranslatedElement {
6262 /**
6363 * Holds if the result of this `TranslatedExpr` is a glvalue.
6464 */
65- private predicate isResultGLValue ( ) {
65+ predicate isResultGLValue ( ) {
66+ // This implementation is overridden in `TranslatedCoreExpr` to mark them
67+ // as glvalues if they have loads on them. It's not overridden in
68+ // `TranslatedResultCopy` since result copies never have loads.
6669 expr .isGLValueCategory ( )
67- or
68- // If this TranslatedExpr doesn't produce the result, then it must represent
69- // a glvalue that is then loaded by a TranslatedLoad.
70- not producesExprResult ( )
7170 }
7271
7372 final override Locatable getAST ( ) { result = expr }
@@ -96,14 +95,28 @@ abstract class TranslatedExpr extends TranslatedElement {
9695abstract class TranslatedCoreExpr extends TranslatedExpr {
9796 final override string toString ( ) { result = expr .toString ( ) }
9897
98+ /**
99+ * Holds if the result of this `TranslatedExpr` is a glvalue.
100+ */
101+ override predicate isResultGLValue ( ) {
102+ super .isResultGLValue ( )
103+ or
104+ // If this TranslatedExpr doesn't produce the result, then it must represent
105+ // a glvalue that is then loaded by a TranslatedLoad.
106+ hasLoad ( )
107+ }
108+
109+ final predicate hasLoad ( ) {
110+ expr .hasLValueToRValueConversion ( ) and
111+ not ignoreLoad ( expr )
112+ }
113+
99114 final override predicate producesExprResult ( ) {
100115 // If there's no load, then this is the only TranslatedExpr for this
101116 // expression.
102- not expr .hasLValueToRValueConversion ( )
103- or
104- // If we're supposed to ignore the load on this expression, then this
105- // is the only TranslatedExpr.
106- ignoreLoad ( expr )
117+ not hasLoad ( ) and
118+ // If there's a result copy, then this expression's result is the copy.
119+ not exprNeedsCopyIfNotLoaded ( expr )
107120 }
108121}
109122
@@ -288,6 +301,48 @@ class TranslatedLoad extends TranslatedExpr, TTranslatedLoad {
288301 private TranslatedCoreExpr getOperand ( ) { result .getExpr ( ) = expr }
289302}
290303
304+ /**
305+ * IR translation of an implicit lvalue-to-rvalue conversion on the result of
306+ * an expression.
307+ */
308+ class TranslatedResultCopy extends TranslatedExpr , TTranslatedResultCopy {
309+ TranslatedResultCopy ( ) { this = TTranslatedResultCopy ( expr ) }
310+
311+ override string toString ( ) { result = "Result of " + expr .toString ( ) }
312+
313+ override Instruction getFirstInstruction ( ) { result = getOperand ( ) .getFirstInstruction ( ) }
314+
315+ override TranslatedElement getChild ( int id ) { id = 0 and result = getOperand ( ) }
316+
317+ override predicate hasInstruction ( Opcode opcode , InstructionTag tag , CppType resultType ) {
318+ tag = ResultCopyTag ( ) and
319+ opcode instanceof Opcode:: CopyValue and
320+ resultType = getOperand ( ) .getResultType ( )
321+ }
322+
323+ override Instruction getInstructionSuccessor ( InstructionTag tag , EdgeKind kind ) {
324+ tag = ResultCopyTag ( ) and
325+ result = getParent ( ) .getChildSuccessor ( this ) and
326+ kind instanceof GotoEdge
327+ }
328+
329+ override Instruction getChildSuccessor ( TranslatedElement child ) {
330+ child = getOperand ( ) and result = getInstruction ( ResultCopyTag ( ) )
331+ }
332+
333+ override Instruction getResult ( ) { result = getInstruction ( ResultCopyTag ( ) ) }
334+
335+ override Instruction getInstructionOperand ( InstructionTag tag , OperandTag operandTag ) {
336+ tag = ResultCopyTag ( ) and
337+ operandTag instanceof UnaryOperandTag and
338+ result = getOperand ( ) .getResult ( )
339+ }
340+
341+ final override predicate producesExprResult ( ) { any ( ) }
342+
343+ private TranslatedCoreExpr getOperand ( ) { result .getExpr ( ) = expr }
344+ }
345+
291346class TranslatedCommaExpr extends TranslatedNonConstantExpr {
292347 override CommaExpr expr ;
293348
@@ -2403,6 +2458,58 @@ class TranslatedErrorExpr extends TranslatedSingleInstructionExpr {
24032458 final override Opcode getOpcode ( ) { result instanceof Opcode:: Error }
24042459}
24052460
2461+ /**
2462+ * Holds if the translation of `expr` will not directly generate any
2463+ * `Instruction` for use as result. For such instructions we can synthesize a
2464+ * `CopyValue` instruction to ensure that there is a 1-to-1 mapping between
2465+ * expressions and result-bearing instructions.
2466+ */
2467+ // This should ideally be a dispatch predicate on TranslatedNonConstantExpr,
2468+ // but it doesn't look monotonic to QL.
2469+ predicate exprNeedsCopyIfNotLoaded ( Expr expr ) {
2470+ (
2471+ expr instanceof AssignExpr
2472+ or
2473+ expr instanceof AssignOperation and
2474+ not expr .isPRValueCategory ( ) // is C++
2475+ or
2476+ expr instanceof PrefixCrementOperation and
2477+ not expr .isPRValueCategory ( ) // is C++
2478+ or
2479+ expr instanceof PointerDereferenceExpr
2480+ or
2481+ expr instanceof AddressOfExpr
2482+ or
2483+ expr instanceof BuiltInOperationBuiltInAddressOf
2484+ or
2485+ // No case for ParenthesisExpr to avoid getting too many instructions
2486+ expr instanceof ReferenceDereferenceExpr
2487+ or
2488+ expr instanceof ReferenceToExpr
2489+ or
2490+ expr instanceof CommaExpr
2491+ or
2492+ expr instanceof ConditionDeclExpr
2493+ // TODO: simplify TranslatedStmtExpr too
2494+ ) and
2495+ not exprImmediatelyDiscarded ( expr )
2496+ }
2497+
2498+ /**
2499+ * Holds if `expr` is immediately discarded. Such expressions do not need a
2500+ * `CopyValue` because it's unlikely that anyone is interested in their value.
2501+ */
2502+ private predicate exprImmediatelyDiscarded ( Expr expr ) {
2503+ exists ( ExprStmt s |
2504+ s = expr .getParent ( ) and
2505+ not exists ( StmtExpr se | s = se .getStmt ( ) .( Block ) .getLastStmt ( ) )
2506+ )
2507+ or
2508+ exists ( CommaExpr c | c .getLeftOperand ( ) = expr )
2509+ or
2510+ exists ( ForStmt for | for .getUpdate ( ) = expr )
2511+ }
2512+
24062513/**
24072514 * The IR translation of an `__assume` expression. We currently translate these as `NoOp`. In the
24082515 * future, we will probably want to do something better. At a minimum, we can model `__assume(0)` as
0 commit comments