@@ -61,8 +61,7 @@ template <common::TypeCategory C, int K>
61
61
struct IsIntegral <evaluate::Type<C, K>> {
62
62
static constexpr bool value{//
63
63
C == common::TypeCategory::Integer ||
64
- C == common::TypeCategory::Unsigned ||
65
- C == common::TypeCategory::Logical};
64
+ C == common::TypeCategory::Unsigned};
66
65
};
67
66
68
67
template <typename T> constexpr bool is_integral_v{IsIntegral<T>::value};
@@ -83,10 +82,25 @@ constexpr bool is_floating_point_v{IsFloatingPoint<T>::value};
83
82
template <typename T>
84
83
constexpr bool is_numeric_v{is_integral_v<T> || is_floating_point_v<T>};
85
84
85
+ template <typename ...> struct IsLogical {
86
+ static constexpr bool value{false };
87
+ };
88
+
89
+ template <common::TypeCategory C, int K>
90
+ struct IsLogical <evaluate::Type<C, K>> {
91
+ static constexpr bool value{C == common::TypeCategory::Logical};
92
+ };
93
+
94
+ template <typename T> constexpr bool is_logical_v{IsLogical<T>::value};
95
+
86
96
template <typename T, typename Op0, typename Op1>
87
97
using ReassocOpBase = evaluate::match::AnyOfPattern< //
88
98
evaluate::match::Add<T, Op0, Op1>, //
89
- evaluate::match::Mul<T, Op0, Op1>>;
99
+ evaluate::match::Mul<T, Op0, Op1>, //
100
+ evaluate::match::LogicalOp<common::LogicalOperator::And, T, Op0, Op1>,
101
+ evaluate::match::LogicalOp<common::LogicalOperator::Or, T, Op0, Op1>,
102
+ evaluate::match::LogicalOp<common::LogicalOperator::Eqv, T, Op0, Op1>,
103
+ evaluate::match::LogicalOp<common::LogicalOperator::Neqv, T, Op0, Op1>>;
90
104
91
105
template <typename T, typename Op0, typename Op1>
92
106
struct ReassocOp : public ReassocOpBase <T, Op0, Op1> {
@@ -110,16 +124,16 @@ struct ReassocRewriter : public evaluate::rewrite::Identity {
110
124
// Try to find cases where the input expression is of the form
111
125
// (1) (a . b) . c, or
112
126
// (2) a . (b . c),
113
- // where . denotes an associative operation (currently + or *) , and a, b, c
114
- // are some subexpresions.
127
+ // where . denotes an associative operation, and a, b, c are some
128
+ // subexpresions.
115
129
// If one of the operands in the nested operation is the atomic variable
116
130
// (with some possible type conversions applied to it), bring it to the
117
131
// top-level operation, and move the top-level operand into the nested
118
132
// operation.
119
133
// For example, assuming x is the atomic variable:
120
134
// (a + x) + b -> (a + b) + x, i.e. (conceptually) swap x and b.
121
135
template <typename T, typename U,
122
- typename = std::enable_if_t <is_numeric_v<T>>>
136
+ typename = std::enable_if_t <is_numeric_v<T> || is_logical_v<T> >>
123
137
evaluate::Expr<T> operator ()(evaluate::Expr<T> &&x, const U &u) {
124
138
if constexpr (is_floating_point_v<T>) {
125
139
if (!context_.langOptions ().AssociativeMath ) {
@@ -133,8 +147,8 @@ struct ReassocRewriter : public evaluate::rewrite::Identity {
133
147
// some order) from the example above.
134
148
evaluate::match::Expr<T> sub[3 ];
135
149
auto inner{reassocOp<T>(sub[0 ], sub[1 ])};
136
- auto outer1{reassocOp<T>(inner, sub[2 ])}; // inner + something
137
- auto outer2{reassocOp<T>(sub[2 ], inner)}; // something + inner
150
+ auto outer1{reassocOp<T>(inner, sub[2 ])}; // inner . something
151
+ auto outer2{reassocOp<T>(sub[2 ], inner)}; // something . inner
138
152
#if !defined(__clang__) && !defined(_MSC_VER) && \
139
153
(__GNUC__ < 8 || (__GNUC__ == 8 && __GNUC_MINOR__ < 5 ))
140
154
// If GCC version < 8.5, use this definition. For the other definition
@@ -167,37 +181,53 @@ struct ReassocRewriter : public evaluate::rewrite::Identity {
167
181
}
168
182
return common::visit (
169
183
[&](auto &&s) {
170
- using Expr = evaluate::Expr<T>;
171
- using TypeS = llvm::remove_cvref_t <decltype (s)>;
172
- // This visitor has to be semantically correct for all possible
173
- // types of s even though at runtime s will only be one of the
174
- // matched types.
175
- // Limit the construction to the operation types that we tried
176
- // to match (otherwise TypeS(op1, op2) would fail for non-binary
177
- // operations).
178
- if constexpr (common::HasMember<TypeS, MatchTypes>) {
179
- Expr atom{*sub[atomIdx].ref };
180
- Expr op1{*sub[(atomIdx + 1 ) % 3 ].ref };
181
- Expr op2{*sub[(atomIdx + 2 ) % 3 ].ref };
182
- return Expr (
183
- TypeS (atom, Expr (TypeS (std::move (op1), std::move (op2)))));
184
- } else {
185
- return Expr (TypeS (s));
186
- }
184
+ // Build the new expression from the matched components.
185
+ return Reconstruct<T, MatchTypes>(s, *sub[atomIdx].ref ,
186
+ *sub[(atomIdx + 1 ) % 3 ].ref , *sub[(atomIdx + 2 ) % 3 ].ref );
187
187
},
188
188
evaluate::match::deparen (x).u );
189
189
}
190
190
return Id::operator ()(std::move (x), u);
191
191
}
192
192
193
193
template <typename T, typename U,
194
- typename = std::enable_if_t <!is_numeric_v<T>>>
194
+ typename = std::enable_if_t <!is_numeric_v<T> && !is_logical_v<T> >>
195
195
evaluate::Expr<T> operator ()(
196
196
evaluate::Expr<T> &&x, const U &u, NonIntegralTag = {}) {
197
197
return Id::operator ()(std::move (x), u);
198
198
}
199
199
200
200
private:
201
+ template <typename T, typename MatchTypes, typename S>
202
+ evaluate::Expr<T> Reconstruct (const S &op, evaluate::Expr<T> atom,
203
+ evaluate::Expr<T> op1, evaluate::Expr<T> op2) {
204
+ using TypeS = llvm::remove_cvref_t <decltype (op)>;
205
+ // This function has to be semantically correct for all possible types
206
+ // of S even though at runtime s will only be one of the matched types.
207
+ // Limit the construction to the operation types that we tried to match
208
+ // (otherwise TypeS(op1, op2) would fail for non-binary operations).
209
+ if constexpr (!common::HasMember<TypeS, MatchTypes>) {
210
+ return evaluate::Expr<T>(TypeS (op));
211
+ } else if constexpr (is_logical_v<T>) {
212
+ constexpr int K{T::kind};
213
+ if constexpr (std::is_same_v<TypeS, evaluate::LogicalOperation<K>>) {
214
+ // Logical operators take an extra argument in their constructor,
215
+ // so they need their own reconstruction code.
216
+ common::LogicalOperator opCode{op.logicalOperator };
217
+ return evaluate::Expr<T>(TypeS ( //
218
+ opCode, std::move (atom),
219
+ evaluate::Expr<T>(TypeS ( //
220
+ opCode, std::move (op1), std::move (op2)))));
221
+ }
222
+ } else {
223
+ // Generic reconstruction.
224
+ return evaluate::Expr<T>(TypeS ( //
225
+ std::move (atom),
226
+ evaluate::Expr<T>(TypeS ( //
227
+ std::move (op1), std::move (op2)))));
228
+ }
229
+ }
230
+
201
231
template <typename T> bool IsAtom (const evaluate::Expr<T> &x) const {
202
232
return IsSameOrConvertOf (evaluate::AsGenericExpr (AsRvalue (x)), atom_);
203
233
}
0 commit comments