fold compound-assign write into the compound-rhs instruction

A compound assignment (`x += y`) previously produced two sequential
control-flow nodes: a 'compound-rhs' node computing the binary operation
`x + y`, followed by an 'assign:0' write node storing the result.

Make the compound-rhs instruction itself perform the write (it becomes a
WriteInstruction whose right-hand side is its own value), and stop
emitting the separate assign:0 node for compound assignments. This saves
one CFG node per compound assignment while preserving the BinaryOperationNode
model (so string-concatenation taint through `s += ...` still holds) and
SSA semantics (the def value is the binary operation).
This commit is contained in:
Owen Mansel-Chan
2026-07-10 01:12:29 +01:00
parent 2bde73cd2d
commit c85744e912
5 changed files with 27 additions and 17 deletions

View File

@@ -502,6 +502,11 @@ module GoCfg {
exists(int i |
(
notBlankIdent(n.(Go::Assignment).getLhs(i)) and
// A compound assignment (`x += y`) folds its write into the
// `compound-rhs` binary-operation instruction rather than emitting a
// separate `assign:i` write node (see
// `IR::EvalCompoundAssignRhsInstruction`).
not n instanceof Go::CompoundAssignStmt and
// The `y := x.(type)` test statement of a type switch is transparent
// (see `skipCfg`): the per-case implicit variables are written at the
// case match nodes (see `IR::TypeSwitchImplicitVariableInstruction`),
@@ -1139,7 +1144,10 @@ module GoCfg {
)
or
(
notBlankIdent(assgn.(Go::Assignment).getLhs(j))
notBlankIdent(assgn.(Go::Assignment).getLhs(j)) and
// Compound assignments fold their write into `compound-rhs` (ord -1)
// above, so they emit no separate `assign:j` node.
not assgn instanceof Go::CompoundAssignStmt
or
notBlankIdent(assgn.(Go::ValueSpec).getNameExpr(j))
or

View File

@@ -726,8 +726,6 @@ module IR {
implicitInitInstruction(any(ValueEntity v | spec.getNameExpr(i) = v.getDeclaration()))
)
or
result.(EvalCompoundAssignRhsInstruction).isAdditional(assgn, "compound-rhs")
or
result.(ExtractTupleElementInstruction).isAdditional(assgn, "extract:" + i.toString())
}
@@ -735,9 +733,11 @@ module IR {
}
/**
* An instruction that computes the (implicit) right-hand side of a compound assignment.
* An instruction that computes the (implicit) right-hand side of a compound assignment,
* such as the `x + y` in `x += y`, and writes the resulting value to the assignment's
* left-hand side.
*/
class EvalCompoundAssignRhsInstruction extends Instruction {
class EvalCompoundAssignRhsInstruction extends WriteInstruction {
CompoundAssignStmt assgn;
EvalCompoundAssignRhsInstruction() { this.isAdditional(assgn, "compound-rhs") }
@@ -745,6 +745,8 @@ module IR {
/** Gets the corresponding compound assignment statement. */
CompoundAssignStmt getAssignment() { result = assgn }
override Instruction getRhs() { result = this }
override Type getResultType() { result = assgn.getRhs().getType() }
override ControlFlow::Root getRoot() { result.isRootOf(assgn) }
@@ -1228,6 +1230,10 @@ module IR {
or
exists(IncDecStmt ids | write.isIn(ids) | lhs = ids.getOperand().stripParens())
or
exists(CompoundAssignStmt ca | write.isAdditional(ca, "compound-rhs") |
lhs = ca.getLhs().stripParens()
)
or
exists(FuncDef fd, int idx |
write.isAdditional(fd.getBody(), "param-init:" + idx.toString()) and
lhs = fd.getParameter(idx).getDeclaration()

View File

@@ -1375,8 +1375,7 @@
| exprs.go:51:3:51:5 | res | exprs.go:51:3:51:5 | After res |
| exprs.go:51:3:51:14 | ... += ... | exprs.go:51:3:51:5 | Before res |
| exprs.go:51:3:51:14 | After ... += ... | exprs.go:50:31:52:2 | After block statement |
| exprs.go:51:3:51:14 | assign:0 ... += ... | exprs.go:51:3:51:14 | After ... += ... |
| exprs.go:51:3:51:14 | compound-rhs ... += ... | exprs.go:51:3:51:14 | assign:0 ... += ... |
| exprs.go:51:3:51:14 | compound-rhs ... += ... | exprs.go:51:3:51:14 | After ... += ... |
| exprs.go:51:10:51:11 | After xs | exprs.go:51:13:51:13 | Before i |
| exprs.go:51:10:51:11 | Before xs | exprs.go:51:10:51:11 | xs |
| exprs.go:51:10:51:11 | xs | exprs.go:51:10:51:11 | After xs |
@@ -2013,8 +2012,7 @@
| main.go:17:3:17:3 | y | main.go:17:3:17:3 | After y |
| main.go:17:3:17:9 | ... += ... | main.go:17:3:17:3 | Before y |
| main.go:17:3:17:9 | After ... += ... | main.go:16:12:18:2 | After block statement |
| main.go:17:3:17:9 | assign:0 ... += ... | main.go:17:3:17:9 | After ... += ... |
| main.go:17:3:17:9 | compound-rhs ... += ... | main.go:17:3:17:9 | assign:0 ... += ... |
| main.go:17:3:17:9 | compound-rhs ... += ... | main.go:17:3:17:9 | After ... += ... |
| main.go:17:8:17:9 | 19 | main.go:17:8:17:9 | After 19 |
| main.go:17:8:17:9 | After 19 | main.go:17:3:17:9 | compound-rhs ... += ... |
| main.go:17:8:17:9 | Before 19 | main.go:17:8:17:9 | 19 |
@@ -2131,8 +2129,7 @@
| main.go:35:2:35:3 | star expression | main.go:35:2:35:3 | After star expression |
| main.go:35:2:35:9 | ... += ... | main.go:35:2:35:3 | Before star expression |
| main.go:35:2:35:9 | After ... += ... | main.go:34:19:36:1 | After block statement |
| main.go:35:2:35:9 | assign:0 ... += ... | main.go:35:2:35:9 | After ... += ... |
| main.go:35:2:35:9 | compound-rhs ... += ... | main.go:35:2:35:9 | assign:0 ... += ... |
| main.go:35:2:35:9 | compound-rhs ... += ... | main.go:35:2:35:9 | After ... += ... |
| main.go:35:3:35:3 | After x | main.go:35:2:35:3 | star expression |
| main.go:35:3:35:3 | Before x | main.go:35:3:35:3 | x |
| main.go:35:3:35:3 | x | main.go:35:3:35:3 | After x |
@@ -2968,8 +2965,7 @@
| stmts5.go:8:2:8:7 | selection of val | stmts5.go:8:2:8:7 | After selection of val |
| stmts5.go:8:2:8:16 | ... += ... | stmts5.go:8:2:8:7 | Before selection of val |
| stmts5.go:8:2:8:16 | After ... += ... | stmts5.go:7:33:9:1 | After block statement |
| stmts5.go:8:2:8:16 | assign:0 ... += ... | stmts5.go:8:2:8:16 | After ... += ... |
| stmts5.go:8:2:8:16 | compound-rhs ... += ... | stmts5.go:8:2:8:16 | assign:0 ... += ... |
| stmts5.go:8:2:8:16 | compound-rhs ... += ... | stmts5.go:8:2:8:16 | After ... += ... |
| stmts5.go:8:12:8:16 | After other | stmts5.go:8:2:8:16 | compound-rhs ... += ... |
| stmts5.go:8:12:8:16 | Before other | stmts5.go:8:12:8:16 | other |
| stmts5.go:8:12:8:16 | other | stmts5.go:8:12:8:16 | After other |

View File

@@ -1,6 +1,6 @@
| main.go:13:6:13:10 | assign:0 value declaration specifier | main.go:13:6:13:6 | x | main.go:13:6:13:10 | zero-init:0 value declaration specifier |
| main.go:14:2:14:8 | assign:0 ... := ... | main.go:14:2:14:2 | y | main.go:14:7:14:8 | 23 |
| main.go:17:3:17:9 | assign:0 ... += ... | main.go:14:2:14:2 | y | main.go:17:3:17:9 | compound-rhs ... += ... |
| main.go:17:3:17:9 | compound-rhs ... += ... | main.go:14:2:14:2 | y | main.go:17:3:17:9 | compound-rhs ... += ... |
| main.go:21:3:21:7 | assign:0 ... = ... | main.go:13:6:13:6 | x | main.go:21:7:21:7 | y |
| main.go:26:28:32:1 | param-init:0 block statement | main.go:26:10:26:10 | x | main.go:26:28:32:1 | arg:0 block statement |
| main.go:27:2:27:13 | assign:0 ... := ... | main.go:27:2:27:2 | a | main.go:27:10:27:10 | x |

View File

@@ -13,13 +13,13 @@
| testdata.go:134:3:134:17 | assign:0 ... = ... | This definition of x is never used. |
| testdata.go:143:3:143:17 | assign:0 ... = ... | This definition of x is never used. |
| testdata.go:164:3:164:17 | assign:0 ... = ... | This definition of x is never used. |
| testdata.go:172:3:172:18 | assign:0 ... += ... | This definition of x is never used. |
| testdata.go:172:3:172:18 | compound-rhs ... += ... | This definition of x is never used. |
| testdata.go:180:3:180:5 | increment statement | This definition of x is never used. |
| testdata.go:201:2:201:24 | assign:0 ... = ... | This definition of x is never used. |
| testdata.go:262:2:262:16 | assign:0 ... = ... | This definition of x is never used. |
| testdata.go:268:2:268:16 | assign:0 ... = ... | This definition of x is never used. |
| testdata.go:309:2:309:7 | assign:0 ... /= ... | This definition of a is never used. |
| testdata.go:321:2:321:7 | assign:0 ... %= ... | This definition of a is never used. |
| testdata.go:309:2:309:7 | compound-rhs ... /= ... | This definition of a is never used. |
| testdata.go:321:2:321:7 | compound-rhs ... %= ... | This definition of a is never used. |
| testdata.go:387:3:387:17 | assign:0 ... = ... | This definition of x is never used. |
| testdata.go:432:3:432:17 | assign:0 ... = ... | This definition of x is never used. |
| testdata.go:434:3:434:17 | assign:0 ... = ... | This definition of x is never used. |