Skip to content

Go/feature/shared ssa library #19011

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 24 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8d844e8
Switch to use-use dataflow. This will make post-update nodes easy to …
owen-mc Nov 10, 2023
06404d8
Add missing QLDoc
owen-mc Nov 28, 2023
d8002a0
Clean up code in basicLocalFlowStep
owen-mc Nov 28, 2023
1904daf
Include first step from SsaVariableCapture
owen-mc Nov 28, 2023
9d88795
Adjust SafeFormatArgumentSanitizer to use-use flow
owen-mc Nov 29, 2023
f67555a
Test result that was missing is now found
owen-mc Nov 10, 2023
03204cf
Expected changes in dataflow edges
owen-mc Nov 10, 2023
487dba3
f extra edge
owen-mc Nov 29, 2023
c040c9a
Line numbers change because 3 lines were added
owen-mc Nov 29, 2023
8b6a173
Changes in edges in .expected files
owen-mc Nov 29, 2023
ef9f740
Extra edge to captured variable
owen-mc Nov 29, 2023
ad0b836
Fix Allocation Size Overflow for use-use flow
owen-mc Nov 30, 2023
68128b3
Expected test changes (odd because post update nodes are still at the…
owen-mc Nov 30, 2023
818ebea
Optimise join order for varBlockReaches
smowton Jan 26, 2021
6c863c6
Fix IncorrectIntegerConversion for use-use flow
owen-mc Nov 30, 2023
5a09b15
accept edge changes
owen-mc Mar 6, 2025
7f5e973
Accept fixed test result
owen-mc Mar 6, 2025
c308978
Make insecure randomness test more realistic
owen-mc Mar 6, 2025
a3dbc5e
Fix TypeAssertionCheck to not block successor flow
owen-mc Mar 7, 2025
635b722
Improve SSA tests for variables in closures
owen-mc Mar 12, 2025
a15ab99
Invoke `SsaImplCommon::Make`
owen-mc Mar 1, 2025
5bc4ccf
Refactor: reorder parameters of `SsaDefinition.definesAt`
owen-mc Mar 8, 2025
521b971
Base SSA classes on shared SSA library
owen-mc Mar 10, 2025
d49f05f
Test changes
owen-mc Mar 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 42 additions & 55 deletions go/ql/lib/semmle/go/dataflow/SSA.qll
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ private predicate unresolvedIdentifier(Ident id, string name) {
/**
* An SSA variable.
*/
class SsaVariable extends TSsaDefinition {
class SsaVariable instanceof SsaDefinition {
/** Gets the source variable corresponding to this SSA variable. */
SsaSourceVariable getSourceVariable() { result = this.(SsaDefinition).getSourceVariable() }

Expand Down Expand Up @@ -107,27 +107,26 @@ class SsaVariable extends TSsaDefinition {
/**
* An SSA definition.
*/
class SsaDefinition extends TSsaDefinition {
class SsaDefinition instanceof ZZZDefinition {
/**
* Holds if this SSA definition defines `v` at index `i` in basic block `bb`.
* Phi nodes are considered to be at index `-1`, while normal variable writes
* are at the index of the control flow node they wrap.
*/
predicate definesAt(SsaSourceVariable v, BasicBlock bb, int i) {
this.(ZZZDefinition).definesAt(v, bb, i)
}

/** Gets the SSA variable defined by this definition. */
SsaVariable getVariable() { result = this }

/** Gets the source variable defined by this definition. */
abstract SsaSourceVariable getSourceVariable();
SsaSourceVariable getSourceVariable() { this.definesAt(result, _, _) }

/**
* Gets the basic block to which this definition belongs.
*/
abstract ReachableBasicBlock getBasicBlock();

/**
* INTERNAL: Use `getBasicBlock()` and `getSourceVariable()` instead.
*
* Holds if this is a definition of source variable `v` at index `idx` in basic block `bb`.
*
* Phi nodes are considered to be at index `-1`, all other definitions at the index of
* the control flow node they correspond to.
*/
abstract predicate definesAt(ReachableBasicBlock bb, int idx, SsaSourceVariable v);
ReachableBasicBlock getBasicBlock() { this.definesAt(_, result, _) }

/**
* INTERNAL: Use `toString()` instead.
Expand All @@ -146,12 +145,12 @@ class SsaDefinition extends TSsaDefinition {
/** Gets the innermost function or file to which this SSA definition belongs. */
ControlFlow::Root getRoot() { result = this.getBasicBlock().getRoot() }

/** Gets the location of this SSA definition. */
Location getLocation() { result = this.(ZZZDefinition).getLocation() }

/** Gets a textual representation of this element. */
string toString() { result = this.prettyPrintDef() }

/** Gets the source location for this element. */
abstract Location getLocation();

/**
* DEPRECATED: Use `getLocation()` instead.
*
Expand All @@ -166,37 +165,36 @@ class SsaDefinition extends TSsaDefinition {
) {
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}

/**
* Gets the first instruction that the value of this `SsaDefinition` can
* reach without passing through any other instructions, but possibly through
* phi nodes.
*/
IR::Instruction getAFirstUse() { firstUse(this, result) }
}

/**
* An SSA definition that corresponds to an explicit assignment or other variable definition.
*/
class SsaExplicitDefinition extends SsaDefinition, TExplicitDef {
class SsaExplicitDefinition extends SsaDefinition {
SsaExplicitDefinition() { not this instanceof SsaImplicitDefinition }

/** Gets the instruction where the definition happens. */
IR::Instruction getInstruction() {
exists(BasicBlock bb, int i | this = TExplicitDef(bb, i, _) | result = bb.getNode(i))
exists(BasicBlock bb, int i | this.definesAt(_, bb, i) | result = bb.getNode(i))
}

/** Gets the right-hand side of the definition. */
IR::Instruction getRhs() { this.getInstruction().writes(_, result) }

override predicate definesAt(ReachableBasicBlock bb, int i, SsaSourceVariable v) {
this = TExplicitDef(bb, i, v)
}

override ReachableBasicBlock getBasicBlock() { this.definesAt(result, _, _) }

override SsaSourceVariable getSourceVariable() { this = TExplicitDef(_, _, result) }

override string prettyPrintRef() {
exists(Location loc | loc = this.getLocation() |
result = "def@" + loc.getStartLine() + ":" + loc.getStartColumn()
)
}

override string prettyPrintDef() { result = "definition of " + this.getSourceVariable() }

override Location getLocation() { result = this.getInstruction().getLocation() }
}

/** Provides a helper predicate for working with explicit SSA definitions. */
Expand All @@ -223,8 +221,6 @@ abstract class SsaImplicitDefinition extends SsaDefinition {
result = this.getKind() + "@" + loc.getStartLine() + ":" + loc.getStartColumn()
)
}

override Location getLocation() { result = this.getBasicBlock().getLocation() }
}

/**
Expand All @@ -234,24 +230,16 @@ abstract class SsaImplicitDefinition extends SsaDefinition {
* Capturing definitions appear at the beginning of such functions, as well as
* at any function call that may affect the value of the variable.
*/
class SsaVariableCapture extends SsaImplicitDefinition, TCapture {
override predicate definesAt(ReachableBasicBlock bb, int i, SsaSourceVariable v) {
this = TCapture(bb, i, v)
class SsaVariableCapture extends SsaImplicitDefinition {
SsaVariableCapture() {
exists(BasicBlock bb, int i, SsaSourceVariable v | this.definesAt(v, bb, i) |
mayCapture(bb, i, v)
)
}

override ReachableBasicBlock getBasicBlock() { this.definesAt(result, _, _) }

override SsaSourceVariable getSourceVariable() { this.definesAt(_, _, result) }

override string getKind() { result = "capture" }

override string prettyPrintDef() { result = "capture variable " + this.getSourceVariable() }

override Location getLocation() {
exists(ReachableBasicBlock bb, int i | this.definesAt(bb, i, _) |
result = bb.getNode(i).getLocation()
)
}
}

/**
Expand All @@ -276,26 +264,16 @@ abstract class SsaPseudoDefinition extends SsaImplicitDefinition {
* in the flow graph where otherwise two or more definitions for the variable
* would be visible.
*/
class SsaPhiNode extends SsaPseudoDefinition, TPhi {
class SsaPhiNode extends SsaPseudoDefinition instanceof ZZZPhiNode {
override SsaVariable getAnInput() {
result = getDefReachingEndOf(this.getBasicBlock().getAPredecessor(), this.getSourceVariable())
}

override predicate definesAt(ReachableBasicBlock bb, int i, SsaSourceVariable v) {
bb = this.getBasicBlock() and v = this.getSourceVariable() and i = -1
}

override ReachableBasicBlock getBasicBlock() { this = TPhi(result, _) }

override SsaSourceVariable getSourceVariable() { this = TPhi(_, result) }

override string getKind() { result = "phi" }

override string prettyPrintDef() {
result = this.getSourceVariable() + " = phi(" + this.ppInputs() + ")"
}

override Location getLocation() { result = this.getBasicBlock().getLocation() }
}

/**
Expand Down Expand Up @@ -410,3 +388,12 @@ DataFlow::Node getASimilarReadNode(DataFlow::Node node) {
result = readFields.similar().getAUse()
)
}

/**
* Gets an instruction such that `pred` and `result` form an adjacent
* use-use-pair of the same`SsaSourceVariable`, that is, the value read in
* `pred` can reach `result` without passing through any other use or any SSA
* definition of the variable except for phi nodes and uncertain implicit
* updates.
*/
IR::Instruction getAnAdjacentUse(IR::Instruction pred) { adjacentUseUse(pred, result) }
Loading