1//===-- DiffConsumer.cpp - Difference Consumer ------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This files implements the LLVM difference Consumer
10//
11//===----------------------------------------------------------------------===//
12
13#include "DiffConsumer.h"
14#include "llvm/IR/Instructions.h"
15#include "llvm/Support/Debug.h"
16#include "llvm/Support/ErrorHandling.h"
17
18using namespace llvm;
19
20static void ComputeNumbering(Function *F, DenseMap<Value*,unsigned> &Numbering){
21  unsigned IN = 0;
22
23  // Arguments get the first numbers.
24  for (Function::arg_iterator
25         AI = F->arg_begin(), AE = F->arg_end(); AI != AE; ++AI)
26    if (!AI->hasName())
27      Numbering[&*AI] = IN++;
28
29  // Walk the basic blocks in order.
30  for (Function::iterator FI = F->begin(), FE = F->end(); FI != FE; ++FI) {
31    if (!FI->hasName())
32      Numbering[&*FI] = IN++;
33
34    // Walk the instructions in order.
35    for (BasicBlock::iterator BI = FI->begin(), BE = FI->end(); BI != BE; ++BI)
36      // void instructions don't get numbers.
37      if (!BI->hasName() && !BI->getType()->isVoidTy())
38        Numbering[&*BI] = IN++;
39  }
40
41  assert(!Numbering.empty() && "asked for numbering but numbering was no-op");
42}
43
44
45void Consumer::anchor() { }
46
47void DiffConsumer::printValue(Value *V, bool isL) {
48  if (V->hasName()) {
49    out << (isa<GlobalValue>(V) ? '@' : '%') << V->getName();
50    return;
51  }
52  if (V->getType()->isVoidTy()) {
53    if (auto *SI = dyn_cast<StoreInst>(V)) {
54      out << "store to ";
55      printValue(SI->getPointerOperand(), isL);
56    } else if (auto *CI = dyn_cast<CallInst>(V)) {
57      out << "call to ";
58      printValue(CI->getCalledOperand(), isL);
59    } else if (auto *II = dyn_cast<InvokeInst>(V)) {
60      out << "invoke to ";
61      printValue(II->getCalledOperand(), isL);
62    } else {
63      out << *V;
64    }
65    return;
66  }
67  if (isa<Constant>(V)) {
68    out << *V;
69    return;
70  }
71
72  unsigned N = contexts.size();
73  while (N > 0) {
74    --N;
75    DiffContext &ctxt = contexts[N];
76    if (!ctxt.IsFunction) continue;
77    if (isL) {
78      if (ctxt.LNumbering.empty())
79        ComputeNumbering(cast<Function>(ctxt.L), ctxt.LNumbering);
80      out << '%' << ctxt.LNumbering[V];
81      return;
82    } else {
83      if (ctxt.RNumbering.empty())
84        ComputeNumbering(cast<Function>(ctxt.R), ctxt.RNumbering);
85      out << '%' << ctxt.RNumbering[V];
86      return;
87    }
88  }
89
90  out << "<anonymous>";
91}
92
93void DiffConsumer::header() {
94  if (contexts.empty()) return;
95  for (SmallVectorImpl<DiffContext>::iterator
96         I = contexts.begin(), E = contexts.end(); I != E; ++I) {
97    if (I->Differences) continue;
98    if (isa<Function>(I->L)) {
99      // Extra newline between functions.
100      if (Differences) out << "\n";
101
102      Function *L = cast<Function>(I->L);
103      Function *R = cast<Function>(I->R);
104      if (L->getName() != R->getName())
105        out << "in function " << L->getName()
106            << " / " << R->getName() << ":\n";
107      else
108        out << "in function " << L->getName() << ":\n";
109    } else if (isa<BasicBlock>(I->L)) {
110      BasicBlock *L = cast<BasicBlock>(I->L);
111      BasicBlock *R = cast<BasicBlock>(I->R);
112      if (L->hasName() && R->hasName() && L->getName() == R->getName())
113        out << "  in block %" << L->getName() << ":\n";
114      else {
115        out << "  in block ";
116        printValue(L, true);
117        out << " / ";
118        printValue(R, false);
119        out << ":\n";
120      }
121    } else if (isa<Instruction>(I->L)) {
122      out << "    in instruction ";
123      printValue(I->L, true);
124      out << " / ";
125      printValue(I->R, false);
126      out << ":\n";
127    }
128
129    I->Differences = true;
130  }
131}
132
133void DiffConsumer::indent() {
134  unsigned N = Indent;
135  while (N--) out << ' ';
136}
137
138bool DiffConsumer::hadDifferences() const {
139  return Differences;
140}
141
142void DiffConsumer::enterContext(Value *L, Value *R) {
143  contexts.push_back(DiffContext(L, R));
144  Indent += 2;
145}
146
147void DiffConsumer::exitContext() {
148  Differences |= contexts.back().Differences;
149  contexts.pop_back();
150  Indent -= 2;
151}
152
153void DiffConsumer::log(StringRef text) {
154  header();
155  indent();
156  out << text << '\n';
157}
158
159void DiffConsumer::logf(const LogBuilder &Log) {
160  header();
161  indent();
162
163  unsigned arg = 0;
164
165  StringRef format = Log.getFormat();
166  while (true) {
167    size_t percent = format.find('%');
168    if (percent == StringRef::npos) {
169      out << format;
170      break;
171    }
172    assert(format[percent] == '%');
173
174    if (percent > 0) out << format.substr(0, percent);
175
176    switch (format[percent+1]) {
177    case '%': out << '%'; break;
178    case 'l': printValue(Log.getArgument(arg++), true); break;
179    case 'r': printValue(Log.getArgument(arg++), false); break;
180    default: llvm_unreachable("unknown format character");
181    }
182
183    format = format.substr(percent+2);
184  }
185
186  out << '\n';
187}
188
189void DiffConsumer::logd(const DiffLogBuilder &Log) {
190  header();
191
192  for (unsigned I = 0, E = Log.getNumLines(); I != E; ++I) {
193    indent();
194    switch (Log.getLineKind(I)) {
195    case DC_match:
196      out << "  ";
197      Log.getLeft(I)->print(dbgs()); dbgs() << '\n';
198      //printValue(Log.getLeft(I), true);
199      break;
200    case DC_left:
201      out << "< ";
202      Log.getLeft(I)->print(dbgs()); dbgs() << '\n';
203      //printValue(Log.getLeft(I), true);
204      break;
205    case DC_right:
206      out << "> ";
207      Log.getRight(I)->print(dbgs()); dbgs() << '\n';
208      //printValue(Log.getRight(I), false);
209      break;
210    }
211    //out << "\n";
212  }
213}
214