C++: IR support for range-based for loops

IR construction was missing support for C++ 11 range-based `for` loops. The extractor generates ASTs for the compiler-generated implementation already, so I had enough information to generate IR. I've expanded on some of the predicates in `RangeBasedForStmt` to access the desugared information.

One complication was that the `DeclStmt`s for the compiler-generated variables seem to have results for `getDeclaration()` but not for `getDeclarationEntry()`. This required handling these slightly differently than we do for other `DeclStmt`s.

The flow for range-based `for` is actually easier than for a regular `for`, because all three components (init, condition, and update) are always present.
This commit is contained in:
Dave Bartolomeo
2019-05-29 14:40:29 -07:00
parent a782585d74
commit aff85c5b24
8 changed files with 492 additions and 4 deletions

View File

@@ -7427,3 +7427,146 @@ ir.cpp:
#-----| -1: this
#-----| Type = const lambda [] type at line 1045, col. 23 *
#-----| ValueCategory = prvalue(load)
# 1050| vector<int>& vector<int>::operator=(vector<int> const&)
# 1050| params:
#-----| 0: p#0
#-----| Type = const vector<int> &
# 1050| vector<int>& vector<int>::operator=(vector<int>&&)
# 1050| params:
#-----| 0: p#0
#-----| Type = vector<int> &&
# 1051| vector<int>::iterator& vector<int>::iterator::operator=(vector<int>::iterator const public&)
# 1051| params:
#-----| 0: p#0
#-----| Type = const iterator &
# 1051| vector<int>::iterator& vector<int>::iterator::operator=(vector<int>::iterator&&)
# 1051| params:
#-----| 0: p#0
#-----| Type = iterator &&
# 1053| vector<T>::iterator& vector<T>::iterator::operator++()
# 1053| params:
# 1053| vector<int>::iterator& vector<int>::iterator::operator++()
# 1053| params:
# 1054| T& vector<T>::iterator::operator*() const
# 1054| params:
# 1054| int& vector<int>::iterator::operator*() const
# 1054| params:
# 1056| bool vector<T>::iterator::operator!=(vector<T>::iterator) const
# 1056| params:
# 1056| 0: right
# 1056| Type = iterator
# 1056| bool vector<int>::iterator::operator!=(vector<int>::iterator) const
# 1056| params:
# 1056| 0: right
# 1056| Type = iterator
# 1059| vector<T>::iterator vector<T>::begin() const
# 1059| params:
# 1059| vector<int>::iterator vector<int>::begin() const
# 1059| params:
# 1060| vector<T>::iterator vector<T>::end() const
# 1060| params:
# 1060| vector<int>::iterator vector<int>::end() const
# 1060| params:
# 1064| bool operator==<T>(iterator, iterator)
# 1064| params:
# 1064| 0: left
# 1064| Type = iterator
# 1064| 1: right
# 1064| Type = iterator
# 1066| bool operator!=<T>(iterator, iterator)
# 1066| params:
# 1066| 0: left
# 1066| Type = iterator
# 1066| 1: right
# 1066| Type = iterator
# 1068| void RangeBasedFor(vector<int> const&)
# 1068| params:
# 1068| 0: v
# 1068| Type = const vector<int> &
# 1068| body: { ... }
# 1069| 0: for(...:...) ...
# 1069| 0: declaration
# 1069| 1: declaration
# 1069| 2: call to operator!=
# 1069| Type = bool
# 1069| ValueCategory = prvalue
#-----| -1: (const iterator)...
#-----| Conversion = glvalue conversion
#-----| Type = const iterator
#-----| ValueCategory = lvalue
#-----| expr: (__begin)
#-----| Type = iterator
#-----| ValueCategory = lvalue
#-----| 0: (__end)
#-----| Type = iterator
#-----| ValueCategory = prvalue(load)
# 1069| 3: (reference dereference)
# 1069| Type = iterator
# 1069| ValueCategory = lvalue
# 1069| expr: call to operator++
# 1069| Type = iterator &
# 1069| ValueCategory = prvalue
#-----| -1: (__begin)
#-----| Type = iterator
#-----| ValueCategory = lvalue
# 1069| 4: declaration
# 1069| 5: { ... }
# 1070| 0: if (...) ...
# 1070| 0: ... > ...
# 1070| Type = bool
# 1070| ValueCategory = prvalue
# 1070| 0: e
# 1070| Type = int
# 1070| ValueCategory = prvalue(load)
# 1070| 1: 0
# 1070| Type = int
# 1070| Value = 0
# 1070| ValueCategory = prvalue
# 1070| 1: { ... }
# 1071| 0: continue;
# 1069| 1: label ...:
# 1075| 1: for(...:...) ...
# 1075| 0: declaration
# 1075| 1: declaration
# 1075| 2: call to operator!=
# 1075| Type = bool
# 1075| ValueCategory = prvalue
#-----| -1: (const iterator)...
#-----| Conversion = glvalue conversion
#-----| Type = const iterator
#-----| ValueCategory = lvalue
#-----| expr: (__begin)
#-----| Type = iterator
#-----| ValueCategory = lvalue
#-----| 0: (__end)
#-----| Type = iterator
#-----| ValueCategory = prvalue(load)
# 1075| 3: (reference dereference)
# 1075| Type = iterator
# 1075| ValueCategory = lvalue
# 1075| expr: call to operator++
# 1075| Type = iterator &
# 1075| ValueCategory = prvalue
#-----| -1: (__begin)
#-----| Type = iterator
#-----| ValueCategory = lvalue
# 1075| 4: declaration
# 1075| 5: { ... }
# 1076| 0: if (...) ...
# 1076| 0: ... < ...
# 1076| Type = bool
# 1076| ValueCategory = prvalue
# 1076| 0: (reference dereference)
# 1076| Type = int
# 1076| ValueCategory = prvalue(load)
# 1076| expr: e
# 1076| Type = const int &
# 1076| ValueCategory = prvalue(load)
# 1076| 1: 5
# 1076| Type = int
# 1076| Value = 5
# 1076| ValueCategory = prvalue
# 1076| 1: { ... }
# 1077| 0: break;
# 1079| 2: label ...:
# 1080| 3: return ...

