Skip to content

Commit 721ba5e

Browse files
authored
Merge pull request github#4825 from rdmarsh2/rdmarsh2/cpp/operand-reuse
C++: share `TOperand` across IR stages
2 parents 312ead4 + dbd8432 commit 721ba5e

25 files changed

+508
-360
lines changed

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll

Lines changed: 35 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -10,79 +10,32 @@ private import Imports::MemoryAccessKind
1010
private import Imports::IRType
1111
private import Imports::Overlap
1212
private import Imports::OperandTag
13-
14-
cached
15-
private newtype TOperand =
16-
TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
17-
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and
18-
not Construction::isInCycle(useInstr) and
19-
strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1
20-
} or
21-
TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
22-
useInstr.getOpcode().hasOperand(tag)
23-
} or
24-
TPhiOperand(
25-
PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
26-
) {
27-
defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
28-
}
29-
30-
/**
31-
* Base class for all register operands. This is a placeholder for the IPA union type that we will
32-
* eventually use for this purpose.
33-
*/
34-
private class RegisterOperandBase extends TRegisterOperand {
35-
/** Gets a textual representation of this element. */
36-
abstract string toString();
37-
}
38-
39-
/**
40-
* Returns the register operand with the specified parameters.
41-
*/
42-
private RegisterOperandBase registerOperand(
43-
Instruction useInstr, RegisterOperandTag tag, Instruction defInstr
44-
) {
45-
result = TRegisterOperand(useInstr, tag, defInstr)
46-
}
47-
48-
/**
49-
* Base class for all non-Phi memory operands. This is a placeholder for the IPA union type that we
50-
* will eventually use for this purpose.
51-
*/
52-
private class NonPhiMemoryOperandBase extends TNonPhiMemoryOperand {
53-
/** Gets a textual representation of this element. */
54-
abstract string toString();
55-
}
56-
57-
/**
58-
* Returns the non-Phi memory operand with the specified parameters.
59-
*/
60-
private NonPhiMemoryOperandBase nonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
61-
result = TNonPhiMemoryOperand(useInstr, tag)
62-
}
13+
private import Imports::TOperand
14+
private import internal.OperandInternal
6315

6416
/**
65-
* Base class for all Phi operands. This is a placeholder for the IPA union type that we will
66-
* eventually use for this purpose.
17+
* An operand of an `Instruction` in this stage of the IR. Implemented as a union of the branches
18+
* of `TOperand` that are used in this stage.
6719
*/
68-
private class PhiOperandBase extends TPhiOperand {
69-
abstract string toString();
70-
}
71-
72-
/**
73-
* Returns the Phi operand with the specified parameters.
74-
*/
75-
private PhiOperandBase phiOperand(
76-
Instruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
77-
) {
78-
result = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
79-
}
20+
private class TStageOperand =
21+
TRegisterOperand or TNonSSAMemoryOperand or TPhiOperand or TChiOperand;
8022

