Skip to content

Commit a75f195

Browse files
committed
C++: Several readability fixes:
1. Added lots of QLDoc explanation about the role of StoreNodeOperand. 2. Renamed '{StoreNode,ReadNode}.getAPredecessor' to 'getInner' and '{StoreNode,ReadNode}.getASuccessor' to 'getOuter'. 3. Be more explicit about which type of 'StoreNode' is used in various places.
1 parent f334201 commit a75f195

File tree

3 files changed

+79
-35
lines changed

3 files changed

+79
-35
lines changed

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ predicate jumpStep(Node n1, Node n2) { none() }
187187
* Thus, `node2` references an object with a field `f` that contains the
188188
* value of `node1`.
189189
*/
190-
predicate storeStep(StoreNode node1, FieldContent f, StoreNode node2) {
190+
predicate storeStep(StoreNodeInstr node1, FieldContent f, StoreNodeInstr node2) {
191191
exists(FieldAddressInstruction fai |
192192
node1.getInstruction() = fai and
193193
node2.getInstruction() = fai.getObjectAddress() and

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll

Lines changed: 72 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -206,21 +206,15 @@ class OperandNode extends Node, TOperandNode {
206206
* A `StoreNode` is a node that has been (or is about to be) the
207207
* source or target of a `storeStep`.
208208
*/
209-
abstract class StoreNode extends Node {
210-
/** Gets the underlying instruction, if any. */
211-
Instruction getInstruction() { none() }
212-
213-
/** Gets the underlying operand, if any. */
214-
Operand getOperand() { none() }
215-
209+
abstract private class StoreNode extends Node {
216210
/** Holds if this node should receive flow from `addr`. */
217211
abstract predicate flowInto(Instruction addr);
218212

219213
override Declaration getEnclosingCallable() { result = this.getFunction() }
220214

221215
/** Holds if this `StoreNode` is the root of the address computation used by a store operation. */
222216
predicate isTerminal() {
223-
not exists(this.getAPredecessor()) and
217+
not exists(this.getInner()) and
224218
not storeStep(this, _, _)
225219
}
226220

@@ -233,20 +227,21 @@ abstract class StoreNode extends Node {
233227
/**
234228
* Gets the `StoreNode` that computes the address used by this `StoreNode`.
235229
*/
236-
abstract StoreNode getAPredecessor();
230+
abstract StoreNode getInner();
237231

238-
/** The inverse of `StoreNode.getAPredecessor`. */
239-
final StoreNode getASuccessor() { result.getAPredecessor() = this }
232+
/** The inverse of `StoreNode.getInner`. */
233+
final StoreNode getOuter() { result.getInner() = this }
240234
}
241235

242-
private class StoreNodeInstr extends StoreNode, TStoreNodeInstr {
236+
class StoreNodeInstr extends StoreNode, TStoreNodeInstr {
243237
Instruction instr;
244238

245239
StoreNodeInstr() { this = TStoreNodeInstr(instr) }
246240

247241
override predicate flowInto(Instruction addr) { this.getInstruction() = addr }
248242

249-
override Instruction getInstruction() { result = instr }
243+
/** Gets the underlying instruction. */
244+
Instruction getInstruction() { result = instr }
250245

251246
override Function getFunction() { result = this.getInstruction().getEnclosingFunction() }
252247

@@ -262,19 +257,45 @@ private class StoreNodeInstr extends StoreNode, TStoreNodeInstr {
262257
Ssa::explicitWrite(_, result, this.getInstruction())
263258
}
264259

265-
override StoreNode getAPredecessor() {
260+
override StoreNodeInstr getInner() {
266261
Ssa::addressFlow(result.getInstruction(), this.getInstruction())
267262
}
268263
}
269264

270-
private class StoreNodeOperand extends StoreNode, TStoreNodeOperand {
265+
/**
266+
* To avoid having `PostUpdateNode`s with multiple pre-update nodes (which can cause performance
267+
* problems) we attach the `PostUpdateNode` that represent output arguments to an operand instead of
268+
* an instruction.
269+
*
270+
* To see why we need this, consider the expression `b->set(new C())`. The IR of this expression looks
271+
* like (simplified):
272+
* ```
273+
* r1(glval<unknown>) = FunctionAddress[set] :
274+
* r2(glval<unknown>) = FunctionAddress[operator new] :
275+
* r3(unsigned long) = Constant[8] :
276+
* r4(void *) = Call[operator new] : func:r2, 0:r3
277+
* r5(C *) = Convert : r4
278+
* r6(glval<unknown>) = FunctionAddress[C] :
279+
* v1(void) = Call[C] : func:r6, this:r5
280+
* v2(void) = Call[set] : func:r1, this:r0, 0:r5
281+
* ```
282+
*
283+
* Notice that both the call to `C` and the call to `set` will have an argument that is the
284+
* result of calling `operator new` (i.e., `r4`). If we only have `PostUpdateNode`s that are
285+
* instructions, both `PostUpdateNode`s would have `r4` as their pre-update node.
286+
*
287+
* We avoid this issue by having a `PostUpdateNode` for each argument, and let the pre-update node of
288+
* each `PostUpdateNode` be the argument _operand_, instead of the defining instruction.
289+
*/
290+
class StoreNodeOperand extends StoreNode, TStoreNodeOperand {
271291
ArgumentOperand operand;
272292

273293
StoreNodeOperand() { this = TStoreNodeOperand(operand) }
274294

275295
override predicate flowInto(Instruction addr) { this.getOperand().getDef() = addr }
276296

277-
override Operand getOperand() { result = operand }
297+
/** Gets the underlying operand. */
298+
Operand getOperand() { result = operand }
278299

279300
override Function getFunction() { result = operand.getDef().getEnclosingFunction() }
280301

@@ -288,7 +309,32 @@ private class StoreNodeOperand extends StoreNode, TStoreNodeOperand {
288309
Ssa::explicitWrite(_, result, operand.getDef())
289310
}
290311

291-
override StoreNode getAPredecessor() { operand.getDef() = result.getInstruction() }
312+
/**
313+
* The result of `StoreNodeOperand.getInner` is the `StoreNodeInstr` representation the instruction
314+
* that defines this operand. This means the graph of `getInner` looks like this:
315+
* ```
316+
* I---I---I
317+
* \ \ \
318+
* O O O
319+
* ```
320+
* where each `StoreNodeOperand` "hooks" into the chain computed by `StoreNodeInstr.getInner`.
321+
* This means that the chain of `getInner` calls on the argument `&o.f` on an expression
322+
* like `func(&o.f)` is:
323+
* ```
324+
* r4---r3---r2
325+
* \
326+
* 0:r4
327+
* ```
328+
* where the IR for `func(&o.f)` looks like (simplified):
329+
* ```
330+
* r1(glval<unknown>) = FunctionAddress[func] :
331+
* r2(glval<O>) = VariableAddress[o] :
332+
* r3(glval<int>) = FieldAddress[f] : r2
333+
* r4(int *) = CopyValue : r3
334+
* v1(void) = Call[func] : func:r1, 0:r4
335+
* ```
336+
*/
337+
override StoreNodeInstr getInner() { operand.getDef() = result.getInstruction() }
292338
}
293339

294340
/**
@@ -326,22 +372,20 @@ class ReadNode extends Node, TReadNode {
326372
* Gets a read node with an underlying instruction that is used by this
327373
* underlying instruction to compute an address of a load instruction.
328374
*/
329-
final ReadNode getAPredecessor() {
330-
Ssa::addressFlow(result.getInstruction(), this.getInstruction())
331-
}
375+
final ReadNode getInner() { Ssa::addressFlow(result.getInstruction(), this.getInstruction()) }
332376

333-
/** The inverse of `ReadNode.getAPredecessor`. */
334-
final ReadNode getASuccessor() { result.getAPredecessor() = this }
377+
/** The inverse of `ReadNode.getInner`. */
378+
final ReadNode getOuter() { result.getInner() = this }
335379

336380
/** Holds if this read node computes a value that will not be used for any future read nodes. */
337381
final predicate isTerminal() {
338-
not exists(this.getASuccessor()) and
382+
not exists(this.getOuter()) and
339383
not readStep(this, _, _)
340384
}
341385

342386
/** Holds if this read node computes a value that has not yet been used for any read operations. */
343387
final predicate isInitial() {
344-
not exists(this.getAPredecessor()) and
388+
not exists(this.getInner()) and
345389
not readStep(_, _, this)
346390
}
347391
}
@@ -787,7 +831,7 @@ private module ReadNodeFlow {
787831
/** Holds if the read node `nodeTo` should receive flow from the read node `nodeFrom`. */
788832
predicate flowThrough(ReadNode nodeFrom, ReadNode nodeTo) {
789833
not readStep(nodeFrom, _, _) and
790-
nodeFrom.getASuccessor() = nodeTo
834+
nodeFrom.getOuter() = nodeTo
791835
}
792836

793837
/**
@@ -800,7 +844,7 @@ private module ReadNodeFlow {
800844
// Use-use flow to another use of the same variable instruction
801845
Ssa::ssaFlow(nFrom, nodeTo)
802846
or
803-
not exists(nFrom.getAPredecessor()) and
847+
not exists(nFrom.getInner()) and
804848
exists(Node store |
805849
Ssa::explicitWrite(_, store.asInstruction(), nFrom.getInstruction()) and
806850
Ssa::ssaFlow(store, nodeTo)
@@ -833,15 +877,15 @@ private module StoreNodeFlow {
833877
predicate flowThrough(StoreNode nFrom, StoreNode nodeTo) {
834878
// Flow through a post update node that doesn't need a store step.
835879
not storeStep(nFrom, _, _) and
836-
nodeTo.getASuccessor() = nFrom
880+
nodeTo.getOuter() = nFrom
837881
}
838882

839883
/**
840884
* Holds if flow should leave the store node `nodeFrom` and enter the node `nodeTo`.
841885
* This happens because we have traversed an entire chain of field dereferences
842886
* after a store operation.
843887
*/
844-
predicate flowOutOf(StoreNode nFrom, Node nodeTo) {
888+
predicate flowOutOf(StoreNodeInstr nFrom, Node nodeTo) {
845889
nFrom.isTerminal() and
846890
Ssa::ssaFlow(nFrom, nodeTo)
847891
}

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ private module Cached {
327327
)
328328
}
329329

330-
private predicate fromStoreNode(StoreNode nodeFrom, Node nodeTo) {
330+
private predicate fromStoreNode(StoreNodeInstr nodeFrom, Node nodeTo) {
331331
// Def-use flow from a `StoreNode`.
332332
exists(IRBlock bb1, int i1, IRBlock bb2, int i2, Def def, Use use |
333333
nodeFrom.isTerminal() and
@@ -388,10 +388,10 @@ private module Cached {
388388
def.hasRankInBlock(block, rnk) and
389389
nodeTo.hasInputAtRankInBlock(block, rnk)
390390
|
391-
exists(StoreNode store |
392-
store = nodeFrom and
393-
store.isTerminal() and
394-
def.getInstruction() = store.getStoreInstruction()
391+
exists(StoreNodeInstr storeNode |
392+
storeNode = nodeFrom and
393+
storeNode.isTerminal() and
394+
def.getInstruction() = storeNode.getStoreInstruction()
395395
)
396396
or
397397
def.getInstruction() = nodeFrom.asInstruction()
@@ -466,7 +466,7 @@ private module Cached {
466466
operand.getDef() = readNode.getInstruction()
467467
)
468468
or
469-
exists(StoreNode storeNode, Instruction def |
469+
exists(StoreNodeInstr storeNode, Instruction def |
470470
storeNode = nTo and
471471
def = operand.getDef()
472472
|

0 commit comments

Comments
 (0)