Skip to content

C++: Handle casts to void in IR #67

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 20, 2018
Merged
Show file tree
Hide file tree
Changes from all 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
14 changes: 14 additions & 0 deletions cpp/ql/src/semmle/code/cpp/exprs/Cast.qll
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,20 @@ class BoolConversion extends Cast {
}
}

/**
* A conversion to `void`.
*/
class VoidConversion extends Cast {
VoidConversion() {
conversionkinds(this, 0) and
getType().getUnspecifiedType() instanceof VoidType
}

override string getSemanticConversionString() {
result = "conversion to void"
}
}

/**
* A conversion between two pointers or glvalues related by inheritance. The
* base class will always be either a direct base class of the derived class,
Expand Down
38 changes: 35 additions & 3 deletions cpp/ql/src/semmle/code/cpp/ir/internal/Instruction.qll
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,40 @@ module InstructionSanity {
not tag instanceof UnmodeledUseOperand
}

/**
* Holds if `Phi` instruction `instr` is missing an operand corresponding to
* the predecessor block `pred`.
*/
query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
pred = instr.getBlock().getAPredecessor() and
not exists(PhiOperand operand |
exists(instr.getOperand(operand)) and
operand.getPredecessorBlock() = pred
)
}

/**
* Holds if an instruction, other than `ExitFunction`, has no successors.
*/
query predicate instructionWithoutSuccessor(Instruction instr) {
not exists(instr.getASuccessor()) and
not instr instanceof ExitFunctionInstruction and
// Phi instructions aren't linked into the instruction-level flow graph.
not instr instanceof PhiInstruction
}

/**
* Holds if a `Phi` instruction is present in a block with fewer than two
* predecessors.
*/
query predicate unnecessaryPhiInstruction(PhiInstruction instr) {
count(instr.getBlock().getAPredecessor()) < 2
}

