Skip to content

Commit 19d2f3a

Browse files
Merge pull request #1550 from jbj/array-aggregate-perf
C++ IR: Fix performance of large value-initialized arrays
2 parents 0652d2a + b4b940a commit 19d2f3a

File tree

8 files changed

+130
-65
lines changed

8 files changed

+130
-65
lines changed

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -255,9 +255,10 @@ class ArrayAggregateLiteral extends AggregateLiteral {
255255
* list, either explicitly with an expression, or implicitly value
256256
* initialized.
257257
*/
258-
pragma[inline]
258+
bindingset[elementIndex]
259259
predicate isInitialized(int elementIndex) {
260-
elementIndex in [0..arrayType.getArraySize() - 1]
260+
elementIndex >= 0 and
261+
elementIndex < arrayType.getArraySize()
261262
}
262263

263264
/**
@@ -268,7 +269,7 @@ class ArrayAggregateLiteral extends AggregateLiteral {
268269
* of an object to `false`, `0`, `nullptr`, or by calling the default
269270
* constructor, as appropriate to the type.
270271
*/
271-
pragma[inline]
272+
bindingset[elementIndex]
272273
predicate isValueInitialized(int elementIndex) {
273274
isInitialized(elementIndex) and
274275
not exists(getElementExpr(elementIndex))

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

Lines changed: 14 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,5 @@
11
private import cpp
22

3-
private predicate fieldIsInitialized(Field field) {
4-
exists(ClassAggregateLiteral initList |
5-
initList.isInitialized(field)
6-
) or
7-
exists(ConstructorFieldInit init |
8-
field = init.getTarget()
9-
)
10-
}
11-
12-
private predicate elementIsInitialized(int elementIndex) {
13-
exists(ArrayAggregateLiteral initList |
14-
initList.isInitialized(elementIndex)
15-
)
16-
}
17-
183
newtype TInstructionTag =
194
OnlyInstructionTag() or // Single instruction (not including implicit Load)
205
InitializeThisTag() or
@@ -64,27 +49,13 @@ newtype TInstructionTag =
6449
ThrowTag() or
6550
UnwindTag() or
6651
InitializerUninitializedTag() or
67-
InitializerFieldAddressTag(Field field) {
68-
fieldIsInitialized(field)
69-
} or
70-
InitializerFieldDefaultValueTag(Field field) {
71-
fieldIsInitialized(field)
72-
} or
73-
InitializerFieldDefaultValueStoreTag(Field field) {
74-
fieldIsInitialized(field)
75-
} or
76-
InitializerElementIndexTag(int elementIndex) {
77-
elementIsInitialized(elementIndex)
78-
} or
79-
InitializerElementAddressTag(int elementIndex) {
80-
elementIsInitialized(elementIndex)
81-
} or
82-
InitializerElementDefaultValueTag(int elementIndex) {
83-
elementIsInitialized(elementIndex)
84-
} or
85-
InitializerElementDefaultValueStoreTag(int elementIndex) {
86-
elementIsInitialized(elementIndex)
87-
} or
52+
InitializerFieldAddressTag() or
53+
InitializerFieldDefaultValueTag() or
54+
InitializerFieldDefaultValueStoreTag() or
55+
InitializerElementIndexTag() or
56+
InitializerElementAddressTag() or
57+
InitializerElementDefaultValueTag() or
58+
InitializerElementDefaultValueStoreTag() or
8859
AsmTag() or
8960
AsmInputTag(int elementIndex) {
9061
exists(AsmStmt asm |
@@ -150,24 +121,13 @@ string getInstructionTagId(TInstructionTag tag) {
150121
tag = CatchTag() and result = "Catch" or
151122
tag = ThrowTag() and result = "Throw" or
152123
tag = UnwindTag() and result = "Unwind" or
153-
exists(Field field, Class cls, int index, string tagName |
154-
field = cls.getCanonicalMember(index) and
155-
(
156-
tag = InitializerFieldAddressTag(field) and tagName = "InitFieldAddr" or
157-
tag = InitializerFieldDefaultValueTag(field) and tagName = "InitFieldDefVal" or
158-
tag = InitializerFieldDefaultValueStoreTag(field) and tagName = "InitFieldDefValStore"
159-
) and
160-
result = tagName + "(" + index + ")"
161-
) or
162-
exists(int index, string tagName |
163-
(
164-
tag = InitializerElementIndexTag(index) and tagName = "InitElemIndex" or
165-
tag = InitializerElementAddressTag(index) and tagName = "InitElemAddr" or
166-
tag = InitializerElementDefaultValueTag(index) and tagName = "InitElemDefVal" or
167-
tag = InitializerElementDefaultValueStoreTag(index) and tagName = "InitElemDefValStore"
168-
) and
169-
result = tagName + "(" + index + ")"
170-
) or
124+
tag = InitializerFieldAddressTag() and result = "InitFieldAddr" or
125+
tag = InitializerFieldDefaultValueTag() and result = "InitFieldDefVal" or
126+
tag = InitializerFieldDefaultValueStoreTag() and result = "InitFieldDefValStore" or
127+
tag = InitializerElementIndexTag() and result = "InitElemIndex" or
128+
tag = InitializerElementAddressTag() and result = "InitElemAddr" or
129+
tag = InitializerElementDefaultValueTag() and result = "InitElemDefVal" or
130+
tag = InitializerElementDefaultValueStoreTag() and result = "InitElemDefValStore" or
171131
tag = AsmTag() and result = "Asm" or
172132
exists(int index |
173133
tag = AsmInputTag(index) and result = "AsmInputTag(" + index + ")"

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ private predicate isFirstValueInitializedElementInRange(
441441
initList.isValueInitialized(elementIndex) and
442442
(
443443
elementIndex = 0 or
444-
not initList.isValueInitialized(elementIndex - 1)
444+
exists(initList.getElementExpr(elementIndex - 1))
445445
)
446446
}
447447

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ abstract class TranslatedFieldInitialization extends TranslatedElement {
497497
}
498498

499499
final InstructionTag getFieldAddressTag() {
500-
result = InitializerFieldAddressTag(field)
500+
result = InitializerFieldAddressTag()
501501
}
502502

503503
final Field getField() {
@@ -625,11 +625,11 @@ class TranslatedFieldValueInitialization extends TranslatedFieldInitialization,
625625
}
626626

627627
private InstructionTag getFieldDefaultValueTag() {
628-
result = InitializerFieldDefaultValueTag(field)
628+
result = InitializerFieldDefaultValueTag()
629629
}
630630

631631
private InstructionTag getFieldDefaultValueStoreTag() {
632-
result = InitializerFieldDefaultValueStoreTag(field)
632+
result = InitializerFieldDefaultValueStoreTag()
633633
}
634634
}
635635

@@ -699,11 +699,11 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
699699
abstract int getElementIndex();
700700

701701
final InstructionTag getElementAddressTag() {
702-
result = InitializerElementAddressTag(getElementIndex())
702+
result = InitializerElementAddressTag()
703703
}
704704

705705
final InstructionTag getElementIndexTag() {
706-
result = InitializerElementIndexTag(getElementIndex())
706+
result = InitializerElementIndexTag()
707707
}
708708

709709
final ArrayAggregateLiteral getInitList() {
@@ -861,11 +861,11 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
861861
}
862862

863863
private InstructionTag getElementDefaultValueTag() {
864-
result = InitializerElementDefaultValueTag(elementIndex)
864+
result = InitializerElementDefaultValueTag()
865865
}
866866

867867
private InstructionTag getElementDefaultValueStoreTag() {
868-
result = InitializerElementDefaultValueStoreTag(elementIndex)
868+
result = InitializerElementDefaultValueStoreTag()
869869
}
870870

871871
private Type getDefaultValueType() {

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

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
#-----| params:
77
#-----| 0: [Parameter] p#0
88
#-----| Type = [RValueReferenceType] __va_list_tag &&
9+
#-----| [Operator,TopLevelFunction] void operator delete(void*)
10+
#-----| params:
11+
#-----| 0: [Parameter] p#0
12+
#-----| Type = [VoidPointerType] void *
913
#-----| [Operator,TopLevelFunction] void operator delete(void*, unsigned long)
1014
#-----| params:
1115
#-----| 0: [Parameter] p#0
@@ -8001,3 +8005,49 @@ ir.cpp:
80018005
# 1147| 2: [Handler] <handler>
80028006
# 1147| 0: [CatchBlock] { ... }
80038007
# 1149| 1: [ReturnStmt] return ...
8008+
perf-regression.cpp:
8009+
# 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&)
8010+
# 4| params:
8011+
#-----| 0: [Parameter] p#0
8012+
#-----| Type = [LValueReferenceType] const Big &
8013+
# 4| [MoveAssignmentOperator] Big& Big::operator=(Big&&)
8014+
# 4| params:
8015+
#-----| 0: [Parameter] p#0
8016+
#-----| Type = [RValueReferenceType] Big &&
8017+
# 4| [CopyConstructor] void Big::Big(Big const&)
8018+
# 4| params:
8019+
#-----| 0: [Parameter] p#0
8020+
#-----| Type = [LValueReferenceType] const Big &
8021+
# 4| [MoveConstructor] void Big::Big(Big&&)
8022+
# 4| params:
8023+
#-----| 0: [Parameter] p#0
8024+
#-----| Type = [RValueReferenceType] Big &&
8025+
# 6| [Constructor] void Big::Big()
8026+
# 6| params:
8027+
# 6| initializations:
8028+
# 6| 0: [ConstructorFieldInit] constructor init of field buffer
8029+
# 6| Type = [ArrayType] char[1073741824]
8030+
# 6| ValueCategory = prvalue
8031+
# 6| 0: [ArrayAggregateLiteral] {...}
8032+
# 6| Type = [ArrayType] char[1073741824]
8033+
# 6| ValueCategory = prvalue
8034+
# 6| body: [Block] { ... }
8035+
# 6| 0: [ReturnStmt] return ...
8036+
# 9| [TopLevelFunction] int main()
8037+
# 9| params:
8038+
# 9| body: [Block] { ... }
8039+
# 10| 0: [DeclStmt] declaration
8040+
# 10| 0: [VariableDeclarationEntry] definition of big
8041+
# 10| Type = [PointerType] Big *
8042+
# 10| init: [Initializer] initializer for big
8043+
# 10| expr: [NewExpr] new
8044+
# 10| Type = [PointerType] Big *
8045+
# 10| ValueCategory = prvalue
8046+
# 10| 1: [ConstructorCall] call to Big
8047+
# 10| Type = [VoidType] void
8048+
# 10| ValueCategory = prvalue
8049+
# 12| 1: [ReturnStmt] return ...
8050+
# 12| 0: [Literal,Zero] 0
8051+
# 12| Type = [IntType] int
8052+
# 12| Value = [Literal,Zero] 0
8053+
# 12| ValueCategory = prvalue
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// This test ensures that we can efficiently generate IR for a large
2+
// value-initialized array.
3+
4+
struct Big {
5+
char buffer[1 << 30]; // 1 GiB
6+
Big() : buffer() {} // This explicit init of `buffer` makes it value-initialized
7+
};
8+
9+
int main() {
10+
Big *big = new Big;
11+
12+
return 0;
13+
}

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5269,3 +5269,43 @@ ir.cpp:
52695269
# 1149| v13_0(void) = NoOp :
52705270
# 1133| v13_1(void) = ReturnVoid :
52715271
#-----| Goto -> Block 1
5272+
5273+
perf-regression.cpp:
5274+
# 6| void Big::Big()
5275+
# 6| Block 0
5276+
# 6| v0_0(void) = EnterFunction :
5277+
# 6| mu0_1(unknown) = AliasedDefinition :
5278+
# 6| mu0_2(unknown) = UnmodeledDefinition :
5279+
# 6| r0_3(glval<Big>) = InitializeThis :
5280+
# 6| r0_4(glval<char[1073741824]>) = FieldAddress[buffer] : r0_3
5281+
# 6| r0_5(int) = Constant[0] :
5282+
# 6| r0_6(glval<char>) = PointerAdd : r0_4, r0_5
5283+
# 6| r0_7(unknown[1073741824]) = Constant[0] :
5284+
# 6| mu0_8(unknown[1073741824]) = Store : &:r0_6, r0_7
5285+
# 6| v0_9(void) = NoOp :
5286+
# 6| v0_10(void) = ReturnVoid :
5287+
# 6| v0_11(void) = UnmodeledUse : mu*
5288+
# 6| v0_12(void) = ExitFunction :
5289+
5290+
# 9| int main()
5291+
# 9| Block 0
5292+
# 9| v0_0(void) = EnterFunction :
5293+
# 9| mu0_1(unknown) = AliasedDefinition :
5294+
# 9| mu0_2(unknown) = UnmodeledDefinition :
5295+
# 10| r0_3(glval<Big *>) = VariableAddress[big] :
5296+
# 10| r0_4(glval<unknown>) = FunctionAddress[operator new] :
5297+
# 10| r0_5(unsigned long) = Constant[1073741824] :
5298+
# 10| r0_6(void *) = Call : func:r0_4, 0:r0_5
5299+
# 10| mu0_7(unknown) = ^CallSideEffect : ~mu0_2
5300+
# 10| r0_8(Big *) = Convert : r0_6
5301+
# 10| r0_9(glval<unknown>) = FunctionAddress[Big] :
5302+
# 10| v0_10(void) = Call : func:r0_9, this:r0_8
5303+
# 10| mu0_11(unknown) = ^CallSideEffect : ~mu0_2
5304+
# 10| mu0_12(Big *) = Store : &:r0_3, r0_8
5305+
# 12| r0_13(glval<int>) = VariableAddress[#return] :
5306+
# 12| r0_14(int) = Constant[0] :
5307+
# 12| mu0_15(int) = Store : &:r0_13, r0_14
5308+
# 9| r0_16(glval<int>) = VariableAddress[#return] :
5309+
# 9| v0_17(void) = ReturnValue : &:r0_16, ~mu0_2
5310+
# 9| v0_18(void) = UnmodeledUse : mu*
5311+
# 9| v0_19(void) = ExitFunction :

cpp/ql/test/library-tests/literals/aggregate_literals/arrays_value_init.ql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ import cpp
22

33
from ArrayType a, ArrayAggregateLiteral al, int i
44
where a = al.getType()
5+
and i = [0 .. al.getUnspecifiedType().(ArrayType).getArraySize()]
56
and al.isValueInitialized(i)
67
select al, a, i

0 commit comments

Comments
 (0)