|
| 1 | +/** |
| 2 | + * Provides the module `Ssa` for working with static single assignment (SSA) form. |
| 3 | + */ |
| 4 | + |
| 5 | +/** |
| 6 | + * Provides classes for working with static single assignment (SSA) form. |
| 7 | + */ |
| 8 | +module Ssa { |
| 9 | + private import semmle.code.powershell.Cfg |
| 10 | + private import powershell |
| 11 | + private import internal.SsaImpl as SsaImpl |
| 12 | + private import CfgNodes::ExprNodes |
| 13 | + |
| 14 | + /** A static single assignment (SSA) definition. */ |
| 15 | + class Definition extends SsaImpl::Definition { |
| 16 | + /** |
| 17 | + * Gets the control flow node of this SSA definition, if any. Phi nodes are |
| 18 | + * examples of SSA definitions without a control flow node, as they are |
| 19 | + * modeled at index `-1` in the relevant basic block. |
| 20 | + */ |
| 21 | + final CfgNode getControlFlowNode() { |
| 22 | + exists(BasicBlock bb, int i | this.definesAt(_, bb, i) | result = bb.getNode(i)) |
| 23 | + } |
| 24 | + |
| 25 | + /** Gets a control-flow node that reads the value of this SSA definition. */ |
| 26 | + final VarReadAccessCfgNode getARead() { result = SsaImpl::getARead(this) } |
| 27 | + |
| 28 | + /** |
| 29 | + * Gets a first control-flow node that reads the value of this SSA definition. |
| 30 | + * That is, a read that can be reached from this definition without passing |
| 31 | + * through other reads. |
| 32 | + */ |
| 33 | + final VarReadAccessCfgNode getAFirstRead() { SsaImpl::firstRead(this, result) } |
| 34 | + |
| 35 | + /** |
| 36 | + * Gets a last control-flow node that reads the value of this SSA definition. |
| 37 | + * That is, a read that can reach the end of the enclosing CFG scope, or another |
| 38 | + * SSA definition for the source variable, without passing through any other read. |
| 39 | + */ |
| 40 | + final VarReadAccessCfgNode getALastRead() { SsaImpl::lastRead(this, result) } |
| 41 | + |
| 42 | + /** |
| 43 | + * Holds if `read1` and `read2` are adjacent reads of this SSA definition. |
| 44 | + * That is, `read2` can be reached from `read1` without passing through |
| 45 | + * another read. |
| 46 | + */ |
| 47 | + final predicate hasAdjacentReads( |
| 48 | + VarReadAccessCfgNode read1, VarReadAccessCfgNode read2 |
| 49 | + ) { |
| 50 | + SsaImpl::adjacentReadPair(this, read1, read2) |
| 51 | + } |
| 52 | + |
| 53 | + /** |
| 54 | + * Gets an SSA definition whose value can flow to this one in one step. This |
| 55 | + * includes inputs to phi nodes and the prior definitions of uncertain writes. |
| 56 | + */ |
| 57 | + private Definition getAPhiInputOrPriorDefinition() { result = this.(PhiNode).getAnInput() } |
| 58 | + |
| 59 | + /** |
| 60 | + * Gets a definition that ultimately defines this SSA definition and is |
| 61 | + * not itself a phi node. |
| 62 | + */ |
| 63 | + final Definition getAnUltimateDefinition() { |
| 64 | + result = this.getAPhiInputOrPriorDefinition*() and |
| 65 | + not result instanceof PhiNode |
| 66 | + } |
| 67 | + |
| 68 | + override string toString() { result = this.getControlFlowNode().toString() } |
| 69 | + |
| 70 | + /** Gets the scope of this SSA definition. */ |
| 71 | + CfgScope getScope() { result = this.getBasicBlock().getScope() } |
| 72 | + } |
| 73 | + |
| 74 | + /** |
| 75 | + * An SSA definition that corresponds to a write. |
| 76 | + */ |
| 77 | + class WriteDefinition extends Definition, SsaImpl::WriteDefinition { |
| 78 | + private VariableWriteAccessCfgNode write; |
| 79 | + |
| 80 | + WriteDefinition() { |
| 81 | + exists(BasicBlock bb, int i, Variable v | |
| 82 | + this.definesAt(v, bb, i) and |
| 83 | + SsaImpl::variableWriteActual(bb, i, v, write) |
| 84 | + ) |
| 85 | + } |
| 86 | + |
| 87 | + /** Gets the underlying write access. */ |
| 88 | + final VariableWriteAccessCfgNode getWriteAccess() { result = write } |
| 89 | + |
| 90 | + /** |
| 91 | + * Holds if this SSA definition assigns `value` to the underlying variable. |
| 92 | + */ |
| 93 | + predicate assigns(CfgNodes::StmtCfgNode value) { |
| 94 | + exists(CfgNodes::StmtNodes::AssignStmtCfgNode a, BasicBlock bb, int i | |
| 95 | + this.definesAt(_, bb, i) and |
| 96 | + a = bb.getNode(i) and |
| 97 | + value = a.getRightHandSide() |
| 98 | + ) |
| 99 | + } |
| 100 | + |
| 101 | + final override string toString() { result = write.toString() } |
| 102 | + |
| 103 | + final override Location getLocation() { result = write.getLocation() } |
| 104 | + } |
| 105 | + |
| 106 | + /** |
| 107 | + * An SSA definition inserted at the beginning of a scope to represent an |
| 108 | + * uninitialized local variable. |
| 109 | + */ |
| 110 | + class UninitializedDefinition extends Definition, SsaImpl::WriteDefinition { |
| 111 | + private Variable v; |
| 112 | + |
| 113 | + UninitializedDefinition() { |
| 114 | + exists(BasicBlock bb, int i | |
| 115 | + this.definesAt(v, bb, i) and |
| 116 | + SsaImpl::uninitializedWrite(bb, i, v) |
| 117 | + ) |
| 118 | + } |
| 119 | + |
| 120 | + final override string toString() { result = "<uninitialized> " + v } |
| 121 | + |
| 122 | + final override Location getLocation() { result = this.getBasicBlock().getLocation() } |
| 123 | + } |
| 124 | + |
| 125 | + /** A phi node. */ |
| 126 | + class PhiNode extends Definition, SsaImpl::PhiNode { |
| 127 | + /** Gets an input of this phi node. */ |
| 128 | + final Definition getAnInput() { this.hasInputFromBlock(result, _) } |
| 129 | + |
| 130 | + /** Holds if `inp` is an input to this phi node along the edge originating in `bb`. */ |
| 131 | + predicate hasInputFromBlock(Definition inp, BasicBlock bb) { |
| 132 | + inp = SsaImpl::phiHasInputFromBlock(this, bb) |
| 133 | + } |
| 134 | + |
| 135 | + override string toString() { result = "phi" } |
| 136 | + |
| 137 | + /** |
| 138 | + * The location of a phi node is the same as the location of the first node |
| 139 | + * in the basic block in which it is defined. |
| 140 | + * |
| 141 | + * Strictly speaking, the node is *before* the first node, but such a location |
| 142 | + * does not exist in the source program. |
| 143 | + */ |
| 144 | + final override Location getLocation() { |
| 145 | + result = this.getBasicBlock().getFirstNode().getLocation() |
| 146 | + } |
| 147 | + } |
| 148 | +} |
0 commit comments