8123
/**
8224
* An operand of an `Instruction`. The operand represents a use of the result of one instruction
8325
* (the defining instruction) in another instruction (the use instruction)
8426
*/
85-
class Operand extends TOperand {
27+
class Operand extends TStageOperand {
28+
cached
29+
Operand() {
30+
// Ensure that the operand does not refer to instructions from earlier stages that are unreachable here
31+
exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or
32+
exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or
33+
exists(Instruction use, Instruction def, IRBlock predecessorBlock |
34+
this = phiOperand(use, def, predecessorBlock, _)
35+
) or
36+
exists(Instruction use | this = chiOperand(use, _))
37+
}
38+
8639
/** Gets a textual representation of this element. */
8740
string toString() { result = "Operand" }
8841

@@ -238,9 +191,11 @@ class Operand extends TOperand {
238191
* An operand that consumes a memory result (e.g. the `LoadOperand` on a `Load` instruction).
239192
*/
240193
class MemoryOperand extends Operand {
194+
cached
241195
MemoryOperand() {
242-
this instanceof NonPhiMemoryOperandBase or
243-
this instanceof PhiOperandBase
196+
this instanceof TNonSSAMemoryOperand or
197+
this instanceof TPhiOperand or
198+
this instanceof TChiOperand
244199
}
245200

246201
/**
@@ -278,7 +233,8 @@ class NonPhiOperand extends Operand {
278233

279234
NonPhiOperand() {
280235
this = registerOperand(useInstr, tag, _) or
281-
this = nonPhiMemoryOperand(useInstr, tag)
236+
this = nonSSAMemoryOperand(useInstr, tag) or
237+
this = chiOperand(useInstr, tag)
282238
}
283239

284240
final override Instruction getUse() { result = useInstr }
@@ -298,10 +254,11 @@ class NonPhiOperand extends Operand {
298254
/**
299255
* An operand that consumes a register (non-memory) result.
300256
*/
301-
class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
257+
class RegisterOperand extends NonPhiOperand, TRegisterOperand {
302258
override RegisterOperandTag tag;
303259
Instruction defInstr;
304260

261+
cached
305262
RegisterOperand() { this = registerOperand(useInstr, tag, defInstr) }
306263

307264
final override string toString() { result = tag.toString() }
@@ -317,10 +274,15 @@ class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
317274
/**
318275
* A memory operand other than the operand of a `Phi` instruction.
319276
*/
320-
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase {
277+
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOperand {
321278
override MemoryOperandTag tag;
322279

323-
NonPhiMemoryOperand() { this = nonPhiMemoryOperand(useInstr, tag) }
280+
cached
281+
NonPhiMemoryOperand() {
282+
this = nonSSAMemoryOperand(useInstr, tag)
283+
or
284+
this = chiOperand(useInstr, tag)
285+
}
324286

325287
final override string toString() { result = tag.toString() }
326288

@@ -462,12 +424,13 @@ class SideEffectOperand extends TypedOperand {
462424
/**
463425
* An operand of a `PhiInstruction`.
464426
*/
465-
class PhiInputOperand extends MemoryOperand, PhiOperandBase {
427+
class PhiInputOperand extends MemoryOperand, TPhiOperand {
466428
PhiInstruction useInstr;
467429
Instruction defInstr;
468430
IRBlock predecessorBlock;
469431
Overlap overlap;
470432

433+
cached
471434
PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) }
472435

473436
override string toString() { result = "Phi" }

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/OperandImports.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
22
import semmle.code.cpp.ir.implementation.IRType as IRType
33
import semmle.code.cpp.ir.internal.Overlap as Overlap
44
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
5+
import semmle.code.cpp.ir.implementation.internal.TOperand as TOperand
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
private import semmle.code.cpp.ir.implementation.internal.TOperand
2+
import AliasedSSAOperands

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ private import Imports::Overlap
66
private import Imports::TInstruction
77
private import Imports::RawIR as RawIR
88
private import SSAInstructions
9+
private import SSAOperands
910
private import NewIR
1011

1112
private class OldBlock = Reachability::ReachableBlock;

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
33
import semmle.code.cpp.ir.internal.Overlap as Overlap
44
import semmle.code.cpp.ir.implementation.internal.TInstruction as TInstruction
55
import semmle.code.cpp.ir.implementation.raw.IR as RawIR
6+
import semmle.code.cpp.ir.implementation.internal.TOperand as TOperand

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ import semmle.code.cpp.ir.implementation.aliased_ssa.IR as NewIR
55
import semmle.code.cpp.ir.implementation.internal.TInstruction::AliasedSSAInstructions as SSAInstructions
66
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
77
import AliasedSSA as Alias
8+
import semmle.code.cpp.ir.implementation.internal.TOperand::AliasedSSAOperands as SSAOperands
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
private import TInstruction
2+
private import OperandTag
3+
private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as RawConstruction
4+
private import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SSAConstruction as UnaliasedConstruction
5+
private import semmle.code.cpp.ir.implementation.aliased_ssa.internal.SSAConstruction as AliasedConstruction
6+
private import semmle.code.cpp.ir.implementation.raw.IR as Raw
7+
private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as Unaliased
8+
private import semmle.code.cpp.ir.implementation.aliased_ssa.IR as Aliased
9+
private import semmle.code.cpp.ir.internal.Overlap
10+
11+
/**
12+
* Provides the newtype used to represent operands across all phases of the IR.
13+
*/
14+
private module Internal {
15+
/**
16+
* An IR operand. `TOperand` is shared across all phases of the IR. There are branches of this
17+
* type for operands created directly from the AST (`TRegisterOperand` and `TNonSSAMemoryOperand`),
18+
* for operands computed by each stage of SSA construction (`T*PhiOperand` and
19+
* `TAliasedChiOperand`), and a placehold branch for operands that do not exist in a given
20+
* stage of IR construction (`TNoOperand`).
21+
*/
22+
cached
23+
newtype TOperand =
24+
// RAW
25+
TRegisterOperand(TRawInstruction useInstr, RegisterOperandTag tag, TRawInstruction defInstr) {
26+
defInstr = RawConstruction::getRegisterOperandDefinition(useInstr, tag) and
27+
not RawConstruction::isInCycle(useInstr) and
28+
strictcount(RawConstruction::getRegisterOperandDefinition(useInstr, tag)) = 1
29+
} or
30+
// Placeholder for Phi and Chi operands in stages that don't have the corresponding instructions
31+
TNoOperand() { none() } or
32+
// Can be "removed" later when there's unreachable code
33+
// These operands can be reused across all three stages. They just get different defs.
34+
TNonSSAMemoryOperand(Raw::Instruction useInstr, MemoryOperandTag tag) {
35+
// Has no definition in raw but will get definitions later
36+
useInstr.getOpcode().hasOperand(tag)
37+
} or
38+
TUnaliasedPhiOperand(
39+
Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr,
40+
Unaliased::IRBlock predecessorBlock, Overlap overlap
41+
) {
42+
defInstr = UnaliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
43+
} or
44+
//// ALIASED
45+
////
46+
// Until we share SSA, these will be all the phis there are. With SSA
47+
// sharing, these will add to the ones that are already there.
48+
// If we share SSA, be careful with the case where we remove all possible
49+
// indirect writes to a variable because they're dead code. In that case it's
50+
// important that we use the same definition of "is variable aliased" across
51+
// the phases.
52+
TAliasedPhiOperand(
53+
TAliasedSSAPhiInstruction useInstr, Aliased::Instruction defInstr,
54+
Aliased::IRBlock predecessorBlock, Overlap overlap
55+
) {
56+
defInstr = AliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
57+
} or
58+
TAliasedChiOperand(TAliasedSSAChiInstruction useInstr, ChiOperandTag tag) { any() }
59+
}
60+
61+
/**
62+
* Reexports some branches from `TOperand` so they can be used in stage modules without importing
63+
* `TOperand` itself.
64+
*/
65+
private module Shared {
66+
class TRegisterOperand = Internal::TRegisterOperand;
67+
68+
/**
69+
* Returns the register operand with the specified parameters.
70+
*/
71+
TRegisterOperand registerOperand(
72+
TRawInstruction useInstr, RegisterOperandTag tag, TRawInstruction defInstr
73+
) {
74+
result = Internal::TRegisterOperand(useInstr, tag, defInstr)
75+
}
76+
77+
class TNonSSAMemoryOperand = Internal::TNonSSAMemoryOperand;
78+
79+
/**
80+
* Returns the non-Phi memory operand with the specified parameters.
81+
*/
82+
TNonSSAMemoryOperand nonSSAMemoryOperand(TRawInstruction useInstr, MemoryOperandTag tag) {
83+
result = Internal::TNonSSAMemoryOperand(useInstr, tag)
84+
}
85+
}
86+
87+
/**
88+
* Provides wrappers for the constructors of each branch of `TOperand` that is used by the
89+
* raw IR stage.
90+
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
91+
* a class alias.
92+
*/
93+
module RawOperands {
94+
import Shared
95+
96+
class TPhiOperand = Internal::TNoOperand;
97+
98+
class TChiOperand = Internal::TNoOperand;
99+
100+
class TNonPhiMemoryOperand = TNonSSAMemoryOperand or TChiOperand;
101+
102+
/**
103+
* Returns the Phi operand with the specified parameters.
104+
*/
105+
TPhiOperand phiOperand(
106+
Raw::PhiInstruction useInstr, Raw::Instruction defInstr, Raw::IRBlock predecessorBlock,
107+
Overlap overlap
108+
) {
109+
none()
110+
}
111+
112+
/**
113+
* Returns the Chi operand with the specified parameters.
114+
*/
115+
TChiOperand chiOperand(Raw::Instruction useInstr, ChiOperandTag tag) { none() }
116+
}
117+
118+
/**
119+
* Provides wrappers for the constructors of each branch of `TOperand` that is used by the
120+
* unaliased SSA stage.
121+
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
122+
* a class alias.
123+
*/
124+
module UnaliasedSSAOperands {
125+
import Shared
126+
127+
class TPhiOperand = Internal::TUnaliasedPhiOperand;
128+
129+
class TChiOperand = Internal::TNoOperand;
130+
131+
class TNonPhiMemoryOperand = TNonSSAMemoryOperand or TChiOperand;
132+
133+
/**
134+
* Returns the Phi operand with the specified parameters.
135+
*/
136+
TPhiOperand phiOperand(
137+
Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr,
138+
Unaliased::IRBlock predecessorBlock, Overlap overlap
139+
) {
140+
result = Internal::TUnaliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
141+
}
142+
143+
/**
144+
* Returns the Chi operand with the specified parameters.
145+
*/
146+
TChiOperand chiOperand(Unaliased::Instruction useInstr, ChiOperandTag tag) { none() }
147+
}
148+
149+
/**
150+
* Provides wrappers for the constructors of each branch of `TOperand` that is used by the
151+
* asliased SSA stage.
152+
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
153+
* a class alias.
154+
*/
155+
module AliasedSSAOperands {
156+
import Shared
157+
158+
class TPhiOperand = Internal::TAliasedPhiOperand;
159+
160+
class TChiOperand = Internal::TAliasedChiOperand;
161+
162+
class TNonPhiMemoryOperand = TNonSSAMemoryOperand or TChiOperand;
163+
164+
/**
165+
* Returns the Phi operand with the specified parameters.
166+
*/
167+
TPhiOperand phiOperand(
168+
TAliasedSSAPhiInstruction useInstr, Aliased::Instruction defInstr,
169+
Aliased::IRBlock predecessorBlock, Overlap overlap
170+
) {
171+
result = Internal::TAliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
172+
}
173+
174+
/**
175+
* Returns the Chi operand with the specified parameters.
176+
*/
177+
TChiOperand chiOperand(TAliasedSSAChiInstruction useInstr, ChiOperandTag tag) {
178+
result = Internal::TAliasedChiOperand(useInstr, tag)
179+
}
180+
}

0 commit comments

Comments
 (0)