mCRL2
Loading...
Searching...
No Matches
jittyc.h
Go to the documentation of this file.
1// Author(s): Muck van Weerdenburg
2// Copyright: see the accompanying file COPYING or copy at
3// https://github.com/mCRL2org/mCRL2/blob/master/COPYING
4//
5// Distributed under the Boost Software License, Version 1.0.
6// (See accompanying file LICENSE_1_0.txt or copy at
7// http://www.boost.org/LICENSE_1_0.txt)
8//
9/// \file mcrl2/data/detail/rewrite/jittyc.h
10
11#ifndef MCRL2_DATA_DETAIL_REWR_JITTYC_H
12#define MCRL2_DATA_DETAIL_REWR_JITTYC_H
13
14#include <utility>
15#include <string>
16
17#include "mcrl2/utilities/uncompiledlibrary.h"
18#include "mcrl2/utilities/toolset_version.h"
19#include "mcrl2/atermpp/standard_containers/vector.h"
20#include "mcrl2/data/detail/rewrite/jitty.h"
21#include "mcrl2/data/detail/rewrite/match_tree.h"
22#include "mcrl2/data/detail/rewrite/nfs_array.h"
23#include "mcrl2/data/substitutions/mutable_map_substitution.h"
24
25#ifdef MCRL2_ENABLE_JITTYC
26
27namespace mcrl2
28{
29namespace data
30{
31namespace detail
32{
33
34typedef std::vector < sort_expression_list> sort_list_vector;
35
36///
37/// \brief The normal_form_cache class stores normal forms of data_expressions that
38/// are inserted in it. By keeping the cache on the stack, the normal forms
39/// in it will not be freed by the ATerm library, and can therefore be used
40/// in the generated jittyc code.
41///
42class normal_form_cache
43{
44 private:
45 std::set<data_expression> m_lookup;
46 public:
47 normal_form_cache()
48 {
49 }
50
51 // Caches cannot be copied or moved. The addresses in the cache must remain available the lifetime of
52 // all rewriters using this cache.
53 normal_form_cache(const normal_form_cache& ) = delete;
54 normal_form_cache(normal_form_cache&& ) = delete;
55 normal_form_cache& operator=(const normal_form_cache& ) = delete;
56 normal_form_cache& operator=(normal_form_cache&& ) = delete;
57
58 /// \brief insert stores the normal form of t in the cache, and returns a string
59 /// that is a C++ representation of the stored normal form. This string can
60 /// be used by the generated rewriter as long as the cache object is alive,
61 /// and its clear() method has not been called.
62 /// \param t The term to normalize.
63 /// \return A C++ string that evaluates to the cached normal form of t.
64 ///
65 std::string insert(const data_expression& t)
66 {
67 std::stringstream ss;
68 const data_expression* cached_term = &*(m_lookup.insert(t).first);
69 ss << "*reinterpret_cast<const data_expression*>(" << (void*)(cached_term) << ")";
70 return ss.str();
71 }
72
73 /// \brief Checks whether the cache is empty.
74 /// \return A boolean indicating whether the cache is empty.
75 bool empty() const
76 {
77 return m_lookup.empty();
78 }
79
80 ~normal_form_cache()
81 {
82 }
83};
84
85class RewriterCompilingJitty: public Rewriter
86{
87 public:
88 typedef Rewriter::substitution_type substitution_type;
89 typedef void (*rewriter_function)(data_expression&, const application&, RewriterCompilingJitty*);
90
91 RewriterCompilingJitty(const data_specification& DataSpec, const used_data_equation_selector&);
92 virtual ~RewriterCompilingJitty();
93
94 rewrite_strategy getStrategy();
95
96 data_expression rewrite(const data_expression& term, substitution_type& sigma);
97
98 void rewrite(data_expression& result, const data_expression& term, substitution_type& sigma);
99
100 // The variable global_sigma is a temporary store to maintain the substitution
101 // sigma during rewriting a single term. It is not a variable for public use.
102 substitution_type *global_sigma;
103 bool rewriting_in_progress;
104 rewrite_stack m_rewrite_stack;
105
106 // The data structures below are used to store the variable lists2
107 // that are used in the compiling rewriter in forall, where and exists.
108 std::vector<variable_list> rewriter_binding_variable_lists;
109 std::map <variable_list, std::size_t> variable_list_indices1;
110
111 // This function assigns a unique index to variable list vl and stores
112 // vl at this position in the vector rewriter_binding_variable_lists. This is
113 // used in the compiling rewriter to obtain this variable again.
114 // Note that the static variable variable_indices is not cleared
115 // during several runs, as generally the variables bound in rewrite
116 // rules do not change.
117 std::size_t binding_variable_list_index(const variable_list& vl)
118 {
119 if (variable_list_indices1.count(vl)>0)
120 {
121 return variable_list_indices1[vl];
122 }
123 const std::size_t index_for_vl=rewriter_binding_variable_lists.size();
124 variable_list_indices1[vl]=index_for_vl;
125 rewriter_binding_variable_lists.push_back(vl);
126 return index_for_vl;
127 }
128
129 inline variable_list binding_variable_list_get(const std::size_t i)
130 {
131 return (rewriter_binding_variable_lists[i]);
132 }
133
134 // The data structures below are used to store single variables
135 // that are bound in lambda, forall and exist operators. When required
136 // in the compiling rewriter, these variables can be retrieved from
137 // the array rewriter_bound_variables. Variable_indices0 is used
138 // to find the right place in this vector. It delivers a variable_index_where_stack_pair.
139 // The first variable in this pair indicates the index of the variable.
140 // The where_stack is used as lambda, forall, and exist can be intermixed with bound variables.
141 // Whenever the variable is bound by a where clause, true is put on the stack.
142 // For a variable that is bound otherwise, false is put on the stack.
143
144 struct variable_index_where_stack_pair
145 {
146 std::size_t variable_index;
147 // a stacked value true indicates variable bound in a where. Otherwise the
148 // variable is bound by a lambda, forall or exist.
149 std::vector<std::string> declaration_stack;
150
151 // Constructor
152 variable_index_where_stack_pair(const std::size_t& vi, const std::vector<std::string>& ds)
153 : variable_index(vi),
154 declaration_stack(ds)
155 {}
156
157 // Default constructor
158 variable_index_where_stack_pair()
159 {}
160 };
161
162 std::vector<variable> rewriter_bound_variables;
163 std::map <variable, variable_index_where_stack_pair > variable_indices0;
164
165 std::size_t bound_variable_index(const variable& v)
166 {
167 assert(variable_indices0.count(v)>0);
168 assert(variable_indices0[v].declaration_stack.back().empty());
169 return variable_indices0[v].variable_index;
170 }
171
172 // Declare a bound variable, and indicate whether it comes from a where clause in which case
173 // it has a non trivial name.
174 // When the variable is bound in a lambda, forall or exists, is_where must be set to false.
175 void bound_variable_index_declare(const variable& v, const std::string& name)
176 {
177 if (variable_indices0.count(v)>0)
178 {
179 variable_indices0[v].declaration_stack.push_back(name);
180 return;
181 }
182 const std::size_t index_for_v=rewriter_bound_variables.size();
183 variable_indices0[v]=variable_index_where_stack_pair(index_for_v,std::vector<std::string>(1,name));
184 rewriter_bound_variables.push_back(v);
185 }
186
187 // Declare a list of variables, which are not variables in a where.
188 void bound_variables_index_declare(const variable_list& vl)
189 {
190 for(const variable& v: vl)
191 {
192 bound_variable_index_declare(v, "");
193 }
194 }
195
196 void bound_variable_index_undeclare(const variable& v)
197 {
198 assert(variable_indices0.count(v)>0);
199 assert(variable_indices0[v].declaration_stack.size()>0);
200 variable_indices0[v].declaration_stack.pop_back();
201 }
202
203 void bound_variables_index_undeclare(const variable_list& vl)
204 {
205 for(const variable& v: vl)
206 {
207 bound_variable_index_undeclare(v);
208 }
209 }
210
211 const variable& bound_variable_get(const std::size_t i)
212 {
213 assert(i<rewriter_bound_variables.size());
214 return (rewriter_bound_variables[i]);
215 }
216
217 // provides a non empty string if this variable represents a where clause.
218 std::string bound_variable_stems_from_whr_clause(const variable& v)
219 {
220 assert(variable_indices0.count(v)>0);
221 assert(variable_indices0[v].declaration_stack.size()>0);
222 return variable_indices0[v].declaration_stack.back(); // if true, the variable is bound in a where clause.
223 }
224
225 // The following values are used to locate rewrite functions in the tables of
226 // precompiled functions.
227 // arity_bound -- The maximum occurring arity + 1
228 // index_bound -- The maximum occurring index + 1
229 std::size_t arity_bound;
230 std::size_t index_bound;
231
232 // The two arrays below are intended to contain the precompiled functions used
233 // for rewriting. They are used to find the relevant compiled rewriting code quickly.
234 std::vector<rewriter_function> functions_when_arguments_are_not_in_normal_form;
235 std::vector<rewriter_function> functions_when_arguments_are_in_normal_form;
236
237 // The following vector is to store normal forms of constants, indexed by the sequence number in a constant.
238 std::vector<data_expression> normal_forms_for_constants;
239
240 // Standard assignment operator.
241 RewriterCompilingJitty& operator=(const RewriterCompilingJitty& other)=delete;
242
243 std::shared_ptr<detail::Rewriter> clone()
244 {
245 return std::shared_ptr<Rewriter>(new RewriterCompilingJitty(*this));
246 }
247
248 protected:
249 class ImplementTree;
250 friend class ImplementTree;
251
252 RewriterJitty jitty_rewriter;
253 std::set < data_equation > rewrite_rules;
254 const match_tree dummy=match_tree();
255 bool made_files;
256 std::map<function_symbol, data_equation_list> jittyc_eqns;
257 std::set<function_symbol> m_extra_symbols;
258
259 std::shared_ptr<uncompiled_library> rewriter_so;
260 std::shared_ptr<normal_form_cache> m_nf_cache;
261
262 // The rewriter maintains a copy of busy and forbidden flag,
263 // to allow for faster access to them. These flags are used extensively and
264 // in clang version 2021 access to them via the compiler, using tlv_get_access,
265 // is relatively slow. It is expected that in some future version of the compiler
266 // such access is faster, and no copy of these flags is needed anymore.
267 public:
268 atermpp::detail::thread_aterm_pool* m_thread_aterm_pool;
269
270 protected:
271 // Copy construction. Not (yet) for public use.
272 RewriterCompilingJitty(RewriterCompilingJitty& other) = default;
273
274 void (*so_rewr_cleanup)();
275 void (*so_rewr)(data_expression& result, const data_expression&, RewriterCompilingJitty*);
276
277 void add_base_nfs(nfs_array& a, const function_symbol& opid, std::size_t arity);
278 void extend_nfs(nfs_array& a, const function_symbol& opid, std::size_t arity);
279 bool opid_is_nf(const function_symbol& opid, std::size_t num_args);
280 void calc_nfs_list(nfs_array& a, const application& args, variable_or_number_list nnfvars);
281 bool calc_nfs(const data_expression& t, variable_or_number_list nnfvars);
282 void CleanupRewriteSystem();
283 void BuildRewriteSystem();
284 void generate_code(const std::string& filename);
285 void generate_rewr_functions(std::ostream& s, const data::function_symbol& func, const data_equation_list& eqs);
286 bool lift_rewrite_rule_to_right_arity(data_equation& e, const std::size_t requested_arity);
287 sort_list_vector get_residual_sorts(const sort_expression& s, const std::size_t actual_arity, const std::size_t requested_arity);
288 match_tree_list create_strategy(const data_equation_list& rules, const std::size_t arity);
289 void term2seq(const data_expression& t, match_tree_list& s, std::size_t *var_cnt, const bool omit_head);
290 match_tree_list create_sequence(const data_equation& rule, std::size_t* var_cnt);
291 match_tree_list subst_var(const match_tree_list& l,
292 const variable& old,
293 const variable& new_val,
294 const std::size_t num,
295 const mutable_map_substitution<>& substs);
296 match_tree build_tree(build_pars pars, std::size_t i);
297 match_tree create_tree(const data_equation_list& rules);
298
299 void thread_initialise()
300 {
301 mCRL2log(mcrl2::log::debug) << "Initialise busy/forbidden flags\n";
302 m_thread_aterm_pool = &atermpp::detail::g_thread_term_pool();
303 }
304};
305
306struct rewriter_interface
307{
308 std::string caller_toolset_version;
309 std::string status;
310 RewriterCompilingJitty* rewriter;
311 void (*rewrite_external)(data_expression& result, const data_expression& t, RewriterCompilingJitty*);
312 void (*rewrite_cleanup)();
313};
314
315}
316}
317}
318
319#endif // MCRL2_ENABLE_JITTYC
320
321#endif // MCRL2_DATA_DETAIL_REWR_JITTYC_H