Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
4 changes: 4 additions & 0 deletions go/ql/lib/change-notes/2026-10-20-shared-ssa.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: breaking
---
* The Go SSA API now uses the shared SSA classes. `SsaVariable`, `SsaExplicitDefinition`, `SsaVariableCapture`, and `SsaPhiNode` have been replaced by `SsaDefinition`, `SsaExplicitWrite`, `SsaUncertainWrite`, and `SsaPhiDefinition`, respectively. Deprecated compatibility classes are available for the old names.
6 changes: 3 additions & 3 deletions go/ql/lib/semmle/go/controlflow/ControlFlowGraph.qll
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,9 @@ module ControlFlow {
/** Holds if this node sets variable or constant `v` to `rhs`. */
predicate writes(ValueEntity v, DataFlow::Node rhs) { super.writes(v, rhs.asInstruction()) }

/** Holds if this node defines SSA variable `v` to be `rhs`. */
predicate definesSsaVariable(SsaVariable v, DataFlow::Node rhs) {
super.getLhs().asSsaVariable() = v and
/** Holds if this node defines SSA definition `def` to be `rhs`. */
predicate definesSsaDefinition(SsaDefinition def, DataFlow::Node rhs) {
super.getLhs().asSsaDefinition() = def and
super.getRhs() = rhs.asInstruction()
}
Comment on lines +143 to 147

Expand Down
42 changes: 8 additions & 34 deletions go/ql/lib/semmle/go/controlflow/Guards.qll
Original file line number Diff line number Diff line change
Expand Up @@ -320,9 +320,9 @@ private module GuardsInput implements
}

private predicate extractedCallResult(Expr use, G::CallExpr call, int resultIndex) {
exists(GoSsa::SsaDefinition def, IR::ExtractTupleElementInstruction extract |
use = def.getVariable().getAUse().(IR::EvalInstruction).getExpr() and
def.(GoSsa::SsaExplicitDefinition).getInstruction() = extract and
exists(GoSsa::Ssa::SsaDefinition def, IR::ExtractTupleElementInstruction extract |
use = def.getARead() and
def.(GoSsa::Ssa::SsaExplicitWrite).getDefinition() = extract and
extract.extractsElement(IR::evalExprInstruction(call), resultIndex)
)
}
Expand Down Expand Up @@ -398,46 +398,20 @@ private module GuardsInput implements
private module GuardsImpl = SharedGuards::Make<Location, CfgImpl::Cfg, GuardsInput>;

private module LogicInput implements GuardsImpl::LogicInputSig {
final private class FinalSsaDefinition = GoSsa::SsaDefinition;

class SsaDefinition extends FinalSsaDefinition {
GuardsInput::Expr getARead() {
result = super.getVariable().getAUse().(IR::EvalInstruction).getExpr()
}
}

class SsaExplicitWrite extends SsaDefinition instanceof GoSsa::SsaExplicitDefinition {
GuardsInput::Expr getValue() { result = super.getRhs().(IR::EvalInstruction).getExpr() }
}

class SsaPhiDefinition extends SsaDefinition instanceof GoSsa::SsaPhiNode {
/** Holds if `inp` is an input to the phi node along the edge originating in `bb`. */
predicate hasInputFromBlock(SsaDefinition inp, BasicBlock bb) {
SsaImpl::phiHasInputFromBlock(this, inp, bb)
}
}

class SsaParameterInit extends SsaDefinition {
SsaParameterInit() {
this.(GoSsa::SsaExplicitDefinition).getInstruction() instanceof IR::InitParameterInstruction
}

GuardsInput::Parameter getParameter() {
this.(GoSsa::SsaExplicitDefinition).getInstruction() = IR::initParamInstruction(result)
}
}
import GoSsa::Ssa

predicate implicitReturnDefinition(GuardsInput::NonOverridableMethod method, SsaDefinition def) {
exists(IR::ReadResultInstruction read |
exists(IR::ReadResultInstruction read, BasicBlock bb, int i |
method.getResultIndex() >= 0 and
read.reads(method.getFunction().getResult(method.getResultIndex())) and
def.getVariable().getAUse() = read
read = bb.getNode(i) and
SsaImpl::ssaDefReachesRead(def.getSourceVariable(), def, bb, i)
)
}

predicate additionalSsaDefinitionValue(SsaDefinition def, GuardValue value) {
exists(IR::EvalImplicitInitInstruction init |
def.(GoSsa::SsaExplicitDefinition).getInstruction() = init and
def.(SsaExplicitWrite).getDefinition() = init and
value.asBooleanValue() = init.getBoolValue()
)
}
Expand Down
43 changes: 34 additions & 9 deletions go/ql/lib/semmle/go/controlflow/IR.qll
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,8 @@ module IR {
lhs = MkLhs(this, _)
or
lhs = MkResultWriteTarget(this)
or
lhs = MkTypeSwitchImplicitVariableTarget(this, _)
) and
initialization = false
or
Expand Down Expand Up @@ -1012,14 +1014,13 @@ module IR {
* An instruction corresponding to the implicit declaration and assignment of a variable
* in a type switch case clause.
*/
class TypeSwitchImplicitVariableInstruction extends Instruction {
class TypeSwitchImplicitVariableInstruction extends WriteInstruction {
CaseClause cc;

TypeSwitchImplicitVariableInstruction() { typeSwitchCaseMatch(this, cc) }

override predicate writes(ValueEntity v, Instruction rhs) {
v = cc.getImplicitlyDeclaredVariable() and
exists(TypeSwitchStmt ts | cc = ts.getACase() | rhs = evalExprInstruction(ts.getExpr()))
override Instruction getRhs() {
exists(TypeSwitchStmt ts | cc = ts.getACase() and result = evalExprInstruction(ts.getExpr()))
}
}

Expand Down Expand Up @@ -1108,14 +1109,24 @@ module IR {
exists(ret.getEnclosingFunction().getResultVar(idx)) and
exists(ret.getAnExpr())
)
} or
/** An implicitly declared variable in a type-switch case. */
MkTypeSwitchImplicitVariableTarget(ControlFlow::Node w, LocalVariable v) {
exists(CaseClause cc | typeSwitchCaseMatch(w, cc) and v = cc.getImplicitlyDeclaredVariable())
}

/** A representation of the target of a write instruction. */
class WriteTarget extends TWriteTarget {
ControlFlow::Node w;

WriteTarget() {
this = MkLhs(w, _) or this = MkLiteralElementTarget(w) or this = MkResultWriteTarget(w)
this = MkLhs(w, _)
or
this = MkLiteralElementTarget(w)
or
this = MkResultWriteTarget(w)
or
this = MkTypeSwitchImplicitVariableTarget(w, _)
}

/** Gets the write instruction of which this is the target. */
Expand All @@ -1127,10 +1138,8 @@ module IR {
/** Gets the name of the variable or field being written to, if any. */
string getName() { none() }

/** Gets the SSA variable being written to, if any. */
SsaVariable asSsaVariable() {
this.getWrite() = result.getDefinition().(SsaExplicitDefinition).getInstruction()
}
/** Gets the SSA definition being written to, if any. */
SsaDefinition asSsaDefinition() { this.getWrite() = result.(SsaExplicitWrite).getDefinition() }
Comment on lines +1141 to +1142

/** Holds if `e` is the variable or field being written to. */
predicate refersTo(ValueEntity e) { none() }
Expand Down Expand Up @@ -1160,6 +1169,22 @@ module IR {
}
}

/** An implicitly declared variable used as the target of a type-switch case write. */
class ImplicitVariableTarget extends WriteTarget {
LocalVariable variable;

ImplicitVariableTarget() { this = MkTypeSwitchImplicitVariableTarget(_, variable) }

override predicate refersTo(ValueEntity e) { e = variable }

override string getName() { result = variable.getName() }

/** Gets the variable this target refers to. */
Variable getVariable() { result = variable }

override Location getLocation() { result = variable.getLocation() }
}

/** A reference to a variable or constant, used as the target of a write. */
class VarOrConstTarget extends WriteTarget {
Expr loc;
Expand Down
6 changes: 3 additions & 3 deletions go/ql/lib/semmle/go/dataflow/GlobalValueNumbering.qll
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ private ControlFlow::Node nodeWithPossibleSideEffect() {
// If the lhs of an assignment is not analyzable by SSA, then
// we need to treat the assignment as having a possible side-effect.
result instanceof Write and
not exists(SsaExplicitDefinition ssa | result = ssa.getInstruction())
not exists(SsaExplicitWrite ssa | result = ssa.getDefinition())
}

private predicate isPureFn(Function f) {
Expand Down Expand Up @@ -527,8 +527,8 @@ private predicate mkDeref(DataFlow::PointerDereferenceNode deref, GVN p, Control
p = globalValueNumber(deref.getOperand())
}

private predicate ssaInit(SsaExplicitDefinition ssa, DataFlow::Node rhs) {
ssa.getRhs() = rhs.asInstruction()
private predicate ssaInit(SsaExplicitWrite ssa, DataFlow::Node rhs) {
ssa.getDefinition().getRhs() = rhs.asInstruction()
}

/** Gets the global value number of data-flow node `nd`. */
Expand Down
Loading
Loading