This is an archive of the discontinued LLVM Phabricator instance.

[analyzer] Optimize constraint generation when the range is a concrete value
ClosedPublic

Authored by mikhail.ramalho on Jun 15 2018, 10:09 AM.

Details

Summary

If a constraint is something like:

$0 = [1,1]

it'll now be created as:

assert($0 == 1)

instead of:

assert($0 >= 1 && $0 <= 1)

In general, ~3% speedup when solving per query in my machine. Biggest improvement was when verifying sqlite3, total time went down from 3000s to 2200s.

I couldn't create a test for this as there is no way to dump the formula yet. D48221 adds a method to dump the formula but there is no way to do it from the command line.

Also, a test that prints the formula will most likely fail in the future, as different solvers print the formula in different formats.

Diff Detail

Repository
rL LLVM

Event Timeline

The change is fine, but I would note that to get more readable formulas it might be better to construct an array first, and then do disjunction over the whole array.
Also helper methods for mkEqual, mkLessEq and mkGreaterEq could be helpful.

This revision is now accepted and ready to land.Jun 15 2018, 12:17 PM
ddcc added a comment.Jun 15 2018, 12:59 PM

Correct me if I'm wrong, but isn't this function mostly duplicate with assumeSymInclusiveRange? I think it would be preferable to refactor out the constraint generation from both into e.g. getZ3RangeExpr, then modify both addRangeConstraints and assumeSymInclusiveRange to call it instead?

@ddcc sounds reasonable, yes.

Refactor duplicated code from assumeSymInclusiveRange and addRangeConstraints to a new method getZ3RangeExpr.

I Forgot to add a couple of /*RetTy=*/nullptr in the previous patch.

ddcc accepted this revision.Jun 16 2018, 11:34 AM

LGTM

Fix constraint being dropped in addRangeConstraints because of a missing BO_LOr operation.

The change is fine, but I would note that to get more readable formulas it might be better to construct an array first, and then do disjunction over the whole array.
Also helper methods for mkEqual, mkLessEq and mkGreaterEq could be helpful.

That would be great to implement in the SMT generic API but we would need to first need to create generic AST and sort wrappers (like Z3Sort and Z3Expr), then we could have something like:

/// Bitvector operations
SMTExpr mkBVAdd(SMTExpr a, SMTExpr b);
SMTExpr mkBVSub(SMTExpr a, SMTExpr b);
SMTExpr mkBVMul(SMTExpr a, SMTExpr b);
SMTExpr mkBVSMod(SMTExpr a, SMTExpr b);
SMTExpr mkBVUMod(SMTExpr a, SMTExpr b);
SMTExpr mkBVSDiv(SMTExpr a, SMTExpr b);
SMTExpr mkBVUDiv(SMTExpr a, SMTExpr b);
SMTExpr mkBVShl(SMTExpr a, SMTExpr b);
SMTExpr mkBVAshr(SMTExpr a, SMTExpr b);
SMTExpr mkBVLshr(SMTExpr a, SMTExpr b);
SMTExpr mkBVNeg(SMTExpr a);
SMTExpr mkBVNot(SMTExpr a);
SMTExpr mkBVNxor(SMTExpr a, SMTExpr b);
SMTExpr mkBVNor(SMTExpr a, SMTExpr b);
SMTExpr mkBVNand(SMTExpr a, SMTExpr b);
SMTExpr mkBVXor(SMTExpr a, SMTExpr b);
SMTExpr mkBVOr(SMTExpr a, SMTExpr b);
SMTExpr mkBVAnd(SMTExpr a, SMTExpr b);
SMTExpr mkImplies(SMTExpr a, SMTExpr b);
SMTExpr mkBVUlt(SMTExpr a, SMTExpr b);
SMTExpr mkBVSlt(SMTExpr a, SMTExpr b);
SMTExpr mkBVUgt(SMTExpr a, SMTExpr b);
SMTExpr mkBVSgt(SMTExpr a, SMTExpr b);
SMTExpr mkBVUle(SMTExpr a, SMTExpr b);
SMTExpr mkBVSle(SMTExpr a, SMTExpr b);
SMTExpr mkBVUge(SMTExpr a, SMTExpr b);
SMTExpr mkBVSge(SMTExpr a, SMTExpr b);
SMTExpr mkXor(SMTExpr a, SMTExpr b);
SMTExpr mkNot(SMTExpr a);
SMTExpr mkEqual(SMTExpr a, SMTExpr b);
SMTExpr mkNotEqual(SMTExpr a, SMTExpr b);

/// Arrays operations
SMTExpr mkStore(SMTExpr a, SMTExpr b, SMTExpr c);
SMTExpr mkSelect(SMTExpr a, SMTExpr b);

/// Sorts
SMTSort getBoolSort();
SMTSort getBVsort(std::size_t width);
SMTSort getArraySort(SMTSort domain, SMTSort range);
SMTSort getFloatSort(const unsigned ew, const unsigned sw);
SMTSort getFloatRoundingMode();

/// Special Floating-points
SMTExpr mkFPNan(std::size_t ew, std::size_t sw);
SMTExpr mkFPInf(bool sgn, std::size_t ew, std::size_t sw);
SMTExpr mkFPRm(std::size_t rm);

/// Floating-points operations
SMTExpr mkFPFma(SMTExpr a, SMTExpr b, SMTExpr c, SMTExpr rm);
SMTExpr mkCastFPToUbv(SMTExpr from, std::size_t width);
SMTExpr mkCastFPToSbv(SMTExpr from, std::size_t width);
SMTExpr mkCastFPToFP(SMTExpr from, SMTSort to, SMTExpr rm);
SMTExpr mkCastUbvToFP(SMTExpr from, SMTSort to, SMTExpr rm);
SMTExpr mkCastSbvToFP(SMTExpr from, SMTSort to, SMTExpr rm);
SMTExpr mkFPAdd(SMTExpr lhs, SMTExpr rhs, SMTExpr rm);
SMTExpr mkFPSub(SMTExpr lhs, SMTExpr rhs, SMTExpr rm);
SMTExpr mkFPMul(SMTExpr lhs, SMTExpr rhs, SMTExpr rm);
SMTExpr mkFPDiv(SMTExpr lhs, SMTExpr rhs, SMTExpr rm);
SMTExpr mkFPNearbyint(SMTExpr from, SMTExpr rm);
SMTExpr mkFPSqrt(SMTExpr rd, SMTExpr rm);
SMTExpr mkFPEqual(SMTExpr lhs, SMTExpr rhs);
SMTExpr mkFPGt(SMTExpr lhs, SMTExpr rhs);
SMTExpr mkFPLt(SMTExpr lhs, SMTExpr rhs);
SMTExpr mkFPGte(SMTExpr lhs, SMTExpr rhs);
SMTExpr mkFPlte(SMTExpr lhs, SMTExpr rhs);
SMTExpr mkFPIsNan(SMTExpr op);
SMTExpr mkFPIsInf(SMTExpr op);
SMTExpr mkFPIsNormal(SMTExpr op);
SMTExpr mkFPUsZero(SMTExpr op);
SMTExpr mkFPIsNegative(SMTExpr op);
SMTExpr mkFPIsPositive(SMTExpr op);
SMTExpr mkFPAbs(SMTExpr op);
SMTExpr mkFPNeg(SMTExpr op);

Unless you're talking about implementing the helpers only in the Z3 specific class, then I'd argue that it'd a waste of time now, as the 3rd deliverable of my proposal is to write the generic SMT API and the Z3 specific implementation anyway.

This revision was automatically updated to reflect the committed changes.