clang 24.0.0git
SMTConv.h
Go to the documentation of this file.
1//== SMTConv.h --------------------------------------------------*- C++ -*--==//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file defines a set of functions to create SMT expressions.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONV_H
14#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONV_H
15
16#include "clang/AST/Expr.h"
19#include "llvm/Support/SMTAPI.h"
20
21#include <algorithm>
22
23namespace clang {
24namespace ento {
25
26class SMTConv {
27public:
28 static inline uint64_t getSMTBitWidth(ASTContext &Ctx, QualType Ty) {
29 Ty = getSymbolicValueType(Ty);
31 return Ctx.getIntWidth(Ty);
32 return Ctx.getTypeSize(Ty);
33 }
34
38
39 // Returns an appropriate sort, given a QualType and it's bit width.
40 static inline llvm::SMTSortRef mkSort(llvm::SMTSolverRef &Solver,
41 const QualType &Ty, unsigned BitWidth) {
42 if (Ty->isBooleanType())
43 return Solver->getBoolSort();
44
45 if (Ty->isRealFloatingType())
46 return Solver->getFloatSort(BitWidth);
47
48 return Solver->getBitvectorSort(BitWidth);
49 }
50
51 /// Constructs an SMTSolverRef from an unary operator.
52 static inline llvm::SMTExprRef fromUnOp(llvm::SMTSolverRef &Solver,
53 const UnaryOperator::Opcode Op,
54 const llvm::SMTExprRef &Exp) {
55 switch (Op) {
56 case UO_Minus:
57 return Solver->mkBVNeg(Exp);
58
59 case UO_Not:
60 return Solver->mkBVNot(Exp);
61
62 case UO_LNot:
63 return Solver->mkNot(Exp);
64
65 default:;
66 }
67 llvm_unreachable("Unimplemented opcode");
68 }
69
70 /// Constructs an SMTSolverRef from a floating-point unary operator.
71 static inline llvm::SMTExprRef fromFloatUnOp(llvm::SMTSolverRef &Solver,
72 const UnaryOperator::Opcode Op,
73 const llvm::SMTExprRef &Exp) {
74 switch (Op) {
75 case UO_Minus:
76 return Solver->mkFPNeg(Exp);
77
78 case UO_LNot:
79 return fromUnOp(Solver, Op, Exp);
80
81 default:;
82 }
83 llvm_unreachable("Unimplemented opcode");
84 }
85
86 /// Construct an SMTSolverRef from a n-ary binary operator.
87 static inline llvm::SMTExprRef
88 fromNBinOp(llvm::SMTSolverRef &Solver, const BinaryOperator::Opcode Op,
89 const std::vector<llvm::SMTExprRef> &ASTs) {
90 assert(!ASTs.empty());
91
92 if (Op != BO_LAnd && Op != BO_LOr)
93 llvm_unreachable("Unimplemented opcode");
94
95 llvm::SMTExprRef res = ASTs.front();
96 for (std::size_t i = 1; i < ASTs.size(); ++i)
97 res = (Op == BO_LAnd) ? Solver->mkAnd(res, ASTs[i])
98 : Solver->mkOr(res, ASTs[i]);
99 return res;
100 }
101
102 /// Construct an SMTSolverRef from a binary operator.
103 static inline llvm::SMTExprRef fromBinOp(llvm::SMTSolverRef &Solver,
104 const llvm::SMTExprRef &LHS,
105 const BinaryOperator::Opcode Op,
106 const llvm::SMTExprRef &RHS,
107 bool isSigned) {
108 assert(*Solver->getSort(LHS) == *Solver->getSort(RHS) &&
109 "AST's must have the same sort!");
110
111 switch (Op) {
112 // Multiplicative operators
113 case BO_Mul:
114 return Solver->mkBVMul(LHS, RHS);
115
116 case BO_Div:
117 return isSigned ? Solver->mkBVSDiv(LHS, RHS) : Solver->mkBVUDiv(LHS, RHS);
118
119 case BO_Rem:
120 return isSigned ? Solver->mkBVSRem(LHS, RHS) : Solver->mkBVURem(LHS, RHS);
121
122 // Additive operators
123 case BO_Add:
124 return Solver->mkBVAdd(LHS, RHS);
125
126 case BO_Sub:
127 return Solver->mkBVSub(LHS, RHS);
128
129 // Bitwise shift operators
130 case BO_Shl:
131 return Solver->mkBVShl(LHS, RHS);
132
133 case BO_Shr:
134 return isSigned ? Solver->mkBVAshr(LHS, RHS) : Solver->mkBVLshr(LHS, RHS);
135
136 // Relational operators
137 case BO_LT:
138 return isSigned ? Solver->mkBVSlt(LHS, RHS) : Solver->mkBVUlt(LHS, RHS);
139
140 case BO_GT:
141 return isSigned ? Solver->mkBVSgt(LHS, RHS) : Solver->mkBVUgt(LHS, RHS);
142
143 case BO_LE:
144 return isSigned ? Solver->mkBVSle(LHS, RHS) : Solver->mkBVUle(LHS, RHS);
145
146 case BO_GE:
147 return isSigned ? Solver->mkBVSge(LHS, RHS) : Solver->mkBVUge(LHS, RHS);
148
149 // Equality operators
150 case BO_EQ:
151 return Solver->mkEqual(LHS, RHS);
152
153 case BO_NE:
154 return fromUnOp(Solver, UO_LNot,
155 fromBinOp(Solver, LHS, BO_EQ, RHS, isSigned));
156
157 // Bitwise operators
158 case BO_And:
159 return Solver->mkBVAnd(LHS, RHS);
160
161 case BO_Xor:
162 return Solver->mkBVXor(LHS, RHS);
163
164 case BO_Or:
165 return Solver->mkBVOr(LHS, RHS);
166
167 // Logical operators
168 case BO_LAnd:
169 return Solver->mkAnd(LHS, RHS);
170
171 case BO_LOr:
172 return Solver->mkOr(LHS, RHS);
173
174 default:;
175 }
176 llvm_unreachable("Unimplemented opcode");
177 }
178
179 /// Construct an SMTSolverRef from a special floating-point binary
180 /// operator.
181 static inline llvm::SMTExprRef
182 fromFloatSpecialBinOp(llvm::SMTSolverRef &Solver, const llvm::SMTExprRef &LHS,
183 const BinaryOperator::Opcode Op,
184 const llvm::APFloat::fltCategory &RHS) {
185 switch (Op) {
186 // Equality operators
187 case BO_EQ:
188 switch (RHS) {
189 case llvm::APFloat::fcInfinity:
190 return Solver->mkFPIsInfinite(LHS);
191
192 case llvm::APFloat::fcNaN:
193 return Solver->mkFPIsNaN(LHS);
194
195 case llvm::APFloat::fcNormal:
196 return Solver->mkFPIsNormal(LHS);
197
198 case llvm::APFloat::fcZero:
199 return Solver->mkFPIsZero(LHS);
200 }
201 break;
202
203 case BO_NE:
204 return fromFloatUnOp(Solver, UO_LNot,
205 fromFloatSpecialBinOp(Solver, LHS, BO_EQ, RHS));
206
207 default:;
208 }
209
210 llvm_unreachable("Unimplemented opcode");
211 }
212
213 /// Construct an SMTSolverRef from a floating-point binary operator.
214 static inline llvm::SMTExprRef fromFloatBinOp(llvm::SMTSolverRef &Solver,
215 const llvm::SMTExprRef &LHS,
216 const BinaryOperator::Opcode Op,
217 const llvm::SMTExprRef &RHS) {
218 assert(*Solver->getSort(LHS) == *Solver->getSort(RHS) &&
219 "AST's must have the same sort!");
220
221 switch (Op) {
222 // Multiplicative operators
223 case BO_Mul:
224 return Solver->mkFPMul(LHS, RHS);
225
226 case BO_Div:
227 return Solver->mkFPDiv(LHS, RHS);
228
229 case BO_Rem:
230 return Solver->mkFPRem(LHS, RHS);
231
232 // Additive operators
233 case BO_Add:
234 return Solver->mkFPAdd(LHS, RHS);
235
236 case BO_Sub:
237 return Solver->mkFPSub(LHS, RHS);
238
239 // Relational operators
240 case BO_LT:
241 return Solver->mkFPLt(LHS, RHS);
242
243 case BO_GT:
244 return Solver->mkFPGt(LHS, RHS);
245
246 case BO_LE:
247 return Solver->mkFPLe(LHS, RHS);
248
249 case BO_GE:
250 return Solver->mkFPGe(LHS, RHS);
251
252 // Equality operators
253 case BO_EQ:
254 return Solver->mkFPEqual(LHS, RHS);
255
256 case BO_NE:
257 return fromFloatUnOp(Solver, UO_LNot,
258 fromFloatBinOp(Solver, LHS, BO_EQ, RHS));
259
260 // Logical operators
261 case BO_LAnd:
262 case BO_LOr:
263 return fromBinOp(Solver, LHS, Op, RHS, /*isSigned=*/false);
264
265 default:;
266 }
267
268 llvm_unreachable("Unimplemented opcode");
269 }
270
271 /// Construct an SMTSolverRef from a QualType FromTy to a QualType ToTy,
272 /// and their bit widths.
273 static inline llvm::SMTExprRef fromCast(llvm::SMTSolverRef &Solver,
274 const llvm::SMTExprRef &Exp,
275 QualType ToTy, uint64_t ToBitWidth,
276 QualType FromTy,
277 uint64_t FromBitWidth) {
278 FromTy = getSymbolicValueType(FromTy);
279 ToTy = getSymbolicValueType(ToTy);
280
281 if (FromTy == ToTy && FromBitWidth == ToBitWidth)
282 return Exp;
283
284 if ((FromTy->isIntegralOrEnumerationType() &&
286 (FromTy->isAnyPointerType() ^ ToTy->isAnyPointerType()) ||
287 (FromTy->isBlockPointerType() ^ ToTy->isBlockPointerType()) ||
288 (FromTy->isReferenceType() ^ ToTy->isReferenceType())) {
289
290 if (FromTy->isBooleanType()) {
291 assert(ToBitWidth > 0 && "BitWidth must be positive!");
292 return Solver->mkIte(
293 Exp, Solver->mkBitvector(llvm::APSInt("1"), ToBitWidth),
294 Solver->mkBitvector(llvm::APSInt("0"), ToBitWidth));
295 }
296
297 if (ToBitWidth > FromBitWidth)
298 return FromTy->isSignedIntegerOrEnumerationType()
299 ? Solver->mkBVSignExt(ToBitWidth - FromBitWidth, Exp)
300 : Solver->mkBVZeroExt(ToBitWidth - FromBitWidth, Exp);
301
302 if (ToBitWidth < FromBitWidth)
303 return Solver->mkBVExtract(ToBitWidth - 1, 0, Exp);
304
305 // Both are bitvectors with the same width, ignore the type cast
306 return Exp;
307 }
308
309 if (FromTy->isRealFloatingType() && ToTy->isRealFloatingType()) {
310 if (ToBitWidth != FromBitWidth)
311 return Solver->mkFPtoFP(Exp, Solver->getFloatSort(ToBitWidth));
312
313 return Exp;
314 }
315
316 if (FromTy->isIntegralOrEnumerationType() && ToTy->isRealFloatingType()) {
317 llvm::SMTSortRef Sort = Solver->getFloatSort(ToBitWidth);
318 return FromTy->isSignedIntegerOrEnumerationType()
319 ? Solver->mkSBVtoFP(Exp, Sort)
320 : Solver->mkUBVtoFP(Exp, Sort);
321 }
322
323 if (FromTy->isRealFloatingType() && ToTy->isIntegralOrEnumerationType())
325 ? Solver->mkFPtoSBV(Exp, ToBitWidth)
326 : Solver->mkFPtoUBV(Exp, ToBitWidth);
327
328 llvm_unreachable("Unsupported explicit type cast!");
329 }
330
331 // Callback function for doCast parameter on APSInt type.
332 static inline llvm::APSInt castAPSInt(llvm::SMTSolverRef &Solver,
333 const llvm::APSInt &V, QualType ToTy,
334 uint64_t ToWidth, QualType FromTy,
335 uint64_t FromWidth) {
336 APSIntType TargetType(ToWidth, !ToTy->isSignedIntegerOrEnumerationType());
337 return TargetType.convert(V);
338 }
339
340 /// Construct an SMTSolverRef from a SymbolData.
341 static inline llvm::SMTExprRef
342 fromData(llvm::SMTSolverRef &Solver, ASTContext &Ctx, const SymbolData *Sym) {
343 const SymbolID ID = Sym->getSymbolID();
344 const QualType Ty = Sym->getType();
345 const uint64_t BitWidth = SMTConv::getSMTBitWidth(Ctx, Ty);
346
348 llvm::raw_svector_ostream OS(Str);
349 OS << Sym->getKindStr() << ID;
350 return Solver->mkSymbol(Str.c_str(), mkSort(Solver, Ty, BitWidth));
351 }
352
353 // Wrapper to generate SMTSolverRef from SymbolCast data.
354 static inline llvm::SMTExprRef getCastExpr(llvm::SMTSolverRef &Solver,
355 ASTContext &Ctx,
356 const llvm::SMTExprRef &Exp,
357 QualType FromTy, QualType ToTy) {
358 return fromCast(Solver, Exp, ToTy, SMTConv::getSMTBitWidth(Ctx, ToTy),
359 FromTy, SMTConv::getSMTBitWidth(Ctx, FromTy));
360 }
361
362 static inline std::optional<llvm::SMTExprRef>
363 convertToBoolExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx,
364 const llvm::SMTExprRef &Exp, QualType Ty) {
365 if (Ty->isBooleanType())
366 return Exp;
367
368 if (Ty->isRealFloatingType()) {
369 llvm::APFloat Zero =
370 llvm::APFloat::getZero(Ctx.getFloatTypeSemantics(Ty));
371 return fromFloatBinOp(Solver, Exp, BO_NE, Solver->mkFloat(Zero));
372 }
373
375 Ty->isBlockPointerType() || Ty->isReferenceType())
376 return fromBinOp(Solver, Exp, BO_NE,
377 Solver->mkBitvector(llvm::APSInt::getUnsigned(0),
378 SMTConv::getSMTBitWidth(Ctx, Ty)),
380 assert(false && "Unsupported type for boolean conversion!");
381 return std::nullopt;
382 }
383
384 // Wrapper to generate SMTSolverRef from unpacked binary symbolic
385 // expression. Sets the RetTy parameter. See getSMTSolverRef().
386 static inline std::optional<llvm::SMTExprRef>
387 getBinExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx,
388 const llvm::SMTExprRef &LHS, QualType LTy,
389 BinaryOperator::Opcode Op, const llvm::SMTExprRef &RHS,
390 QualType RTy, QualType &RetTy) {
391 llvm::SMTExprRef NewLHS = LHS;
392 llvm::SMTExprRef NewRHS = RHS;
393
394 // Update the return type parameter if the output type has changed.
395 // A boolean result can be represented as an integer type in C/C++, but at
396 // this point we only care about the SMT sorts. Set it as a boolean type
397 // to avoid subsequent SMT errors.
399 doTypeConversion(Solver, Ctx, NewLHS, NewRHS, LTy, RTy);
400 RetTy = Ctx.BoolTy;
401 } else if (BinaryOperator::isLogicalOp(Op)) {
402 RetTy = Ctx.BoolTy;
403 auto LHSOpt = convertToBoolExpr(Solver, Ctx, LHS, LTy);
404 auto RHSOpt = convertToBoolExpr(Solver, Ctx, RHS, RTy);
405 if (!LHSOpt || !RHSOpt)
406 return std::nullopt;
407 NewLHS = LHSOpt.value();
408 NewRHS = RHSOpt.value();
409 return fromBinOp(Solver, NewLHS, Op, NewRHS, false);
410 } else {
411 doTypeConversion(Solver, Ctx, NewLHS, NewRHS, LTy, RTy);
412 RetTy = LTy;
413 }
414
415 // If the two operands are pointers and the operation is a subtraction,
416 // the result is of type ptrdiff_t, which is signed
417 if (LTy->isAnyPointerType() && RTy->isAnyPointerType() && Op == BO_Sub)
418 RetTy = Ctx.getPointerDiffType();
419
420 return LTy->isRealFloatingType()
421 ? fromFloatBinOp(Solver, NewLHS, Op, NewRHS)
422 : fromBinOp(Solver, NewLHS, Op, NewRHS,
424 }
425
426 // Wrapper to generate SMTSolverRef from BinarySymExpr.
427 // Sets the hasComparison and RetTy parameters. See getSMTSolverRef().
428 static inline std::optional<llvm::SMTExprRef>
429 getSymBinExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx,
430 const BinarySymExpr *BSE, bool *hasComparison,
431 QualType &RetTy) {
432 QualType LTy, RTy;
434
435 if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) {
436 std::optional<llvm::SMTExprRef> LHS =
437 getSymExpr(Solver, Ctx, SIE->getLHS(), LTy, hasComparison);
438 if (!LHS)
439 return std::nullopt;
440 llvm::APSInt NewRInt;
441 std::tie(NewRInt, RTy) = fixAPSInt(Ctx, SIE->getRHS());
442 llvm::SMTExprRef RHS =
443 Solver->mkBitvector(NewRInt, NewRInt.getBitWidth());
444 return getBinExpr(Solver, Ctx, LHS.value(), LTy, Op, RHS, RTy, RetTy);
445 }
446
447 if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) {
448 llvm::APSInt NewLInt;
449 std::tie(NewLInt, LTy) = fixAPSInt(Ctx, ISE->getLHS());
450 llvm::SMTExprRef LHS =
451 Solver->mkBitvector(NewLInt, NewLInt.getBitWidth());
452 std::optional<llvm::SMTExprRef> RHS =
453 getSymExpr(Solver, Ctx, ISE->getRHS(), RTy, hasComparison);
454 if (!RHS)
455 return std::nullopt;
456 return getBinExpr(Solver, Ctx, LHS, LTy, Op, RHS.value(), RTy, RetTy);
457 }
458
459 if (const SymSymExpr *SSM = dyn_cast<SymSymExpr>(BSE)) {
460 std::optional<llvm::SMTExprRef> LHS =
461 getSymExpr(Solver, Ctx, SSM->getLHS(), LTy, hasComparison);
462 std::optional<llvm::SMTExprRef> RHS =
463 getSymExpr(Solver, Ctx, SSM->getRHS(), RTy, hasComparison);
464 if (!LHS || !RHS)
465 return std::nullopt;
466 return getBinExpr(Solver, Ctx, LHS.value(), LTy, Op, RHS.value(), RTy,
467 RetTy);
468 }
469
470 assert(false && "Unsupported BinarySymExpr type!");
471 return std::nullopt;
472 }
473
474 // Recursive implementation to unpack and generate symbolic expression.
475 // Sets the hasComparison and RetTy parameters. See getExpr().
476 static inline std::optional<llvm::SMTExprRef>
477 getSymExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, SymbolRef Sym,
478 QualType &RetTy, bool *hasComparison) {
479 if (const SymbolData *SD = dyn_cast<SymbolData>(Sym)) {
480 RetTy = getSymbolicValueType(Sym->getType());
481
482 return fromData(Solver, Ctx, SD);
483 }
484
485 if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) {
486 RetTy = getSymbolicValueType(Sym->getType());
487
488 QualType FromTy;
489 std::optional<llvm::SMTExprRef> Exp =
490 getSymExpr(Solver, Ctx, SC->getOperand(), FromTy, hasComparison);
491 if (!Exp)
492 return std::nullopt;
493 // Casting an expression with a comparison invalidates it. Note that this
494 // must occur after the recursive call above.
495 // e.g. (signed char) (x > 0)
496 if (hasComparison)
497 *hasComparison = false;
498 return getCastExpr(Solver, Ctx, Exp.value(), FromTy, RetTy);
499 }
500
501 if (const UnarySymExpr *USE = dyn_cast<UnarySymExpr>(Sym)) {
502 RetTy = getSymbolicValueType(Sym->getType());
503
504 QualType OperandTy;
505 std::optional<llvm::SMTExprRef> OperandExp =
506 getSymExpr(Solver, Ctx, USE->getOperand(), OperandTy, hasComparison);
507 if (!OperandExp)
508 return std::nullopt;
509 // When the operand is a bool expr, but the operator is an integeral
510 // operator, casting the bool expr to the integer before creating the
511 // unary operator.
512 // E.g. -(5 && a)
513 if (OperandTy == Ctx.BoolTy && OperandTy != RetTy &&
514 RetTy->isIntegerType()) {
515
516 if (hasComparison)
517 *hasComparison = false;
518
519 OperandExp = fromCast(Solver, OperandExp.value(), RetTy,
520 Ctx.getTypeSize(RetTy), OperandTy, 1);
521 OperandTy = RetTy;
522 }
523
524 llvm::SMTExprRef UnaryExp =
525 OperandTy->isRealFloatingType()
526 ? fromFloatUnOp(Solver, USE->getOpcode(), OperandExp.value())
527 : fromUnOp(Solver, USE->getOpcode(), OperandExp.value());
528
529 // Currently, without the `support-symbolic-integer-casts=true` option,
530 // we do not emit `SymbolCast`s for implicit casts.
531 // One such implicit cast is missing if the operand of the unary operator
532 // has a different type than the unary itself.
533 if (Ctx.getTypeSize(OperandTy) != Ctx.getTypeSize(Sym->getType())) {
534 if (hasComparison)
535 *hasComparison = false;
536 return getCastExpr(Solver, Ctx, UnaryExp, OperandTy, RetTy);
537 }
538 return UnaryExp;
539 }
540
541 if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) {
542 std::optional<llvm::SMTExprRef> Exp =
543 getSymBinExpr(Solver, Ctx, BSE, hasComparison, RetTy);
544 // Set the hasComparison parameter, in post-order traversal order.
545 if (hasComparison)
546 *hasComparison = BinaryOperator::isComparisonOp(BSE->getOpcode());
547 return Exp;
548 }
549 assert(false && "Unsupported SymbolRef type!");
550 return std::nullopt;
551 }
552
553 // Generate an SMTSolverRef that represents the given symbolic expression.
554 // Sets the hasComparison parameter if the expression has a comparison
555 // operator. Sets the RetTy parameter to the final return type after
556 // promotions and casts.
557 static inline std::optional<llvm::SMTExprRef>
558 getExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, SymbolRef Sym,
559 QualType &RetTy, bool *hasComparison = nullptr) {
560 if (hasComparison) {
561 *hasComparison = false;
562 }
563
564 return getSymExpr(Solver, Ctx, Sym, RetTy, hasComparison);
565 }
566
567 // Generate an SMTSolverRef that compares the expression to zero.
568 static inline llvm::SMTExprRef getZeroExpr(llvm::SMTSolverRef &Solver,
569 ASTContext &Ctx,
570 const llvm::SMTExprRef &Exp,
571 QualType Ty, bool Assumption) {
572 if (Ty->isRealFloatingType()) {
573 llvm::APFloat Zero =
574 llvm::APFloat::getZero(Ctx.getFloatTypeSemantics(Ty));
575 return fromFloatBinOp(Solver, Exp, Assumption ? BO_EQ : BO_NE,
576 Solver->mkFloat(Zero));
577 }
578
580 Ty->isBlockPointerType() || Ty->isReferenceType()) {
581
582 // Skip explicit comparison for boolean types
583 bool isSigned = Ty->isSignedIntegerOrEnumerationType();
584 if (Ty->isBooleanType())
585 return Assumption ? fromUnOp(Solver, UO_LNot, Exp) : Exp;
586
587 return fromBinOp(Solver, Exp, Assumption ? BO_EQ : BO_NE,
588 Solver->mkBitvector(llvm::APSInt("0"),
589 SMTConv::getSMTBitWidth(Ctx, Ty)),
590 isSigned);
591 }
592
593 llvm_unreachable("Unsupported type for zero value!");
594 }
595
596 // Wrapper to generate SMTSolverRef from a range. If From == To, an
597 // equality will be created instead.
598 static inline std::optional<llvm::SMTExprRef>
599 getRangeExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, SymbolRef Sym,
600 const llvm::APSInt &From, const llvm::APSInt &To, bool InRange) {
601 // Convert lower bound
602 QualType FromTy;
603 llvm::APSInt NewFromInt;
604 std::tie(NewFromInt, FromTy) = fixAPSInt(Ctx, From);
605 llvm::SMTExprRef FromExp =
606 Solver->mkBitvector(NewFromInt, NewFromInt.getBitWidth());
607
608 // Convert symbol
609 QualType SymTy;
610 std::optional<llvm::SMTExprRef> Exp = getExpr(Solver, Ctx, Sym, SymTy);
611 if (!Exp)
612 return std::nullopt;
613 // Construct single (in)equality
614 if (From == To) {
615 QualType UnusedRetTy;
616 return getBinExpr(Solver, Ctx, Exp.value(), SymTy,
617 InRange ? BO_EQ : BO_NE, FromExp, FromTy,
618 /*RetTy=*/UnusedRetTy);
619 }
620
621 QualType ToTy;
622 llvm::APSInt NewToInt;
623 std::tie(NewToInt, ToTy) = fixAPSInt(Ctx, To);
624 llvm::SMTExprRef ToExp =
625 Solver->mkBitvector(NewToInt, NewToInt.getBitWidth());
626 assert(FromTy == ToTy && "Range values have different types!");
627
628 // Construct two (in)equalities, and a logical and/or
629 QualType UnusedRetTy;
630 std::optional<llvm::SMTExprRef> LHS =
631 getBinExpr(Solver, Ctx, Exp.value(), SymTy, InRange ? BO_GE : BO_LT,
632 FromExp, FromTy, /*RetTy=*/UnusedRetTy);
633 std::optional<llvm::SMTExprRef> RHS = getBinExpr(
634 Solver, Ctx, Exp.value(), SymTy, InRange ? BO_LE : BO_GT, ToExp, ToTy,
635 /*RetTy=*/UnusedRetTy);
636 if (!LHS || !RHS)
637 return std::nullopt;
638 return fromBinOp(Solver, LHS.value(), InRange ? BO_LAnd : BO_LOr,
639 RHS.value(), SymTy->isSignedIntegerOrEnumerationType());
640 }
641
642 // Recover the QualType of an APSInt.
643 // TODO: Refactor to put elsewhere
645 const llvm::APSInt &Int) {
646 return Ctx.getBitIntType(Int.isUnsigned(), Int.getBitWidth());
647 }
648
649 // Get the QualTy for the input APSInt, and fix it if it has a bitwidth of 1.
650 static inline std::pair<llvm::APSInt, QualType>
651 fixAPSInt(ASTContext &Ctx, const llvm::APSInt &Int) {
652 return {Int, getAPSIntType(Ctx, Int)};
653 }
654
655 // Perform implicit type conversion on binary symbolic expressions.
656 // May modify all input parameters.
657 // TODO: Refactor to use built-in conversion functions
658 static inline void doTypeConversion(llvm::SMTSolverRef &Solver,
659 ASTContext &Ctx, llvm::SMTExprRef &LHS,
660 llvm::SMTExprRef &RHS, QualType &LTy,
661 QualType &RTy) {
662 assert(!LTy.isNull() && !RTy.isNull() && "Input type is null!");
663
664 // Perform type conversion
665 if ((LTy->isIntegralOrEnumerationType() &&
667 (LTy->isArithmeticType() && RTy->isArithmeticType())) {
669 Solver, Ctx, LHS, LTy, RHS, RTy);
670 return;
671 }
672
673 if (LTy->isRealFloatingType() || RTy->isRealFloatingType()) {
675 Solver, Ctx, LHS, LTy, RHS, RTy);
676 return;
677 }
678
679 if ((LTy->isAnyPointerType() || RTy->isAnyPointerType()) ||
680 (LTy->isBlockPointerType() || RTy->isBlockPointerType()) ||
681 (LTy->isReferenceType() || RTy->isReferenceType())) {
682 // TODO: Refactor to Sema::FindCompositePointerType(), and
683 // Sema::CheckCompareOperands().
684
685 uint64_t LBitWidth = Ctx.getTypeSize(LTy);
686 uint64_t RBitWidth = Ctx.getTypeSize(RTy);
687
688 // Cast the non-pointer type to the pointer type.
689 // TODO: Be more strict about this.
690 if ((LTy->isAnyPointerType() ^ RTy->isAnyPointerType()) ||
691 (LTy->isBlockPointerType() ^ RTy->isBlockPointerType()) ||
692 (LTy->isReferenceType() ^ RTy->isReferenceType())) {
693 if (LTy->isNullPtrType() || LTy->isBlockPointerType() ||
694 LTy->isReferenceType()) {
695 LHS = fromCast(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth);
696 LTy = RTy;
697 } else {
698 RHS = fromCast(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth);
699 RTy = LTy;
700 }
701 }
702
703 // Cast the void pointer type to the non-void pointer type.
704 // For void types, this assumes that the casted value is equal to the
705 // value of the original pointer, and does not account for alignment
706 // requirements.
707 if (LTy->isVoidPointerType() ^ RTy->isVoidPointerType()) {
708 assert((Ctx.getTypeSize(LTy) == Ctx.getTypeSize(RTy)) &&
709 "Pointer types have different bitwidths!");
710 if (RTy->isVoidPointerType())
711 RTy = LTy;
712 else
713 LTy = RTy;
714 }
715
716 if (LTy == RTy)
717 return;
718 }
719
720 // Fallback: for the solver, assume that these types don't really matter
721 if ((LTy.getCanonicalType() == RTy.getCanonicalType()) ||
723 LTy = RTy;
724 return;
725 }
726
727 // TODO: Refine behavior for invalid type casts
728 }
729
730 // Perform implicit integer type conversion.
731 // May modify all input parameters.
732 // TODO: Refactor to use Sema::handleIntegerConversion()
733 template <typename T, T (*doCast)(llvm::SMTSolverRef &Solver, const T &,
734 QualType, uint64_t, QualType, uint64_t)>
735 static inline void doIntTypeConversion(llvm::SMTSolverRef &Solver,
736 ASTContext &Ctx, T &LHS, QualType &LTy,
737 T &RHS, QualType &RTy) {
738 uint64_t LBitWidth = SMTConv::getSMTBitWidth(Ctx, LTy);
739 uint64_t RBitWidth = SMTConv::getSMTBitWidth(Ctx, RTy);
740
741 assert(!LTy.isNull() && !RTy.isNull() && "Input type is null!");
742 // Always perform integer promotion before checking type equality.
743 // Otherwise, e.g. (bool) a + (bool) b could trigger a backend assertion
744 if (Ctx.isPromotableIntegerType(LTy)) {
745 QualType NewTy = Ctx.getPromotedIntegerType(LTy);
746 uint64_t NewBitWidth = Ctx.getTypeSize(NewTy);
747 LHS = (*doCast)(Solver, LHS, NewTy, NewBitWidth, LTy, LBitWidth);
748 LTy = NewTy;
749 LBitWidth = NewBitWidth;
750 }
751 if (Ctx.isPromotableIntegerType(RTy)) {
752 QualType NewTy = Ctx.getPromotedIntegerType(RTy);
753 uint64_t NewBitWidth = Ctx.getTypeSize(NewTy);
754 RHS = (*doCast)(Solver, RHS, NewTy, NewBitWidth, RTy, RBitWidth);
755 RTy = NewTy;
756 RBitWidth = NewBitWidth;
757 }
758
759 if (LTy == RTy)
760 return;
761
762 // Perform integer type conversion
763 // Note: Safe to skip updating bitwidth because this must terminate
764 bool isLSignedTy = LTy->isSignedIntegerOrEnumerationType();
765 bool isRSignedTy = RTy->isSignedIntegerOrEnumerationType();
766
767 int order = Ctx.getIntegerTypeOrder(LTy, RTy);
768 if (isLSignedTy == isRSignedTy) {
769 // Same signedness; use the higher-ranked type
770 if (order == 1) {
771 RHS = (*doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth);
772 RTy = LTy;
773 } else {
774 LHS = (*doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth);
775 LTy = RTy;
776 }
777 } else if (order != (isLSignedTy ? 1 : -1)) {
778 // The unsigned type has greater than or equal rank to the
779 // signed type, so use the unsigned type
780 if (isRSignedTy) {
781 RHS = (*doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth);
782 RTy = LTy;
783 } else {
784 LHS = (*doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth);
785 LTy = RTy;
786 }
787 } else if (LBitWidth != RBitWidth) {
788 // The two types are different widths; if we are here, that
789 // means the signed type is larger than the unsigned type, so
790 // use the signed type.
791 if (isLSignedTy) {
792 RHS = (doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth);
793 RTy = LTy;
794 } else {
795 LHS = (*doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth);
796 LTy = RTy;
797 }
798 } else {
799 // The signed type is higher-ranked than the unsigned type,
800 // but isn't actually any bigger (like unsigned int and long
801 // on most 32-bit systems). Use the unsigned type corresponding
802 // to the signed type.
803 QualType NewTy =
804 Ctx.getCorrespondingUnsignedType(isLSignedTy ? LTy : RTy);
805 RHS = (*doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth);
806 RTy = NewTy;
807 LHS = (doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth);
808 LTy = NewTy;
809 }
810 }
811
812 // Perform implicit floating-point type conversion.
813 // May modify all input parameters.
814 // TODO: Refactor to use Sema::handleFloatConversion()
815 template <typename T, T (*doCast)(llvm::SMTSolverRef &Solver, const T &,
816 QualType, uint64_t, QualType, uint64_t)>
817 static inline void
818 doFloatTypeConversion(llvm::SMTSolverRef &Solver, ASTContext &Ctx, T &LHS,
819 QualType &LTy, T &RHS, QualType &RTy) {
820 uint64_t LBitWidth = Ctx.getTypeSize(LTy);
821 uint64_t RBitWidth = Ctx.getTypeSize(RTy);
822
823 // Perform float-point type promotion
824 if (!LTy->isRealFloatingType()) {
825 LHS = (*doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth);
826 LTy = RTy;
827 LBitWidth = RBitWidth;
828 }
829 if (!RTy->isRealFloatingType()) {
830 RHS = (*doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth);
831 RTy = LTy;
832 RBitWidth = LBitWidth;
833 }
834
835 if (LTy == RTy)
836 return;
837
838 // If we have two real floating types, convert the smaller operand to the
839 // bigger result
840 // Note: Safe to skip updating bitwidth because this must terminate
841 int order = Ctx.getFloatingTypeOrder(LTy, RTy);
842 if (order > 0) {
843 RHS = (*doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth);
844 RTy = LTy;
845 } else if (order == 0) {
846 LHS = (*doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth);
847 LTy = RTy;
848 } else {
849 llvm_unreachable("Unsupported floating-point type cast!");
850 }
851 }
852};
853} // namespace ento
854} // namespace clang
855
856#endif
#define V(N, I)
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:239
unsigned getIntWidth(QualType T) const
const llvm::fltSemantics & getFloatTypeSemantics(QualType T) const
Return the APFloat 'semantics' for the specified scalar floating point type.
int getIntegerTypeOrder(QualType LHS, QualType RHS) const
Return the highest ranked integer type, see C99 6.3.1.8p1.
QualType getPointerDiffType() const
Return the unique type for "ptrdiff_t" (C99 7.17) defined in <stddef.h>.
int getFloatingTypeOrder(QualType LHS, QualType RHS) const
Compare the rank of the two specified floating point types, ignoring the domain of the type (i....
CanQualType BoolTy
uint64_t getTypeSize(QualType T) const
Return the size of the specified (complete) type T, in bits.
QualType getPromotedIntegerType(QualType PromotableType) const
Return the type that PromotableType will promote to: C99 6.3.1.1p2, assuming that PromotableType is a...
QualType getCorrespondingUnsignedType(QualType T) const
bool isPromotableIntegerType(QualType T) const
More type predicates useful for type checking/promotion.
QualType getBitIntType(bool Unsigned, unsigned NumBits) const
Return a bit-precise integer type with the specified signedness and bit count.
static bool isLogicalOp(Opcode Opc)
Definition Expr.h:4215
static bool isComparisonOp(Opcode Opc)
Definition Expr.h:4182
bool isComparisonOp() const
Definition Expr.h:4183
BinaryOperatorKind Opcode
Definition Expr.h:4087
A (possibly-)qualified type.
Definition TypeBase.h:938
bool isNull() const
Return true if this QualType doesn't point to a type yet.
Definition TypeBase.h:1005
QualType getCanonicalType() const
Definition TypeBase.h:8470
QualType getAtomicUnqualifiedType() const
Remove all qualifiers including _Atomic.
Definition Type.cpp:1745
bool isBlockPointerType() const
Definition TypeBase.h:8675
bool isBooleanType() const
Definition TypeBase.h:9164
bool isSignedIntegerOrEnumerationType() const
Determines whether this is an integer type that is signed or an enumeration types whose underlying ty...
Definition Type.cpp:2319
bool isVoidPointerType() const
Definition Type.cpp:749
bool isArithmeticType() const
Definition Type.cpp:2454
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition TypeBase.h:9071
bool isReferenceType() const
Definition TypeBase.h:8679
bool isIntegralOrEnumerationType() const
Determine whether this type is an integral or enumeration type.
Definition TypeBase.h:9149
bool isObjCObjectPointerType() const
Definition TypeBase.h:8834
bool isRealFloatingType() const
Floating point categories.
Definition Type.cpp:2437
bool isAnyPointerType() const
Definition TypeBase.h:8663
bool isNullPtrType() const
Definition TypeBase.h:9064
UnaryOperatorKind Opcode
Definition Expr.h:2302
A record of the "type" of an APSInt, used for conversions.
Definition APSIntType.h:19
llvm::APSInt convert(const llvm::APSInt &Value) const LLVM_READONLY
Convert and return a new APSInt with the given value, but this type's bit width and signedness.
Definition APSIntType.h:48
Represents a symbolic expression involving a binary operator.
BinaryOperator::Opcode getOpcode() const
static llvm::SMTSortRef mkSort(llvm::SMTSolverRef &Solver, const QualType &Ty, unsigned BitWidth)
Definition SMTConv.h:40
static std::optional< llvm::SMTExprRef > getRangeExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, SymbolRef Sym, const llvm::APSInt &From, const llvm::APSInt &To, bool InRange)
Definition SMTConv.h:599
static llvm::SMTExprRef fromFloatBinOp(llvm::SMTSolverRef &Solver, const llvm::SMTExprRef &LHS, const BinaryOperator::Opcode Op, const llvm::SMTExprRef &RHS)
Construct an SMTSolverRef from a floating-point binary operator.
Definition SMTConv.h:214
static std::optional< llvm::SMTExprRef > getBinExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, const llvm::SMTExprRef &LHS, QualType LTy, BinaryOperator::Opcode Op, const llvm::SMTExprRef &RHS, QualType RTy, QualType &RetTy)
Definition SMTConv.h:387
static QualType getAPSIntType(ASTContext &Ctx, const llvm::APSInt &Int)
Definition SMTConv.h:644
static llvm::SMTExprRef getZeroExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, const llvm::SMTExprRef &Exp, QualType Ty, bool Assumption)
Definition SMTConv.h:568
static void doIntTypeConversion(llvm::SMTSolverRef &Solver, ASTContext &Ctx, T &LHS, QualType &LTy, T &RHS, QualType &RTy)
Definition SMTConv.h:735
static std::optional< llvm::SMTExprRef > convertToBoolExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, const llvm::SMTExprRef &Exp, QualType Ty)
Definition SMTConv.h:363
static llvm::SMTExprRef fromNBinOp(llvm::SMTSolverRef &Solver, const BinaryOperator::Opcode Op, const std::vector< llvm::SMTExprRef > &ASTs)
Construct an SMTSolverRef from a n-ary binary operator.
Definition SMTConv.h:88
static std::optional< llvm::SMTExprRef > getSymExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, SymbolRef Sym, QualType &RetTy, bool *hasComparison)
Definition SMTConv.h:477
static void doTypeConversion(llvm::SMTSolverRef &Solver, ASTContext &Ctx, llvm::SMTExprRef &LHS, llvm::SMTExprRef &RHS, QualType &LTy, QualType &RTy)
Definition SMTConv.h:658
static std::optional< llvm::SMTExprRef > getSymBinExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, const BinarySymExpr *BSE, bool *hasComparison, QualType &RetTy)
Definition SMTConv.h:429
static std::optional< llvm::SMTExprRef > getExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, SymbolRef Sym, QualType &RetTy, bool *hasComparison=nullptr)
Definition SMTConv.h:558
static llvm::SMTExprRef fromFloatSpecialBinOp(llvm::SMTSolverRef &Solver, const llvm::SMTExprRef &LHS, const BinaryOperator::Opcode Op, const llvm::APFloat::fltCategory &RHS)
Construct an SMTSolverRef from a special floating-point binary operator.
Definition SMTConv.h:182
static QualType getSymbolicValueType(QualType Ty)
Definition SMTConv.h:35
static llvm::SMTExprRef fromData(llvm::SMTSolverRef &Solver, ASTContext &Ctx, const SymbolData *Sym)
Construct an SMTSolverRef from a SymbolData.
Definition SMTConv.h:342
static void doFloatTypeConversion(llvm::SMTSolverRef &Solver, ASTContext &Ctx, T &LHS, QualType &LTy, T &RHS, QualType &RTy)
Definition SMTConv.h:818
static llvm::SMTExprRef fromBinOp(llvm::SMTSolverRef &Solver, const llvm::SMTExprRef &LHS, const BinaryOperator::Opcode Op, const llvm::SMTExprRef &RHS, bool isSigned)
Construct an SMTSolverRef from a binary operator.
Definition SMTConv.h:103
static llvm::SMTExprRef getCastExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, const llvm::SMTExprRef &Exp, QualType FromTy, QualType ToTy)
Definition SMTConv.h:354
static std::pair< llvm::APSInt, QualType > fixAPSInt(ASTContext &Ctx, const llvm::APSInt &Int)
Definition SMTConv.h:651
static llvm::SMTExprRef fromCast(llvm::SMTSolverRef &Solver, const llvm::SMTExprRef &Exp, QualType ToTy, uint64_t ToBitWidth, QualType FromTy, uint64_t FromBitWidth)
Construct an SMTSolverRef from a QualType FromTy to a QualType ToTy, and their bit widths.
Definition SMTConv.h:273
static uint64_t getSMTBitWidth(ASTContext &Ctx, QualType Ty)
Definition SMTConv.h:28
static llvm::APSInt castAPSInt(llvm::SMTSolverRef &Solver, const llvm::APSInt &V, QualType ToTy, uint64_t ToWidth, QualType FromTy, uint64_t FromWidth)
Definition SMTConv.h:332
static llvm::SMTExprRef fromFloatUnOp(llvm::SMTSolverRef &Solver, const UnaryOperator::Opcode Op, const llvm::SMTExprRef &Exp)
Constructs an SMTSolverRef from a floating-point unary operator.
Definition SMTConv.h:71
static llvm::SMTExprRef fromUnOp(llvm::SMTSolverRef &Solver, const UnaryOperator::Opcode Op, const llvm::SMTExprRef &Exp)
Constructs an SMTSolverRef from an unary operator.
Definition SMTConv.h:52
virtual QualType getType() const =0
SymbolID getSymbolID() const
Get a unique identifier for this symbol.
Definition SymExpr.h:77
Represents a cast expression.
A symbol representing data which can be stored in a memory location (region).
Definition SymExpr.h:138
virtual StringRef getKindStr() const =0
Get a string representation of the kind of the region.
Represents a symbolic expression involving a unary operator.
BinarySymExprImpl< APSIntPtr, const SymExpr *, SymExpr::Kind::IntSymExprKind > IntSymExpr
Represents a symbolic expression like 3 - 'x'.
const SymExpr * SymbolRef
Definition SymExpr.h:133
BinarySymExprImpl< const SymExpr *, const SymExpr *, SymExpr::Kind::SymSymExprKind > SymSymExpr
Represents a symbolic expression like 'x' + 'y'.
BinarySymExprImpl< const SymExpr *, APSIntPtr, SymExpr::Kind::SymIntExprKind > SymIntExpr
Represents a symbolic expression like 'x' + 3.
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
unsigned SymbolID
Definition SymExpr.h:28
Top level wrappers for InstallAPI frontend operations.
const FunctionProtoType * T