C++: handle calls to noreturn functions

This commit is contained in:
Robert Marsh
2022-12-02 16:59:12 -05:00
parent 383b2e183d
commit c01ee597fa
20 changed files with 277 additions and 6 deletions

View File

@@ -19,6 +19,9 @@ newtype TInstruction =
) {
IRConstruction::Raw::hasInstruction(tag1, tag2)
} or
TRawUnreachedInstruction(IRFunctionBase irFunc) {
IRConstruction::hasUnreachedInstruction(irFunc)
} or
TUnaliasedSsaPhiInstruction(
TRawInstruction blockStartInstr, UnaliasedSsa::Ssa::MemoryLocation memoryLocation
) {

View File

@@ -178,7 +178,7 @@ module Raw {
}
}
class TStageInstruction = TRawInstruction;
class TStageInstruction = TRawInstruction or TRawUnreachedInstruction;
predicate hasInstruction(TRawInstruction instr) { any() }
@@ -393,6 +393,16 @@ Instruction getPrimaryInstructionForSideEffect(SideEffectInstruction instruction
.getPrimaryInstructionForSideEffect(getInstructionTag(instruction))
}
predicate hasUnreachedInstruction(IRFunction func) {
exists(Call c |
c.getEnclosingFunction() = func.getFunction() and
(
c.getTarget().hasSpecifier("_Noreturn") or
c.getTarget().getAnAttribute().hasName("noreturn")
)
)
}
import CachedForDebugging
cached

View File

@@ -34,6 +34,7 @@ newtype TInstructionTag =
CallTargetTag() or
CallTag() or
CallSideEffectTag() or
CallNoReturnTag() or
AllocationSizeTag() or
AllocationElementSizeTag() or
AllocationExtentConvertTag() or

View File

@@ -66,7 +66,9 @@ abstract class TranslatedCall extends TranslatedExpr {
)
or
child = getSideEffects() and
result = getParent().getChildSuccessor(this)
if this.isNoReturn()
then result = any(UnreachedInstruction instr | this.getEnclosingFunction().getFunction() = instr.getEnclosingFunction())
else result = getParent().getChildSuccessor(this)
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -161,6 +163,10 @@ abstract class TranslatedCall extends TranslatedExpr {
*/
abstract predicate hasArguments();
predicate isNoReturn() {
none()
}
final TranslatedSideEffects getSideEffects() { result.getExpr() = expr }
}

View File