View File

@@ -1046,6 +1046,39 @@ void Lambda(int x, const String& s) {
lambda_inits(6);
}
template<typename T>
struct vector {
struct iterator {
T* p;
iterator& operator++();
T& operator*() const;
bool operator!=(iterator right) const;
};
iterator begin() const;
iterator end() const;
};
template<typename T>
bool operator==(typename vector<T>::iterator left, typename vector<T>::iterator right);
template<typename T>
bool operator!=(typename vector<T>::iterator left, typename vector<T>::iterator right);
void RangeBasedFor(const vector<int>& v) {
for (int e : v) {
if (e > 0) {
continue;
}
}
for (const int& e : v) {
if (e < 5) {
break;
}
}
}
#if 0 // Explicit capture of `this` requires possible extractor fixes.
struct LambdaContainer {

View File

@@ -4861,3 +4861,140 @@ ir.cpp:
# 1045| v0_29(void) = ReturnValue : &:r0_28, ~mu0_2
# 1045| v0_30(void) = UnmodeledUse : mu*
# 1045| v0_31(void) = ExitFunction :
# 1068| void RangeBasedFor(vector<int> const&)
# 1068| Block 0
# 1068| v0_0(void) = EnterFunction :
# 1068| mu0_1(unknown) = AliasedDefinition :
# 1068| mu0_2(unknown) = UnmodeledDefinition :
# 1068| r0_3(glval<vector<int> &>) = VariableAddress[v] :
# 1068| mu0_4(vector<int> &) = InitializeParameter[v] : &:r0_3
# 1069| r0_5(glval<vector<int> &>) = VariableAddress[(__range)] :
# 1069| r0_6(glval<vector<int> &>) = VariableAddress[v] :
# 1069| r0_7(vector<int> &) = Load : &:r0_6, ~mu0_2
# 1069| mu0_8(vector<int> &) = Store : &:r0_5, r0_7
# 1069| r0_9(glval<iterator>) = VariableAddress[(__begin)] :
#-----| r0_10(glval<vector<int> &>) = VariableAddress[(__range)] :
#-----| r0_11(vector<int> &) = Load : &:r0_10, ~mu0_2
# 1069| r0_12(glval<unknown>) = FunctionAddress[begin] :
# 1069| r0_13(iterator) = Call : func:r0_12, this:r0_11
# 1069| mu0_14(unknown) = ^CallSideEffect : ~mu0_2
# 1069| mu0_15(iterator) = Store : &:r0_9, r0_13
# 1069| r0_16(glval<iterator>) = VariableAddress[(__end)] :
#-----| r0_17(glval<vector<int> &>) = VariableAddress[(__range)] :
#-----| r0_18(vector<int> &) = Load : &:r0_17, ~mu0_2
# 1069| r0_19(glval<unknown>) = FunctionAddress[end] :
# 1069| r0_20(iterator) = Call : func:r0_19, this:r0_18
# 1069| mu0_21(unknown) = ^CallSideEffect : ~mu0_2
# 1069| mu0_22(iterator) = Store : &:r0_16, r0_20
#-----| Goto -> Block 1
#-----| Block 1
#-----| r1_0(glval<iterator>) = VariableAddress[(__begin)] :
#-----| r1_1(glval<iterator>) = Convert : r1_0
# 1069| r1_2(glval<unknown>) = FunctionAddress[operator!=] :
#-----| r1_3(glval<iterator>) = VariableAddress[(__end)] :
#-----| r1_4(iterator) = Load : &:r1_3, ~mu0_2
# 1069| r1_5(bool) = Call : func:r1_2, this:r1_1, 0:r1_4
# 1069| mu1_6(unknown) = ^CallSideEffect : ~mu0_2
# 1069| v1_7(void) = ConditionalBranch : r1_5
#-----| False -> Block 5
#-----| True -> Block 2
# 1069| Block 2
# 1069| r2_0(glval<int>) = VariableAddress[e] :
#-----| r2_1(glval<iterator>) = VariableAddress[(__begin)] :
#-----| r2_2(glval<iterator>) = Convert : r2_1
# 1069| r2_3(glval<unknown>) = FunctionAddress[operator*] :
# 1069| r2_4(int &) = Call : func:r2_3, this:r2_2
# 1069| mu2_5(unknown) = ^CallSideEffect : ~mu0_2
# 1069| r2_6(int) = Load : &:r2_4, ~mu0_2
# 1069| mu2_7(int) = Store : &:r2_0, r2_6
# 1070| r2_8(glval<int>) = VariableAddress[e] :
# 1070| r2_9(int) = Load : &:r2_8, ~mu0_2
# 1070| r2_10(int) = Constant[0] :
# 1070| r2_11(bool) = CompareGT : r2_9, r2_10
# 1070| v2_12(void) = ConditionalBranch : r2_11
#-----| False -> Block 4
#-----| True -> Block 3
# 1071| Block 3
# 1071| v3_0(void) = NoOp :
#-----| Goto -> Block 4
# 1069| Block 4
# 1069| v4_0(void) = NoOp :
#-----| r4_1(glval<iterator>) = VariableAddress[(__begin)] :
# 1069| r4_2(glval<unknown>) = FunctionAddress[operator++] :
# 1069| r4_3(iterator &) = Call : func:r4_2, this:r4_1
# 1069| mu4_4(unknown) = ^CallSideEffect : ~mu0_2
#-----| Goto (back edge) -> Block 1
# 1075| Block 5
# 1075| r5_0(glval<vector<int> &>) = VariableAddress[(__range)] :
# 1075| r5_1(glval<vector<int> &>) = VariableAddress[v] :
# 1075| r5_2(vector<int> &) = Load : &:r5_1, ~mu0_2
# 1075| mu5_3(vector<int> &) = Store : &:r5_0, r5_2
# 1075| r5_4(glval<iterator>) = VariableAddress[(__begin)] :
#-----| r5_5(glval<vector<int> &>) = VariableAddress[(__range)] :
#-----| r5_6(vector<int> &) = Load : &:r5_5, ~mu0_2
# 1075| r5_7(glval<unknown>) = FunctionAddress[begin] :
# 1075| r5_8(iterator) = Call : func:r5_7, this:r5_6
# 1075| mu5_9(unknown) = ^CallSideEffect : ~mu0_2
# 1075| mu5_10(iterator) = Store : &:r5_4, r5_8
# 1075| r5_11(glval<iterator>) = VariableAddress[(__end)] :
#-----| r5_12(glval<vector<int> &>) = VariableAddress[(__range)] :
#-----| r5_13(vector<int> &) = Load : &:r5_12, ~mu0_2
# 1075| r5_14(glval<unknown>) = FunctionAddress[end] :
# 1075| r5_15(iterator) = Call : func:r5_14, this:r5_13
# 1075| mu5_16(unknown) = ^CallSideEffect : ~mu0_2
# 1075| mu5_17(iterator) = Store : &:r5_11, r5_15
#-----| Goto -> Block 6
#-----| Block 6
#-----| r6_0(glval<iterator>) = VariableAddress[(__begin)] :
#-----| r6_1(glval<iterator>) = Convert : r6_0
# 1075| r6_2(glval<unknown>) = FunctionAddress[operator!=] :
#-----| r6_3(glval<iterator>) = VariableAddress[(__end)] :
#-----| r6_4(iterator) = Load : &:r6_3, ~mu0_2
# 1075| r6_5(bool) = Call : func:r6_2, this:r6_1, 0:r6_4
# 1075| mu6_6(unknown) = ^CallSideEffect : ~mu0_2
# 1075| v6_7(void) = ConditionalBranch : r6_5
#-----| False -> Block 10
#-----| True -> Block 8
#-----| Block 7
#-----| r7_0(glval<iterator>) = VariableAddress[(__begin)] :
# 1075| r7_1(glval<unknown>) = FunctionAddress[operator++] :
# 1075| r7_2(iterator &) = Call : func:r7_1, this:r7_0
# 1075| mu7_3(unknown) = ^CallSideEffect : ~mu0_2
#-----| Goto (back edge) -> Block 6
# 1075| Block 8
# 1075| r8_0(glval<int &>) = VariableAddress[e] :
#-----| r8_1(glval<iterator>) = VariableAddress[(__begin)] :
#-----| r8_2(glval<iterator>) = Convert : r8_1
# 1075| r8_3(glval<unknown>) = FunctionAddress[operator*] :
# 1075| r8_4(int &) = Call : func:r8_3, this:r8_2
# 1075| mu8_5(unknown) = ^CallSideEffect : ~mu0_2
# 1075| r8_6(glval<int>) = Convert : r8_4
# 1075| mu8_7(int &) = Store : &:r8_0, r8_6
# 1076| r8_8(glval<int &>) = VariableAddress[e] :
# 1076| r8_9(int &) = Load : &:r8_8, ~mu0_2
# 1076| r8_10(int) = Load : &:r8_9, ~mu0_2
# 1076| r8_11(int) = Constant[5] :
# 1076| r8_12(bool) = CompareLT : r8_10, r8_11
# 1076| v8_13(void) = ConditionalBranch : r8_12
#-----| False -> Block 7
#-----| True -> Block 9
# 1077| Block 9
# 1077| v9_0(void) = NoOp :
#-----| Goto -> Block 10
# 1079| Block 10
# 1079| v10_0(void) = NoOp :
# 1080| v10_1(void) = NoOp :
# 1068| v10_2(void) = ReturnVoid :
# 1068| v10_3(void) = UnmodeledUse : mu*
# 1068| v10_4(void) = ExitFunction :