mirror of
https://github.com/github/codeql.git
synced 2025-12-21 19:26:31 +01:00
C++ IR: Fix performance of large array value init
There were two problems here. 1. The inline predicates `isInitialized` and `isValueInitialized` on `ArrayAggregateLiteral` caused their callers to materialize every `int` that was a valid index into the array. This was slow on huge value-initialized arrays. 2. The `isInitialized` predicate was used in the `TInstructionTag` IPA type, creating a numbered tuple for each integer in it. This seemed to be entirely unnecessary since the `TranslatedElement`s using those tags were already indexed appropriately.
This commit is contained in:
@@ -255,9 +255,10 @@ class ArrayAggregateLiteral extends AggregateLiteral {
|
|||||||
* list, either explicitly with an expression, or implicitly value
|
* list, either explicitly with an expression, or implicitly value
|
||||||
* initialized.
|
* initialized.
|
||||||
*/
|
*/
|
||||||
pragma[inline]
|
bindingset[elementIndex]
|
||||||
predicate isInitialized(int elementIndex) {
|
predicate isInitialized(int elementIndex) {
|
||||||
elementIndex in [0..arrayType.getArraySize() - 1]
|
elementIndex >= 0 and
|
||||||
|
elementIndex < arrayType.getArraySize()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -268,7 +269,7 @@ class ArrayAggregateLiteral extends AggregateLiteral {
|
|||||||
* of an object to `false`, `0`, `nullptr`, or by calling the default
|
* of an object to `false`, `0`, `nullptr`, or by calling the default
|
||||||
* constructor, as appropriate to the type.
|
* constructor, as appropriate to the type.
|
||||||
*/
|
*/
|
||||||
pragma[inline]
|
bindingset[elementIndex]
|
||||||
predicate isValueInitialized(int elementIndex) {
|
predicate isValueInitialized(int elementIndex) {
|
||||||
isInitialized(elementIndex) and
|
isInitialized(elementIndex) and
|
||||||
not exists(getElementExpr(elementIndex))
|
not exists(getElementExpr(elementIndex))
|
||||||
|
|||||||
@@ -9,12 +9,6 @@ private predicate fieldIsInitialized(Field field) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate elementIsInitialized(int elementIndex) {
|
|
||||||
exists(ArrayAggregateLiteral initList |
|
|
||||||
initList.isInitialized(elementIndex)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
newtype TInstructionTag =
|
newtype TInstructionTag =
|
||||||
OnlyInstructionTag() or // Single instruction (not including implicit Load)
|
OnlyInstructionTag() or // Single instruction (not including implicit Load)
|
||||||
InitializeThisTag() or
|
InitializeThisTag() or
|
||||||
@@ -73,18 +67,10 @@ newtype TInstructionTag =
|
|||||||
InitializerFieldDefaultValueStoreTag(Field field) {
|
InitializerFieldDefaultValueStoreTag(Field field) {
|
||||||
fieldIsInitialized(field)
|
fieldIsInitialized(field)
|
||||||
} or
|
} or
|
||||||
InitializerElementIndexTag(int elementIndex) {
|
InitializerElementIndexTag() or
|
||||||
elementIsInitialized(elementIndex)
|
InitializerElementAddressTag() or
|
||||||
} or
|
InitializerElementDefaultValueTag() or
|
||||||
InitializerElementAddressTag(int elementIndex) {
|
InitializerElementDefaultValueStoreTag() or
|
||||||
elementIsInitialized(elementIndex)
|
|
||||||
} or
|
|
||||||
InitializerElementDefaultValueTag(int elementIndex) {
|
|
||||||
elementIsInitialized(elementIndex)
|
|
||||||
} or
|
|
||||||
InitializerElementDefaultValueStoreTag(int elementIndex) {
|
|
||||||
elementIsInitialized(elementIndex)
|
|
||||||
} or
|
|
||||||
AsmTag() or
|
AsmTag() or
|
||||||
AsmInputTag(int elementIndex) {
|
AsmInputTag(int elementIndex) {
|
||||||
exists(AsmStmt asm |
|
exists(AsmStmt asm |
|
||||||
@@ -159,15 +145,10 @@ string getInstructionTagId(TInstructionTag tag) {
|
|||||||
) and
|
) and
|
||||||
result = tagName + "(" + index + ")"
|
result = tagName + "(" + index + ")"
|
||||||
) or
|
) or
|
||||||
exists(int index, string tagName |
|
tag = InitializerElementIndexTag() and result = "InitElemIndex" or
|
||||||
(
|
tag = InitializerElementAddressTag() and result = "InitElemAddr" or
|
||||||
tag = InitializerElementIndexTag(index) and tagName = "InitElemIndex" or
|
tag = InitializerElementDefaultValueTag() and result = "InitElemDefVal" or
|
||||||
tag = InitializerElementAddressTag(index) and tagName = "InitElemAddr" or
|
tag = InitializerElementDefaultValueStoreTag() and result = "InitElemDefValStore" or
|
||||||
tag = InitializerElementDefaultValueTag(index) and tagName = "InitElemDefVal" or
|
|
||||||
tag = InitializerElementDefaultValueStoreTag(index) and tagName = "InitElemDefValStore"
|
|
||||||
) and
|
|
||||||
result = tagName + "(" + index + ")"
|
|
||||||
) or
|
|
||||||
tag = AsmTag() and result = "Asm" or
|
tag = AsmTag() and result = "Asm" or
|
||||||
exists(int index |
|
exists(int index |
|
||||||
tag = AsmInputTag(index) and result = "AsmInputTag(" + index + ")"
|
tag = AsmInputTag(index) and result = "AsmInputTag(" + index + ")"
|
||||||
|
|||||||
@@ -441,7 +441,7 @@ private predicate isFirstValueInitializedElementInRange(
|
|||||||
initList.isValueInitialized(elementIndex) and
|
initList.isValueInitialized(elementIndex) and
|
||||||
(
|
(
|
||||||
elementIndex = 0 or
|
elementIndex = 0 or
|
||||||
not initList.isValueInitialized(elementIndex - 1)
|
exists(initList.getElementExpr(elementIndex - 1))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -699,11 +699,11 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
|
|||||||
abstract int getElementIndex();
|
abstract int getElementIndex();
|
||||||
|
|
||||||
final InstructionTag getElementAddressTag() {
|
final InstructionTag getElementAddressTag() {
|
||||||
result = InitializerElementAddressTag(getElementIndex())
|
result = InitializerElementAddressTag()
|
||||||
}
|
}
|
||||||
|
|
||||||
final InstructionTag getElementIndexTag() {
|
final InstructionTag getElementIndexTag() {
|
||||||
result = InitializerElementIndexTag(getElementIndex())
|
result = InitializerElementIndexTag()
|
||||||
}
|
}
|
||||||
|
|
||||||
final ArrayAggregateLiteral getInitList() {
|
final ArrayAggregateLiteral getInitList() {
|
||||||
@@ -861,11 +861,11 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
|
|||||||
}
|
}
|
||||||
|
|
||||||
private InstructionTag getElementDefaultValueTag() {
|
private InstructionTag getElementDefaultValueTag() {
|
||||||
result = InitializerElementDefaultValueTag(elementIndex)
|
result = InitializerElementDefaultValueTag()
|
||||||
}
|
}
|
||||||
|
|
||||||
private InstructionTag getElementDefaultValueStoreTag() {
|
private InstructionTag getElementDefaultValueStoreTag() {
|
||||||
result = InitializerElementDefaultValueStoreTag(elementIndex)
|
result = InitializerElementDefaultValueStoreTag()
|
||||||
}
|
}
|
||||||
|
|
||||||
private Type getDefaultValueType() {
|
private Type getDefaultValueType() {
|
||||||
|
|||||||
@@ -6,6 +6,10 @@
|
|||||||
#-----| params:
|
#-----| params:
|
||||||
#-----| 0: [Parameter] p#0
|
#-----| 0: [Parameter] p#0
|
||||||
#-----| Type = [RValueReferenceType] __va_list_tag &&
|
#-----| Type = [RValueReferenceType] __va_list_tag &&
|
||||||
|
#-----| [Operator,TopLevelFunction] void operator delete(void*)
|
||||||
|
#-----| params:
|
||||||
|
#-----| 0: [Parameter] p#0
|
||||||
|
#-----| Type = [VoidPointerType] void *
|
||||||
#-----| [Operator,TopLevelFunction] void operator delete(void*, unsigned long)
|
#-----| [Operator,TopLevelFunction] void operator delete(void*, unsigned long)
|
||||||
#-----| params:
|
#-----| params:
|
||||||
#-----| 0: [Parameter] p#0
|
#-----| 0: [Parameter] p#0
|
||||||
@@ -8001,3 +8005,49 @@ ir.cpp:
|
|||||||
# 1147| 2: [Handler] <handler>
|
# 1147| 2: [Handler] <handler>
|
||||||
# 1147| 0: [CatchBlock] { ... }
|
# 1147| 0: [CatchBlock] { ... }
|
||||||
# 1149| 1: [ReturnStmt] return ...
|
# 1149| 1: [ReturnStmt] return ...
|
||||||
|
perf-regression.cpp:
|
||||||
|
# 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&)
|
||||||
|
# 4| params:
|
||||||
|
#-----| 0: [Parameter] p#0
|
||||||
|
#-----| Type = [LValueReferenceType] const Big &
|
||||||
|
# 4| [MoveAssignmentOperator] Big& Big::operator=(Big&&)
|
||||||
|
# 4| params:
|
||||||
|
#-----| 0: [Parameter] p#0
|
||||||
|
#-----| Type = [RValueReferenceType] Big &&
|
||||||
|
# 4| [CopyConstructor] void Big::Big(Big const&)
|
||||||
|
# 4| params:
|
||||||
|
#-----| 0: [Parameter] p#0
|
||||||
|
#-----| Type = [LValueReferenceType] const Big &
|
||||||
|
# 4| [MoveConstructor] void Big::Big(Big&&)
|
||||||
|
# 4| params:
|
||||||
|
#-----| 0: [Parameter] p#0
|
||||||
|
#-----| Type = [RValueReferenceType] Big &&
|
||||||
|
# 6| [Constructor] void Big::Big()
|
||||||
|
# 6| params:
|
||||||
|
# 6| initializations:
|
||||||
|
# 6| 0: [ConstructorFieldInit] constructor init of field buffer
|
||||||
|
# 6| Type = [ArrayType] char[1073741824]
|
||||||
|
# 6| ValueCategory = prvalue
|
||||||
|
# 6| 0: [ArrayAggregateLiteral] {...}
|
||||||
|
# 6| Type = [ArrayType] char[1073741824]
|
||||||
|
# 6| ValueCategory = prvalue
|
||||||
|
# 6| body: [Block] { ... }
|
||||||
|
# 6| 0: [ReturnStmt] return ...
|
||||||
|
# 9| [TopLevelFunction] int main()
|
||||||
|
# 9| params:
|
||||||
|
# 9| body: [Block] { ... }
|
||||||
|
# 10| 0: [DeclStmt] declaration
|
||||||
|
# 10| 0: [VariableDeclarationEntry] definition of big
|
||||||
|
# 10| Type = [PointerType] Big *
|
||||||
|
# 10| init: [Initializer] initializer for big
|
||||||
|
# 10| expr: [NewExpr] new
|
||||||
|
# 10| Type = [PointerType] Big *
|
||||||
|
# 10| ValueCategory = prvalue
|
||||||
|
# 10| 1: [ConstructorCall] call to Big
|
||||||
|
# 10| Type = [VoidType] void
|
||||||
|
# 10| ValueCategory = prvalue
|
||||||
|
# 12| 1: [ReturnStmt] return ...
|
||||||
|
# 12| 0: [Literal,Zero] 0
|
||||||
|
# 12| Type = [IntType] int
|
||||||
|
# 12| Value = [Literal,Zero] 0
|
||||||
|
# 12| ValueCategory = prvalue
|
||||||
|
|||||||
13
cpp/ql/test/library-tests/ir/ir/perf-regression.cpp
Normal file
13
cpp/ql/test/library-tests/ir/ir/perf-regression.cpp
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// This test ensures that we can efficiently generate IR for a large
|
||||||
|
// value-initialized array.
|
||||||
|
|
||||||
|
struct Big {
|
||||||
|
char buffer[1 << 30]; // 1 GiB
|
||||||
|
Big() : buffer() {} // This explicit init of `buffer` makes it value-initialized
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
Big *big = new Big;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -5269,3 +5269,43 @@ ir.cpp:
|
|||||||
# 1149| v13_0(void) = NoOp :
|
# 1149| v13_0(void) = NoOp :
|
||||||
# 1133| v13_1(void) = ReturnVoid :
|
# 1133| v13_1(void) = ReturnVoid :
|
||||||
#-----| Goto -> Block 1
|
#-----| Goto -> Block 1
|
||||||
|
|
||||||
|
perf-regression.cpp:
|
||||||
|
# 6| void Big::Big()
|
||||||
|
# 6| Block 0
|
||||||
|
# 6| v0_0(void) = EnterFunction :
|
||||||
|
# 6| mu0_1(unknown) = AliasedDefinition :
|
||||||
|
# 6| mu0_2(unknown) = UnmodeledDefinition :
|
||||||
|
# 6| r0_3(glval<Big>) = InitializeThis :
|
||||||
|
# 6| r0_4(glval<char[1073741824]>) = FieldAddress[buffer] : r0_3
|
||||||
|
# 6| r0_5(int) = Constant[0] :
|
||||||
|
# 6| r0_6(glval<char>) = PointerAdd : r0_4, r0_5
|
||||||
|
# 6| r0_7(unknown[1073741824]) = Constant[0] :
|
||||||
|
# 6| mu0_8(unknown[1073741824]) = Store : &:r0_6, r0_7
|
||||||
|
# 6| v0_9(void) = NoOp :
|
||||||
|
# 6| v0_10(void) = ReturnVoid :
|
||||||
|
# 6| v0_11(void) = UnmodeledUse : mu*
|
||||||
|
# 6| v0_12(void) = ExitFunction :
|
||||||
|
|
||||||
|
# 9| int main()
|
||||||
|
# 9| Block 0
|
||||||
|
# 9| v0_0(void) = EnterFunction :
|
||||||
|
# 9| mu0_1(unknown) = AliasedDefinition :
|
||||||
|
# 9| mu0_2(unknown) = UnmodeledDefinition :
|
||||||
|
# 10| r0_3(glval<Big *>) = VariableAddress[big] :
|
||||||
|
# 10| r0_4(glval<unknown>) = FunctionAddress[operator new] :
|
||||||
|
# 10| r0_5(unsigned long) = Constant[1073741824] :
|
||||||
|
# 10| r0_6(void *) = Call : func:r0_4, 0:r0_5
|
||||||
|
# 10| mu0_7(unknown) = ^CallSideEffect : ~mu0_2
|
||||||
|
# 10| r0_8(Big *) = Convert : r0_6
|
||||||
|
# 10| r0_9(glval<unknown>) = FunctionAddress[Big] :
|
||||||
|
# 10| v0_10(void) = Call : func:r0_9, this:r0_8
|
||||||
|
# 10| mu0_11(unknown) = ^CallSideEffect : ~mu0_2
|
||||||
|
# 10| mu0_12(Big *) = Store : &:r0_3, r0_8
|
||||||
|
# 12| r0_13(glval<int>) = VariableAddress[#return] :
|
||||||
|
# 12| r0_14(int) = Constant[0] :
|
||||||
|
# 12| mu0_15(int) = Store : &:r0_13, r0_14
|
||||||
|
# 9| r0_16(glval<int>) = VariableAddress[#return] :
|
||||||
|
# 9| v0_17(void) = ReturnValue : &:r0_16, ~mu0_2
|
||||||
|
# 9| v0_18(void) = UnmodeledUse : mu*
|
||||||
|
# 9| v0_19(void) = ExitFunction :
|
||||||
|
|||||||
@@ -2,5 +2,6 @@ import cpp
|
|||||||
|
|
||||||
from ArrayType a, ArrayAggregateLiteral al, int i
|
from ArrayType a, ArrayAggregateLiteral al, int i
|
||||||
where a = al.getType()
|
where a = al.getType()
|
||||||
|
and i = [0 .. al.getUnspecifiedType().(ArrayType).getArraySize()]
|
||||||
and al.isValueInitialized(i)
|
and al.isValueInitialized(i)
|
||||||
select al, a, i
|
select al, a, i
|
||||||
|
|||||||
Reference in New Issue
Block a user