@@ -18,7 +18,8 @@ private newtype TIRDataFlowNode =
1818 TVariableNode ( Variable var ) or
1919 // `FieldNodes` are used as targets of certain `storeStep`s to implement handling of stores to
2020 // nested structs.
21- TFieldNode ( FieldAddressInstruction field )
21+ TFieldNode ( FieldAddressInstruction field ) or
22+ TPartialDefinitionNode ( PartialDefinition pd )
2223
2324/**
2425 * A node in a data flow graph.
@@ -258,6 +259,23 @@ class FieldNode extends Node, TFieldNode {
258259
259260 override string toString ( ) { result = this .getField ( ) .toString ( ) }
260261}
262+
263+ /**
264+ * INTERNAL: do not use. A `FieldNode` represents the state of an object after modifying one
265+ * of its fields.
266+ */
267+ class PostUpdateFieldNode extends PartialDefinition {
268+ override FieldNode node ;
269+
270+ override FieldNode getPreUpdateNode ( ) { result = node }
271+
272+ override Expr getDefinedExpr ( ) {
273+ result = node .getFieldInstruction ( ) .getObjectAddress ( ) .getUnconvertedResultExpression ( )
274+ }
275+
276+ Field getField ( ) { result = node .getField ( ) }
277+ }
278+
261279/**
262280 * An expression, viewed as a node in a data flow graph.
263281 */
@@ -395,11 +413,37 @@ deprecated class UninitializedNode extends Node {
395413 * This class exists to match the interface used by Java. There are currently no non-abstract
396414 * classes that extend it. When we implement field flow, we can revisit this.
397415 */
398- abstract class PostUpdateNode extends InstructionNode {
416+ abstract class PostUpdateNode extends Node {
399417 /**
400418 * Gets the node before the state update.
401419 */
402420 abstract Node getPreUpdateNode ( ) ;
421+
422+ override Function getFunction ( ) { result = getPreUpdateNode ( ) .getFunction ( ) }
423+
424+ override IRType getType ( ) { result = getPreUpdateNode ( ) .getType ( ) }
425+
426+ override Location getLocation ( ) { result = getPreUpdateNode ( ) .getLocation ( ) }
427+ }
428+
429+ private newtype TPartialDefinition =
430+ MkPartialDefinition ( Node node ) {
431+ isPointerStoreNode ( node , _, _) or
432+ isArrayStoreNode ( node , _, _) or
433+ node instanceof FieldNode
434+ }
435+
436+ /** INTERNAL: do not use. A partial definition of a node. */
437+ abstract class PartialDefinition extends TPartialDefinition {
438+ Node node ;
439+
440+ PartialDefinition ( ) { this = MkPartialDefinition ( node ) }
441+
442+ abstract Node getPreUpdateNode ( ) ;
443+
444+ abstract Expr getDefinedExpr ( ) ;
445+
446+ string toString ( ) { result = node .toString ( ) + " [partial definition]" }
403447}
404448
405449/**
@@ -415,132 +459,73 @@ abstract class PostUpdateNode extends InstructionNode {
415459 * setY(&x); // a partial definition of the object `x`.
416460 * ```
417461 */
418- abstract private class PartialDefinitionNode extends PostUpdateNode {
419- abstract Expr getDefinedExpr ( ) ;
420- }
421-
422- private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode {
423- override ChiInstruction instr ;
424- StoreInstruction store ;
462+ class PartialDefinitionNode extends PostUpdateNode , TPartialDefinitionNode {
463+ PartialDefinition pd ;
425464
426- ExplicitFieldStoreQualifierNode ( ) {
427- not instr .isResultConflated ( ) and
428- instr .getPartial ( ) = store and
429- (
430- instr .getUpdatedInterval ( _, _) or
431- store .getDestinationAddress ( ) instanceof FieldAddressInstruction
432- )
433- }
434-
435- // By using an operand as the result of this predicate we avoid the dataflow inconsistency errors
436- // caused by having multiple nodes sharing the same pre update node. This inconsistency error can cause
437- // a tuple explosion in the big step dataflow relation since it can make many nodes be the entry node
438- // into a big step.
439- override Node getPreUpdateNode ( ) { result .asOperand ( ) = instr .getTotalOperand ( ) }
440-
441- override Expr getDefinedExpr ( ) {
442- result =
443- store
444- .getDestinationAddress ( )
445- .( FieldAddressInstruction )
446- .getObjectAddress ( )
447- .getUnconvertedResultExpression ( )
448- }
449- }
465+ PartialDefinitionNode ( ) { this = TPartialDefinitionNode ( pd ) }
450466
451- /**
452- * Not every store instruction generates a chi instruction that we can attach a PostUpdateNode to.
453- * For instance, an update to a field of a struct containing only one field. For these cases we
454- * attach the PostUpdateNode to the store instruction. There's no obvious pre update node for this case
455- * (as the entire memory is updated), so `getPreUpdateNode` is implemented as `none()`.
456- */
457- private class ExplicitSingleFieldStoreQualifierNode extends PartialDefinitionNode {
458- override StoreInstruction instr ;
467+ /** Gets the expression that is partially defined by this node, if any. */
468+ Expr getDefinedExpr ( ) { result = pd .getDefinedExpr ( ) }
459469
460- ExplicitSingleFieldStoreQualifierNode ( ) {
461- not exists ( ChiInstruction chi | chi .getPartial ( ) = instr ) and
462- // Without this condition any store would create a `PostUpdateNode`.
463- instr .getDestinationAddress ( ) instanceof FieldAddressInstruction
464- }
470+ override Node getPreUpdateNode ( ) { result = pd .getPreUpdateNode ( ) }
465471
466- override Node getPreUpdateNode ( ) { none ( ) }
472+ PartialDefinition getPartialDefinition ( ) { result = pd }
467473
468- override Expr getDefinedExpr ( ) {
469- result =
470- instr
471- .getDestinationAddress ( )
472- .( FieldAddressInstruction )
473- .getObjectAddress ( )
474- .getUnconvertedResultExpression ( )
475- }
476- }
477-
478- private FieldAddressInstruction getFieldInstruction ( Instruction instr ) {
479- result = instr or
480- result = instr .( CopyValueInstruction ) .getUnary ( )
474+ override string toString ( ) { result = getPreUpdateNode ( ) .toString ( ) + " [post update]" }
481475}
482476
483- /**
484- * The target of a `fieldStoreStepAfterArraySuppression` store step, which is used to convert
485- * an `ArrayContent` to a `FieldContent` when the `WriteSideEffect` instruction stores
486- * into a field. See the QLDoc for `suppressArrayRead` for an example of where such a conversion
487- * is inserted.
488- */
489- private class WriteSideEffectFieldStoreQualifierNode extends PartialDefinitionNode {
490- override ChiInstruction instr ;
491- WriteSideEffectInstruction write ;
492- FieldAddressInstruction field ;
493-
494- WriteSideEffectFieldStoreQualifierNode ( ) {
495- not instr .isResultConflated ( ) and
496- instr .getPartial ( ) = write and
497- field = getFieldInstruction ( write .getDestinationAddress ( ) )
498- }
499-
500- override Node getPreUpdateNode ( ) { result .asOperand ( ) = instr .getTotalOperand ( ) }
501-
502- override Expr getDefinedExpr ( ) {
503- result = field .getObjectAddress ( ) .getUnconvertedResultExpression ( )
504- }
477+ private predicate isArrayStoreNode (
478+ InstructionNode node , ChiInstruction chi , PointerAddInstruction add
479+ ) {
480+ chi = node .getInstruction ( ) and
481+ not chi .isResultConflated ( ) and
482+ exists ( StoreInstruction store |
483+ chi .getPartial ( ) = store and
484+ add = store .getDestinationAddress ( )
485+ )
505486}
506487
507488/**
508489 * The `PostUpdateNode` that is the target of a `arrayStoreStepChi` store step. The overriden
509490 * `ChiInstruction` corresponds to the instruction represented by `node2` in `arrayStoreStepChi`.
510491 */
511- private class ArrayStoreNode extends PartialDefinitionNode {
512- override ChiInstruction instr ;
492+ private class ArrayStoreNode extends PartialDefinition {
493+ override InstructionNode node ;
494+ ChiInstruction chi ;
513495 PointerAddInstruction add ;
514496
515- ArrayStoreNode ( ) {
516- not instr .isResultConflated ( ) and
517- exists ( StoreInstruction store |
518- instr .getPartial ( ) = store and
519- add = store .getDestinationAddress ( )
520- )
521- }
497+ ArrayStoreNode ( ) { isArrayStoreNode ( node , chi , add ) }
522498
523- override Node getPreUpdateNode ( ) { result .asOperand ( ) = instr .getTotalOperand ( ) }
499+ override Node getPreUpdateNode ( ) { result .asOperand ( ) = chi .getTotalOperand ( ) }
524500
525501 override Expr getDefinedExpr ( ) { result = add .getLeft ( ) .getUnconvertedResultExpression ( ) }
526502}
527503
504+ private predicate isPointerStoreNode ( InstructionNode node , ChiInstruction chi , LoadInstruction load ) {
505+ chi = node .getInstruction ( ) and
506+ not chi .isResultConflated ( ) and
507+ exists ( StoreInstruction store |
508+ chi .getPartial ( ) = store and
509+ load = store .getDestinationAddress ( ) .( CopyValueInstruction ) .getUnary ( )
510+ )
511+ }
512+
528513/**
529514 * The `PostUpdateNode` that is the target of a `arrayStoreStepChi` store step. The overriden
530515 * `ChiInstruction` corresponds to the instruction represented by `node2` in `arrayStoreStepChi`.
531516 */
532- private class PointerStoreNode extends PostUpdateNode {
533- override ChiInstruction instr ;
534-
535- PointerStoreNode ( ) {
536- not instr .isResultConflated ( ) and
537- exists ( StoreInstruction store |
538- instr .getPartial ( ) = store and
539- store .getDestinationAddress ( ) .( CopyValueInstruction ) .getUnary ( ) instanceof LoadInstruction
540- )
541- }
517+ private class PointerStoreNode extends PartialDefinition {
518+ override InstructionNode node ;
519+ ChiInstruction chi ;
520+ LoadInstruction load ;
521+
522+ PointerStoreNode ( ) { isPointerStoreNode ( node , chi , load ) }
523+
524+ override Node getPreUpdateNode ( ) { result .asOperand ( ) = chi .getTotalOperand ( ) }
542525
543- override Node getPreUpdateNode ( ) { result .asOperand ( ) = instr .getTotalOperand ( ) }
526+ override Expr getDefinedExpr ( ) {
527+ result = load .getSourceAddress ( ) .getUnconvertedResultExpression ( )
528+ }
544529}
545530
546531/**
@@ -553,7 +538,7 @@ private class PointerStoreNode extends PostUpdateNode {
553538 * returned. This node will have its `getArgument()` equal to `&x` and its
554539 * `getVariableAccess()` equal to `x`.
555540 */
556- class DefinitionByReferenceNode extends InstructionNode {
541+ class DefinitionByReferenceNode extends InstructionNode , PostUpdateNode {
557542 override WriteSideEffectInstruction instr ;
558543
559544 /** Gets the unconverted argument corresponding to this node. */
@@ -581,6 +566,22 @@ class DefinitionByReferenceNode extends InstructionNode {
581566 not exists ( instr .getPrimaryInstruction ( ) .( CallInstruction ) .getStaticCallTarget ( ) ) and
582567 result = "output argument"
583568 }
569+
570+ override Function getFunction ( ) { result = instr .getEnclosingFunction ( ) }
571+
572+ override IRType getType ( ) { result = instr .getResultIRType ( ) }
573+
574+ override Location getLocation ( ) { result = instr .getLocation ( ) }
575+
576+ // Make the read side effect's side effect operand the pre update node of this write side effect.
577+ // This ensures that we match up the parameter index of the parameter indirection's modification.
578+ override Node getPreUpdateNode ( ) {
579+ exists ( ReadSideEffectInstruction read |
580+ read .getPrimaryInstruction ( ) = instr .getPrimaryInstruction ( ) and
581+ read .getArgumentDef ( ) = instr .getDestinationAddress ( ) and
582+ result .asOperand ( ) = read .getSideEffectOperand ( )
583+ )
584+ }
584585}
585586
586587/**
@@ -673,6 +674,35 @@ Node uninitializedNode(LocalVariable v) { none() }
673674 */
674675predicate localFlowStep ( Node nodeFrom , Node nodeTo ) { simpleLocalFlowStep ( nodeFrom , nodeTo ) }
675676
677+ private predicate flowOutOfPostUpdate ( PartialDefinitionNode nodeFrom , Node nodeTo ) {
678+ // flow from the "outermost" field to the `ChiInstruction`, or `StoreInstruction`
679+ // if no `ChiInstruction` exists.
680+ exists ( AddressOperand addressOperand , PostUpdateFieldNode pd |
681+ pd = nodeFrom .getPartialDefinition ( ) and
682+ not exists ( pd .getPreUpdateNode ( ) .getObjectNode ( ) ) and
683+ pd .getPreUpdateNode ( ) .getNextNode * ( ) = getFieldNodeForFieldInstruction ( addressOperand .getDef ( ) ) and
684+ (
685+ exists ( ChiInstruction chi |
686+ nodeTo .asInstruction ( ) = chi and
687+ chi .getPartial ( ) .getAnOperand ( ) = addressOperand
688+ )
689+ or
690+ exists ( StoreInstruction store |
691+ not exists ( ChiInstruction chi | chi .getPartial ( ) = store ) and
692+ nodeTo .asInstruction ( ) = store and
693+ store .getDestinationAddressOperand ( ) = addressOperand
694+ )
695+ )
696+ )
697+ or
698+ // Note: This partial definition cannot be a `PostUpdateFieldNode` since these nodes do not have an
699+ // operand node as their pre update node.
700+ exists ( PartialDefinition pd |
701+ pd = nodeFrom .getPartialDefinition ( ) and
702+ nodeTo .asInstruction ( ) .( ChiInstruction ) .getTotalOperand ( ) = pd .getPreUpdateNode ( ) .asOperand ( )
703+ )
704+ }
705+
676706private predicate flowIntoReadNode ( Node nodeFrom , Node nodeTo ) {
677707 // flow from the "innermost" field to the load of that field.
678708 exists ( FieldNode fieldNode | nodeTo = fieldNode |
@@ -715,6 +745,8 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
715745 simpleOperandLocalFlowStep ( nodeFrom .asInstruction ( ) , nodeTo .asOperand ( ) )
716746 or
717747 flowIntoReadNode ( nodeFrom , nodeTo )
748+ or
749+ flowOutOfPostUpdate ( nodeFrom , nodeTo )
718750}
719751
720752pragma [ noinline]
0 commit comments