Skip to content

Commit b931e88

Browse files
authored
Merge pull request #67 from dave-bartolomeo/dave/CastToVoid
C++: Handle casts to `void` in IR
2 parents 5e6f34f + 332e944 commit b931e88

File tree

17 files changed

+382
-12
lines changed

17 files changed

+382
-12
lines changed

cpp/ql/src/semmle/code/cpp/exprs/Cast.qll

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,20 @@ class BoolConversion extends Cast {
292292
}
293293
}
294294

295+
/**
296+
* A conversion to `void`.
297+
*/
298+
class VoidConversion extends Cast {
299+
VoidConversion() {
300+
conversionkinds(this, 0) and
301+
getType().getUnspecifiedType() instanceof VoidType
302+
}
303+
304+
override string getSemanticConversionString() {
305+
result = "conversion to void"
306+
}
307+
}
308+
295309
/**
296310
* A conversion between two pointers or glvalues related by inheritance. The
297311
* base class will always be either a direct base class of the derived class,

cpp/ql/src/semmle/code/cpp/ir/internal/Instruction.qll

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,40 @@ module InstructionSanity {
6767
not tag instanceof UnmodeledUseOperand
6868
}
6969

70+
/**
71+
* Holds if `Phi` instruction `instr` is missing an operand corresponding to
72+
* the predecessor block `pred`.
73+
*/
74+
query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
75+
pred = instr.getBlock().getAPredecessor() and
76+
not exists(PhiOperand operand |
77+
exists(instr.getOperand(operand)) and
78+
operand.getPredecessorBlock() = pred
79+
)
80+
}
81+
82+
/**
83+
* Holds if an instruction, other than `ExitFunction`, has no successors.
84+
*/
85+
query predicate instructionWithoutSuccessor(Instruction instr) {
86+
not exists(instr.getASuccessor()) and
87+
not instr instanceof ExitFunctionInstruction and
88+
// Phi instructions aren't linked into the instruction-level flow graph.
89+
not instr instanceof PhiInstruction
90+
}
91+
92+
/**
93+
* Holds if a `Phi` instruction is present in a block with fewer than two
94+
* predecessors.
95+
*/
96+
query predicate unnecessaryPhiInstruction(PhiInstruction instr) {
97+
count(instr.getBlock().getAPredecessor()) < 2
98+
}
99+
100+
/**
101+
* Holds if instruction `op` consumes an operand `operand` that was defined in
102+
* a different function.
103+
*/
70104
query predicate operandAcrossFunctions(
71105
Instruction op, Instruction operand, OperandTag tag
72106
) {
@@ -296,8 +330,7 @@ class Instruction extends Construction::TInstruction {
296330

297331
/**
298332
* Gets the size of the result produced by this instruction, in bytes. If the
299-
* instruction does not produce a result, or if the result does not have a
300-
* known constant size, this predicate does not hold.
333+
* result does not have a known constant size, this predicate does not hold.
301334
*
302335
* If `this.isGLValue()` holds for this instruction, the value of
303336
* `getResultSize()` will always be the size of a pointer.
@@ -312,7 +345,6 @@ class Instruction extends Construction::TInstruction {
312345
else if resultType instanceof UnknownType then
313346
result = Construction::getInstructionResultSize(this)
314347
else (
315-
not resultType instanceof VoidType and
316348
result = resultType.getSize()
317349
)
318350
}

cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedElement.qll

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ private predicate ignoreElement(Element element) {
8989
* a value.
9090
*/
9191
private predicate isNativeCondition(Expr expr) {
92-
expr instanceof BinaryLogicalOperation
92+
expr instanceof BinaryLogicalOperation and
93+
not expr.isConstant()
9394
}
9495

9596
/**
@@ -101,7 +102,8 @@ private predicate isFlexibleCondition(Expr expr) {
101102
expr instanceof ParenthesisExpr or
102103
expr instanceof NotExpr
103104
) and
104-
usedAsCondition(expr)
105+
usedAsCondition(expr) and
106+
not expr.isConstant()
105107
}
106108

107109
/**

cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedExpr.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1139,7 +1139,8 @@ class TranslatedSimpleConversion extends TranslatedSingleInstructionConversion {
11391139
conv instanceof IntegralToPointerConversion or
11401140
conv instanceof GlvalueConversion or
11411141
conv instanceof ArrayToPointerConversion or
1142-
conv instanceof PrvalueAdjustmentConversion
1142+
conv instanceof PrvalueAdjustmentConversion or
1143+
conv instanceof VoidConversion
11431144
}
11441145

11451146
override Opcode getOpcode() {

cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/Instruction.qll

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,40 @@ module InstructionSanity {
6767
not tag instanceof UnmodeledUseOperand
6868
}
6969

70+
/**
71+
* Holds if `Phi` instruction `instr` is missing an operand corresponding to
72+
* the predecessor block `pred`.
73+
*/
74+
query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
75+
pred = instr.getBlock().getAPredecessor() and
76+
not exists(PhiOperand operand |
77+
exists(instr.getOperand(operand)) and
78+
operand.getPredecessorBlock() = pred
79+
)
80+
}
81+
82+
/**
83+
* Holds if an instruction, other than `ExitFunction`, has no successors.
84+
*/
85+
query predicate instructionWithoutSuccessor(Instruction instr) {
86+
not exists(instr.getASuccessor()) and
87+
not instr instanceof ExitFunctionInstruction and
88+
// Phi instructions aren't linked into the instruction-level flow graph.
89+
not instr instanceof PhiInstruction
90+
}
91+
92+
/**
93+
* Holds if a `Phi` instruction is present in a block with fewer than two
94+
* predecessors.
95+
*/
96+
query predicate unnecessaryPhiInstruction(PhiInstruction instr) {
97+
count(instr.getBlock().getAPredecessor()) < 2
98+
}
99+
100+
/**
101+
* Holds if instruction `op` consumes an operand `operand` that was defined in
102+
* a different function.
103+
*/
70104
query predicate operandAcrossFunctions(
71105
Instruction op, Instruction operand, OperandTag tag
72106
) {
@@ -296,8 +330,7 @@ class Instruction extends Construction::TInstruction {
296330

297331
/**
298332
* Gets the size of the result produced by this instruction, in bytes. If the
299-
* instruction does not produce a result, or if the result does not have a
300-
* known constant size, this predicate does not hold.
333+
* result does not have a known constant size, this predicate does not hold.
301334
*
302335
* If `this.isGLValue()` holds for this instruction, the value of
303336
* `getResultSize()` will always be the size of a pointer.
@@ -312,7 +345,6 @@ class Instruction extends Construction::TInstruction {
312345
else if resultType instanceof UnknownType then
313346
result = Construction::getInstructionResultSize(this)
314347
else (
315-
not resultType instanceof VoidType and
316348
result = resultType.getSize()
317349
)
318350
}

cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/Instruction.qll

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,40 @@ module InstructionSanity {
6767
not tag instanceof UnmodeledUseOperand
6868
}
6969

70+
/**
71+
* Holds if `Phi` instruction `instr` is missing an operand corresponding to
72+
* the predecessor block `pred`.
73+
*/
74+
query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
75+
pred = instr.getBlock().getAPredecessor() and
76+
not exists(PhiOperand operand |
77+
exists(instr.getOperand(operand)) and
78+
operand.getPredecessorBlock() = pred
79+
)
80+
}
81+
82+
/**
83+
* Holds if an instruction, other than `ExitFunction`, has no successors.
84+
*/
85+
query predicate instructionWithoutSuccessor(Instruction instr) {
86+
not exists(instr.getASuccessor()) and
87+
not instr instanceof ExitFunctionInstruction and
88+
// Phi instructions aren't linked into the instruction-level flow graph.
89+
not instr instanceof PhiInstruction
90+
}
91+
92+
/**
93+
* Holds if a `Phi` instruction is present in a block with fewer than two
94+
* predecessors.
95+
*/
96+
query predicate unnecessaryPhiInstruction(PhiInstruction instr) {
97+
count(instr.getBlock().getAPredecessor()) < 2
98+
}
99+
100+
/**
101+
* Holds if instruction `op` consumes an operand `operand` that was defined in
102+
* a different function.
103+
*/
70104
query predicate operandAcrossFunctions(
71105
Instruction op, Instruction operand, OperandTag tag
72106
) {
@@ -296,8 +330,7 @@ class Instruction extends Construction::TInstruction {
296330

297331
/**
298332
* Gets the size of the result produced by this instruction, in bytes. If the
299-
* instruction does not produce a result, or if the result does not have a
300-
* known constant size, this predicate does not hold.
333+
* result does not have a known constant size, this predicate does not hold.
301334
*
302335
* If `this.isGLValue()` holds for this instruction, the value of
303336
* `getResultSize()` will always be the size of a pointer.
@@ -312,7 +345,6 @@ class Instruction extends Construction::TInstruction {
312345
else if resultType instanceof UnknownType then
313346
result = Construction::getInstructionResultSize(this)
314347
else (
315-
not resultType instanceof VoidType and
316348
result = resultType.getSize()
317349
)
318350
}

cpp/ql/test/library-tests/conversions/conversions.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,3 +235,16 @@ void FuncPtrConversions(int(*pfn)(int), void* p) {
235235
p = (void*)pfn;
236236
pfn = (int(*)(int))p;
237237
}
238+
239+
int Func();
240+
241+
void ConversionsToVoid() {
242+
int x;
243+
(void)x;
244+
static_cast<void>(x);
245+
(void)Func();
246+
static_cast<void>(Func());
247+
(void)1;
248+
static_cast<void>(1);
249+
}
250+

cpp/ql/test/library-tests/conversions/conversions.expected

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,9 @@
143143
| conversions.cpp:231:28:231:63 | dynamic_cast<PolymorphicDerived>... | dynamic_cast | lval | PolymorphicDerived | PolymorphicBase |
144144
| conversions.cpp:235:7:235:16 | (void *)... | pointer conversion | prval | void * | ..(*)(..) |
145145
| conversions.cpp:236:9:236:22 | (..(*)(..))... | pointer conversion | prval | ..(*)(..) | void * |
146+
| conversions.cpp:243:3:243:9 | (void)... | conversion to void | prval | void | int |
147+
| conversions.cpp:244:3:244:22 | static_cast<void>... | conversion to void | prval | void | int |
148+
| conversions.cpp:245:3:245:14 | (void)... | conversion to void | prval | void | int |
149+
| conversions.cpp:246:3:246:27 | static_cast<void>... | conversion to void | prval | void | int |
150+
| conversions.cpp:247:3:247:9 | (void)... | conversion to void | prval | void | int |
151+
| conversions.cpp:248:3:248:22 | static_cast<void>... | conversion to void | prval | void | int |
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
missingOperand
22
unexpectedOperand
33
duplicateOperand
4+
missingPhiOperand
5+
instructionWithoutSuccessor
6+
unnecessaryPhiInstruction
47
operandAcrossFunctions
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
missingOperand
22
unexpectedOperand
33
duplicateOperand
4+
missingPhiOperand
5+
instructionWithoutSuccessor
6+
unnecessaryPhiInstruction
47
operandAcrossFunctions

cpp/ql/test/library-tests/ir/ir/PrintAST.expected

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5900,3 +5900,60 @@ ir.cpp:
59005900
# 897| Type = __va_list_tag[1]
59015901
# 897| ValueCategory = lvalue
59025902
# 898| 8: return ...
5903+
# 900| CastToVoid(int) -> void
5904+
# 900| params:
5905+
# 900| 0: x
5906+
# 900| Type = int
5907+
# 900| body: { ... }
5908+
# 901| 0: ExprStmt
5909+
# 901| 0: (void)...
5910+
# 901| Conversion = conversion to void
5911+
# 901| Type = void
5912+
# 901| ValueCategory = prvalue
5913+
# 901| expr: x
5914+
# 901| Type = int
5915+
# 901| ValueCategory = lvalue
5916+
# 902| 1: return ...
5917+
# 904| ConstantConditions(int) -> void
5918+
# 904| params:
5919+
# 904| 0: x
5920+
# 904| Type = int
5921+
# 904| body: { ... }
5922+
# 905| 0: declaration
5923+
# 905| 0: definition of a
5924+
# 905| Type = bool
5925+
# 905| init: initializer for a
5926+
# 905| expr: ... && ...
5927+
# 905| Type = bool
5928+
# 905| Value = 1
5929+
# 905| ValueCategory = prvalue
5930+
# 905| 0: 1
5931+
# 905| Type = bool
5932+
# 905| Value = 1
5933+
# 905| ValueCategory = prvalue
5934+
# 905| 1: 1
5935+
# 905| Type = bool
5936+
# 905| Value = 1
5937+
# 905| ValueCategory = prvalue
5938+
# 906| 1: declaration
5939+
# 906| 0: definition of b
5940+
# 906| Type = int
5941+
# 906| init: initializer for b
5942+
# 906| expr: ... ? ... : ...
5943+
# 906| Type = int
5944+
# 906| ValueCategory = prvalue
5945+
# 906| 0: (...)
5946+
# 906| Type = bool
5947+
# 906| Value = 1
5948+
# 906| ValueCategory = prvalue
5949+
# 906| expr: 1
5950+
# 906| Type = bool
5951+
# 906| Value = 1
5952+
# 906| ValueCategory = prvalue
5953+
# 906| 1: x
5954+
# 906| Type = int
5955+
# 906| ValueCategory = prvalue(load)
5956+
# 906| 2: x
5957+
# 906| Type = int
5958+
# 906| ValueCategory = prvalue(load)
5959+
# 907| 2: return ...
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
missingOperand
22
unexpectedOperand
33
duplicateOperand
4+
missingPhiOperand
5+
instructionWithoutSuccessor
6+
unnecessaryPhiInstruction
47
operandAcrossFunctions

cpp/ql/test/library-tests/ir/ir/aliased_ssa_ir.expected

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3846,3 +3846,57 @@ ir.cpp:
38463846
# 888| v0_40(void) = ReturnVoid :
38473847
# 888| v0_41(void) = UnmodeledUse : mu*
38483848
# 888| v0_42(void) = ExitFunction :
3849+
3850+
# 900| CastToVoid(int) -> void
3851+
# 900| Block 0
3852+
# 900| v0_0(void) = EnterFunction :
3853+
# 900| mu0_1(unknown) = UnmodeledDefinition :
3854+
# 900| r0_2(int) = InitializeParameter[x] :
3855+
# 900| r0_3(glval<int>) = VariableAddress[x] :
3856+
# 900| mu0_4(int) = Store : r0_3, r0_2
3857+
# 901| r0_5(glval<int>) = VariableAddress[x] :
3858+
# 901| v0_6(void) = Convert : r0_5
3859+
# 902| v0_7(void) = NoOp :
3860+
# 900| v0_8(void) = ReturnVoid :
3861+
# 900| v0_9(void) = UnmodeledUse : mu*
3862+
# 900| v0_10(void) = ExitFunction :
3863+
3864+
# 904| ConstantConditions(int) -> void
3865+
# 904| Block 0
3866+
# 904| v0_0(void) = EnterFunction :
3867+
# 904| mu0_1(unknown) = UnmodeledDefinition :
3868+
# 904| r0_2(int) = InitializeParameter[x] :
3869+
# 904| r0_3(glval<int>) = VariableAddress[x] :
3870+
# 904| m0_4(int) = Store : r0_3, r0_2
3871+
# 905| r0_5(glval<bool>) = VariableAddress[a] :
3872+
# 905| r0_6(bool) = Constant[1] :
3873+
# 905| m0_7(bool) = Store : r0_5, r0_6
3874+
# 906| r0_8(glval<int>) = VariableAddress[b] :
3875+
# 906| r0_9(bool) = Constant[1] :
3876+
# 906| v0_10(void) = ConditionalBranch : r0_9
3877+
#-----| False -> Block 3
3878+
#-----| True -> Block 2
3879+
3880+
# 906| Block 1
3881+
# 906| m1_0(int) = Phi : from 2:m2_3, from 3:m3_3
3882+
# 906| r1_1(glval<int>) = VariableAddress[#temp906:11] :
3883+
# 906| r1_2(int) = Load : r1_1, m1_0
3884+
# 906| m1_3(int) = Store : r0_8, r1_2
3885+
# 907| v1_4(void) = NoOp :
3886+
# 904| v1_5(void) = ReturnVoid :
3887+
# 904| v1_6(void) = UnmodeledUse : mu*
3888+
# 904| v1_7(void) = ExitFunction :
3889+
3890+
# 906| Block 2
3891+
# 906| r2_0(glval<int>) = VariableAddress[x] :
3892+
# 906| r2_1(int) = Load : r2_0, m0_4
3893+
# 906| r2_2(glval<int>) = VariableAddress[#temp906:11] :
3894+
# 906| m2_3(int) = Store : r2_2, r2_1
3895+
#-----| Goto -> Block 1
3896+
3897+
# 906| Block 3
3898+
# 906| r3_0(glval<int>) = VariableAddress[x] :
3899+
# 906| r3_1(int) = Load : r3_0, m0_4
3900+
# 906| r3_2(glval<int>) = VariableAddress[#temp906:11] :
3901+
# 906| m3_3(int) = Store : r3_2, r3_1
3902+
#-----| Goto -> Block 1

0 commit comments

Comments
 (0)