libTriton version 1.0 build 1592
Loading...
Searching...
No Matches
bitwuzlaSolver.cpp
Go to the documentation of this file.
1
2/*
3** Copyright (C) - Triton
4**
5** This program is under the terms of the Apache License 2.0.
6*/
7
8#include <fstream>
9#include <regex>
10#include <string>
11
12#include <triton/astContext.hpp>
14#include <triton/exceptions.hpp>
20
21
22namespace triton {
23 namespace engines {
24 namespace solver {
25
27 this->timeout = 0;
28 this->memoryLimit = 0;
29
30 // Set bitwuzla abort function.
31 bitwuzla_set_abort_callback(this->abortCallback);
32 }
33
34
35 int32_t BitwuzlaSolver::terminateCallback(void* state) {
36 auto p = reinterpret_cast<SolverParams*>(state);
37
38 // Count elapsed time.
39 auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - p->start).count();
40
41 // Check timeout expired.
42 if (p->timeout && delta > p->timeout) {
44 return 1;
45 }
46
47 // Check memory limit.
48 #if defined(__unix__)
49
50 // Complete seconds elapsed from the start of solving.
51 auto delta_s = delta / 1000;
52
53 // Check memory limit every second. Don't perform check in first 100ms of solving.
54 if (p->memory_limit && delta > 100 && delta_s > p->last_mem_check) {
55 p->last_mem_check = delta_s;
56
57 // Parse system file to get current process memory consumption (VmRSS field).
58 size_t memory_usage = 0;
59 std::ifstream str("/proc/self/status");
60 if (str.is_open()) {
61 std::string line;
62 static std::regex vmsize("VmRSS:\\s*([0-9]+)\\s*kB");
63 while (std::getline(str, line)) {
64 std::smatch match;
65 if (std::regex_match(line, match, vmsize)) {
66 memory_usage = strtoul(match.str(1).c_str(), NULL, 10) / 1024;
67 break;
68 }
69 }
70 }
71 if (!memory_usage) {
72 return 0;
73 }
74 if (memory_usage > p->memory_limit) {
76 return 1;
77 }
78 }
79 #endif
80
81 return 0;
82 }
83
84
85 void BitwuzlaSolver::abortCallback(const char* msg) {
87 }
88
89
90 std::vector<std::unordered_map<triton::usize, SolverModel>> BitwuzlaSolver::getModels(const triton::ast::SharedAbstractNode& node,
91 triton::uint32 limit,
93 triton::uint32 timeout,
94 triton::uint32* solvingTime) const {
95 if (node == nullptr)
96 throw triton::exceptions::SolverEngine("BitwuzlaSolver::getModels(): Node cannot be null.");
97
98 if (node->isLogical() == false)
99 throw triton::exceptions::SolverEngine("BitwuzlaSolver::getModels(): Must be a logical node.");
100
101 // Create solver.
102 auto bzlaOptions = bitwuzla_options_new();
103 bitwuzla_set_option(bzlaOptions, BITWUZLA_OPT_PRODUCE_MODELS, 1);
104 auto bzlaTermMgr = bitwuzla_term_manager_new();
105 auto bzla = bitwuzla_new(bzlaTermMgr, bzlaOptions);
106
107 // Convert Triton' AST to solver terms.
108 auto bzlaAst = triton::ast::TritonToBitwuzla();
109 bitwuzla_assert(bzla, bzlaAst.convert(node, bzla));
110
111 auto tmout = timeout != 0 ? timeout : this->timeout;
112
113 // Set solving params.
114 SolverParams p(tmout, this->memoryLimit);
115 if (tmout || this->memoryLimit) {
116 bitwuzla_set_termination_callback(bzla, this->terminateCallback, reinterpret_cast<void*>(&p));
117 }
118
119 // Get time of solving start.
120 auto start = std::chrono::system_clock::now();
121
122 // Check result.
123 auto res = bitwuzla_check_sat(bzla);
124
125 // Write back status.
126 if (status) {
127 switch (res) {
128 case BITWUZLA_SAT:
130 break;
131 case BITWUZLA_UNSAT:
133 break;
134 case BITWUZLA_UNKNOWN:
135 *status = p.status;
136 break;
137 }
138 }
139
140 std::vector<std::unordered_map<triton::usize, SolverModel>> ret;
141 while(res == BITWUZLA_SAT && limit >= 1) {
142 std::vector<BitwuzlaTerm> solution;
143 solution.reserve(bzlaAst.getVariables().size());
144
145 // Parse model.
146 std::unordered_map<triton::usize, SolverModel> model;
147 for (const auto& it : bzlaAst.getVariables()) {
148 const char* svalue = bitwuzla_term_value_get_str_fmt(bitwuzla_get_value(bzla, it.first), 2);
149 auto value = this->fromBvalueToUint512(svalue);
150 auto m = SolverModel(it.second, value);
151 model[m.getId()] = m;
152
153 // Negate current model to escape duplication in the next solution.
154 const auto& symvar_sort = bzlaAst.getBitvectorSorts().at(it.second->getSize());
155 auto cur_val = bitwuzla_mk_bv_value(bzlaTermMgr, symvar_sort, svalue, 2);
156 auto n = bitwuzla_mk_term2(bzlaTermMgr, BITWUZLA_KIND_EQUAL, it.first, cur_val);
157 solution.push_back(bitwuzla_mk_term1(bzlaTermMgr, BITWUZLA_KIND_NOT, n));
158 }
159
160 // Check that model is available.
161 if (model.empty()) {
162 break;
163 }
164
165 // Push model.
166 ret.push_back(model);
167
168 if (--limit) {
169 // Escape last model.
170 if (solution.size() > 1) {
171 bitwuzla_assert(bzla, bitwuzla_mk_term(bzlaTermMgr, BITWUZLA_KIND_OR, solution.size(), solution.data()));
172 }
173 else {
174 bitwuzla_assert(bzla, solution.front());
175 }
176
177 // Get next model.
178 res = bitwuzla_check_sat(bzla);
179 }
180 }
181
182 // Get time of solving end.
183 auto end = std::chrono::system_clock::now();
184
185 if (solvingTime)
186 *solvingTime = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
187
188 bitwuzla_delete(bzla);
189 bitwuzla_term_manager_delete(bzlaTermMgr);
190 bitwuzla_options_delete(bzlaOptions);
191
192 return ret;
193 }
194
195
198
199 this->getModels(node, 0, &st, timeout, solvingTime);
200
201 if (status) {
202 *status = st;
203 }
204 return st == triton::engines::solver::SAT;
205 }
206
207
208 std::unordered_map<triton::usize, SolverModel> BitwuzlaSolver::getModel(const triton::ast::SharedAbstractNode& node, triton::engines::solver::status_e* status, triton::uint32 timeout, triton::uint32* solvingTime) const {
209 auto models = this->getModels(node, 1, status, timeout, solvingTime);
210 return models.empty() ? std::unordered_map<triton::usize, SolverModel>() : models.front();
211 }
212
213
215 if (node == nullptr) {
216 throw triton::exceptions::AstLifting("BitwuzlaSolver::evaluate(): node cannot be null.");
217 }
218
219 auto bzlaOptions = bitwuzla_options_new();
220 bitwuzla_set_option(bzlaOptions, BITWUZLA_OPT_PRODUCE_MODELS, 1);
221 auto bzlaTermMgr = bitwuzla_term_manager_new();
222 auto bzla = bitwuzla_new(bzlaTermMgr, bzlaOptions);
223
224 // Query check-sat on empty solver to put Bitwuzla in SAT-state. Thus, it should be able to evaluate concrete formulas.
225 if (bitwuzla_check_sat(bzla) != BITWUZLA_SAT) {
226 bitwuzla_delete(bzla);
227 bitwuzla_options_delete(bzlaOptions);
228 throw triton::exceptions::SolverEngine("BitwuzlaSolver::evaluate(): empty solver didn't return SAT.");
229 }
230
231 // Evaluate concrete AST in solver.
232 auto bzlaAst = triton::ast::TritonToBitwuzla(true);
233 auto term_value = bitwuzla_get_value(bzla, bzlaAst.convert(node, bzla));
234
235 triton::uint512 res = 0;
236 if (bitwuzla_term_is_bool(term_value)) {
237 res = bitwuzla_term_value_get_bool(term_value);
238 } else {
239 res = triton::uint512{bitwuzla_term_value_get_str_fmt(term_value, 10)};
240 }
241
242 bitwuzla_delete(bzla);
243 bitwuzla_term_manager_delete(bzlaTermMgr);
244 bitwuzla_options_delete(bzlaOptions);
245
246 return res;
247 }
248
249
250 std::string BitwuzlaSolver::getName(void) const {
251 return "Bitwuzla";
252 }
253
254
256 this->timeout = ms;
257 }
258
259
261 this->memoryLimit = limit;
262 }
263
264 triton::uint512 BitwuzlaSolver::fromBvalueToUint512(const char* value) const {
265 triton::usize len = strlen(value);
266 triton::usize pos = 0;
267 triton::uint512 res = 0;
268
269 // Convert short bitvector string directly.
270 if (len <= 64) {
271 return std::strtoull(value, 0, 2L);
272 }
273
274 // Convert long bitvector string by 64-bit chunks.
275 while (pos < len) {
276 triton::usize sublen = std::min(len - pos, (triton::usize) 64UL);
277 char* substr = new char[sublen + 1];
278 std::memcpy(substr, value + pos, sublen);
279 substr[sublen] = '\0';
280 res = (res << sublen) + std::strtoull(substr, 0, 2L);
281 pos += sublen;
282 delete[] substr;
283 }
284
285 return res;
286 }
287
288 };
289 };
290};
Converts a Triton's AST to Bitwuzla's AST.
TRITON_EXPORT std::unordered_map< triton::usize, SolverModel > getModel(const triton::ast::SharedAbstractNode &node, triton::engines::solver::status_e *status=nullptr, triton::uint32 timeout=0, triton::uint32 *solvingTime=nullptr) const
Computes and returns a model from a symbolic constraint.
TRITON_EXPORT std::string getName(void) const
Returns the name of this solver.
TRITON_EXPORT std::vector< std::unordered_map< triton::usize, SolverModel > > getModels(const triton::ast::SharedAbstractNode &node, triton::uint32 limit, triton::engines::solver::status_e *status=nullptr, triton::uint32 timeout=0, triton::uint32 *solvingTime=nullptr) const
Computes and returns several models from a symbolic constraint. The limit is the number of models ret...
TRITON_EXPORT BitwuzlaSolver()
Constructor.
TRITON_EXPORT triton::uint512 evaluate(const triton::ast::SharedAbstractNode &node) const
Evaluates a Triton's AST via Bitwuzla and returns a concrete value.
TRITON_EXPORT void setTimeout(triton::uint32 ms)
Defines a solver timeout (in milliseconds).
TRITON_EXPORT void setMemoryLimit(triton::uint32 mem)
Defines a solver memory consumption limit (in megabytes).
static void abortCallback(const char *msg)
Callback function that implements aborting of Bitwuzla solver with throwing exception.
static int32_t terminateCallback(void *state)
Callback function that implements termination of Bitwuzla solver on timeout and memory limit.
TRITON_EXPORT bool isSat(const triton::ast::SharedAbstractNode &node, triton::engines::solver::status_e *status=nullptr, triton::uint32 timeout=0, triton::uint32 *solvingTime=nullptr) const
Returns true if an expression is satisfiable.
This class is used to represent a constraint model solved.
The exception class used by all AST lifting (e.g z3 <-> triton).
The exception class used by the solver engine.
std::shared_ptr< triton::ast::AbstractNode > SharedAbstractNode
Shared Abstract Node.
Definition ast.hpp:59
std::size_t usize
unsigned MAX_INT 32 or 64 bits according to the CPU.
std::uint32_t uint32
unisgned 32-bits
The Triton namespace.