Skip to content

Commit aa058c2

Browse files
committed
C++: Generate IR for assertions in release builds.
1 parent a18f3b6 commit aa058c2

File tree

5 files changed

+805
-164
lines changed

5 files changed

+805
-164
lines changed

cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/InstructionTag.qll

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,11 @@ newtype TInstructionTag =
104104
} or
105105
SizeofVlaDimensionTag(int index) {
106106
exists(VlaDeclStmt v | exists(v.getTransitiveVlaDimensionStmt(index)))
107-
}
107+
} or
108+
AssertionVarAddressTag() or
109+
AssertionVarLoadTag() or
110+
AssertionOpTag() or
111+
AssertionBranchTag()
108112

109113
class InstructionTag extends TInstructionTag {
110114
final string toString() { result = getInstructionTagId(this) }
@@ -296,4 +300,12 @@ string getInstructionTagId(TInstructionTag tag) {
296300
tag = CoAwaitBranchTag() and result = "CoAwaitBranch"
297301
or
298302
tag = BoolToIntConversionTag() and result = "BoolToIntConversion"
303+
or
304+
tag = AssertionVarAddressTag() and result = "AssertionVarAddress"
305+
or
306+
tag = AssertionVarLoadTag() and result = "AssertionVarLoad"
307+
or
308+
tag = AssertionOpTag() and result = "AssertionOp"
309+
or
310+
tag = AssertionBranchTag() and result = "AssertionBranch"
299311
}
Lines changed: 373 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,373 @@
1+
private import cpp
2+
private import semmle.code.cpp.ir.internal.IRUtilities
3+
private import semmle.code.cpp.ir.implementation.internal.OperandTag
4+
private import semmle.code.cpp.ir.internal.CppType
5+
private import semmle.code.cpp.ir.internal.TempVariableTag
6+
private import InstructionTag
7+
private import TranslatedElement
8+
private import TranslatedStmt
9+
private import TranslatedFunction
10+
11+
/**
12+
* Holds if `s` is a statement that may be an expanded assertion in a
13+
* release build.
14+
*/
15+
pragma[nomagic]
16+
private predicate stmtCandidate(Stmt s) {
17+
not s.isFromUninstantiatedTemplate(_) and
18+
(
19+
// The expansion of `__analysis_assume(x != 0);` when `__analysis_assume` is
20+
// empty is the empty statement.
21+
s instanceof EmptyStmt
22+
or
23+
// The expansion of `assert(x != 0)` when `assert` is `((void)0)` is a zero literal
24+
// with a void type.
25+
exists(Expr e |
26+
e = s.(ExprStmt).getExpr() and
27+
e.getValue() = "0" and
28+
e.getActualType() instanceof VoidType
29+
)
30+
)
31+
}
32+
33+
pragma[nomagic]
34+
private predicate macroInvocationLocation(int startline, Function f, MacroInvocation mi) {
35+
mi.getMacroName() = ["assert", "__analysis_assume"] and
36+
mi.getLocation().hasLocationInfo(_, startline, _, _, _) and
37+
f.getEntryPoint().isAffectedByMacro(mi)
38+
}
39+
40+
pragma[nomagic]
41+
private predicate stmtParentLocation(int startline, Function f, StmtParent p) {
42+
p.getEnclosingFunction() = f and
43+
p.getLocation().hasLocationInfo(_, startline, _, _, _)
44+
}
45+
46+
/**
47+
* Holds if `mi` is a macro invocation with a name that is known
48+
* to correspond to an assertion macro, and the macro invocation
49+
* is the only thing on the line.
50+
*/
51+
pragma[nomagic]
52+
private predicate assertion0(MacroInvocation mi, Stmt s) {
53+
stmtCandidate(s) and
54+
s =
55+
unique(StmtParent p, int startline, Function f |
56+
macroInvocationLocation(startline, f, mi) and
57+
stmtParentLocation(startline, f, p) and
58+
// Also do not count the elements from the expanded macro. i.e., when checking
59+
// if `assert(x)` is the only thing on the line we do not count the
60+
// generated `((void)0)` expression.
61+
not p = mi.getAnExpandedElement()
62+
|
63+
p
64+
)
65+
}
66+
67+
private Function getEnclosingFunctionForMacroInvocation(MacroInvocation mi) {
68+
exists(Stmt s |
69+
assertion0(mi, s) and
70+
result = s.getEnclosingFunction()
71+
)
72+
}
73+
74+
/**
75+
* Holds if `arg` has two components and the `i`'th component of the string
76+
* `arg` is `s`, and the components are seperated by an operation with
77+
* opcode `opcode`.
78+
*/
79+
bindingset[arg]
80+
pragma[inline_late]
81+
private predicate parseArgument(string arg, string s, int i, Opcode opcode) {
82+
s =
83+
arg.regexpCapture("([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)\\s?<=\\s?([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)",
84+
i + 1) and
85+
opcode instanceof Opcode::CompareLE
86+
or
87+
s =
88+
arg.regexpCapture("([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)\\s?>=\\s?([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)",
89+
i + 1) and
90+
opcode instanceof Opcode::CompareGE
91+
or
92+
not arg.regexpMatch("([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)\\s?<=\\s?([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)") and
93+
s =
94+
arg.regexpCapture("([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)\\s?<\\s?([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)",
95+
i + 1) and
96+
opcode instanceof Opcode::CompareLT
97+
or
98+
not arg.regexpMatch("([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)\\s?>=\\s?([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)") and
99+
s =
100+
arg.regexpCapture("([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)\\s?>\\s?([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)",
101+
i + 1) and
102+
opcode instanceof Opcode::CompareGT
103+
or
104+
s =
105+
arg.regexpCapture("([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)\\s?!=\\s?([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)",
106+
i + 1) and
107+
opcode instanceof Opcode::CompareNE
108+
or
109+
s =
110+
arg.regexpCapture("([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)\\s?==\\s?([a-zA-Z_][a-zA-Z_0-9]*|[0-9]+)",
111+
i + 1) and
112+
opcode instanceof Opcode::CompareEQ
113+
}
114+
115+
/** Gets a local variable named `s` in `f`. */
116+
pragma[nomagic]
117+
private LocalScopeVariable getAVariableWithNameInFunction(Function f, string s) {
118+
result.getName() = s and
119+
result.getFunction() = f
120+
}
121+
122+
/**
123+
* Holds if the `i`'th component of the macro invocation `mi` with opcode
124+
* `opcode` is a reference to `var`.
125+
*/
126+
private predicate hasVarAccessMacroArgument(MacroInvocation mi, Variable var, int i, Opcode opcode) {
127+
exists(string arg, string s, Function f |
128+
arg = mi.getUnexpandedArgument(0) and
129+
f = getEnclosingFunctionForMacroInvocation(mi) and
130+
not exists(s.toInt()) and
131+
parseArgument(arg, s, i, opcode) and
132+
var = unique( | | getAVariableWithNameInFunction(f, s))
133+
)
134+
}
135+
136+
/**
137+
* Holds if the `i`'th component of the macro invocation `mi` with opcode
138+
* `opcode` is a
139+
* constant with the value `k`.
140+
*/
141+
private predicate hasConstMacroArgument(MacroInvocation mi, int k, int i, Opcode opcode) {
142+
exists(string arg, string s |
143+
assertion0(mi, _) and
144+
arg = mi.getUnexpandedArgument(0) and
145+
s.toInt() = k and
146+
parseArgument(arg, s, i, opcode)
147+
)
148+
}
149+
150+
predicate hasAssertionOperand(MacroInvocation mi, int i) {
151+
hasVarAccessMacroArgument(mi, _, i, _)
152+
or
153+
hasConstMacroArgument(mi, _, i, _)
154+
}
155+
156+
private predicate hasAssertionOpcode(MacroInvocation mi, Opcode opcode) {
157+
hasVarAccessMacroArgument(mi, _, _, opcode)
158+
or
159+
hasConstMacroArgument(mi, _, _, opcode)
160+
}
161+
162+
/**
163+
* Holds if `mi` is a macro invocation that is an assertion that should be generated
164+
* in the control-flow graph at `s`.
165+
*/
166+
predicate assertion(MacroInvocation mi, Stmt s) {
167+
assertion0(mi, s) and
168+
hasAssertionOperand(mi, 0) and
169+
hasAssertionOperand(mi, 1)
170+
}
171+
172+
/** The translation of an operand of an assertion. */
173+
abstract private class TranslatedAssertionOperand extends TranslatedElement,
174+
TTranslatedAssertionOperand
175+
{
176+
MacroInvocation mi;
177+
int index;
178+
179+
TranslatedAssertionOperand() { this = TTranslatedAssertionOperand(mi, index) }
180+
181+
MacroInvocation getMacroInvocation() { result = mi }
182+
183+
/**
184+
* Gets the statement that is being replaced by the assertion that uses this
185+
* operand.
186+
*/
187+
Stmt getStmt() { assertion(mi, result) }
188+
189+
final override Locatable getAst() { result = this.getStmt() }
190+
191+
final override TranslatedElement getChild(int id) { none() }
192+
193+
final override Declaration getFunction() { result = this.getStmt().getEnclosingFunction() }
194+
195+
/** Gets the instruction which holds the result of this operand. */
196+
abstract Instruction getResult();
197+
198+
final override string toString() { result = "Operand of assertion: " + mi }
199+
200+
/** Gets the index of this operand (i.e., `0` or `1`). */
201+
final int getIndex() { result = index }
202+
}
203+
204+
/** An operand of an assertion that is a variable access. */
205+
private class TranslatedAssertionVarAccess extends TranslatedAssertionOperand {
206+
TranslatedAssertionVarAccess() { hasVarAccessMacroArgument(mi, _, index, _) }
207+
208+
Variable getVariable() { hasVarAccessMacroArgument(mi, result, index, _) }
209+
210+
final override IRUserVariable getInstructionVariable(InstructionTag tag) {
211+
tag = AssertionVarAddressTag() and
212+
result.getVariable() = this.getVariable()
213+
}
214+
215+
final override Instruction getFirstInstruction(EdgeKind kind) {
216+
result = this.getInstruction(AssertionVarAddressTag()) and kind instanceof GotoEdge
217+
}
218+
219+
final override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
220+
tag = AssertionVarAddressTag() and
221+
kind instanceof GotoEdge and
222+
result = this.getInstruction(AssertionVarLoadTag())
223+
or
224+
tag = AssertionVarLoadTag() and
225+
result = getTranslatedAssertionMacroInvocation(mi).getChildSuccessor(this, kind)
226+
}
227+
228+
final override Instruction getALastInstructionInternal() {
229+
result = this.getInstruction(AssertionVarLoadTag())
230+
}
231+
232+
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
233+
exists(Variable v | v = this.getVariable() |
234+
opcode instanceof Opcode::VariableAddress and
235+
tag = AssertionVarAddressTag() and
236+
resultType = getTypeForGLValue(v.getType())
237+
or
238+
opcode instanceof Opcode::Load and
239+
tag = AssertionVarLoadTag() and
240+
resultType = getTypeForPRValue(v.getType())
241+
)
242+
}
243+
244+
final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
245+
tag = AssertionVarLoadTag() and
246+
operandTag instanceof AddressOperandTag and
247+
result = this.getInstruction(AssertionVarAddressTag())
248+
}
249+
250+
final override Instruction getResult() { result = this.getInstruction(AssertionVarLoadTag()) }
251+
}
252+
253+
/** An operand of an assertion that is a constant access. */
254+
private class TranslatedAssertionConst extends TranslatedAssertionOperand {
255+
TranslatedAssertionConst() { hasConstMacroArgument(mi, _, index, _) }
256+
257+
int getConst() { hasConstMacroArgument(mi, result, index, _) }
258+
259+
final override string getInstructionConstantValue(InstructionTag tag) {
260+
tag = OnlyInstructionTag() and
261+
result = this.getConst().toString()
262+
}
263+
264+
final override Instruction getFirstInstruction(EdgeKind kind) {
265+
result = this.getInstruction(OnlyInstructionTag()) and
266+
kind instanceof GotoEdge
267+
}
268+
269+
final override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
270+
tag = OnlyInstructionTag() and
271+
result = getTranslatedAssertionMacroInvocation(mi).getChildSuccessor(this, kind)
272+
}
273+
274+
final override Instruction getALastInstructionInternal() {
275+
result = this.getInstruction(OnlyInstructionTag())
276+
}
277+
278+
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
279+
opcode instanceof Opcode::Constant and
280+
tag = OnlyInstructionTag() and
281+
resultType = getIntType()
282+
}
283+
284+
final override Instruction getResult() { result = this.getInstruction(OnlyInstructionTag()) }
285+
}
286+
287+
/**
288+
* Gets the `TranslatedAssertionMacroInvocation` corresponding to the macro
289+
* invocation `mi`.
290+
*/
291+
TranslatedAssertionMacroInvocation getTranslatedAssertionMacroInvocation(MacroInvocation mi) {
292+
result.getMacroInvocation() = mi
293+
}
294+
295+
/**
296+
* A synthesized assertion which would have otherwise been invisible because the
297+
* database represents a release build where assertions are disabled.
298+
*/
299+
private class TranslatedAssertionMacroInvocation extends TranslatedStmt {
300+
MacroInvocation mi;
301+
302+
TranslatedAssertionMacroInvocation() { assertion(mi, stmt) }
303+
304+
final override Instruction getFirstInstruction(EdgeKind kind) {
305+
result = this.getLeft().getFirstInstruction(kind)
306+
}
307+
308+
TranslatedAssertionOperand getLeft() {
309+
result.getMacroInvocation() = mi and
310+
result.getIndex() = 0
311+
}
312+
313+
TranslatedAssertionOperand getRight() {
314+
result.getMacroInvocation() = mi and
315+
result.getIndex() = 1
316+
}
317+
318+
final override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
319+
tag = AssertionOpTag() and
320+
kind instanceof GotoEdge and
321+
result = this.getInstruction(AssertionBranchTag())
322+
or
323+
tag = AssertionBranchTag() and
324+
kind instanceof TrueEdge and
325+
result = this.getParent().getChildSuccessor(this, _)
326+
}
327+
328+
final override TranslatedElement getChildInternal(int id) {
329+
id = 0 and result = this.getLeft()
330+
or
331+
id = 1 and result = this.getRight()
332+
}
333+
334+
final override Instruction getALastInstructionInternal() {
335+
result = this.getInstruction(AssertionBranchTag())
336+
}
337+
338+
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
339+
tag = AssertionOpTag() and
340+
resultType = getBoolType() and
341+
hasAssertionOpcode(mi, opcode)
342+
or
343+
tag = AssertionBranchTag() and
344+
resultType = getVoidType() and
345+
opcode instanceof Opcode::ConditionalBranch
346+
}
347+
348+
final override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
349+
child = this.getLeft() and
350+
result = this.getRight().getFirstInstruction(kind)
351+
or
352+
child = this.getRight() and
353+
kind instanceof GotoEdge and
354+
result = this.getInstruction(AssertionOpTag())
355+
}
356+
357+
final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
358+
tag = AssertionOpTag() and
359+
(
360+
operandTag instanceof LeftOperandTag and
361+
result = this.getLeft().getResult()
362+
or
363+
operandTag instanceof RightOperandTag and
364+
result = this.getRight().getResult()
365+
)
366+
or
367+
tag = AssertionBranchTag() and
368+
operandTag instanceof ConditionOperandTag and
369+
result = this.getInstruction(AssertionOpTag())
370+
}
371+
372+
MacroInvocation getMacroInvocation() { result = mi }
373+
}

0 commit comments

Comments
 (0)