GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/rewriter.cpp Lines: 162 185 87.6 %
Date: 2021-09-07 Branches: 304 734 41.4 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Andres Noetzli, Dejan Jovanovic
4
 *
5
 * This file is part of the cvc5 project.
6
 *
7
 * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS
8
 * in the top-level source directory and their institutional affiliations.
9
 * All rights reserved.  See the file COPYING in the top-level source
10
 * directory for licensing information.
11
 * ****************************************************************************
12
 *
13
 * The Rewriter class.
14
 */
15
16
#include "theory/rewriter.h"
17
18
#include "options/theory_options.h"
19
#include "proof/conv_proof_generator.h"
20
#include "smt/smt_engine.h"
21
#include "smt/smt_engine_scope.h"
22
#include "smt/smt_statistics_registry.h"
23
#include "theory/builtin/proof_checker.h"
24
#include "theory/evaluator.h"
25
#include "theory/quantifiers/extended_rewrite.h"
26
#include "theory/rewriter_tables.h"
27
#include "theory/theory.h"
28
#include "util/resource_manager.h"
29
30
using namespace std;
31
32
namespace cvc5 {
33
namespace theory {
34
35
/** Attribute true for nodes that have been rewritten with proofs enabled */
36
struct RewriteWithProofsAttributeId
37
{
38
};
39
typedef expr::Attribute<RewriteWithProofsAttributeId, bool>
40
    RewriteWithProofsAttribute;
41
42
// Note that this function is a simplified version of Theory::theoryOf for
43
// (type-based) theoryOfMode. We expand and simplify it here for the sake of
44
// efficiency.
45
162270564
static TheoryId theoryOf(TNode node) {
46
162270564
  if (node.getKind() == kind::EQUAL)
47
  {
48
    // Equality is owned by the theory that owns the domain
49
24856934
    return Theory::theoryOf(node[0].getType());
50
  }
51
  // Regular nodes are owned by the kind
52
137413630
  return kindToTheoryId(node.getKind());
53
}
54
55
/**
56
 * TheoryEngine::rewrite() keeps a stack of things that are being pre-
57
 * and post-rewritten.  Each element of the stack is a
58
 * RewriteStackElement.
59
 */
60
135886480
struct RewriteStackElement {
61
  /**
62
   * Construct a fresh stack element.
63
   */
64
40507622
  RewriteStackElement(TNode node, TheoryId theoryId)
65
40507622
      : d_node(node),
66
        d_original(node),
67
        d_theoryId(theoryId),
68
        d_originalTheoryId(theoryId),
69
40507622
        d_nextChild(0)
70
  {
71
40507622
  }
72
73
240202346
  TheoryId getTheoryId() { return static_cast<TheoryId>(d_theoryId); }
74
75
23837198
  TheoryId getOriginalTheoryId()
76
  {
77
23837198
    return static_cast<TheoryId>(d_originalTheoryId);
78
  }
79
80
  /** The node we're currently rewriting */
81
  Node d_node;
82
  /** Original node (either the unrewritten node or the node after prerewriting)
83
   */
84
  Node d_original;
85
  /** Id of the theory that's currently rewriting this node */
86
  unsigned d_theoryId : 8;
87
  /** Id of the original theory that started the rewrite */
88
  unsigned d_originalTheoryId : 8;
89
  /** Index of the child this node is done rewriting */
90
  unsigned d_nextChild : 32;
91
  /** Builder for this node */
92
  NodeBuilder d_builder;
93
};
94
95
RewriteResponse identityRewrite(RewriteEnvironment* re, TNode n)
96
{
97
  return RewriteResponse(REWRITE_DONE, n);
98
}
99
100
44077723
Node Rewriter::rewrite(TNode node) {
101
44077723
  if (node.getNumChildren() == 0)
102
  {
103
    // Nodes with zero children should never change via rewriting. We return
104
    // eagerly for the sake of efficiency here.
105
9172257
    return node;
106
  }
107
34905466
  return getInstance()->rewriteTo(theoryOf(node), node);
108
}
109
110
61316
TrustNode Rewriter::rewriteWithProof(TNode node,
111
                                     bool isExtEq)
112
{
113
  // must set the proof checker before calling this
114
61316
  Assert(d_tpg != nullptr);
115
61316
  if (isExtEq)
116
  {
117
    // theory rewriter is responsible for rewriting the equality
118
37
    TheoryRewriter* tr = getInstance()->d_theoryRewriters[theoryOf(node)];
119
37
    Assert(tr != nullptr);
120
37
    return tr->rewriteEqualityExtWithProof(node);
121
  }
122
122558
  Node ret = getInstance()->rewriteTo(theoryOf(node), node, d_tpg.get());
123
61279
  return TrustNode::mkTrustRewrite(node, ret, d_tpg.get());
124
}
125
126
3786
void Rewriter::setProofNodeManager(ProofNodeManager* pnm)
127
{
128
  // if not already initialized with proof support
129
3786
  if (d_tpg == nullptr)
130
  {
131
3786
    Trace("rewriter") << "Rewriter::setProofNodeManager" << std::endl;
132
    // the rewriter is staticly determinstic, thus use static cache policy
133
    // for the term conversion proof generator
134
7572
    d_tpg.reset(new TConvProofGenerator(pnm,
135
                                        nullptr,
136
                                        TConvPolicy::FIXPOINT,
137
                                        TConvCachePolicy::STATIC,
138
3786
                                        "Rewriter::TConvProofGenerator"));
139
  }
140
3786
}
141
142
140
Node Rewriter::rewriteEqualityExt(TNode node)
143
{
144
140
  Assert(node.getKind() == kind::EQUAL);
145
  // note we don't force caching of this method currently
146
140
  return d_theoryRewriters[theoryOf(node)]->rewriteEqualityExt(node);
147
}
148
149
129074
void Rewriter::registerTheoryRewriter(theory::TheoryId tid,
150
                                      TheoryRewriter* trew)
151
{
152
129074
  d_theoryRewriters[tid] = trew;
153
129074
}
154
155
13653
TheoryRewriter* Rewriter::getTheoryRewriter(theory::TheoryId theoryId)
156
{
157
13653
  return d_theoryRewriters[theoryId];
158
}
159
160
34966782
Rewriter* Rewriter::getInstance()
161
{
162
34966782
  return smt::currentSmtEngine()->getRewriter();
163
}
164
165
36250041
Node Rewriter::rewriteTo(theory::TheoryId theoryId,
166
                         Node node,
167
                         TConvProofGenerator* tcpg)
168
{
169
  RewriteWithProofsAttribute rpfa;
170
#ifdef CVC5_ASSERTIONS
171
36250041
  bool isEquality = node.getKind() == kind::EQUAL && (!node[0].getType().isBoolean());
172
173
36250041
  if (d_rewriteStack == nullptr)
174
  {
175
9533
    d_rewriteStack.reset(new std::unordered_set<Node>());
176
  }
177
#endif
178
179
36250041
  Trace("rewriter") << "Rewriter::rewriteTo(" << theoryId << "," << node << ")"<< std::endl;
180
181
  // Check if it's been cached already
182
72500082
  Node cached = getPostRewriteCache(theoryId, node);
183
36250041
  if (!cached.isNull() && (tcpg == nullptr || node.getAttribute(rpfa)))
184
  {
185
29698922
    return cached;
186
  }
187
188
  // Put the node on the stack in order to start the "recursive" rewrite
189
13102238
  vector<RewriteStackElement> rewriteStack;
190
6551119
  rewriteStack.push_back(RewriteStackElement(node, theoryId));
191
192
6551119
  ResourceManager* rm = NULL;
193
6551119
  bool hasSmtEngine = smt::smtEngineInScope();
194
6551119
  if (hasSmtEngine) {
195
6551119
    rm = smt::currentResourceManager();
196
  }
197
  // Rewrite until the stack is empty
198
  for (;;){
199
74464123
    if (hasSmtEngine)
200
    {
201
74464123
      rm->spendResource(Resource::RewriteStep);
202
    }
203
204
    // Get the top of the recursion stack
205
74464123
    RewriteStackElement& rewriteStackTop = rewriteStack.back();
206
207
148928246
    Trace("rewriter") << "Rewriter::rewriting: "
208
148928246
                      << rewriteStackTop.getTheoryId() << ","
209
74464123
                      << rewriteStackTop.d_node << std::endl;
210
211
    // Before rewriting children we need to do a pre-rewrite of the node
212
74464123
    if (rewriteStackTop.d_nextChild == 0)
213
    {
214
      // Check if the pre-rewrite has already been done (it's in the cache)
215
40507622
      cached = getPreRewriteCache(rewriteStackTop.getTheoryId(),
216
                                  rewriteStackTop.d_node);
217
81015244
      if (cached.isNull()
218
40507622
          || (tcpg != nullptr && !rewriteStackTop.d_node.getAttribute(rpfa)))
219
      {
220
        // Rewrite until fix-point is reached
221
        for(;;) {
222
          // Perform the pre-rewrite
223
          RewriteResponse response = preRewrite(
224
14608164
              rewriteStackTop.getTheoryId(), rewriteStackTop.d_node, tcpg);
225
226
          // Put the rewritten node to the top of the stack
227
13450813
          rewriteStackTop.d_node = response.d_node;
228
13450813
          TheoryId newTheory = theoryOf(rewriteStackTop.d_node);
229
          // In the pre-rewrite, if changing theories, we just call the other theories pre-rewrite
230
26901626
          if (newTheory == rewriteStackTop.getTheoryId()
231
13450813
              && response.d_status == REWRITE_DONE)
232
          {
233
12293462
            break;
234
          }
235
1157351
          rewriteStackTop.d_theoryId = newTheory;
236
1157351
        }
237
238
        // Cache the rewrite
239
12293462
        setPreRewriteCache(rewriteStackTop.getOriginalTheoryId(),
240
                           rewriteStackTop.d_original,
241
                           rewriteStackTop.d_node);
242
      }
243
      // Otherwise we're have already been pre-rewritten (in pre-rewrite cache)
244
      else {
245
        // Continue with the cached version
246
28214160
        rewriteStackTop.d_node = cached;
247
28214160
        rewriteStackTop.d_theoryId = theoryOf(cached);
248
      }
249
    }
250
251
74464123
    rewriteStackTop.d_original = rewriteStackTop.d_node;
252
    // Now it's time to rewrite the children, check if this has already been done
253
74464123
    cached = getPostRewriteCache(rewriteStackTop.getTheoryId(),
254
                                 rewriteStackTop.d_node);
255
    // If not, go through the children
256
148928246
    if (cached.isNull()
257
74464123
        || (tcpg != nullptr && !rewriteStackTop.d_node.getAttribute(rpfa)))
258
    {
259
      // The child we need to rewrite
260
45500241
      unsigned child = rewriteStackTop.d_nextChild++;
261
262
      // To build the rewritten expression we set up the builder
263
45500241
      if(child == 0) {
264
11543740
        if (rewriteStackTop.d_node.getNumChildren() > 0)
265
        {
266
          // The children will add themselves to the builder once they're done
267
10915424
          rewriteStackTop.d_builder << rewriteStackTop.d_node.getKind();
268
10915424
          kind::MetaKind metaKind = rewriteStackTop.d_node.getMetaKind();
269
10915424
          if (metaKind == kind::metakind::PARAMETERIZED) {
270
709943
            rewriteStackTop.d_builder << rewriteStackTop.d_node.getOperator();
271
          }
272
        }
273
      }
274
275
      // Process the next child
276
45500241
      if (child < rewriteStackTop.d_node.getNumChildren())
277
      {
278
        // The child node
279
67913006
        Node childNode = rewriteStackTop.d_node[child];
280
        // Push the rewrite request to the stack (NOTE: rewriteStackTop might be a bad reference now)
281
33956503
        rewriteStack.push_back(RewriteStackElement(childNode, theoryOf(childNode)));
282
        // Go on with the rewriting
283
33956503
        continue;
284
      }
285
286
      // Incorporate the children if necessary
287
11543738
      if (rewriteStackTop.d_node.getNumChildren() > 0)
288
      {
289
21830844
        Node rewritten = rewriteStackTop.d_builder;
290
10915422
        rewriteStackTop.d_node = rewritten;
291
10915422
        rewriteStackTop.d_theoryId = theoryOf(rewriteStackTop.d_node);
292
      }
293
294
      // Done with all pre-rewriting, so let's do the post rewrite
295
      for(;;) {
296
        // Do the post-rewrite
297
        RewriteResponse response = postRewrite(
298
12061992
            rewriteStackTop.getTheoryId(), rewriteStackTop.d_node, tcpg);
299
300
        // We continue with the response we got
301
11802862
        TheoryId newTheoryId = theoryOf(response.d_node);
302
23605724
        if (newTheoryId != rewriteStackTop.getTheoryId()
303
11802862
            || response.d_status == REWRITE_AGAIN_FULL)
304
        {
305
          // In the post rewrite if we've changed theories, we must do a full rewrite
306
1283296
          Assert(response.d_node != rewriteStackTop.d_node);
307
          //TODO: this is not thread-safe - should make this assertion dependent on sequential build
308
#ifdef CVC5_ASSERTIONS
309
1283296
          Assert(d_rewriteStack->find(response.d_node)
310
                 == d_rewriteStack->end());
311
1283296
          d_rewriteStack->insert(response.d_node);
312
#endif
313
2566592
          Node rewritten = rewriteTo(newTheoryId, response.d_node, tcpg);
314
1283296
          rewriteStackTop.d_node = rewritten;
315
#ifdef CVC5_ASSERTIONS
316
1283296
          d_rewriteStack->erase(response.d_node);
317
#endif
318
1283296
          break;
319
        }
320
10519566
        else if (response.d_status == REWRITE_DONE)
321
        {
322
#ifdef CVC5_ASSERTIONS
323
          RewriteResponse r2 =
324
20520880
              d_theoryRewriters[newTheoryId]->postRewrite(response.d_node);
325
20520880
          Assert(r2.d_node == response.d_node)
326
10260440
              << r2.d_node << " != " << response.d_node;
327
#endif
328
10260440
          rewriteStackTop.d_node = response.d_node;
329
10260440
          break;
330
        }
331
        // Check for trivial rewrite loops of size 1 or 2
332
259126
        Assert(response.d_node != rewriteStackTop.d_node);
333
259126
        Assert(d_theoryRewriters[rewriteStackTop.getTheoryId()]
334
                   ->postRewrite(response.d_node)
335
                   .d_node
336
               != rewriteStackTop.d_node);
337
259126
        rewriteStackTop.d_node = response.d_node;
338
259126
      }
339
340
      // We're done with the post rewrite, so we add to the cache
341
11543736
      if (tcpg != nullptr)
342
      {
343
        // if proofs are enabled, mark that we've rewritten with proofs
344
334970
        rewriteStackTop.d_original.setAttribute(rpfa, true);
345
334970
        if (!cached.isNull())
346
        {
347
          // We may have gotten a different node, due to non-determinism in
348
          // theory rewriters (e.g. quantifiers rewriter which introduces
349
          // fresh BOUND_VARIABLE). This can happen if we wrote once without
350
          // proofs and then rewrote again with proofs.
351
320994
          if (rewriteStackTop.d_node != cached)
352
          {
353
            Trace("rewriter-proof") << "WARNING: Rewritten forms with and "
354
                                       "without proofs were not equivalent"
355
                                    << std::endl;
356
            Trace("rewriter-proof")
357
                << "   original: " << rewriteStackTop.d_original << std::endl;
358
            Trace("rewriter-proof")
359
                << "with proofs: " << rewriteStackTop.d_node << std::endl;
360
            Trace("rewriter-proof") << " w/o proofs: " << cached << std::endl;
361
            Node eq = rewriteStackTop.d_node.eqNode(cached);
362
            // we make this a post-rewrite, since we are processing a node that
363
            // has finished post-rewriting above
364
            tcpg->addRewriteStep(rewriteStackTop.d_node,
365
                                 cached,
366
                                 PfRule::TRUST_REWRITE,
367
                                 {},
368
                                 {eq},
369
                                 false);
370
            // don't overwrite the cache, should be the same
371
            rewriteStackTop.d_node = cached;
372
          }
373
        }
374
      }
375
11543736
      setPostRewriteCache(rewriteStackTop.getOriginalTheoryId(),
376
                          rewriteStackTop.d_original,
377
                          rewriteStackTop.d_node);
378
    }
379
    else
380
    {
381
      // We were already in cache, so just remember it
382
28963882
      rewriteStackTop.d_node = cached;
383
28963882
      rewriteStackTop.d_theoryId = theoryOf(cached);
384
    }
385
386
    // If this is the last node, just return
387
40507618
    if (rewriteStack.size() == 1) {
388
6551117
      Assert(!isEquality || rewriteStackTop.d_node.getKind() == kind::EQUAL
389
             || rewriteStackTop.d_node.isConst());
390
6551117
      return rewriteStackTop.d_node;
391
    }
392
393
    // We're done with this node, append it to the parent
394
33956501
    rewriteStack[rewriteStack.size() - 2].d_builder << rewriteStackTop.d_node;
395
33956501
    rewriteStack.pop_back();
396
67913004
  }
397
398
  Unreachable();
399
} /* Rewriter::rewriteTo() */
400
401
13450813
RewriteResponse Rewriter::preRewrite(theory::TheoryId theoryId,
402
                                     TNode n,
403
                                     TConvProofGenerator* tcpg)
404
{
405
13450813
  if (tcpg != nullptr)
406
  {
407
    // call the trust rewrite response interface
408
    TrustRewriteResponse tresponse =
409
927412
        d_theoryRewriters[theoryId]->preRewriteWithProof(n);
410
    // process the trust rewrite response: store the proof step into
411
    // tcpg if necessary and then convert to rewrite response.
412
463706
    return processTrustRewriteResponse(theoryId, tresponse, true, tcpg);
413
  }
414
12987107
  return d_theoryRewriters[theoryId]->preRewrite(n);
415
}
416
417
11802864
RewriteResponse Rewriter::postRewrite(theory::TheoryId theoryId,
418
                                      TNode n,
419
                                      TConvProofGenerator* tcpg)
420
{
421
11802864
  if (tcpg != nullptr)
422
  {
423
    // same as above, for post-rewrite
424
    TrustRewriteResponse tresponse =
425
674336
        d_theoryRewriters[theoryId]->postRewriteWithProof(n);
426
337168
    return processTrustRewriteResponse(theoryId, tresponse, false, tcpg);
427
  }
428
11465698
  return d_theoryRewriters[theoryId]->postRewrite(n);
429
}
430
431
800874
RewriteResponse Rewriter::processTrustRewriteResponse(
432
    theory::TheoryId theoryId,
433
    const TrustRewriteResponse& tresponse,
434
    bool isPre,
435
    TConvProofGenerator* tcpg)
436
{
437
800874
  Assert(tcpg != nullptr);
438
1601748
  TrustNode trn = tresponse.d_node;
439
800874
  Assert(trn.getKind() == TrustNodeKind::REWRITE);
440
1601748
  Node proven = trn.getProven();
441
800874
  if (proven[0] != proven[1])
442
  {
443
195330
    ProofGenerator* pg = trn.getGenerator();
444
195330
    if (pg == nullptr)
445
    {
446
390660
      Node tidn = builtin::BuiltinProofRuleChecker::mkTheoryIdNode(theoryId);
447
      // add small step trusted rewrite
448
      Node rid = mkMethodId(isPre ? MethodId::RW_REWRITE_THEORY_PRE
449
390660
                                  : MethodId::RW_REWRITE_THEORY_POST);
450
781320
      tcpg->addRewriteStep(proven[0],
451
                           proven[1],
452
                           PfRule::THEORY_REWRITE,
453
                           {},
454
                           {proven, tidn, rid},
455
585990
                           isPre);
456
    }
457
    else
458
    {
459
      // store proven rewrite step
460
      tcpg->addRewriteStep(proven[0], proven[1], pg, isPre);
461
    }
462
  }
463
1601748
  return RewriteResponse(tresponse.d_status, trn.getNode());
464
}
465
466
void Rewriter::clearCaches()
467
{
468
#ifdef CVC5_ASSERTIONS
469
  d_rewriteStack.reset(nullptr);
470
#endif
471
472
  clearCachesInternal();
473
}
474
475
1673614
Node Rewriter::rewriteViaMethod(TNode n, MethodId idr)
476
{
477
1673614
  if (idr == MethodId::RW_REWRITE)
478
  {
479
1645114
    return rewrite(n);
480
  }
481
28500
  if (idr == MethodId::RW_EXT_REWRITE)
482
  {
483
28
    quantifiers::ExtendedRewriter er;
484
14
    return er.extendedRewrite(n);
485
  }
486
28486
  if (idr == MethodId::RW_REWRITE_EQ_EXT)
487
  {
488
140
    return rewriteEqualityExt(n);
489
  }
490
28346
  if (idr == MethodId::RW_EVALUATE)
491
  {
492
    Evaluator eval;
493
28346
    return eval.eval(n, {}, {}, false);
494
  }
495
  if (idr == MethodId::RW_IDENTITY)
496
  {
497
    // does nothing
498
    return n;
499
  }
500
  // unknown rewriter
501
  Unhandled() << "Rewriter::rewriteViaMethod: no rewriter for " << idr
502
              << std::endl;
503
  return n;
504
}
505
506
}  // namespace theory
507
29502
}  // namespace cvc5