@@ -7,6 +7,9 @@ predicate isInfeasibleInstructionSuccessor(Instruction instr, EdgeKind kind) {
conditionValue = getConstantValue(instr.(ConditionalBranchInstruction).getCondition()) and
if conditionValue = 0 then kind instanceof TrueEdge else kind instanceof FalseEdge
)
or
instr.getSuccessor(kind) instanceof UnreachedInstruction and
kind instanceof GotoEdge
}
pragma[noinline]
@@ -41,7 +44,9 @@ class ReachableBlock extends IRBlockBase {
* An instruction that is contained in a reachable block.
*/
class ReachableInstruction extends Instruction {
ReachableInstruction() { this.getBlock() instanceof ReachableBlock }
ReachableInstruction() {
this.getBlock() instanceof ReachableBlock and not this instanceof UnreachedInstruction
}
}
module Graph {

View File

@@ -34,9 +34,13 @@ private module Cached {
cached
predicate hasUnreachedInstructionCached(IRFunction irFunc) {
exists(OldInstruction oldInstruction |
exists(OldIR::Instruction oldInstruction |
irFunc = oldInstruction.getEnclosingIRFunction() and
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
(
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
or
oldInstruction.getOpcode() instanceof Opcode::Unreached
)
)
}

View File

@@ -7,6 +7,9 @@ predicate isInfeasibleInstructionSuccessor(Instruction instr, EdgeKind kind) {
conditionValue = getConstantValue(instr.(ConditionalBranchInstruction).getCondition()) and
if conditionValue = 0 then kind instanceof TrueEdge else kind instanceof FalseEdge
)
or
instr.getSuccessor(kind) instanceof UnreachedInstruction and
kind instanceof GotoEdge
}
pragma[noinline]
@@ -41,7 +44,9 @@ class ReachableBlock extends IRBlockBase {
* An instruction that is contained in a reachable block.
*/
class ReachableInstruction extends Instruction {
ReachableInstruction() { this.getBlock() instanceof ReachableBlock }
ReachableInstruction() {
this.getBlock() instanceof ReachableBlock and not this instanceof UnreachedInstruction
}
}
module Graph {

View File

@@ -14408,6 +14408,35 @@ ir.cpp:
# 1894| Conversion = [IntegralConversion] integral conversion
# 1894| Type = [IntType] int
# 1894| ValueCategory = prvalue
# 1897| [TopLevelFunction] void noreturnFunc()
# 1897| <params>:
# 1899| [TopLevelFunction] int noreturnTest(int)
# 1899| <params>:
# 1899| getParameter(0): [Parameter] x
# 1899| Type = [IntType] int
# 1899| getEntryPoint(): [BlockStmt] { ... }
# 1900| getStmt(0): [IfStmt] if (...) ...
# 1900| getCondition(): [LTExpr] ... < ...
# 1900| Type = [BoolType] bool
# 1900| ValueCategory = prvalue
# 1900| getLesserOperand(): [VariableAccess] x
# 1900| Type = [IntType] int
# 1900| ValueCategory = prvalue(load)
# 1900| getGreaterOperand(): [Literal] 10
# 1900| Type = [IntType] int
# 1900| Value = [Literal] 10
# 1900| ValueCategory = prvalue
# 1900| getThen(): [BlockStmt] { ... }
# 1901| getStmt(0): [ReturnStmt] return ...
# 1901| getExpr(): [VariableAccess] x
# 1901| Type = [IntType] int
# 1901| ValueCategory = prvalue(load)
# 1902| getElse(): [BlockStmt] { ... }
# 1903| getStmt(0): [ExprStmt] ExprStmt
# 1903| getExpr(): [FunctionCall] call to noreturnFunc
# 1903| Type = [VoidType] void
# 1903| ValueCategory = prvalue
# 1905| getStmt(1): [ReturnStmt] return ...
perf-regression.cpp:
# 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&)
# 4| <params>:

View File

@@ -6,6 +6,8 @@ missingOperandType
duplicateChiOperand
sideEffectWithoutPrimary
instructionWithoutSuccessor
| ir.cpp:1754:41:1754:42 | Chi: call to CopyConstructorTestVirtualClass | Instruction 'Chi: call to CopyConstructorTestVirtualClass' has no successors in function '$@'. | ir.cpp:1750:5:1750:34 | int implicit_copy_constructor_test(CopyConstructorTestNonVirtualClass const&, CopyConstructorTestVirtualClass const&) | int implicit_copy_constructor_test(CopyConstructorTestNonVirtualClass const&, CopyConstructorTestVirtualClass const&) |
| ir.cpp:1903:9:1903:20 | Chi: call to noreturnFunc | Instruction 'Chi: call to noreturnFunc' has no successors in function '$@'. | ir.cpp:1899:5:1899:16 | int noreturnTest(int) | int noreturnTest(int) |
ambiguousSuccessors
unexplainedLoop
unnecessaryPhiInstruction

View File

@@ -6,6 +6,8 @@ missingOperandType
duplicateChiOperand
sideEffectWithoutPrimary
instructionWithoutSuccessor
| ir.cpp:1754:41:1754:42 | Chi: call to CopyConstructorTestVirtualClass | Instruction 'Chi: call to CopyConstructorTestVirtualClass' has no successors in function '$@'. | ir.cpp:1750:5:1750:34 | int implicit_copy_constructor_test(CopyConstructorTestNonVirtualClass const&, CopyConstructorTestVirtualClass const&) | int implicit_copy_constructor_test(CopyConstructorTestNonVirtualClass const&, CopyConstructorTestVirtualClass const&) |
| ir.cpp:1903:9:1903:20 | Chi: call to noreturnFunc | Instruction 'Chi: call to noreturnFunc' has no successors in function '$@'. | ir.cpp:1899:5:1899:16 | int noreturnTest(int) | int noreturnTest(int) |
ambiguousSuccessors
unexplainedLoop
unnecessaryPhiInstruction

View File

@@ -1894,4 +1894,14 @@ int test_global_template_int() {
return local_int + (int)local_char;
}
[[noreturn]] void noreturnFunc();
int noreturnTest(int x) {
if (x < 10) {
return x;
} else {
noreturnFunc();
}
}
// semmle-extractor-options: -std=c++17 --clang

View File

@@ -8783,6 +8783,25 @@
| ir.cpp:1894:29:1894:38 | Address | &:r1894_4 |
| ir.cpp:1894:29:1894:38 | Load | m1893_4 |
| ir.cpp:1894:29:1894:38 | Unary | r1894_5 |
| ir.cpp:1899:5:1899:16 | Address | &:r1899_7 |
| ir.cpp:1899:5:1899:16 | ChiPartial | partial:m1899_3 |
| ir.cpp:1899:5:1899:16 | ChiTotal | total:m1899_2 |
| ir.cpp:1899:5:1899:16 | Load | m1901_4 |
| ir.cpp:1899:5:1899:16 | SideEffect | m1899_3 |
| ir.cpp:1899:22:1899:22 | Address | &:r1899_5 |
| ir.cpp:1900:9:1900:9 | Address | &:r1900_1 |
| ir.cpp:1900:9:1900:9 | Left | r1900_2 |
| ir.cpp:1900:9:1900:9 | Load | m1899_6 |
| ir.cpp:1900:9:1900:14 | Condition | r1900_4 |
| ir.cpp:1900:13:1900:14 | Right | r1900_3 |
| ir.cpp:1901:9:1901:17 | Address | &:r1901_1 |
| ir.cpp:1901:16:1901:16 | Address | &:r1901_2 |
| ir.cpp:1901:16:1901:16 | Load | m1899_6 |
| ir.cpp:1901:16:1901:16 | StoreValue | r1901_3 |
| ir.cpp:1903:9:1903:20 | CallTarget | func:r1903_1 |
| ir.cpp:1903:9:1903:20 | ChiPartial | partial:m1903_3 |
| ir.cpp:1903:9:1903:20 | ChiTotal | total:m1899_4 |
| ir.cpp:1903:9:1903:20 | SideEffect | ~m1899_4 |
| perf-regression.cpp:6:3:6:5 | Address | &:r6_5 |
| perf-regression.cpp:6:3:6:5 | Address | &:r6_5 |
| perf-regression.cpp:6:3:6:5 | Address | &:r6_7 |

View File

@@ -10105,6 +10105,37 @@ ir.cpp:
# 1891| v1891_6(void) = AliasedUse : ~m?
# 1891| v1891_7(void) = ExitFunction :
# 1899| int noreturnTest(int)
# 1899| Block 0
# 1899| v1899_1(void) = EnterFunction :
# 1899| mu1899_2(unknown) = AliasedDefinition :
# 1899| mu1899_3(unknown) = InitializeNonLocal :
# 1899| r1899_4(glval<int>) = VariableAddress[x] :
# 1899| mu1899_5(int) = InitializeParameter[x] : &:r1899_4
# 1900| r1900_1(glval<int>) = VariableAddress[x] :
# 1900| r1900_2(int) = Load[x] : &:r1900_1, ~m?
# 1900| r1900_3(int) = Constant[10] :
# 1900| r1900_4(bool) = CompareLT : r1900_2, r1900_3
# 1900| v1900_5(void) = ConditionalBranch : r1900_4
#-----| False -> Block 2
#-----| True -> Block 1
# 1901| Block 1
# 1901| r1901_1(glval<int>) = VariableAddress[#return] :
# 1901| r1901_2(glval<int>) = VariableAddress[x] :
# 1901| r1901_3(int) = Load[x] : &:r1901_2, ~m?
# 1901| mu1901_4(int) = Store[#return] : &:r1901_1, r1901_3
# 1899| r1899_6(glval<int>) = VariableAddress[#return] :
# 1899| v1899_7(void) = ReturnValue : &:r1899_6, ~m?
# 1899| v1899_8(void) = AliasedUse : ~m?
# 1899| v1899_9(void) = ExitFunction :
# 1903| Block 2
# 1903| r1903_1(glval<unknown>) = FunctionAddress[noreturnFunc] :
# 1903| v1903_2(void) = Call[noreturnFunc] : func:r1903_1
# 1903| mu1903_3(unknown) = ^CallSideEffect : ~m?
# 1905| v1905_1(void) = Unreached :
perf-regression.cpp:
# 6| void Big::Big()
# 6| Block 0

View File

@@ -6,6 +6,7 @@ missingOperandType
duplicateChiOperand
sideEffectWithoutPrimary
instructionWithoutSuccessor
| ssa.cpp:427:9:427:20 | Chi: call to noreturnFunc | Instruction 'Chi: call to noreturnFunc' has no successors in function '$@'. | ssa.cpp:423:5:423:16 | int noreturnTest(int) | int noreturnTest(int) |
ambiguousSuccessors
unexplainedLoop
unnecessaryPhiInstruction

View File

@@ -6,6 +6,7 @@ missingOperandType
duplicateChiOperand
sideEffectWithoutPrimary
instructionWithoutSuccessor
| ssa.cpp:427:9:427:20 | Chi: call to noreturnFunc | Instruction 'Chi: call to noreturnFunc' has no successors in function '$@'. | ssa.cpp:423:5:423:16 | int noreturnTest(int) | int noreturnTest(int) |
ambiguousSuccessors
unexplainedLoop
unnecessaryPhiInstruction

View File

@@ -2091,3 +2091,38 @@ ssa.cpp:
# 417| v417_5(void) = ReturnVoid :
# 417| v417_6(void) = AliasedUse : m417_3
# 417| v417_7(void) = ExitFunction :
# 423| int noreturnTest(int)
# 423| Block 0
# 423| v423_1(void) = EnterFunction :
# 423| m423_2(unknown) = AliasedDefinition :
# 423| m423_3(unknown) = InitializeNonLocal :
# 423| m423_4(unknown) = Chi : total:m423_2, partial:m423_3
# 423| r423_5(glval<int>) = VariableAddress[x] :
# 423| m423_6(int) = InitializeParameter[x] : &:r423_5
# 424| r424_1(glval<int>) = VariableAddress[x] :
# 424| r424_2(int) = Load[x] : &:r424_1, m423_6
# 424| r424_3(int) = Constant[10] :
# 424| r424_4(bool) = CompareLT : r424_2, r424_3
# 424| v424_5(void) = ConditionalBranch : r424_4
#-----| False -> Block 2
#-----| True -> Block 1
# 425| Block 1
# 425| r425_1(glval<int>) = VariableAddress[#return] :
# 425| r425_2(glval<int>) = VariableAddress[x] :
# 425| r425_3(int) = Load[x] : &:r425_2, m423_6
# 425| m425_4(int) = Store[#return] : &:r425_1, r425_3
# 423| r423_7(glval<int>) = VariableAddress[#return] :
# 423| v423_8(void) = ReturnValue : &:r423_7, m425_4
# 423| v423_9(void) = AliasedUse : m423_3
# 423| v423_10(void) = ExitFunction :
# 427| Block 2
# 427| r427_1(glval<unknown>) = FunctionAddress[noreturnFunc] :
# 427| v427_2(void) = Call[noreturnFunc] : func:r427_1
# 427| m427_3(unknown) = ^CallSideEffect : ~m423_4
# 427| m427_4(unknown) = Chi : total:m423_4, partial:m427_3
# 423| Block 3
# 423| v423_11(void) = Unreached :

View File

@@ -2080,3 +2080,38 @@ ssa.cpp:
# 417| v417_5(void) = ReturnVoid :
# 417| v417_6(void) = AliasedUse : m417_3
# 417| v417_7(void) = ExitFunction :
# 423| int noreturnTest(int)
# 423| Block 0
# 423| v423_1(void) = EnterFunction :
# 423| m423_2(unknown) = AliasedDefinition :
# 423| m423_3(unknown) = InitializeNonLocal :
# 423| m423_4(unknown) = Chi : total:m423_2, partial:m423_3
# 423| r423_5(glval<int>) = VariableAddress[x] :
# 423| m423_6(int) = InitializeParameter[x] : &:r423_5
# 424| r424_1(glval<int>) = VariableAddress[x] :
# 424| r424_2(int) = Load[x] : &:r424_1, m423_6
# 424| r424_3(int) = Constant[10] :
# 424| r424_4(bool) = CompareLT : r424_2, r424_3
# 424| v424_5(void) = ConditionalBranch : r424_4
#-----| False -> Block 2
#-----| True -> Block 1
# 425| Block 1
# 425| r425_1(glval<int>) = VariableAddress[#return] :
# 425| r425_2(glval<int>) = VariableAddress[x] :
# 425| r425_3(int) = Load[x] : &:r425_2, m423_6
# 425| m425_4(int) = Store[#return] : &:r425_1, r425_3
# 423| r423_7(glval<int>) = VariableAddress[#return] :
# 423| v423_8(void) = ReturnValue : &:r423_7, m425_4
# 423| v423_9(void) = AliasedUse : m423_3
# 423| v423_10(void) = ExitFunction :
# 427| Block 2
# 427| r427_1(glval<unknown>) = FunctionAddress[noreturnFunc] :
# 427| v427_2(void) = Call[noreturnFunc] : func:r427_1
# 427| m427_3(unknown) = ^CallSideEffect : ~m423_4
# 427| m427_4(unknown) = Chi : total:m423_4, partial:m427_3
# 423| Block 3
# 423| v423_11(void) = Unreached :

View File

@@ -417,3 +417,13 @@ void vla(int n1, int n2, int n3, bool b1) {
void nested_array_designators() {
int x[1][2] = {[0][0] = 1234, [0][1] = 5678};
}
[[noreturn]] void noreturnFunc();
int noreturnTest(int x) {
if (x < 10) {
return x;
} else {
noreturnFunc();
}
}

View File

@@ -1940,3 +1940,34 @@ ssa.cpp:
# 417| v417_4(void) = ReturnVoid :
# 417| v417_5(void) = AliasedUse : ~m?
# 417| v417_6(void) = ExitFunction :
# 423| int noreturnTest(int)
# 423| Block 0
# 423| v423_1(void) = EnterFunction :
# 423| mu423_2(unknown) = AliasedDefinition :
# 423| mu423_3(unknown) = InitializeNonLocal :
# 423| r423_4(glval<int>) = VariableAddress[x] :
# 423| m423_5(int) = InitializeParameter[x] : &:r423_4
# 424| r424_1(glval<int>) = VariableAddress[x] :
# 424| r424_2(int) = Load[x] : &:r424_1, m423_5
# 424| r424_3(int) = Constant[10] :
# 424| r424_4(bool) = CompareLT : r424_2, r424_3
# 424| v424_5(void) = ConditionalBranch : r424_4
#-----| False -> Block 2
#-----| True -> Block 1
# 425| Block 1
# 425| r425_1(glval<int>) = VariableAddress[#return] :
# 425| r425_2(glval<int>) = VariableAddress[x] :
# 425| r425_3(int) = Load[x] : &:r425_2, m423_5
# 425| m425_4(int) = Store[#return] : &:r425_1, r425_3
# 423| r423_6(glval<int>) = VariableAddress[#return] :
# 423| v423_7(void) = ReturnValue : &:r423_6, m425_4
# 423| v423_8(void) = AliasedUse : ~m?
# 423| v423_9(void) = ExitFunction :
# 427| Block 2
# 427| r427_1(glval<unknown>) = FunctionAddress[noreturnFunc] :
# 427| v427_2(void) = Call[noreturnFunc] : func:r427_1
# 427| mu427_3(unknown) = ^CallSideEffect : ~m?
# 423| v423_10(void) = Unreached :

View File

@@ -1940,3 +1940,34 @@ ssa.cpp:
# 417| v417_4(void) = ReturnVoid :
# 417| v417_5(void) = AliasedUse : ~m?
# 417| v417_6(void) = ExitFunction :
# 423| int noreturnTest(int)
# 423| Block 0
# 423| v423_1(void) = EnterFunction :
# 423| mu423_2(unknown) = AliasedDefinition :
# 423| mu423_3(unknown) = InitializeNonLocal :
# 423| r423_4(glval<int>) = VariableAddress[x] :
# 423| m423_5(int) = InitializeParameter[x] : &:r423_4
# 424| r424_1(glval<int>) = VariableAddress[x] :
# 424| r424_2(int) = Load[x] : &:r424_1, m423_5
# 424| r424_3(int) = Constant[10] :
# 424| r424_4(bool) = CompareLT : r424_2, r424_3
# 424| v424_5(void) = ConditionalBranch : r424_4
#-----| False -> Block 2
#-----| True -> Block 1
# 425| Block 1
# 425| r425_1(glval<int>) = VariableAddress[#return] :
# 425| r425_2(glval<int>) = VariableAddress[x] :
# 425| r425_3(int) = Load[x] : &:r425_2, m423_5
# 425| m425_4(int) = Store[#return] : &:r425_1, r425_3
# 423| r423_6(glval<int>) = VariableAddress[#return] :
# 423| v423_7(void) = ReturnValue : &:r423_6, m425_4
# 423| v423_8(void) = AliasedUse : ~m?
# 423| v423_9(void) = ExitFunction :
# 427| Block 2
# 427| r427_1(glval<unknown>) = FunctionAddress[noreturnFunc] :
# 427| v427_2(void) = Call[noreturnFunc] : func:r427_1
# 427| mu427_3(unknown) = ^CallSideEffect : ~m?
# 423| v423_10(void) = Unreached :