/**
* Holds if instruction `op` consumes an operand `operand` that was defined in
* a different function.
*/
query predicate operandAcrossFunctions(
Instruction op, Instruction operand, OperandTag tag
) {
Expand Down Expand Up @@ -296,8 +330,7 @@ class Instruction extends Construction::TInstruction {

/**
* Gets the size of the result produced by this instruction, in bytes. If the
* instruction does not produce a result, or if the result does not have a
* known constant size, this predicate does not hold.
* result does not have a known constant size, this predicate does not hold.
*
* If `this.isGLValue()` holds for this instruction, the value of
* `getResultSize()` will always be the size of a pointer.
Expand All @@ -312,7 +345,6 @@ class Instruction extends Construction::TInstruction {
else if resultType instanceof UnknownType then
result = Construction::getInstructionResultSize(this)
else (
not resultType instanceof VoidType and
result = resultType.getSize()
)
}
Expand Down
6 changes: 4 additions & 2 deletions cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedElement.qll
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ private predicate ignoreElement(Element element) {
* a value.
*/
private predicate isNativeCondition(Expr expr) {
expr instanceof BinaryLogicalOperation
expr instanceof BinaryLogicalOperation and
not expr.isConstant()
}

/**
Expand All @@ -101,7 +102,8 @@ private predicate isFlexibleCondition(Expr expr) {
expr instanceof ParenthesisExpr or
expr instanceof NotExpr
) and
usedAsCondition(expr)
usedAsCondition(expr) and
not expr.isConstant()
}

/**
Expand Down
3 changes: 2 additions & 1 deletion cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedExpr.qll
Original file line number Diff line number Diff line change
Expand Up @@ -1139,7 +1139,8 @@ class TranslatedSimpleConversion extends TranslatedSingleInstructionConversion {
conv instanceof IntegralToPointerConversion or
conv instanceof GlvalueConversion or
conv instanceof ArrayToPointerConversion or
conv instanceof PrvalueAdjustmentConversion
conv instanceof PrvalueAdjustmentConversion or
conv instanceof VoidConversion
}

override Opcode getOpcode() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,40 @@ module InstructionSanity {
not tag instanceof UnmodeledUseOperand
}

/**
* Holds if `Phi` instruction `instr` is missing an operand corresponding to
* the predecessor block `pred`.
*/
query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
pred = instr.getBlock().getAPredecessor() and
not exists(PhiOperand operand |
exists(instr.getOperand(operand)) and
operand.getPredecessorBlock() = pred
)
}

/**
* Holds if an instruction, other than `ExitFunction`, has no successors.
*/
query predicate instructionWithoutSuccessor(Instruction instr) {
not exists(instr.getASuccessor()) and
not instr instanceof ExitFunctionInstruction and
// Phi instructions aren't linked into the instruction-level flow graph.
not instr instanceof PhiInstruction
}

/**
* Holds if a `Phi` instruction is present in a block with fewer than two
* predecessors.
*/
query predicate unnecessaryPhiInstruction(PhiInstruction instr) {
count(instr.getBlock().getAPredecessor()) < 2
}

/**
* Holds if instruction `op` consumes an operand `operand` that was defined in
* a different function.
*/
query predicate operandAcrossFunctions(
Instruction op, Instruction operand, OperandTag tag
) {
Expand Down Expand Up @@ -296,8 +330,7 @@ class Instruction extends Construction::TInstruction {

/**
* Gets the size of the result produced by this instruction, in bytes. If the
* instruction does not produce a result, or if the result does not have a
* known constant size, this predicate does not hold.
* result does not have a known constant size, this predicate does not hold.
*
* If `this.isGLValue()` holds for this instruction, the value of
* `getResultSize()` will always be the size of a pointer.
Expand All @@ -312,7 +345,6 @@ class Instruction extends Construction::TInstruction {
else if resultType instanceof UnknownType then
result = Construction::getInstructionResultSize(this)
else (
not resultType instanceof VoidType and
result = resultType.getSize()
)
}
Expand Down
38 changes: 35 additions & 3 deletions cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/Instruction.qll
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,40 @@ module InstructionSanity {
not tag instanceof UnmodeledUseOperand
}

/**
* Holds if `Phi` instruction `instr` is missing an operand corresponding to
* the predecessor block `pred`.
*/
query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
pred = instr.getBlock().getAPredecessor() and
not exists(PhiOperand operand |
exists(instr.getOperand(operand)) and
operand.getPredecessorBlock() = pred
)
}

/**
* Holds if an instruction, other than `ExitFunction`, has no successors.
*/
query predicate instructionWithoutSuccessor(Instruction instr) {
not exists(instr.getASuccessor()) and
not instr instanceof ExitFunctionInstruction and
// Phi instructions aren't linked into the instruction-level flow graph.
not instr instanceof PhiInstruction
}

/**
* Holds if a `Phi` instruction is present in a block with fewer than two
* predecessors.
*/
query predicate unnecessaryPhiInstruction(PhiInstruction instr) {
count(instr.getBlock().getAPredecessor()) < 2
}

/**
* Holds if instruction `op` consumes an operand `operand` that was defined in
* a different function.
*/
query predicate operandAcrossFunctions(
Instruction op, Instruction operand, OperandTag tag
) {
Expand Down Expand Up @@ -296,8 +330,7 @@ class Instruction extends Construction::TInstruction {

/**
* Gets the size of the result produced by this instruction, in bytes. If the
* instruction does not produce a result, or if the result does not have a
* known constant size, this predicate does not hold.
* result does not have a known constant size, this predicate does not hold.
*
* If `this.isGLValue()` holds for this instruction, the value of
* `getResultSize()` will always be the size of a pointer.
Expand All @@ -312,7 +345,6 @@ class Instruction extends Construction::TInstruction {
else if resultType instanceof UnknownType then
result = Construction::getInstructionResultSize(this)
else (
not resultType instanceof VoidType and
result = resultType.getSize()
)
}
Expand Down
13 changes: 13 additions & 0 deletions cpp/ql/test/library-tests/conversions/conversions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,16 @@ void FuncPtrConversions(int(*pfn)(int), void* p) {
p = (void*)pfn;
pfn = (int(*)(int))p;
}

int Func();

void ConversionsToVoid() {
int x;
(void)x;
static_cast<void>(x);
(void)Func();
static_cast<void>(Func());
(void)1;
static_cast<void>(1);
}

6 changes: 6 additions & 0 deletions cpp/ql/test/library-tests/conversions/conversions.expected
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,9 @@
| conversions.cpp:231:28:231:63 | dynamic_cast<PolymorphicDerived>... | dynamic_cast | lval | PolymorphicDerived | PolymorphicBase |
| conversions.cpp:235:7:235:16 | (void *)... | pointer conversion | prval | void * | ..(*)(..) |
| conversions.cpp:236:9:236:22 | (..(*)(..))... | pointer conversion | prval | ..(*)(..) | void * |
| conversions.cpp:243:3:243:9 | (void)... | conversion to void | prval | void | int |
| conversions.cpp:244:3:244:22 | static_cast<void>... | conversion to void | prval | void | int |
| conversions.cpp:245:3:245:14 | (void)... | conversion to void | prval | void | int |
| conversions.cpp:246:3:246:27 | static_cast<void>... | conversion to void | prval | void | int |
| conversions.cpp:247:3:247:9 | (void)... | conversion to void | prval | void | int |
| conversions.cpp:248:3:248:22 | static_cast<void>... | conversion to void | prval | void | int |
3 changes: 3 additions & 0 deletions cpp/ql/test/library-tests/ir/ir/AliasedSSAIRSanity.expected
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
missingOperand
unexpectedOperand
duplicateOperand
missingPhiOperand
instructionWithoutSuccessor
unnecessaryPhiInstruction
operandAcrossFunctions
3 changes: 3 additions & 0 deletions cpp/ql/test/library-tests/ir/ir/IRSanity.expected
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
missingOperand
unexpectedOperand
duplicateOperand
missingPhiOperand
instructionWithoutSuccessor
unnecessaryPhiInstruction
operandAcrossFunctions
57 changes: 57 additions & 0 deletions cpp/ql/test/library-tests/ir/ir/PrintAST.expected
Original file line number Diff line number Diff line change
Expand Up @@ -5900,3 +5900,60 @@ ir.cpp:
# 897| Type = __va_list_tag[1]
# 897| ValueCategory = lvalue
# 898| 8: return ...
# 900| CastToVoid(int) -> void
# 900| params:
# 900| 0: x
# 900| Type = int
# 900| body: { ... }
# 901| 0: ExprStmt
# 901| 0: (void)...
# 901| Conversion = conversion to void
# 901| Type = void
# 901| ValueCategory = prvalue
# 901| expr: x
# 901| Type = int
# 901| ValueCategory = lvalue
# 902| 1: return ...
# 904| ConstantConditions(int) -> void
# 904| params:
# 904| 0: x
# 904| Type = int
# 904| body: { ... }
# 905| 0: declaration
# 905| 0: definition of a
# 905| Type = bool
# 905| init: initializer for a
# 905| expr: ... && ...
# 905| Type = bool
# 905| Value = 1
# 905| ValueCategory = prvalue
# 905| 0: 1
# 905| Type = bool
# 905| Value = 1
# 905| ValueCategory = prvalue
# 905| 1: 1
# 905| Type = bool
# 905| Value = 1
# 905| ValueCategory = prvalue
# 906| 1: declaration
# 906| 0: definition of b
# 906| Type = int
# 906| init: initializer for b
# 906| expr: ... ? ... : ...
# 906| Type = int
# 906| ValueCategory = prvalue
# 906| 0: (...)
# 906| Type = bool
# 906| Value = 1
# 906| ValueCategory = prvalue
# 906| expr: 1
# 906| Type = bool
# 906| Value = 1
# 906| ValueCategory = prvalue
# 906| 1: x
# 906| Type = int
# 906| ValueCategory = prvalue(load)
# 906| 2: x
# 906| Type = int
# 906| ValueCategory = prvalue(load)
# 907| 2: return ...
3 changes: 3 additions & 0 deletions cpp/ql/test/library-tests/ir/ir/SSAIRSanity.expected
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
missingOperand
unexpectedOperand
duplicateOperand
missingPhiOperand
instructionWithoutSuccessor
unnecessaryPhiInstruction
operandAcrossFunctions
54 changes: 54 additions & 0 deletions cpp/ql/test/library-tests/ir/ir/aliased_ssa_ir.expected
Original file line number Diff line number Diff line change
Expand Up @@ -3846,3 +3846,57 @@ ir.cpp:
# 888| v0_40(void) = ReturnVoid :
# 888| v0_41(void) = UnmodeledUse : mu*
# 888| v0_42(void) = ExitFunction :

# 900| CastToVoid(int) -> void
# 900| Block 0
# 900| v0_0(void) = EnterFunction :
# 900| mu0_1(unknown) = UnmodeledDefinition :
# 900| r0_2(int) = InitializeParameter[x] :
# 900| r0_3(glval<int>) = VariableAddress[x] :
# 900| mu0_4(int) = Store : r0_3, r0_2
# 901| r0_5(glval<int>) = VariableAddress[x] :
# 901| v0_6(void) = Convert : r0_5
# 902| v0_7(void) = NoOp :
# 900| v0_8(void) = ReturnVoid :
# 900| v0_9(void) = UnmodeledUse : mu*
# 900| v0_10(void) = ExitFunction :

# 904| ConstantConditions(int) -> void
# 904| Block 0
# 904| v0_0(void) = EnterFunction :
# 904| mu0_1(unknown) = UnmodeledDefinition :
# 904| r0_2(int) = InitializeParameter[x] :
# 904| r0_3(glval<int>) = VariableAddress[x] :
# 904| m0_4(int) = Store : r0_3, r0_2
# 905| r0_5(glval<bool>) = VariableAddress[a] :
# 905| r0_6(bool) = Constant[1] :
# 905| m0_7(bool) = Store : r0_5, r0_6
# 906| r0_8(glval<int>) = VariableAddress[b] :
# 906| r0_9(bool) = Constant[1] :
# 906| v0_10(void) = ConditionalBranch : r0_9
#-----| False -> Block 3
#-----| True -> Block 2

# 906| Block 1
# 906| m1_0(int) = Phi : from 2:m2_3, from 3:m3_3
# 906| r1_1(glval<int>) = VariableAddress[#temp906:11] :
# 906| r1_2(int) = Load : r1_1, m1_0
# 906| m1_3(int) = Store : r0_8, r1_2
# 907| v1_4(void) = NoOp :
# 904| v1_5(void) = ReturnVoid :
# 904| v1_6(void) = UnmodeledUse : mu*
# 904| v1_7(void) = ExitFunction :

# 906| Block 2
# 906| r2_0(glval<int>) = VariableAddress[x] :
# 906| r2_1(int) = Load : r2_0, m0_4
# 906| r2_2(glval<int>) = VariableAddress[#temp906:11] :
# 906| m2_3(int) = Store : r2_2, r2_1
#-----| Goto -> Block 1

# 906| Block 3
# 906| r3_0(glval<int>) = VariableAddress[x] :
# 906| r3_1(int) = Load : r3_0, m0_4
# 906| r3_2(glval<int>) = VariableAddress[#temp906:11] :
# 906| m3_3(int) = Store : r3_2, r3_1
#-----| Goto -> Block 1
Loading