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
421 return LTy->isRealFloatingType()
422 ? fromFloatBinOp(Solver, NewLHS, Op, NewRHS)
423 : fromBinOp(Solver, NewLHS, Op, NewRHS,
425 }
426
427 // Wrapper to generate SMTSolverRef from BinarySymExpr.
428 // Sets the hasComparison and RetTy parameters. See getSMTSolverRef().
429 static inline std::optional<llvm::SMTExprRef>
430 getSymBinExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx,
431 const BinarySymExpr *BSE, bool *hasComparison,
432 QualType &RetTy) {
433 QualType LTy, RTy;
435
436 if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) {
437 std::optional<llvm::SMTExprRef> LHS =
438 getSymExpr(Solver, Ctx, SIE->getLHS(), LTy, hasComparison);
439 if (!LHS)
440 return std::nullopt;
441 llvm::APSInt NewRInt;
442 std::tie(NewRInt, RTy) = fixAPSInt(Ctx, SIE->getRHS());
443 llvm::SMTExprRef RHS =
444 Solver->mkBitvector(NewRInt, NewRInt.getBitWidth());
445 return getBinExpr(Solver, Ctx, LHS.value(), LTy, Op, RHS, RTy, RetTy);
446 }
447
448 if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) {
449 llvm::APSInt NewLInt;
450 std::tie(NewLInt, LTy) = fixAPSInt(Ctx, ISE->getLHS());
451 llvm::SMTExprRef LHS =
452 Solver->mkBitvector(NewLInt, NewLInt.getBitWidth());
453 std::optional<llvm::SMTExprRef> RHS =
454 getSymExpr(Solver, Ctx, ISE->getRHS(), RTy, hasComparison);
455 if (!RHS)
456 return std::nullopt;
457 return getBinExpr(Solver, Ctx, LHS, LTy, Op, RHS.value(), RTy, RetTy);
458 }
459
460 if (const SymSymExpr *SSM = dyn_cast<SymSymExpr>(BSE)) {
461 std::optional<llvm::SMTExprRef> LHS =
462 getSymExpr(Solver, Ctx, SSM->getLHS(), LTy, hasComparison);
463 std::optional<llvm::SMTExprRef> RHS =
464 getSymExpr(Solver, Ctx, SSM->getRHS(), RTy, hasComparison);
465 if (!LHS || !RHS)
466 return std::nullopt;
467 return getBinExpr(Solver, Ctx, LHS.value(), LTy, Op, RHS.value(), RTy,
468 RetTy);
469 }
470
471 assert(false && "Unsupported BinarySymExpr type!");
472 return std::nullopt;
473 }
474
475 // Recursive implementation to unpack and generate symbolic expression.
476 // Sets the hasComparison and RetTy parameters. See getExpr().
477 static inline std::optional<llvm::SMTExprRef>
478 getSymExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, SymbolRef Sym,
479 QualType &RetTy, bool *hasComparison) {
480 if (const SymbolData *SD = dyn_cast<SymbolData>(Sym)) {
481 RetTy = getSymbolicValueType(Sym->getType());
482
483 return fromData(Solver, Ctx, SD);
484 }
485
486 if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) {
487 RetTy = getSymbolicValueType(Sym->getType());
488
489 QualType FromTy;
490 std::optional<llvm::SMTExprRef> Exp =
491 getSymExpr(Solver, Ctx, SC->getOperand(), FromTy, hasComparison);
492 if (!Exp)
493 return std::nullopt;
494 // Casting an expression with a comparison invalidates it. Note that this
495 // must occur after the recursive call above.
496 // e.g. (signed char) (x > 0)
497 if (hasComparison)
498 *hasComparison = false;
499 return getCastExpr(Solver, Ctx, Exp.value(), FromTy, RetTy);
500 }
501
502 if (const UnarySymExpr *USE = dyn_cast<UnarySymExpr>(Sym)) {
503 RetTy = getSymbolicValueType(Sym->getType());
504
505 QualType OperandTy;
506 std::optional<llvm::SMTExprRef> OperandExp =
507 getSymExpr(Solver, Ctx, USE->getOperand(), OperandTy, hasComparison);
508 if (!OperandExp)
509 return std::nullopt;
510 // When the operand is a bool expr, but the operator is an integeral
511 // operator, casting the bool expr to the integer before creating the
512 // unary operator.
513 // E.g. -(5 && a)
514 if (OperandTy == Ctx.BoolTy && OperandTy != RetTy &&
515 RetTy->isIntegerType()) {
516
517 if (hasComparison)
518 *hasComparison = false;
519
520 OperandExp = fromCast(Solver, OperandExp.value(), RetTy,
521 Ctx.getTypeSize(RetTy), OperandTy, 1);
522 OperandTy = RetTy;
523 }
524
525 llvm::SMTExprRef UnaryExp =
526 OperandTy->isRealFloatingType()
527 ? fromFloatUnOp(Solver, USE->getOpcode(), OperandExp.value())
528 : fromUnOp(Solver, USE->getOpcode(), OperandExp.value());
529
530 // Currently, without the `support-symbolic-integer-casts=true` option,
531 // we do not emit `SymbolCast`s for implicit casts.
532 // One such implicit cast is missing if the operand of the unary operator
533 // has a different type than the unary itself.
534 if (Ctx.getTypeSize(OperandTy) != Ctx.getTypeSize(Sym->getType())) {
535 if (hasComparison)
536 *hasComparison = false;
537 return getCastExpr(Solver, Ctx, UnaryExp, OperandTy, RetTy);
538 }
539 return UnaryExp;
540 }
541
542 if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) {
543 std::optional<llvm::SMTExprRef> Exp =
544 getSymBinExpr(Solver, Ctx, BSE, hasComparison, RetTy);
545 // Set the hasComparison parameter, in post-order traversal order.
546 if (hasComparison)
547 *hasComparison = BinaryOperator::isComparisonOp(BSE->getOpcode());
548 return Exp;
549 }
550 assert(false && "Unsupported SymbolRef type!");
551 return std::nullopt;
552 }
553
554 // Generate an SMTSolverRef that represents the given symbolic expression.
555 // Sets the hasComparison parameter if the expression has a comparison
556 // operator. Sets the RetTy parameter to the final return type after
557 // promotions and casts.
558 static inline std::optional<llvm::SMTExprRef>
559 getExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, SymbolRef Sym,
560 QualType &RetTy, bool *hasComparison = nullptr) {
561 if (hasComparison) {
562 *hasComparison = false;
563 }
564
565 return getSymExpr(Solver, Ctx, Sym, RetTy, hasComparison);
566 }
567
568 // Generate an SMTSolverRef that compares the expression to zero.
569 static inline llvm::SMTExprRef getZeroExpr(llvm::SMTSolverRef &Solver,
570 ASTContext &Ctx,
571 const llvm::SMTExprRef &Exp,
572 QualType Ty, bool Assumption) {
573 if (Ty->isRealFloatingType()) {
574 llvm::APFloat Zero =
575 llvm::APFloat::getZero(Ctx.getFloatTypeSemantics(Ty));
576 return fromFloatBinOp(Solver, Exp, Assumption ? BO_EQ : BO_NE,
577 Solver->mkFloat(Zero));
578 }
579
581 Ty->isBlockPointerType() || Ty->isReferenceType()) {
582
583 // Skip explicit comparison for boolean types
584 bool isSigned = Ty->isSignedIntegerOrEnumerationType();
585 if (Ty->isBooleanType())
586 return Assumption ? fromUnOp(Solver, UO_LNot, Exp) : Exp;
587
588 return fromBinOp(Solver, Exp, Assumption ? BO_EQ : BO_NE,
589 Solver->mkBitvector(llvm::APSInt("0"),
590 SMTConv::getSMTBitWidth(Ctx, Ty)),
591 isSigned);
592 }
593
594 llvm_unreachable("Unsupported type for zero value!");
595 }
596
597 // Wrapper to generate SMTSolverRef from a range. If From == To, an
598 // equality will be created instead.
599 static inline std::optional<llvm::SMTExprRef>
600 getRangeExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, SymbolRef Sym,
601 const llvm::APSInt &From, const llvm::APSInt &To, bool InRange) {
602 // Convert lower bound
603 QualType FromTy;
604 llvm::APSInt NewFromInt;
605 std::tie(NewFromInt, FromTy) = fixAPSInt(Ctx, From);
606 llvm::SMTExprRef FromExp =
607 Solver->mkBitvector(NewFromInt, NewFromInt.getBitWidth());
608
609 // Convert symbol
610 QualType SymTy;
611 std::optional<llvm::SMTExprRef> Exp = getExpr(Solver, Ctx, Sym, SymTy);
612 if (!Exp)
613 return std::nullopt;
614 // Construct single (in)equality
615 if (From == To) {
616 QualType UnusedRetTy;
617 return getBinExpr(Solver, Ctx, Exp.value(), SymTy,
618 InRange ? BO_EQ : BO_NE, FromExp, FromTy,
619 /*RetTy=*/UnusedRetTy);
620 }
621
622 QualType ToTy;
623 llvm::APSInt NewToInt;
624 std::tie(NewToInt, ToTy) = fixAPSInt(Ctx, To);
625 llvm::SMTExprRef ToExp =
626 Solver->mkBitvector(NewToInt, NewToInt.getBitWidth());
627 assert(FromTy == ToTy && "Range values have different types!");
628
629 // Construct two (in)equalities, and a logical and/or
630 QualType UnusedRetTy;
631 std::optional<llvm::SMTExprRef> LHS =
632 getBinExpr(Solver, Ctx, Exp.value(), SymTy, InRange ? BO_GE : BO_LT,
633 FromExp, FromTy, /*RetTy=*/UnusedRetTy);
634 std::optional<llvm::SMTExprRef> RHS = getBinExpr(
635 Solver, Ctx, Exp.value(), SymTy, InRange ? BO_LE : BO_GT, ToExp, ToTy,
636 /*RetTy=*/UnusedRetTy);
637 if (!LHS || !RHS)
638 return std::nullopt;
639 return fromBinOp(Solver, LHS.value(), InRange ? BO_LAnd : BO_LOr,
640 RHS.value(), SymTy->isSignedIntegerOrEnumerationType());
641 }
642
643 // Recover the QualType of an APSInt.
644 // TODO: Refactor to put elsewhere
646 const llvm::APSInt &Int) {
647 return Ctx.getBitIntType(Int.isUnsigned(), Int.getBitWidth());
648 }
649
650 // Get the QualTy for the input APSInt, and fix it if it has a bitwidth of 1.
651 static inline std::pair<llvm::APSInt, QualType>
652 fixAPSInt(ASTContext &Ctx, const llvm::APSInt &Int) {
653 return {Int, getAPSIntType(Ctx, Int)};
654 }
655
656 // Perform implicit type conversion on binary symbolic expressions.
657 // May modify all input parameters.
658 // TODO: Refactor to use built-in conversion functions
659 static inline void doTypeConversion(llvm::SMTSolverRef &Solver,
660 ASTContext &Ctx, llvm::SMTExprRef &LHS,
661 llvm::SMTExprRef &RHS, QualType &LTy,
662 QualType &RTy) {
663 assert(!LTy.isNull() && !RTy.isNull() && "Input type is null!");
664
665 // Perform type conversion
666 if ((LTy->isIntegralOrEnumerationType() &&
668 (LTy->isArithmeticType() && RTy->isArithmeticType())) {
670 Solver, Ctx, LHS, LTy, RHS, RTy);
671 return;
672 }
673
674 if (LTy->isRealFloatingType() || RTy->isRealFloatingType()) {
676 Solver, Ctx, LHS, LTy, RHS, RTy);
677 return;
678 }
679
680 if ((LTy->isAnyPointerType() || RTy->isAnyPointerType()) ||
681 (LTy->isBlockPointerType() || RTy->isBlockPointerType()) ||
682 (LTy->isReferenceType() || RTy->isReferenceType())) {
683 // TODO: Refactor to Sema::FindCompositePointerType(), and
684 // Sema::CheckCompareOperands().
685
686 uint64_t LBitWidth = Ctx.getTypeSize(LTy);
687 uint64_t RBitWidth = Ctx.getTypeSize(RTy);
688
689 // Cast the non-pointer type to the pointer type.
690 // TODO: Be more strict about this.
691 if ((LTy->isAnyPointerType() ^ RTy->isAnyPointerType()) ||
692 (LTy->isBlockPointerType() ^ RTy->isBlockPointerType()) ||
693 (LTy->isReferenceType() ^ RTy->isReferenceType())) {
694 if (LTy->isNullPtrType() || LTy->isBlockPointerType() ||
695 LTy->isReferenceType()) {
696 LHS = fromCast(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth);
697 LTy = RTy;
698 } else {
699 RHS = fromCast(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth);
700 RTy = LTy;
701 }
702 }
703
704 // Cast the void pointer type to the non-void pointer type.
705 // For void types, this assumes that the casted value is equal to the
706 // value of the original pointer, and does not account for alignment
707 // requirements.
708 if (LTy->isVoidPointerType() ^ RTy->isVoidPointerType()) {
709 assert((Ctx.getTypeSize(LTy) == Ctx.getTypeSize(RTy)) &&
710 "Pointer types have different bitwidths!");
711 if (RTy->isVoidPointerType())
712 RTy = LTy;
713 else
714 LTy = RTy;
715 }
716
717 if (LTy == RTy)
718 return;
719 }
720
721 // Fallback: for the solver, assume that these types don't really matter
722 if ((LTy.getCanonicalType() == RTy.getCanonicalType()) ||
724 LTy = RTy;
725 return;
726 }
727
728 // TODO: Refine behavior for invalid type casts
729 }
730
731 // Perform implicit integer type conversion.
732 // May modify all input parameters.
733 // TODO: Refactor to use Sema::handleIntegerConversion()
734 template <typename T, T (*doCast)(llvm::SMTSolverRef &Solver, const T &,
735 QualType, uint64_t, QualType, uint64_t)>
736 static inline void doIntTypeConversion(llvm::SMTSolverRef &Solver,
737 ASTContext &Ctx, T &LHS, QualType &LTy,
738 T &RHS, QualType &RTy) {
739 uint64_t LBitWidth = SMTConv::getSMTBitWidth(Ctx, LTy);
740 uint64_t RBitWidth = SMTConv::getSMTBitWidth(Ctx, RTy);
741
742 assert(!LTy.isNull() && !RTy.isNull() && "Input type is null!");
743 // Always perform integer promotion before checking type equality.
744 // Otherwise, e.g. (bool) a + (bool) b could trigger a backend assertion
745 if (Ctx.isPromotableIntegerType(LTy)) {
746 QualType NewTy = Ctx.getPromotedIntegerType(LTy);
747 uint64_t NewBitWidth = Ctx.getTypeSize(NewTy);
748 LHS = (*doCast)(Solver, LHS, NewTy, NewBitWidth, LTy, LBitWidth);
749 LTy = NewTy;
750 LBitWidth = NewBitWidth;
751 }
752 if (Ctx.isPromotableIntegerType(RTy)) {
753 QualType NewTy = Ctx.getPromotedIntegerType(RTy);
754 uint64_t NewBitWidth = Ctx.getTypeSize(NewTy);
755 RHS = (*doCast)(Solver, RHS, NewTy, NewBitWidth, RTy, RBitWidth);
756 RTy = NewTy;
757 RBitWidth = NewBitWidth;
758 }
759
760 if (LTy == RTy)
761 return;
762
763 // Perform integer type conversion
764 // Note: Safe to skip updating bitwidth because this must terminate
765 bool isLSignedTy = LTy->isSignedIntegerOrEnumerationType();
766 bool isRSignedTy = RTy->isSignedIntegerOrEnumerationType();
767
768 int order = Ctx.getIntegerTypeOrder(LTy, RTy);
769 if (isLSignedTy == isRSignedTy) {
770 // Same signedness; use the higher-ranked type
771 if (order == 1) {
772 RHS = (*doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth);
773 RTy = LTy;
774 } else {
775 LHS = (*doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth);
776 LTy = RTy;
777 }
778 } else if (order != (isLSignedTy ? 1 : -1)) {
779 // The unsigned type has greater than or equal rank to the
780 // signed type, so use the unsigned type
781 if (isRSignedTy) {
782 RHS = (*doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth);
783 RTy = LTy;
784 } else {
785 LHS = (*doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth);
786 LTy = RTy;
787 }
788 } else if (LBitWidth != RBitWidth) {
789 // The two types are different widths; if we are here, that
790 // means the signed type is larger than the unsigned type, so
791 // use the signed type.
792 if (isLSignedTy) {
793 RHS = (doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth);
794 RTy = LTy;
795 } else {
796 LHS = (*doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth);
797 LTy = RTy;
798 }
799 } else {
800 // The signed type is higher-ranked than the unsigned type,
801 // but isn't actually any bigger (like unsigned int and long
802 // on most 32-bit systems). Use the unsigned type corresponding
803 // to the signed type.
804 QualType NewTy =
805 Ctx.getCorrespondingUnsignedType(isLSignedTy ? LTy : RTy);
806 RHS = (*doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth);
807 RTy = NewTy;
808 LHS = (doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth);
809 LTy = NewTy;
810 }
811 }
812
813 // Perform implicit floating-point type conversion.
814 // May modify all input parameters.
815 // TODO: Refactor to use Sema::handleFloatConversion()
816 template <typename T, T (*doCast)(llvm::SMTSolverRef &Solver, const T &,
817 QualType, uint64_t, QualType, uint64_t)>
818 static inline void
819 doFloatTypeConversion(llvm::SMTSolverRef &Solver, ASTContext &Ctx, T &LHS,
820 QualType &LTy, T &RHS, QualType &RTy) {
821 uint64_t LBitWidth = Ctx.getTypeSize(LTy);
822 uint64_t RBitWidth = Ctx.getTypeSize(RTy);
823
824 // Perform float-point type promotion
825 if (!LTy->isRealFloatingType()) {
826 LHS = (*doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth);
827 LTy = RTy;
828 LBitWidth = RBitWidth;
829 }
830 if (!RTy->isRealFloatingType()) {
831 RHS = (*doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth);
832 RTy = LTy;
833 RBitWidth = LBitWidth;
834 }
835
836 if (LTy == RTy)
837 return;
838
839 // If we have two real floating types, convert the smaller operand to the
840 // bigger result
841 // Note: Safe to skip updating bitwidth because this must terminate
842 int order = Ctx.getFloatingTypeOrder(LTy, RTy);
843 if (order > 0) {
844 RHS = (*doCast)(Solver, RHS, LTy, LBitWidth, RTy, RBitWidth);
845 RTy = LTy;
846 } else if (order == 0) {
847 LHS = (*doCast)(Solver, LHS, RTy, RBitWidth, LTy, LBitWidth);
848 LTy = RTy;
849 } else {
850 llvm_unreachable("Unsupported floating-point type cast!");
851 }
852 }
853};
854} // namespace ento
855} // namespace clang
856
857#endif
#define V(N, I)
Expr * getExpr()
Get 'expr' part of the associated expression/statement.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:223
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:4177
static bool isComparisonOp(Opcode Opc)
Definition Expr.h:4144
bool isComparisonOp() const
Definition Expr.h:4145
BinaryOperatorKind Opcode
Definition Expr.h:4049
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:8541
QualType getAtomicUnqualifiedType() const
Remove all qualifiers including _Atomic.
Definition Type.cpp:1719
bool isBlockPointerType() const
Definition TypeBase.h:8746
bool isBooleanType() const
Definition TypeBase.h:9229
bool isSignedIntegerOrEnumerationType() const
Determines whether this is an integer type that is signed or an enumeration types whose underlying ty...
Definition Type.cpp:2293
bool isVoidPointerType() const
Definition Type.cpp:749
bool isArithmeticType() const
Definition Type.cpp:2426
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition TypeBase.h:9136
bool isReferenceType() const
Definition TypeBase.h:8750
bool isIntegralOrEnumerationType() const
Determine whether this type is an integral or enumeration type.
Definition TypeBase.h:9214
bool isObjCObjectPointerType() const
Definition TypeBase.h:8905
bool isRealFloatingType() const
Floating point categories.
Definition Type.cpp:2409
bool isAnyPointerType() const
Definition TypeBase.h:8734
bool isNullPtrType() const
Definition TypeBase.h:9129
UnaryOperatorKind Opcode
Definition Expr.h:2264
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:600
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:645
static llvm::SMTExprRef getZeroExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, const llvm::SMTExprRef &Exp, QualType Ty, bool Assumption)
Definition SMTConv.h:569
static void doIntTypeConversion(llvm::SMTSolverRef &Solver, ASTContext &Ctx, T &LHS, QualType &LTy, T &RHS, QualType &RTy)
Definition SMTConv.h:736
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:478
static void doTypeConversion(llvm::SMTSolverRef &Solver, ASTContext &Ctx, llvm::SMTExprRef &LHS, llvm::SMTExprRef &RHS, QualType &LTy, QualType &RTy)
Definition SMTConv.h:659
static std::optional< llvm::SMTExprRef > getSymBinExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, const BinarySymExpr *BSE, bool *hasComparison, QualType &RetTy)
Definition SMTConv.h:430
static std::optional< llvm::SMTExprRef > getExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, SymbolRef Sym, QualType &RetTy, bool *hasComparison=nullptr)
Definition SMTConv.h:559
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:819
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:652
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
The JSON file list parser is used to communicate input to InstallAPI.
const FunctionProtoType * T