smt_astt smt_convt::overflow_cast(const expr2tc &expr) { // If in integer mode, this is completely pointless. Return false. if (int_encoding) return mk_smt_bool(false); const overflow_cast2t &ocast = to_overflow_cast2t(expr); unsigned int width = ocast.operand->type->get_width(); unsigned int bits = ocast.bits; smt_sortt boolsort = boolean_sort; if (ocast.bits >= width || ocast.bits == 0) { std::cerr << "SMT conversion: overflow-typecast got wrong number of bits" << std::endl; abort(); } // Basically: if it's positive in the first place, ensure all the top bits // are zero. If neg, then all the top are 1's /and/ the next bit, so that // it's considered negative in the next interpretation. constant_int2tc zero(ocast.operand->type, BigInt(0)); lessthan2tc isnegexpr(ocast.operand, zero); smt_astt isneg = convert_ast(isnegexpr); smt_astt orig_val = convert_ast(ocast.operand); // Difference bits unsigned int pos_zero_bits = width - bits; unsigned int neg_one_bits = (width - bits) + 1; smt_sortt pos_zero_bits_sort = mk_sort(SMT_SORT_BV, pos_zero_bits, false); smt_sortt neg_one_bits_sort = mk_sort(SMT_SORT_BV, neg_one_bits, false); smt_astt pos_bits = mk_smt_bvint(BigInt(0), false, pos_zero_bits); smt_astt neg_bits = mk_smt_bvint(BigInt((1 << neg_one_bits) - 1), false, neg_one_bits); smt_astt pos_sel = mk_extract(orig_val, width - 1, width - pos_zero_bits, pos_zero_bits_sort); smt_astt neg_sel = mk_extract(orig_val, width - 1, width - neg_one_bits, neg_one_bits_sort); smt_astt pos_eq = mk_func_app(boolsort, SMT_FUNC_EQ, pos_bits, pos_sel); smt_astt neg_eq = mk_func_app(boolsort, SMT_FUNC_EQ, neg_bits, neg_sel); // isneg -> neg_eq, !isneg -> pos_eq smt_astt notisneg = mk_func_app(boolsort, SMT_FUNC_NOT, &isneg, 1); smt_astt c1 = mk_func_app(boolsort, SMT_FUNC_IMPLIES, isneg, neg_eq); smt_astt c2 = mk_func_app(boolsort, SMT_FUNC_IMPLIES, notisneg, pos_eq); smt_astt nooverflow = mk_func_app(boolsort, SMT_FUNC_AND, c1, c2); return mk_func_app(boolsort, SMT_FUNC_NOT, &nooverflow, 1); }
smt_astt smt_convt::overflow_neg(const expr2tc &expr) { // If in integer mode, this is completely pointless. Return false. if (int_encoding) return mk_smt_bool(false); // Single failure mode: MIN_INT can't be neg'd const overflow_neg2t &neg = to_overflow_neg2t(expr); unsigned int width = neg.operand->type->get_width(); constant_int2tc min_int(neg.operand->type, BigInt(1 << (width - 1))); equality2tc val(neg.operand, min_int); return convert_ast(val); }
smt_astt smt_convt::overflow_arith(const expr2tc &expr) { // If in integer mode, this is completely pointless. Return false. if (int_encoding) return mk_smt_bool(false); const overflow2t &overflow = to_overflow2t(expr); const arith_2ops &opers = static_cast<const arith_2ops &>(*overflow.operand); assert(opers.side_1->type == opers.side_2->type); constant_int2tc zero(opers.side_1->type, BigInt(0)); lessthan2tc op1neg(opers.side_1, zero); lessthan2tc op2neg(opers.side_2, zero); equality2tc op1iszero(opers.side_1, zero); equality2tc op2iszero(opers.side_2, zero); or2tc containszero(op1iszero, op2iszero); // Guess whether we're performing a signed or unsigned comparison. bool is_signed = (is_signedbv_type(opers.side_1) || is_signedbv_type(opers.side_2)); if (is_add2t(overflow.operand)) { if (is_signed) { // Two cases: pos/pos, and neg/neg, which can over and underflow resp. // In pos/neg cases, no overflow or underflow is possible, for any value. constant_int2tc zero(opers.side_1->type, BigInt(0)); lessthan2tc op1pos(zero, opers.side_1); lessthan2tc op2pos(zero, opers.side_2); and2tc both_pos(op1pos, op2pos); not2tc negop1(op1pos); not2tc negop2(op2pos); and2tc both_neg(negop1, negop2); implies2tc nooverflow(both_pos, greaterthanequal2tc(overflow.operand, zero)); implies2tc nounderflow(both_neg, lessthanequal2tc(overflow.operand, zero)); return convert_ast(not2tc(and2tc(nooverflow, nounderflow))); } else { // Just ensure the result is >= both operands. greaterthanequal2tc ge1(overflow.operand, opers.side_1); greaterthanequal2tc ge2(overflow.operand, opers.side_2); and2tc res(ge1, ge2); not2tc inv(res); return convert_ast(inv); } } else if (is_sub2t(overflow.operand)) { if (is_signed) { // Convert to be an addition neg2tc negop2(opers.side_2->type, opers.side_2); add2tc anadd(opers.side_1->type, opers.side_1, negop2); expr2tc add_overflows(new overflow2t(anadd)); // Corner case: subtracting MIN_INT from many things overflows. The result // should always be positive. constant_int2tc zero(opers.side_1->type, BigInt(0)); uint64_t topbit = 1ULL << (opers.side_1->type->get_width() - 1); constant_int2tc min_int(opers.side_1->type, BigInt(topbit)); equality2tc is_min_int(min_int, opers.side_2); implies2tc imp(is_min_int, greaterthan2tc(overflow.operand, zero)); return convert_ast(or2tc(add_overflows, is_min_int)); } else { // Just ensure the result is >= the operands. lessthanequal2tc le1(overflow.operand, opers.side_1); lessthanequal2tc le2(overflow.operand, opers.side_2); and2tc res(le1, le2); not2tc inv(res); return convert_ast(inv); } } else { assert(is_mul2t(overflow.operand) && "unexpected overflow_arith operand"); // Zero extend; multiply; Make a decision based on the top half. unsigned int sz = zero->type->get_width(); smt_sortt boolsort = boolean_sort; smt_sortt normalsort = mk_sort(SMT_SORT_BV, sz, false); smt_sortt bigsort = mk_sort(SMT_SORT_BV, sz * 2, false); // All one bit vector is tricky, might be 64 bits wide for all we know. constant_int2tc allonesexpr(zero->type, BigInt((sz == 64) ? 0xFFFFFFFFFFFFFFFFULL : ((1ULL << sz) - 1))); smt_astt allonesvector = convert_ast(allonesexpr); smt_astt arg1_ext, arg2_ext; if (is_signed) { // sign extend top bits. arg1_ext = convert_ast(opers.side_1); arg1_ext = convert_sign_ext(arg1_ext, bigsort, sz - 1, sz); arg2_ext = convert_ast(opers.side_2); arg2_ext = convert_sign_ext(arg2_ext, bigsort, sz - 1, sz); } else { // Zero extend the top parts arg1_ext = convert_ast(opers.side_1); arg1_ext = convert_zero_ext(arg1_ext, bigsort, sz); arg2_ext = convert_ast(opers.side_2); arg2_ext = convert_zero_ext(arg2_ext, bigsort, sz); } smt_astt result = mk_func_app(bigsort, SMT_FUNC_BVMUL, arg1_ext, arg2_ext); // Extract top half. smt_astt toppart = mk_extract(result, (sz * 2) - 1, sz, normalsort); if (is_signed) { // It should either be zero or all one's; which depends on what // configuration of signs it had. If both pos / both neg, then the top // should all be zeros, otherwise all ones. Implement with xor. smt_astt op1neg_ast = convert_ast(op1neg); smt_astt op2neg_ast = convert_ast(op2neg); smt_astt allonescond = mk_func_app(boolsort, SMT_FUNC_XOR, op1neg_ast, op2neg_ast); smt_astt zerovector = convert_ast(zero); smt_astt initial_switch = mk_func_app(normalsort, SMT_FUNC_ITE, allonescond, allonesvector, zerovector); // either value being zero means the top must be zero. smt_astt contains_zero_ast = convert_ast(containszero); smt_astt second_switch = mk_func_app(normalsort, SMT_FUNC_ITE, contains_zero_ast, zerovector, initial_switch); smt_astt is_eq = mk_func_app(boolsort, SMT_FUNC_EQ, second_switch, toppart); return mk_func_app(boolsort, SMT_FUNC_NOT, &is_eq, 1); } else { // It should be zero; if not, overflow smt_astt iseq = mk_func_app(boolsort, SMT_FUNC_EQ, toppart, convert_ast(zero)); return mk_func_app(boolsort, SMT_FUNC_NOT, &iseq, 1); } } return NULL; }
smt_astt smt_convt::convert_byte_update(const expr2tc &expr) { const byte_update2t &data = to_byte_update2t(expr); assert(is_scalar_type(data.source_value) && "Byte update only works on " "scalar variables now"); if (!is_constant_int2t(data.source_offset)) { expr2tc source = data.source_value; unsigned int src_width = source->type->get_width(); if (!is_bv_type(source)) source = typecast2tc(get_uint_type(src_width), source); expr2tc offs = data.source_offset; // Endian-ness: if we're in non-"native" endian-ness mode, then flip the // offset distance. The rest of these calculations will still apply. if (data.big_endian) { auto data_size = type_byte_size(*source->type); constant_int2tc data_size_expr(source->type, data_size - 1); sub2tc sub(source->type, data_size_expr, offs); offs = sub; } if (offs->type->get_width() != src_width) offs = typecast2tc(get_uint_type(src_width), offs); expr2tc update = data.update_value; if (update->type->get_width() != src_width) update = typecast2tc(get_uint_type(src_width), update); // The approach: mask, shift and or. XXX, byte order? // Massively inefficient. expr2tc eight = constant_int2tc(get_uint_type(src_width), BigInt(8)); expr2tc effs = constant_int2tc(eight->type, BigInt(255)); offs = mul2tc(eight->type, offs, eight); expr2tc shl = shl2tc(offs->type, effs, offs); expr2tc noteffs = bitnot2tc(effs->type, shl); source = bitand2tc(source->type, source, noteffs); expr2tc shl2 = shl2tc(offs->type, update, offs); return convert_ast(bitor2tc(offs->type, shl2, source)); } // We are merging two values: an 8 bit update value, and a larger source // value that we will have to merge it into. Start off by collecting // information about the source values and their widths. assert(is_number_type(data.source_value->type) && "Byte update of unsupported data type"); smt_astt value, src_value; unsigned int width_op0, width_op2, src_offset; value = convert_ast(data.update_value); src_value = convert_ast(data.source_value); width_op2 = data.update_value->type->get_width(); width_op0 = data.source_value->type->get_width(); src_offset = to_constant_int2t(data.source_offset).constant_value.to_ulong(); // Flip location if we're in big-endian mode if (data.big_endian) { unsigned int data_size = type_byte_size(*data.source_value->type).to_ulong() - 1; src_offset = data_size - src_offset; } if (int_encoding) { std::cerr << "Can't byte update in integer mode; rerun in bitvector mode" << std::endl; abort(); } // Assertion some of our assumptions, which broadly mean that we'll only work // on bytes that are going into non-byte words assert(width_op2 == 8 && "Can't byte update non-byte operations"); assert(width_op2 != width_op0 && "Can't byte update bytes, sorry"); smt_astt top, middle, bottom; // Build in three parts: the most significant bits, any in the middle, and // the bottom, of the reconstructed / merged output. There might not be a // middle if the update byte is at the top or the bottom. unsigned int top_of_update = (8 * src_offset) + 8; unsigned int bottom_of_update = (8 * src_offset); if (top_of_update == width_op0) { top = value; } else { smt_sortt s = mk_sort(SMT_SORT_BV, width_op0 - top_of_update, false); top = mk_extract(src_value, width_op0 - 1, top_of_update, s); } if (top == value) { middle = NULL; } else { middle = value; } if (src_offset == 0) { middle = NULL; bottom = value; } else { smt_sortt s = mk_sort(SMT_SORT_BV, bottom_of_update, false); bottom = mk_extract(src_value, bottom_of_update - 1, 0, s); } // Concatenate the top and bottom, and possible middle, together. smt_astt concat; if (middle != NULL) { smt_sortt s = mk_sort(SMT_SORT_BV, width_op0 - bottom_of_update, false); concat = mk_func_app(s, SMT_FUNC_CONCAT, top, middle); } else { concat = top; } return mk_func_app(src_value->sort, SMT_FUNC_CONCAT, concat, bottom); }
smt_astt smt_convt::convert_byte_extract(const expr2tc &expr) { const byte_extract2t &data = to_byte_extract2t(expr); assert(is_scalar_type(data.source_value) && "Byte extract now only works on " "scalar variables"); if (!is_constant_int2t(data.source_offset)) { expr2tc source = data.source_value; unsigned int src_width = source->type->get_width(); if (!is_bv_type(source)) { source = typecast2tc(get_uint_type(src_width), source); } // The approach: the argument is now a bitvector. Just shift it the // appropriate amount, according to the source offset, and select out the // bottom byte. expr2tc offs = data.source_offset; // Endian-ness: if we're in non-"native" endian-ness mode, then flip the // offset distance. The rest of these calculations will still apply. if (data.big_endian) { auto data_size = type_byte_size(*source->type); constant_int2tc data_size_expr(source->type, data_size - 1); sub2tc sub(source->type, data_size_expr, offs); offs = sub; } if (offs->type->get_width() != src_width) // Z3 requires these two arguments to be the same width offs = typecast2tc(source->type, data.source_offset); lshr2tc shr(source->type, source, offs); smt_astt ext = convert_ast(shr); smt_astt res = mk_extract(ext, 7, 0, convert_sort(get_uint8_type())); return res; } const constant_int2t &intref = to_constant_int2t(data.source_offset); unsigned width; width = data.source_value->type->get_width(); uint64_t upper, lower; if (!data.big_endian) { upper = ((intref.constant_value.to_long() + 1) * 8) - 1; //((i+1)*w)-1; lower = intref.constant_value.to_long() * 8; //i*w; } else { uint64_t max = width - 1; upper = max - (intref.constant_value.to_long() * 8); //max-(i*w); lower = max - ((intref.constant_value.to_long() + 1) * 8 - 1); //max-((i+1)*w-1); } smt_astt source = convert_ast(data.source_value);; if (int_encoding) { std::cerr << "Refusing to byte extract in integer mode; re-run in " "bitvector mode" << std::endl; abort(); } else { if (is_bv_type(data.source_value)) { ; } else if (is_fixedbv_type(data.source_value)) { ; } else if (is_bool_type(data.source_value)) { // We cdan extract a byte from a bool -- zero or one. typecast2tc cast(get_uint8_type(), data.source_value); source = convert_ast(cast); } else { std::cerr << "Unrecognized type in operand to byte extract." << std::endl; data.dump(); abort(); } unsigned int sort_sz = data.source_value->type->get_width(); if (sort_sz <= upper) { smt_sortt s = mk_sort(SMT_SORT_BV, 8, false); return mk_smt_symbol("out_of_bounds_byte_extract", s); } else { return mk_extract(source, upper, lower, convert_sort(expr->type)); } } }