mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
[SMTChecker] Support bitwise or, xor and not.
This commit is contained in:
parent
bff0f9bda7
commit
11a7763f49
@ -10,6 +10,7 @@ Compiler Features:
|
||||
* Yul: Report error when using non-string literals for ``datasize()``, ``dataoffset()``, ``linkersymbol()``, ``loadimmutable()``, ``setimmutable()``.
|
||||
* Yul Optimizer: LoopInvariantCodeMotion can move reading operations outside for-loops as long as the affected area is not modified inside the loop.
|
||||
* SMTChecker: Support conditional operator.
|
||||
* SMTChecker: Support bitwise or, xor and not operators.
|
||||
|
||||
Bugfixes:
|
||||
* Optimizer: Keep side-effects of ``x`` in ``byte(a, shr(b, x))`` even if the constants ``a`` and ``b`` would make the expression zero unconditionally. This optimizer rule is very hard if not impossible to trigger in a way that it can result in invalid code, though.
|
||||
|
@ -188,8 +188,14 @@ CVC4::Expr CVC4Interface::toCVC4Expr(Expression const& _expr)
|
||||
return m_context.mkExpr(CVC4::kind::INTS_DIVISION_TOTAL, arguments[0], arguments[1]);
|
||||
else if (n == "mod")
|
||||
return m_context.mkExpr(CVC4::kind::INTS_MODULUS, arguments[0], arguments[1]);
|
||||
else if (n == "bvnot")
|
||||
return m_context.mkExpr(CVC4::kind::BITVECTOR_NOT, arguments[0]);
|
||||
else if (n == "bvand")
|
||||
return m_context.mkExpr(CVC4::kind::BITVECTOR_AND, arguments[0], arguments[1]);
|
||||
else if (n == "bvor")
|
||||
return m_context.mkExpr(CVC4::kind::BITVECTOR_OR, arguments[0], arguments[1]);
|
||||
else if (n == "bvxor")
|
||||
return m_context.mkExpr(CVC4::kind::BITVECTOR_XOR, arguments[0], arguments[1]);
|
||||
else if (n == "int2bv")
|
||||
{
|
||||
size_t size = std::stoul(_expr.arguments[1].name);
|
||||
|
@ -95,7 +95,10 @@ public:
|
||||
{"*", 2},
|
||||
{"/", 2},
|
||||
{"mod", 2},
|
||||
{"bvnot", 1},
|
||||
{"bvand", 2},
|
||||
{"bvor", 2},
|
||||
{"bvxor", 2},
|
||||
{"int2bv", 2},
|
||||
{"bv2int", 1},
|
||||
{"select", 2},
|
||||
@ -286,11 +289,26 @@ public:
|
||||
auto intSort = _a.sort;
|
||||
return Expression("mod", {std::move(_a), std::move(_b)}, intSort);
|
||||
}
|
||||
friend Expression operator~(Expression _a)
|
||||
{
|
||||
auto bvSort = _a.sort;
|
||||
return Expression("bvnot", {std::move(_a)}, bvSort);
|
||||
}
|
||||
friend Expression operator&(Expression _a, Expression _b)
|
||||
{
|
||||
auto bvSort = _a.sort;
|
||||
return Expression("bvand", {std::move(_a), std::move(_b)}, bvSort);
|
||||
}
|
||||
friend Expression operator^(Expression _a, Expression _b)
|
||||
{
|
||||
auto bvSort = _a.sort;
|
||||
return Expression("bvxor", {std::move(_a), std::move(_b)}, bvSort);
|
||||
}
|
||||
friend Expression operator|(Expression _a, Expression _b)
|
||||
{
|
||||
auto bvSort = _a.sort;
|
||||
return Expression("bvor", {std::move(_a), std::move(_b)}, bvSort);
|
||||
}
|
||||
Expression operator()(std::vector<Expression> _arguments) const
|
||||
{
|
||||
smtAssert(
|
||||
|
@ -181,8 +181,14 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr)
|
||||
return arguments[0] / arguments[1];
|
||||
else if (n == "mod")
|
||||
return z3::mod(arguments[0], arguments[1]);
|
||||
else if (n == "bvnot")
|
||||
return ~arguments[0];
|
||||
else if (n == "bvand")
|
||||
return arguments[0] & arguments[1];
|
||||
else if (n == "bvor")
|
||||
return arguments[0] | arguments[1];
|
||||
else if (n == "bvxor")
|
||||
return arguments[0] ^ arguments[1];
|
||||
else if (n == "int2bv")
|
||||
{
|
||||
size_t size = std::stoul(_expr.arguments[1].name);
|
||||
|
@ -456,6 +456,8 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple)
|
||||
|
||||
void SMTEncoder::endVisit(UnaryOperation const& _op)
|
||||
{
|
||||
if (TokenTraits::isBitOp(_op.getOperator()))
|
||||
return bitwiseNotOperation(_op);
|
||||
if (_op.annotation().type->category() == Type::Category::RationalNumber)
|
||||
return;
|
||||
|
||||
@ -1406,20 +1408,7 @@ void SMTEncoder::bitwiseOperation(BinaryOperation const& _op)
|
||||
auto commonType = _op.annotation().commonType;
|
||||
solAssert(commonType, "");
|
||||
|
||||
unsigned bvSize = 256;
|
||||
bool isSigned = false;
|
||||
if (auto const* intType = dynamic_cast<IntegerType const*>(commonType))
|
||||
{
|
||||
bvSize = intType->numBits();
|
||||
isSigned = intType->isSigned();
|
||||
}
|
||||
else if (auto const* fixedType = dynamic_cast<FixedPointType const*>(commonType))
|
||||
{
|
||||
bvSize = fixedType->numBits();
|
||||
isSigned = fixedType->isSigned();
|
||||
}
|
||||
else if (auto const* fixedBytesType = dynamic_cast<FixedBytesType const*>(commonType))
|
||||
bvSize = fixedBytesType->numBytes() * 8;
|
||||
auto [bvSize, isSigned] = smt::typeBvSizeAndSignedness(commonType);
|
||||
|
||||
auto bvLeft = smtutil::Expression::int2bv(expr(_op.leftExpression(), commonType), bvSize);
|
||||
auto bvRight = smtutil::Expression::int2bv(expr(_op.rightExpression(), commonType), bvSize);
|
||||
@ -1427,18 +1416,26 @@ void SMTEncoder::bitwiseOperation(BinaryOperation const& _op)
|
||||
optional<smtutil::Expression> result;
|
||||
if (_op.getOperator() == Token::BitAnd)
|
||||
result = bvLeft & bvRight;
|
||||
// TODO implement the other operators
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
1093_error,
|
||||
_op.location(),
|
||||
"Assertion checker does not yet implement this bitwise operator."
|
||||
);
|
||||
else if (_op.getOperator() == Token::BitOr)
|
||||
result = bvLeft | bvRight;
|
||||
else if (_op.getOperator() == Token::BitXor)
|
||||
result = bvLeft ^ bvRight;
|
||||
|
||||
solAssert(result, "");
|
||||
if (result)
|
||||
defineExpr(_op, smtutil::Expression::bv2int(*result, isSigned));
|
||||
}
|
||||
|
||||
void SMTEncoder::bitwiseNotOperation(UnaryOperation const& _op)
|
||||
{
|
||||
solAssert(_op.getOperator() == Token::BitNot, "");
|
||||
|
||||
auto [bvSize, isSigned] = smt::typeBvSizeAndSignedness(_op.annotation().type);
|
||||
|
||||
auto bvOperand = smtutil::Expression::int2bv(expr(_op.subExpression(), _op.annotation().type), bvSize);
|
||||
defineExpr(_op, smtutil::Expression::bv2int(~bvOperand, isSigned));
|
||||
}
|
||||
|
||||
smtutil::Expression SMTEncoder::division(smtutil::Expression _left, smtutil::Expression _right, IntegerType const& _type)
|
||||
{
|
||||
// Signed division in SMTLIB2 rounds differently for negative division.
|
||||
|
@ -115,6 +115,7 @@ protected:
|
||||
void compareOperation(BinaryOperation const& _op);
|
||||
void booleanOperation(BinaryOperation const& _op);
|
||||
void bitwiseOperation(BinaryOperation const& _op);
|
||||
void bitwiseNotOperation(UnaryOperation const& _op);
|
||||
|
||||
void initContract(ContractDefinition const& _contract);
|
||||
void initFunction(FunctionDefinition const& _function);
|
||||
|
@ -432,6 +432,18 @@ smtutil::Expression zeroValue(frontend::TypePointer const& _type)
|
||||
return 0;
|
||||
}
|
||||
|
||||
pair<unsigned, bool> typeBvSizeAndSignedness(frontend::TypePointer const& _type)
|
||||
{
|
||||
if (auto const* intType = dynamic_cast<IntegerType const*>(_type))
|
||||
return {intType->numBits(), intType->isSigned()};
|
||||
else if (auto const* fixedType = dynamic_cast<FixedPointType const*>(_type))
|
||||
return {fixedType->numBits(), fixedType->isSigned()};
|
||||
else if (auto const* fixedBytesType = dynamic_cast<FixedBytesType const*>(_type))
|
||||
return {fixedBytesType->numBytes() * 8, false};
|
||||
else
|
||||
solAssert(false, "");
|
||||
}
|
||||
|
||||
void setSymbolicUnknownValue(SymbolicVariable const& _variable, EncodingContext& _context)
|
||||
{
|
||||
setSymbolicUnknownValue(_variable.currentValue(), _variable.type(), _context);
|
||||
|
@ -67,6 +67,8 @@ smtutil::Expression minValue(frontend::IntegerType const& _type);
|
||||
smtutil::Expression maxValue(frontend::IntegerType const& _type);
|
||||
smtutil::Expression zeroValue(frontend::TypePointer const& _type);
|
||||
|
||||
std::pair<unsigned, bool> typeBvSizeAndSignedness(frontend::TypePointer const& type);
|
||||
|
||||
void setSymbolicZeroValue(SymbolicVariable const& _variable, EncodingContext& _context);
|
||||
void setSymbolicZeroValue(smtutil::Expression _expr, frontend::TypePointer const& _type, EncodingContext& _context);
|
||||
void setSymbolicUnknownValue(SymbolicVariable const& _variable, EncodingContext& _context);
|
||||
|
12
test/libsolidity/smtCheckerTests/operators/bitwise_combo.sol
Normal file
12
test/libsolidity/smtCheckerTests/operators/bitwise_combo.sol
Normal file
@ -0,0 +1,12 @@
|
||||
pragma experimental SMTChecker;
|
||||
|
||||
contract C {
|
||||
function f() public pure {
|
||||
uint8 x = 0xff;
|
||||
uint8 y = ~x;
|
||||
assert(x & y == 0);
|
||||
assert(x | y == 0xff);
|
||||
assert(x ^ y == 0xff);
|
||||
}
|
||||
}
|
||||
// ----
|
@ -0,0 +1,8 @@
|
||||
pragma experimental SMTChecker;
|
||||
contract C {
|
||||
function f() public pure returns (byte) {
|
||||
return (~byte(0xFF));
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 5084: (102-112): Type conversion is not yet fully supported and might yield false positives.
|
@ -0,0 +1,21 @@
|
||||
pragma experimental SMTChecker;
|
||||
|
||||
contract C {
|
||||
function f() public pure {
|
||||
int16 x = 1;
|
||||
assert(~x == 0);
|
||||
x = 0xff;
|
||||
assert(~x == 0);
|
||||
x = 0x0f;
|
||||
assert(~x == 0xf0);
|
||||
x = -1;
|
||||
assert(~x != 0);
|
||||
x = -2;
|
||||
assert(~x == 1);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 6328: (91-106): Assertion violation happens here
|
||||
// Warning 6328: (122-137): Assertion violation happens here
|
||||
// Warning 6328: (153-171): Assertion violation happens here
|
||||
// Warning 6328: (185-200): Assertion violation happens here
|
@ -0,0 +1,15 @@
|
||||
pragma experimental SMTChecker;
|
||||
|
||||
contract C {
|
||||
function f() public pure {
|
||||
uint8 x = 0xff;
|
||||
assert(~x == 0x00);
|
||||
uint16 y = 0xff00;
|
||||
assert(~y == 0xff);
|
||||
assert(~y == 0xffff);
|
||||
assert(~y == 0x0000);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 6328: (159-179): Assertion violation happens here
|
||||
// Warning 6328: (183-203): Assertion violation happens here
|
@ -0,0 +1,9 @@
|
||||
pragma experimental SMTChecker;
|
||||
contract C {
|
||||
function f() public pure returns (byte) {
|
||||
return (byte(0x0F) | (byte(0xF0)));
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 5084: (101-111): Type conversion is not yet fully supported and might yield false positives.
|
||||
// Warning 5084: (115-125): Type conversion is not yet fully supported and might yield false positives.
|
@ -0,0 +1,21 @@
|
||||
pragma experimental SMTChecker;
|
||||
|
||||
contract C {
|
||||
function f() public pure {
|
||||
int16 x = 1;
|
||||
int16 y = 0;
|
||||
assert(x | y == 1);
|
||||
x = 0; y = 0;
|
||||
assert(x | y != 0);
|
||||
y = 240;
|
||||
x = 15;
|
||||
int16 z = x | y;
|
||||
assert(z == 255);
|
||||
x = -1; y = 200;
|
||||
assert(x | y == x);
|
||||
assert(x | z != -1);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 6328: (144-162): Assertion violation happens here
|
||||
// Warning 6328: (267-286): Assertion violation happens here
|
@ -0,0 +1,17 @@
|
||||
pragma experimental SMTChecker;
|
||||
|
||||
contract C {
|
||||
function f() public pure {
|
||||
uint8 x = 1;
|
||||
uint16 y = 0;
|
||||
assert(x | y != 0);
|
||||
x = 0xff;
|
||||
y = 0xff00;
|
||||
assert(x | y == 0xff);
|
||||
assert(x | y == 0xffff);
|
||||
assert(x | y == 0x0000);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 6328: (155-176): Assertion violation happens here
|
||||
// Warning 6328: (207-230): Assertion violation happens here
|
@ -6,4 +6,3 @@ contract Simp {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 1093: (142-152): Assertion checker does not yet implement this bitwise operator.
|
||||
|
@ -0,0 +1,19 @@
|
||||
pragma experimental SMTChecker;
|
||||
|
||||
contract C {
|
||||
function f() public pure {
|
||||
int8 x = 1;
|
||||
int16 y = 0;
|
||||
assert(x ^ y == 1);
|
||||
int16 z = -1;
|
||||
assert(x ^ z == -2);
|
||||
assert(y ^ z == -1);
|
||||
assert(y ^ z > 0);
|
||||
x = 7; y = 3;
|
||||
assert(x ^ y < 5);
|
||||
assert(x ^ y > 5);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 6328: (189-206): Assertion violation happens here
|
||||
// Warning 6328: (247-264): Assertion violation happens here
|
@ -0,0 +1,17 @@
|
||||
pragma experimental SMTChecker;
|
||||
|
||||
contract C {
|
||||
function f() public pure {
|
||||
uint8 x = 1;
|
||||
uint16 y = 0;
|
||||
assert(x ^ y != 0);
|
||||
x = 0xff;
|
||||
y = 0xff00;
|
||||
assert(x ^ y == 0xff);
|
||||
assert(x ^ y == 0xffff);
|
||||
assert(x ^ y == 0x0000);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 6328: (155-176): Assertion violation happens here
|
||||
// Warning 6328: (207-230): Assertion violation happens here
|
Loading…
Reference in New Issue
Block a user