Enzyme main
Loading...
Searching...
No Matches
TypeTree.h
Go to the documentation of this file.
1//===- TypeTree.cpp - Declaration of Type Analysis Type Trees -----------===//
2//
3// Enzyme Project
4//
5// Part of the Enzyme Project, under the Apache License v2.0 with LLVM
6// Exceptions. See https://llvm.org/LICENSE.txt for license information.
7// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8//
9// If using this code in an academic setting, please cite the following:
10// @incollection{enzymeNeurips,
11// title = {Instead of Rewriting Foreign Code for Machine Learning,
12// Automatically Synthesize Fast Gradients},
13// author = {Moses, William S. and Churavy, Valentin},
14// booktitle = {Advances in Neural Information Processing Systems 33},
15// year = {2020},
16// note = {To appear in},
17// }
18//
19//===----------------------------------------------------------------------===//
20//
21// This file contains the declaration of TypeTrees -- a class
22// representing all of the underlying types of a particular LLVM value. This
23// consists of a map of memory offsets to an underlying ConcreteType. This
24// permits TypeTrees to represent distinct underlying types at different
25// locations. Presently, TypeTree's have both a fixed depth of memory lookups
26// and a maximum offset to ensure that Type Analysis eventually terminates.
27// In the future this should be modified to better represent recursive types
28// rather than limiting the depth.
29//
30//===----------------------------------------------------------------------===//
31#ifndef ENZYME_TYPE_ANALYSIS_TYPE_TREE_H
32#define ENZYME_TYPE_ANALYSIS_TYPE_TREE_H 1
33
34#include "llvm/Support/ErrorHandling.h"
35#include "llvm/Support/raw_ostream.h"
36#include <map>
37#include <set>
38#include <string>
39#include <vector>
40
41#include "../Utils.h"
42#include "BaseType.h"
43#include "ConcreteType.h"
44
45/// Maximum offset for type trees to keep
46extern "C" {
47extern llvm::cl::opt<int> MaxTypeOffset;
48extern llvm::cl::opt<bool> EnzymeTypeWarning;
49extern llvm::cl::opt<unsigned> EnzymeMaxTypeDepth;
50}
51
52/// Helper function to print a vector of ints to a string
53static inline std::string to_string(const std::vector<int> x) {
54 std::string out = "[";
55 for (unsigned i = 0; i < x.size(); ++i) {
56 if (i != 0)
57 out += ",";
58 out += std::to_string(x[i]);
59 }
60 out += "]";
61 return out;
62}
63
64class TypeTree;
65
66typedef std::shared_ptr<const TypeTree> TypeResult;
67typedef std::map<const std::vector<int>, ConcreteType> ConcreteTypeMapType;
68typedef std::map<const std::vector<int>, const TypeResult> TypeTreeMapType;
69
70/// Class representing the underlying types of values as
71/// sequences of offsets to a ConcreteType
72class TypeTree : public std::enable_shared_from_this<TypeTree> {
73private:
74 // mapping of known indices to type if one exists
75 ConcreteTypeMapType mapping;
76 std::vector<int> minIndices;
77
78public:
81 if (dat != ConcreteType(BaseType::Unknown)) {
82 mapping.insert(std::pair<const std::vector<int>, ConcreteType>({}, dat));
83 }
84 }
85
86 static TypeTree parse(llvm::StringRef str, llvm::LLVMContext &ctx) {
87 using namespace llvm;
88 assert(str[0] == '{');
89 str = str.substr(1);
90
91 TypeTree Result;
92 while (true) {
93 while (str[0] == ' ')
94 str = str.substr(1);
95 if (str[0] == '}')
96 break;
97
98 assert(str[0] == '[');
99 str = str.substr(1);
100
101 std::vector<int> idxs;
102 while (true) {
103 while (str[0] == ' ')
104 str = str.substr(1);
105 if (str[0] == ']') {
106 str = str.substr(1);
107 break;
108 }
109
110 int idx;
111 bool failed = str.consumeInteger(10, idx);
112 (void)failed;
113 assert(!failed);
114 idxs.push_back(idx);
115
116 while (str[0] == ' ')
117 str = str.substr(1);
118
119 if (str[0] == ',') {
120 str = str.substr(1);
121 }
122 }
123
124 while (str[0] == ' ')
125 str = str.substr(1);
126
127 assert(str[0] == ':');
128 str = str.substr(1);
129
130 while (str[0] == ' ')
131 str = str.substr(1);
132
133 auto endval = str.find(',');
134 auto endval2 = str.find('}');
135 auto endval3 = str.find(' ');
136
137 if (endval2 != StringRef::npos &&
138 (endval == StringRef::npos || endval2 < endval))
139 endval = endval2;
140 if (endval3 != StringRef::npos &&
141 (endval == StringRef::npos || endval3 < endval))
142 endval = endval3;
143 assert(endval != StringRef::npos);
144
145 auto tystr = str.substr(0, endval);
146 str = str.substr(endval);
147
148 ConcreteType CT(tystr, ctx);
149 Result.mapping.emplace(idxs, CT);
150 if (Result.minIndices.size() < idxs.size()) {
151 for (size_t i = Result.minIndices.size(), end = idxs.size(); i < end;
152 ++i) {
153 Result.minIndices.push_back(idxs[i]);
154 }
155 }
156 for (size_t i = 0, end = idxs.size(); i < end; ++i) {
157 if (idxs[i] < Result.minIndices[i])
158 Result.minIndices[i] = idxs[i];
159 }
160
161 while (str[0] == ' ')
162 str = str.substr(1);
163
164 if (str[0] == ',') {
165 str = str.substr(1);
166 }
167 }
168
169 return Result;
170 }
171
172 /// Utility helper to lookup the mapping
173 const ConcreteTypeMapType &getMapping() const { return mapping; }
174
175 /// Lookup the underlying ConcreteType at a given offset sequence
176 /// or Unknown if none exists
177 ConcreteType operator[](const std::vector<int> Seq) const {
178 auto Found0 = mapping.find(Seq);
179 if (Found0 != mapping.end())
180 return Found0->second;
181 size_t Len = Seq.size();
182 if (Len == 0)
183 return BaseType::Unknown;
184
185 std::vector<std::vector<int>> todo[2];
186 todo[0].push_back({});
187 int parity = 0;
188 for (size_t i = 0, Len = Seq.size(); i < Len - 1; ++i) {
189 for (auto prev : todo[parity]) {
190 prev.push_back(-1);
191 if (mapping.find(prev) != mapping.end())
192 todo[1 - parity].push_back(prev);
193 if (Seq[i] != -1) {
194 prev.back() = Seq[i];
195 if (mapping.find(prev) != mapping.end())
196 todo[1 - parity].push_back(prev);
197 }
198 }
199 todo[parity].clear();
200 parity = 1 - parity;
201 }
202
203 size_t i = Len - 1;
204 for (auto prev : todo[parity]) {
205 prev.push_back(-1);
206 auto Found = mapping.find(prev);
207 if (Found != mapping.end())
208 return Found->second;
209 if (Seq[i] != -1) {
210 prev.back() = Seq[i];
211 Found = mapping.find(prev);
212 if (Found != mapping.end())
213 return Found->second;
214 }
215 }
216 return BaseType::Unknown;
217 }
218
219 // Return true if this type tree is fully known (i.e. there
220 // is no more information which could be added).
221 bool IsFullyDetermined() const {
222 std::vector<int> offsets = {-1};
223 while (1) {
224 auto found = mapping.find(offsets);
225 if (found == mapping.end())
226 return false;
227 if (found->second != BaseType::Pointer)
228 return true;
229 offsets.push_back(-1);
230 }
231 }
232
233 void remove(const std::vector<int> Seq) { mapping.erase(Seq); }
234
235 /// Return if changed
236 bool insert(const std::vector<int> Seq, ConcreteType CT,
237 bool PointerIntSame = false) {
238 size_t SeqSize = Seq.size();
239 if (SeqSize > EnzymeMaxTypeDepth) {
240 if (EnzymeTypeWarning) {
241 if (CustomErrorHandler) {
242 CustomErrorHandler("TypeAnalysisDepthLimit", nullptr,
243 ErrorType::TypeDepthExceeded, this, nullptr,
244 nullptr);
245 } else
246 llvm::errs() << "not handling more than " << EnzymeMaxTypeDepth
247 << " pointer lookups deep dt:" << str()
248 << " adding v: " << to_string(Seq) << ": " << CT.str()
249 << "\n";
250 }
251 return false;
252 }
253 if (SeqSize == 0) {
254 mapping.insert(std::pair<const std::vector<int>, ConcreteType>(Seq, CT));
255 return true;
256 }
257
258 // check types at lower pointer offsets are either pointer or
259 // anything. Don't insert into an anything
260 {
261 std::vector<int> tmp(Seq);
262 while (tmp.size() > 0) {
263 tmp.erase(tmp.end() - 1);
264 auto found = mapping.find(tmp);
265 if (found != mapping.end()) {
266 if (found->second == BaseType::Anything)
267 return false;
268 if (found->second != BaseType::Pointer) {
269 llvm::errs() << "FAILED CT: " << str()
270 << " adding Seq: " << to_string(Seq) << ": "
271 << CT.str() << "\n";
272 }
273 assert(found->second == BaseType::Pointer);
274 }
275 }
276 }
277
278 bool changed = false;
279 // Check if there is an existing match, e.g. [-1, -1, -1] and inserting
280 // [-1, 8, -1]
281 {
282 for (const auto &pair : llvm::make_early_inc_range(mapping)) {
283 if (pair.first.size() == SeqSize) {
284 // Whether the the inserted val (e.g. [-1, 0] or [0, 0]) is at least
285 // as general as the existing map val (e.g. [0, 0]).
286 bool newMoreGeneralThanOld = true;
287 // Whether the the existing val (e.g. [-1, 0] or [0, 0]) is at least
288 // as general as the inserted map val (e.g. [0, 0]).
289 bool oldMoreGeneralThanNew = true;
290 for (unsigned i = 0; i < SeqSize; i++) {
291 if (pair.first[i] == Seq[i])
292 continue;
293 if (Seq[i] == -1) {
294 oldMoreGeneralThanNew = false;
295 } else if (pair.first[i] == -1) {
296 newMoreGeneralThanOld = false;
297 } else {
298 oldMoreGeneralThanNew = false;
299 newMoreGeneralThanOld = false;
300 break;
301 }
302 }
303
304 if (oldMoreGeneralThanNew) {
305 // Inserting an existing or less general version
306 if (CT == pair.second)
307 return false;
308
309 // Inserting an existing or less general version (with pointer-int
310 // equivalence)
311 if (PointerIntSame)
312 if ((CT == BaseType::Pointer &&
313 pair.second == BaseType::Integer) ||
314 (CT == BaseType::Integer && pair.second == BaseType::Pointer))
315 return false;
316
317 // Inserting into an anything. Since from above we know this is not
318 // an anything, the inserted value contains no new information
319 if (pair.second == BaseType::Anything)
320 return false;
321
322 // Inserting say a [0]:anything into a [-1]:Float
323 if (CT == BaseType::Anything)
324 continue;
325
326 // Otherwise, inserting a non-equivalent pair into a more general
327 // slot. This is invalid.
328 llvm::errs() << "inserting into : " << str() << " with "
329 << to_string(Seq) << " of " << CT.str() << "\n";
330 llvm_unreachable("illegal insertion");
331 } else if (newMoreGeneralThanOld) {
332 // This new is strictly more general than the old. If they were
333 // equivalent, the case above would have been hit.
334
335 if (CT == BaseType::Anything || CT == pair.second) {
336 // previous equivalent values or values overwritten by
337 // an anything are removed
338 changed = true;
339 mapping.erase(pair.first);
340 continue;
341 }
342
343 // Inserting an existing or less general version (with pointer-int
344 // equivalence)
345 if (PointerIntSame)
346 if ((CT == BaseType::Pointer &&
347 pair.second == BaseType::Integer) ||
348 (CT == BaseType::Integer &&
349 pair.second == BaseType::Pointer)) {
350 changed = true;
351 mapping.erase(pair.first);
352 continue;
353 }
354
355 // Keep lingering anythings if not being overwritten, even if this
356 // (e.g. Float) applies to more locations. Therefore it is legal to
357 // have [-1]:Float, [8]:Anything
358 if (CT != BaseType::Anything && pair.second == BaseType::Anything)
359 continue;
360
361 // Otherwise, inserting a more general non-equivalent pair. This is
362 // invalid.
363 llvm::errs() << "inserting into : " << str() << " with "
364 << to_string(Seq) << " of " << CT.str() << "\n";
365 llvm_unreachable("illegal insertion");
366 }
367 }
368 }
369 }
370
371 bool possibleDeletion = false;
372 size_t minLen =
373 (minIndices.size() <= SeqSize) ? minIndices.size() : SeqSize;
374 for (size_t i = 0; i < minLen; i++) {
375 if (minIndices[i] > Seq[i]) {
376 if (minIndices[i] > MaxTypeOffset)
377 possibleDeletion = true;
378 minIndices[i] = Seq[i];
379 }
380 }
381
382 if (minIndices.size() < SeqSize) {
383 for (size_t i = minIndices.size(), end = SeqSize; i < end; ++i) {
384 minIndices.push_back(Seq[i]);
385 }
386 }
387
388 if (possibleDeletion) {
389 for (const auto &pair : llvm::make_early_inc_range(mapping)) {
390 size_t i = 0;
391 bool mustKeep = false;
392 bool considerErase = false;
393 for (int val : pair.first) {
394 if (val > MaxTypeOffset) {
395 if (val == minIndices[i]) {
396 mustKeep = true;
397 break;
398 }
399 considerErase = true;
400 }
401 ++i;
402 }
403 if (!mustKeep && considerErase) {
404 mapping.erase(pair.first);
405 changed = true;
406 }
407 }
408 }
409
410 size_t i = 0;
411 bool keep = false;
412 bool considerErase = false;
413 for (auto val : Seq) {
414 if (val > MaxTypeOffset) {
415 if (val == minIndices[i]) {
416 keep = true;
417 break;
418 }
419 considerErase = true;
420 }
421 i++;
422 }
423 if (considerErase && !keep)
424 return changed;
425 mapping.insert(std::pair<const std::vector<int>, ConcreteType>(Seq, CT));
426 return true;
427 }
428
429 /// How this TypeTree compares with another
430 bool operator<(const TypeTree &vd) const { return mapping < vd.mapping; }
431
432 /// Whether this TypeTree contains any information
433 bool isKnown() const {
434#ifndef NDEBUG
435 for (const auto &pair : mapping) {
436 // we should assert here as we shouldn't keep any unknown maps for
437 // efficiency
438 assert(pair.second.isKnown());
439 }
440#endif
441 return mapping.size() != 0;
442 }
443
444 /// Whether this TypeTree knows any non-pointer information
445 bool isKnownPastPointer() const {
446 for (auto &pair : mapping) {
447 // we should assert here as we shouldn't keep any unknown maps for
448 // efficiency
449 assert(pair.second.isKnown());
450 if (pair.first.size() == 0) {
451 assert(pair.second == BaseType::Pointer ||
452 pair.second == BaseType::Anything);
453 continue;
454 }
455 return true;
456 }
457 return false;
458 }
459
460 /// Select only the Integer ConcreteTypes
462 TypeTree vd;
463 for (auto &pair : mapping) {
464 if (pair.second == BaseType::Integer) {
465 vd.insert(pair.first, pair.second);
466 }
467 }
468
469 return vd;
470 }
471
472 /// Prepend an offset to all mappings
473 TypeTree Only(int Off, llvm::Instruction *orig) const {
474 TypeTree Result;
475 Result.minIndices.reserve(1 + minIndices.size());
476 Result.minIndices.push_back(Off);
477 for (auto midx : minIndices)
478 Result.minIndices.push_back(midx);
479
480 if (Result.minIndices.size() > EnzymeMaxTypeDepth) {
481 Result.minIndices.pop_back();
482 if (EnzymeTypeWarning) {
483 if (CustomErrorHandler) {
484 CustomErrorHandler("TypeAnalysisDepthLimit", wrap(orig),
485 ErrorType::TypeDepthExceeded, this, nullptr,
486 nullptr);
487 } else if (orig) {
488 EmitWarning("TypeAnalysisDepthLimit", *orig, *orig,
489 " not handling more than ", EnzymeMaxTypeDepth,
490 " pointer lookups deep dt: ", str(), " only(", Off, ")");
491 } else {
492 llvm::errs() << "not handling more than " << EnzymeMaxTypeDepth
493 << " pointer lookups deep dt:" << str() << " only("
494 << Off << "): "
495 << "\n";
496 }
497 }
498 }
499
500 for (const auto &pair : mapping) {
501 if (pair.first.size() == EnzymeMaxTypeDepth)
502 continue;
503 std::vector<int> Vec;
504 Vec.reserve(pair.first.size() + 1);
505 Vec.push_back(Off);
506 for (auto Val : pair.first)
507 Vec.push_back(Val);
508 Result.mapping.insert(
509 std::pair<const std::vector<int>, ConcreteType>(Vec, pair.second));
510 }
511 return Result;
512 }
513
514 /// Peel off the outermost index at offset 0
515 TypeTree Data0() const {
516 TypeTree Result;
517
518 for (const auto &pair : mapping) {
519 if (pair.first.size() == 0) {
520 llvm::errs() << str() << "\n";
521 }
522 assert(pair.first.size() != 0);
523
524 if (pair.first[0] == -1) {
525 std::vector<int> next(pair.first.begin() + 1, pair.first.end());
526 Result.mapping.insert(
527 std::pair<const std::vector<int>, ConcreteType>(next, pair.second));
528 for (size_t i = 0, Len = next.size(); i < Len; ++i) {
529 if (i == Result.minIndices.size())
530 Result.minIndices.push_back(next[i]);
531 else if (next[i] < Result.minIndices[i])
532 Result.minIndices[i] = next[i];
533 }
534 }
535 }
536 for (const auto &pair : mapping) {
537 if (pair.first[0] == 0) {
538 std::vector<int> next(pair.first.begin() + 1, pair.first.end());
539 // We do insertion like this to force an error
540 // on the orIn operation if there is an incompatible
541 // merge. The insert operation does not error.
542 Result.orIn(next, pair.second);
543 }
544 }
545
546 return Result;
547 }
548
549 /// Optimized version of Data0()[{}]
551 ConcreteType CT = operator[]({-1});
552 CT |= operator[]({0});
553 return CT;
554 }
555
556 /// Remove any mappings in the range [start, end) or [len, inf)
557 /// This function has special handling for -1's
558 TypeTree Clear(size_t start, size_t end, size_t len) const {
559 TypeTree Result;
560
561 // Note that below do insertion with the orIn operator
562 // to force an error if there is an incompatible
563 // merge. The insert operation does not error.
564
565 for (const auto &pair : mapping) {
566 assert(pair.first.size() != 0);
567
568 if (pair.first[0] == -1) {
569 // For "all index" calculations, explicitly
570 // add mappings for regions in range
571 auto next = pair.first;
572 for (size_t i = 0; i < start; ++i) {
573 next[0] = i;
574 Result.orIn(next, pair.second);
575 }
576 for (size_t i = end; i < len; ++i) {
577 next[0] = i;
578 Result.orIn(next, pair.second);
579 }
580 } else if ((size_t)pair.first[0] < start ||
581 ((size_t)pair.first[0] >= end &&
582 (size_t)pair.first[0] < len)) {
583 // Otherwise simply check that the given offset is in range
584
585 Result.insert(pair.first, pair.second);
586 }
587 }
588
589 // TODO canonicalize this
590 return Result;
591 }
592
593 /// Select all submappings whose first index is in range [0, len) and remove
594 /// the first index. This is the inverse of the `Only` operation
595 TypeTree Lookup(size_t len, const llvm::DataLayout &dl) const {
596
597 // Map of indices[1:] => ( End => possible Index[0] )
598 std::map<std::vector<int>, std::map<ConcreteType, std::set<int>>> staging;
599
600 for (const auto &pair : mapping) {
601 assert(pair.first.size() != 0);
602
603 // Pointer is at offset 0 from this object
604 if (pair.first[0] != 0 && pair.first[0] != -1)
605 continue;
606
607 if (pair.first.size() == 1) {
608 assert(pair.second == ConcreteType(BaseType::Pointer) ||
609 pair.second == ConcreteType(BaseType::Anything));
610 continue;
611 }
612
613 if (pair.first[1] == -1) {
614 } else {
615 if ((size_t)pair.first[1] >= len)
616 continue;
617 }
618
619 std::vector<int> next(pair.first.begin() + 2, pair.first.end());
620
621 staging[next][pair.second].insert(pair.first[1]);
622 }
623
624 TypeTree Result;
625 for (auto &pair : staging) {
626 auto &pnext = pair.first;
627 for (auto &pair2 : pair.second) {
628 auto dt = pair2.first;
629 const auto &set = pair2.second;
630
631 bool legalCombine = set.count(-1);
632
633 // See if we can canonicalize the outermost index into a -1
634 if (!legalCombine) {
635 size_t chunk = 1;
636 // Implicit pointer
637 if (pnext.size() > 0) {
638 chunk = dl.getPointerSizeInBits() / 8;
639 } else {
640 if (auto flt = dt.isFloat()) {
641 chunk = dl.getTypeSizeInBits(flt) / 8;
642 } else if (dt == BaseType::Pointer) {
643 chunk = dl.getPointerSizeInBits() / 8;
644 }
645 }
646
647 legalCombine = true;
648 for (size_t i = 0; i < len; i += chunk) {
649 if (!set.count(i)) {
650 legalCombine = false;
651 break;
652 }
653 }
654 }
655
656 std::vector<int> next;
657 next.reserve(pnext.size() + 1);
658 next.push_back(-1);
659 for (auto v : pnext)
660 next.push_back(v);
661
662 if (legalCombine) {
663 Result.insert(next, dt, /*intsAreLegalPointerSub*/ true);
664 } else {
665 for (auto e : set) {
666 next[0] = e;
667 Result.insert(next, dt);
668 }
669 }
670 }
671 }
672
673 return Result;
674 }
675
676 /// Given that this tree represents something of at most size len,
677 /// canonicalize this, creating -1's where possible
678 void CanonicalizeInPlace(size_t len, const llvm::DataLayout &dl) {
679 bool canonicalized = true;
680 for (const auto &pair : mapping) {
681 assert(pair.first.size() != 0);
682 if (pair.first[0] != -1) {
683 canonicalized = false;
684 break;
685 }
686 }
687 if (canonicalized)
688 return;
689
690 // Map of indices[1:] => ( End => possible Index[0] )
691 std::map<const std::vector<int>, std::map<ConcreteType, std::set<int>>>
692 staging;
693
694 for (const auto &pair : mapping) {
695
696 std::vector<int> next(pair.first.begin() + 1, pair.first.end());
697 if (pair.first[0] != -1) {
698 if ((size_t)pair.first[0] >= len) {
699 llvm::errs() << str() << "\n";
700 llvm::errs() << " canonicalizing " << len << "\n";
701 llvm::report_fatal_error("Canonicalization failed");
702 }
703 }
704 staging[next][pair.second].insert(pair.first[0]);
705 }
706
707 // TypeTree mappings which did not get combined
708 std::map<const std::vector<int>, ConcreteType> unCombinedToAdd;
709
710 // TypeTree mappings which did get combined into an outer -1
711 std::map<const std::vector<int>, ConcreteType> combinedToAdd;
712
713 for (const auto &pair : staging) {
714 auto &pnext = pair.first;
715 for (const auto &pair2 : pair.second) {
716 auto dt = pair2.first;
717 const auto &set = pair2.second;
718
719 bool legalCombine = false;
720
721 // See if we can canonicalize the outermost index into a -1
722 if (!set.count(-1)) {
723 size_t chunk = 1;
724 if (pnext.size() > 0) {
725 chunk = dl.getPointerSizeInBits() / 8;
726 } else {
727 if (auto flt = dt.isFloat()) {
728 chunk = dl.getTypeSizeInBits(flt) / 8;
729 } else if (dt == BaseType::Pointer) {
730 chunk = dl.getPointerSizeInBits() / 8;
731 }
732 }
733
734 legalCombine = true;
735 for (size_t i = 0; i < len; i += chunk) {
736 if (!set.count(i)) {
737 legalCombine = false;
738 break;
739 }
740 }
741 }
742
743 std::vector<int> next;
744 next.reserve(pnext.size() + 1);
745 next.push_back(-1);
746 for (auto v : pnext)
747 next.push_back(v);
748
749 if (legalCombine) {
750 combinedToAdd.emplace(next, dt);
751 } else {
752 for (auto e : set) {
753 next[0] = e;
754 unCombinedToAdd.emplace(next, dt);
755 }
756 }
757 }
758 }
759
760 // If we combined nothing, just return since there are no
761 // changes.
762 if (combinedToAdd.size() == 0) {
763 return;
764 }
765
766 // Non-combined ones do not conflict, since they were already in
767 // a TT which we can assume contained no conflicts.
768 mapping = std::move(unCombinedToAdd);
769 if (minIndices.size() > 0) {
770 minIndices[0] = -1;
771 }
772
773 // Fusing several terms into a minus one can create a conflict
774 // if the prior minus one was already in the map
775 // time, or also generated by fusion.
776 // E.g. {-1:Anything, [0]:Pointer} on 8 -> create a [-1]:Pointer
777 // which conflicts
778 // Alternatively [-1,-1,-1]:Pointer, and generated a [-1,0,-1] fusion
779 for (const auto &pair : combinedToAdd) {
780 insert(pair.first, pair.second);
781 }
782
783 return;
784 }
785
786 /// Keep only pointers (or anything's) to a repeated value (represented by -1)
787 TypeTree KeepMinusOne(bool &legal) const {
788 TypeTree dat;
789
790 for (const auto &pair : mapping) {
791
792 assert(pair.first.size() != 0);
793
794 // Pointer is at offset 0 from this object
795 if (pair.first[0] != 0 && pair.first[0] != -1)
796 continue;
797
798 if (pair.first.size() == 1) {
799 if (pair.second == BaseType::Pointer ||
800 pair.second == BaseType::Anything) {
801 dat.insert(pair.first, pair.second);
802 continue;
803 }
804 legal = false;
805 break;
806 }
807
808 if (pair.first[1] == -1) {
809 dat.insert(pair.first, pair.second);
810 }
811 }
812
813 return dat;
814 }
815
816 llvm::Type *allFloat(llvm::Value *val, const llvm::DataLayout &dl,
817 bool anythingIsFloat = false) const {
818 auto dt = operator[]({-1});
819 if (dt != BaseType::Anything && dt != BaseType::Unknown)
820 return dt.isFloat();
821 if (anythingIsFloat && dt == BaseType::Anything)
822 return (llvm::Type *)0x123;
823
824 if (val->getType()->isTokenTy() || val->getType()->isVoidTy())
825 return nullptr;
826
827 size_t size = (dl.getTypeSizeInBits(val->getType()) + 7) / 8;
828
829 auto m0 = TypeTree::operator[]({0});
830
831 llvm::Type *flt = m0.isFloat();
832 if (!flt) {
833 if (!(anythingIsFloat && m0 == BaseType::Anything)) {
834 return nullptr;
835 }
836 }
837 // If offset 0 is Anything (and anythingIsFloat), flt is still null here;
838 // walk bytewise until a concrete float type is found.
839 size_t chunk = flt ? dl.getTypeSizeInBits(flt) / 8 : 1;
840 for (size_t i = chunk; i < size; i += chunk) {
841 auto mx = TypeTree::operator[]({(int)i});
842 if (auto f2 = mx.isFloat()) {
843 if (f2 != flt) {
844 if (anythingIsFloat && !flt) {
845 flt = f2;
846 chunk = dl.getTypeSizeInBits(flt) / 8;
847 continue;
848 }
849 return nullptr;
850 }
851 } else if (anythingIsFloat && mx == BaseType::Anything) {
852 continue;
853 } else
854 return nullptr;
855 }
856
857 if (!flt && anythingIsFloat) {
858 return (llvm::Type *)0x123;
859 }
860 return flt;
861 }
862 bool anyFloat(llvm::Value *val, const llvm::DataLayout &dl) const {
863 auto dt = operator[]({-1});
864 if (dt != BaseType::Anything && dt != BaseType::Unknown)
865 return dt.isFloat();
866
867 if (val->getType()->isTokenTy() || val->getType()->isVoidTy())
868 return false;
869
870 size_t ObjSize = (dl.getTypeSizeInBits(val->getType()) + 7) / 8;
871 for (size_t i = 0; i < ObjSize;) {
872 dt = operator[]({(int)i});
873 if (dt == BaseType::Integer) {
874 i++;
875 continue;
876 }
877 if (dt == BaseType::Pointer) {
878 i += dl.getPointerSize(0);
879 continue;
880 }
881 return true;
882 }
883 return false;
884 }
885 bool anyPointer(llvm::Value *val, const llvm::DataLayout &dl) const {
886 auto dt = operator[]({-1});
887 if (dt != BaseType::Anything && dt != BaseType::Unknown)
888 return dt == BaseType::Pointer;
889
890 if (val->getType()->isTokenTy() || val->getType()->isVoidTy())
891 return false;
892
893 size_t ObjSize = (dl.getTypeSizeInBits(val->getType()) + 7) / 8;
894 for (size_t i = 0; i < ObjSize;) {
895 dt = operator[]({(int)i});
896 if (dt == BaseType::Integer) {
897 i++;
898 continue;
899 }
900 if (auto FT = dt.isFloat()) {
901 i += (dl.getTypeSizeInBits(FT) + 7) / 8;
902 continue;
903 }
904 return true;
905 }
906 return false;
907 }
908
909 /// Replace mappings in the range in [offset, offset+maxSize] with those in
910 // [addOffset, addOffset + maxSize]. In other words, select all mappings in
911 // [offset, offset+maxSize] then add `addOffset`
912 TypeTree ShiftIndices(const llvm::DataLayout &dl, const int offset,
913 const int maxSize, size_t addOffset = 0) const {
914
915 // If we have no terms 1+ layer deep return the current result as a shift
916 // won't change anything. This also makes the latercode simpler as it
917 // can assume at least a first index exists.
918 if (minIndices.size() == 0)
919 return *this;
920
921 // If we have no size in return, simply return an empty type tree. Again
922 // this simplifies later code which can assume that a minus one expantion
923 // will always result in an added variable (which would not be the case
924 // on a size == 0).
925 if (maxSize == 0)
926 return TypeTree();
927
928 TypeTree Result;
929
930 // The normal orIn / insert methods do collision checking, which is slow
931 // (and presently O(n)). This is because an expansion of a -1 which could
932 // conflict with a fixed value. Consider calling this
933 // ShiftIndicies(offset=0, maxSize=2, addOffset=0, tt={[-1]:Integer,
934 // [1]:Anything}) the -1 would expand to [0]:Int, [1]:Int, which would need
935 // to be merged with [1]:Anything
936 //
937 // The only possible values which can cause a conflict are minus -1's.
938 // As a result, we start with a fast insertion (aka without check) of
939 // non-expanded values, since they just do a literal shift which needs no
940 // extra checking, besides bounds checks.
941 //
942 // Since we're doing things manually, we also need to manually preserve TT
943 // invariants. Specifically, TT limits all values to have offsets <
944 // MAX_OFFSET, unless it is the smallest offset at that depth. (e.g. so we
945 // can still hava typetree {[123456]:Int}, even if limit is 100).
946 //
947 // First compute the minimum 0th index to be kept.
948 Result.minIndices.resize(minIndices.size(), INT_MAX);
949
950 for (const auto &pair : mapping) {
951 if (pair.first.size() == 0) {
952 if (pair.second == BaseType::Pointer ||
953 pair.second == BaseType::Anything) {
954 Result.mapping.emplace(pair.first, pair.second);
955 continue;
956 }
957
958 llvm::errs() << "could not unmerge " << str() << "\n";
959 assert(0 && "ShiftIndices called on a nonpointer/anything");
960 llvm_unreachable("ShiftIndices called on a nonpointer/anything");
961 }
962
963 int next0 = pair.first[0];
964
965 if (next0 == -1) {
966 if (maxSize == -1) {
967 // Max size does not clip the next index
968
969 // If we have a follow up offset add, we lose the -1 since we only
970 // represent [0, inf) with -1 not the [addOffset, inf) required here
971 if (addOffset != 0) {
972 next0 = addOffset;
973 }
974
975 } else {
976 // We're going to insert addOffset + 0...maxSize so the new minIndex
977 // is addOffset
978 Result.minIndices[0] = addOffset;
979 for (size_t i = 1, sz = pair.first.size(); i < sz; i++)
980 if (pair.first[i] < Result.minIndices[i])
981 Result.minIndices[i] = pair.first[i];
982 continue;
983 }
984 } else {
985 // Too small for range
986 if (next0 < offset) {
987 continue;
988 }
989 next0 -= offset;
990
991 if (maxSize != -1) {
992 if (next0 >= maxSize)
993 continue;
994 }
995
996 next0 += addOffset;
997 }
998 if (next0 < Result.minIndices[0])
999 Result.minIndices[0] = next0;
1000 for (size_t i = 1, sz = pair.first.size(); i < sz; i++)
1001 if (pair.first[i] < Result.minIndices[i])
1002 Result.minIndices[i] = pair.first[i];
1003 }
1004
1005 // Max depth of actual inserted values
1006 size_t maxInsertedDepth = 0;
1007
1008 // Insert all
1009 for (const auto &pair : mapping) {
1010 if (pair.first.size() == 0)
1011 continue;
1012
1013 int next0 = pair.first[0];
1014
1015 if (next0 == -1) {
1016 if (maxSize == -1) {
1017 // Max size does not clip the next index
1018
1019 // If we have a follow up offset add, we lose the -1 since we only
1020 // represent [0, inf) with -1 not the [addOffset, inf) required here
1021 if (addOffset != 0) {
1022 next0 = addOffset;
1023 }
1024
1025 } else {
1026 // This needs to become 0...maxSize handled separately as it is the
1027 // only insertion that could have collisions
1028 continue;
1029 }
1030 } else {
1031 // Too small for range
1032 if (next0 < offset) {
1033 continue;
1034 }
1035 next0 -= offset;
1036
1037 if (maxSize != -1) {
1038 if (next0 >= maxSize)
1039 continue;
1040 }
1041
1042 next0 += addOffset;
1043 }
1044
1045 // If after moving this would not merit being kept for being a min index
1046 // or being within the max type offset, skip it.
1047 if (next0 > MaxTypeOffset) {
1048 bool minIndex = next0 == Result.minIndices[0];
1049 if (!minIndex)
1050 for (size_t i = 1; i < pair.first.size(); i++) {
1051 if (pair.first[i] == Result.minIndices[i]) {
1052 minIndex = true;
1053 break;
1054 }
1055 }
1056 if (!minIndex)
1057 continue;
1058 }
1059
1060 std::vector<int> next(pair.first);
1061 next[0] = next0;
1062 Result.mapping.emplace(next, pair.second);
1063 if (next.size() > maxInsertedDepth)
1064 maxInsertedDepth = next.size();
1065 }
1066
1067 // Insert and expand the minus one, if needed
1068 if (maxSize != -1)
1069 for (const auto &pair : mapping) {
1070 if (pair.first.size() == 0)
1071 continue;
1072 if (pair.first[0] != -1)
1073 continue;
1074
1075 size_t chunk = 1;
1076 std::vector<int> next(pair.first);
1077 auto op = operator[]({next[0]});
1078 if (auto flt = op.isFloat()) {
1079 chunk = dl.getTypeSizeInBits(flt) / 8;
1080 } else if (op == BaseType::Pointer) {
1081 chunk = dl.getPointerSizeInBits() / 8;
1082 }
1083 auto offincr = (chunk - offset % chunk) % chunk;
1084 bool inserted = false;
1085 for (int i = offincr; i < maxSize; i += chunk) {
1086 next[0] = i + addOffset;
1087 ConcreteType prev(pair.second);
1088 // We can use faster checks here, since we know there can be no
1089 // -1's that we would conflict with, only conflicts from previous
1090 // fixed value insertions.
1091 auto found = Result.mapping.find(next);
1092 if (found != Result.mapping.end()) {
1093 // orIn returns if changed, update the value in the map if so
1094 // with the new value.
1095 if (prev.orIn(found->second, /*pointerIntSame*/ false))
1096 found->second = prev;
1097 } else {
1098 Result.mapping.emplace(next, pair.second);
1099 }
1100 inserted = true;
1101 }
1102 if (inserted && next.size() > maxInsertedDepth)
1103 maxInsertedDepth = next.size();
1104 }
1105
1106 // Resize minIndices down if we dropped any higher-depth indices for being
1107 // out of scope.
1108 Result.minIndices.resize(maxInsertedDepth);
1109 return Result;
1110 }
1111
1112 /// Keep only mappings where the type is not an `Anything`
1114 TypeTree Result;
1115 Result.minIndices.reserve(minIndices.size());
1116 for (const auto &pair : mapping) {
1117 if (pair.second == ConcreteType(BaseType::Anything))
1118 continue;
1119 Result.mapping.insert(pair);
1120 for (size_t i = 0, Len = pair.first.size(); i < Len; ++i) {
1121 if (i == Result.minIndices.size())
1122 Result.minIndices.push_back(pair.first[i]);
1123 else if (pair.first[i] < Result.minIndices[i])
1124 Result.minIndices[i] = pair.first[i];
1125 }
1126 }
1127 return Result;
1128 }
1129
1130 /// Replace -1 with 0
1132 TypeTree dat;
1133 for (const auto &pair : mapping) {
1134 if (pair.second == ConcreteType(BaseType::Anything))
1135 continue;
1136 std::vector<int> nex = pair.first;
1137 for (auto &v : nex)
1138 if (v == -1)
1139 v = 0;
1140 dat.insert(nex, pair.second);
1141 }
1142 return dat;
1143 }
1144
1145 /// Replace all integer subtypes with anything
1147 for (auto &pair : mapping) {
1148 if (pair.second == BaseType::Integer) {
1149 pair.second = BaseType::Anything;
1150 }
1151 }
1152 }
1153
1154 /// Keep only mappings where the type is an `Anything`
1156 TypeTree dat;
1157 for (const auto &pair : mapping) {
1158 if (pair.second != ConcreteType(BaseType::Anything))
1159 continue;
1160 dat.insert(pair.first, pair.second);
1161 }
1162 return dat;
1163 }
1164
1165 /// Chceck equality of two TypeTrees
1166 bool operator==(const TypeTree &RHS) const { return mapping == RHS.mapping; }
1167
1168 /// Set this to another TypeTree, returning if this was changed
1169 bool operator=(const TypeTree &RHS) {
1170 if (*this == RHS)
1171 return false;
1172 minIndices = RHS.minIndices;
1173 mapping.clear();
1174 for (const auto &elems : RHS.mapping) {
1175 mapping.emplace(elems);
1176 }
1177 return true;
1178 }
1179
1180 bool checkedOrIn(const std::vector<int> &Seq, ConcreteType RHS,
1181 bool PointerIntSame, bool &LegalOr) {
1182 assert(RHS != BaseType::Unknown);
1183 ConcreteType CT = operator[](Seq);
1184
1185 bool subchanged = CT.checkedOrIn(RHS, PointerIntSame, LegalOr);
1186 if (!subchanged)
1187 return false;
1188 if (!LegalOr)
1189 return subchanged;
1190
1191 auto SeqSize = Seq.size();
1192
1193 if (SeqSize > 0) {
1194 // check pointer abilities from before
1195 for (size_t i = 0; i < SeqSize; ++i) {
1196 std::vector<int> tmp(Seq.begin(), Seq.end() - 1 - i);
1197 auto found = mapping.find(tmp);
1198 if (found != mapping.end()) {
1199 if (!(found->second == BaseType::Pointer ||
1200 found->second == BaseType::Anything)) {
1201 LegalOr = false;
1202 return false;
1203 }
1204 }
1205 }
1206
1207 // Check if there is an existing match, e.g. [-1, -1, -1] and inserting
1208 // [-1, 8, -1]
1209 {
1210 for (const auto &pair : llvm::make_early_inc_range(mapping)) {
1211 if (pair.first.size() == SeqSize) {
1212 // Whether the the inserted val (e.g. [-1, 0] or [0, 0]) is at least
1213 // as general as the existing map val (e.g. [0, 0]).
1214 bool newMoreGeneralThanOld = true;
1215 // Whether the the existing val (e.g. [-1, 0] or [0, 0]) is at least
1216 // as general as the inserted map val (e.g. [0, 0]).
1217 bool oldMoreGeneralThanNew = true;
1218 for (unsigned i = 0; i < SeqSize; i++) {
1219 if (pair.first[i] == Seq[i])
1220 continue;
1221 if (Seq[i] == -1) {
1222 oldMoreGeneralThanNew = false;
1223 } else if (pair.first[i] == -1) {
1224 newMoreGeneralThanOld = false;
1225 } else {
1226 oldMoreGeneralThanNew = false;
1227 newMoreGeneralThanOld = false;
1228 break;
1229 }
1230 }
1231
1232 if (oldMoreGeneralThanNew) {
1233 // Inserting an existing or less general version
1234 if (CT == pair.second)
1235 return false;
1236
1237 // Inserting an existing or less general version (with pointer-int
1238 // equivalence)
1239 if (PointerIntSame)
1240 if ((CT == BaseType::Pointer &&
1241 pair.second == BaseType::Integer) ||
1242 (CT == BaseType::Integer &&
1243 pair.second == BaseType::Pointer))
1244 return false;
1245
1246 // Inserting into an anything. Since from above we know this is
1247 // not an anything, the inserted value contains no new information
1248 if (pair.second == BaseType::Anything)
1249 return false;
1250
1251 // Inserting say a [0]:anything into a [-1]:Float
1252 if (CT == BaseType::Anything) {
1253 // If both at same index, remove old index
1254 if (newMoreGeneralThanOld)
1255 mapping.erase(pair.first);
1256 continue;
1257 }
1258
1259 // Otherwise, inserting a non-equivalent pair into a more general
1260 // slot. This is invalid.
1261 LegalOr = false;
1262 return false;
1263 } else if (newMoreGeneralThanOld) {
1264 // This new is strictly more general than the old. If they were
1265 // equivalent, the case above would have been hit.
1266
1267 if (CT == BaseType::Anything || CT == pair.second) {
1268 // previous equivalent values or values overwritten by
1269 // an anything are removed
1270 mapping.erase(pair.first);
1271 continue;
1272 }
1273
1274 // Inserting an existing or less general version (with pointer-int
1275 // equivalence)
1276 if (PointerIntSame)
1277 if ((CT == BaseType::Pointer &&
1278 pair.second == BaseType::Integer) ||
1279 (CT == BaseType::Integer &&
1280 pair.second == BaseType::Pointer)) {
1281 mapping.erase(pair.first);
1282 continue;
1283 }
1284
1285 // Keep lingering anythings if not being overwritten, even if this
1286 // (e.g. Float) applies to more locations. Therefore it is legal
1287 // to have [-1]:Float, [8]:Anything
1288 if (CT != BaseType::Anything && pair.second == BaseType::Anything)
1289 continue;
1290
1291 // Otherwise, inserting a more general non-equivalent pair. This
1292 // is invalid.
1293 LegalOr = false;
1294 return false;
1295 }
1296 }
1297 }
1298 }
1299 }
1300
1301 return insert(Seq, CT);
1302 }
1303
1304 bool orIn(const std::vector<int> &Seq, ConcreteType RHS,
1305 bool PointerIntSame = false) {
1306 bool LegalOr = true;
1307 bool Result = checkedOrIn(Seq, RHS, PointerIntSame, LegalOr);
1308 assert(LegalOr);
1309 return Result;
1310 }
1311
1312 /// Set this to the logical or of itself and RHS, returning whether this value
1313 /// changed Setting `PointerIntSame` considers pointers and integers as
1314 /// equivalent If this is an illegal operation, `LegalOr` will be set to false
1315 bool checkedOrIn(const TypeTree &RHS, bool PointerIntSame, bool &LegalOr) {
1316 // TODO detect recursive merge and simplify
1317
1318 bool changed = false;
1319 for (auto &pair : RHS.mapping) {
1320 changed |= checkedOrIn(pair.first, pair.second, PointerIntSame, LegalOr);
1321 }
1322 return changed;
1323 }
1324
1325 /// Set this to the logical or of itself and RHS, returning whether this value
1326 /// changed Setting `PointerIntSame` considers pointers and integers as
1327 /// equivalent This function will error if doing an illegal Operation
1328 bool orIn(const TypeTree &RHS, bool PointerIntSame) {
1329 bool Legal = true;
1330 bool Result = checkedOrIn(RHS, PointerIntSame, Legal);
1331 if (!Legal) {
1332 llvm::errs() << "Illegal orIn: " << str() << " right: " << RHS.str()
1333 << " PointerIntSame=" << PointerIntSame << "\n";
1334 assert(0 && "Performed illegal ConcreteType::orIn");
1335 llvm_unreachable("Performed illegal ConcreteType::orIn");
1336 }
1337 return Result;
1338 }
1339
1340 /// Set this to the logical or of itself and RHS, returning whether this value
1341 /// changed Setting `PointerIntSame` considers pointers and integers as
1342 /// equivalent This function will error if doing an illegal Operation
1343 bool orIn(const std::vector<int> Seq, ConcreteType CT, bool PointerIntSame) {
1344 bool Legal = true;
1345 bool Result = checkedOrIn(Seq, CT, PointerIntSame, Legal);
1346 if (!Legal) {
1347 llvm::errs() << "Illegal orIn: " << str() << " right: " << to_string(Seq)
1348 << " CT: " << CT.str()
1349 << " PointerIntSame=" << PointerIntSame << "\n";
1350 assert(0 && "Performed illegal ConcreteType::orIn");
1351 llvm_unreachable("Performed illegal ConcreteType::orIn");
1352 }
1353 return Result;
1354 }
1355
1356 /// Set this to the logical or of itself and RHS, returning whether this value
1357 /// changed This assumes that pointers and integers are distinct This function
1358 /// will error if doing an illegal Operation
1359 bool operator|=(const TypeTree &RHS) {
1360 return orIn(RHS, /*PointerIntSame*/ false);
1361 }
1362
1363 /// Set this to the logical and of itself and RHS, returning whether this
1364 /// value changed If this and RHS are incompatible at an index, the result
1365 /// will be BaseType::Unknown
1366 bool andIn(const TypeTree &RHS) {
1367 bool changed = false;
1368
1369 for (auto &pair : llvm::make_early_inc_range(mapping)) {
1371 auto fd = RHS.mapping.find(pair.first);
1372 if (fd != RHS.mapping.end()) {
1373 other = fd->second;
1374 }
1375 changed = (pair.second &= other);
1376 if (pair.second == BaseType::Unknown) {
1377 mapping.erase(pair.first);
1378 }
1379 }
1380
1381 return changed;
1382 }
1383
1384 /// Set this to the logical and of itself and RHS, returning whether this
1385 /// value changed If this and RHS are incompatible at an index, the result
1386 /// will be BaseType::Unknown
1387 bool operator&=(const TypeTree &RHS) { return andIn(RHS); }
1388
1389 /// Set this to the logical `binop` of itself and RHS, using the Binop Op,
1390 /// returning true if this was changed.
1391 /// This function will error on an invalid type combination
1392 bool binopIn(bool &Legal, const TypeTree &RHS,
1393 llvm::BinaryOperator::BinaryOps Op) {
1394 bool changed = false;
1395
1396 for (auto &pair : llvm::make_early_inc_range(mapping)) {
1397 // TODO propagate non-first level operands:
1398 // Special handling is necessary here because a pointer to an int
1399 // binop with something should not apply the binop rules to the
1400 // underlying data but instead a different rule
1401 if (pair.first.size() > 0) {
1402 mapping.erase(pair.first);
1403 continue;
1404 }
1405
1406 ConcreteType CT(pair.second);
1408
1409 // Mutual mappings
1410 auto found = RHS.mapping.find(pair.first);
1411 if (found != RHS.mapping.end()) {
1412 RightCT = found->second;
1413 }
1414 bool SubLegal = true;
1415 changed |= CT.binopIn(SubLegal, RightCT, Op);
1416 if (!SubLegal) {
1417 Legal = false;
1418 return changed;
1419 }
1420 if (CT == BaseType::Unknown) {
1421 mapping.erase(pair.first);
1422 } else {
1423 pair.second = CT;
1424 }
1425 }
1426
1427 // mapings just on the right
1428 for (auto &pair : RHS.mapping) {
1429 // TODO propagate non-first level operands:
1430 // Special handling is necessary here because a pointer to an int
1431 // binop with something should not apply the binop rules to the
1432 // underlying data but instead a different rule
1433 if (pair.first.size() > 0) {
1434 continue;
1435 }
1436
1437 if (mapping.find(pair.first) == RHS.mapping.end()) {
1439 bool SubLegal = true;
1440 changed |= CT.binopIn(SubLegal, pair.second, Op);
1441 if (!SubLegal) {
1442 Legal = false;
1443 return changed;
1444 }
1445 if (CT != BaseType::Unknown) {
1446 mapping.insert(std::make_pair(pair.first, CT));
1447 }
1448 }
1449 }
1450
1451 return changed;
1452 }
1453
1454 /// Returns a string representation of this TypeTree
1455 std::string str() const {
1456 std::string out = "{";
1457 bool first = true;
1458 for (auto &pair : mapping) {
1459 if (!first) {
1460 out += ", ";
1461 }
1462 out += "[";
1463 for (unsigned i = 0; i < pair.first.size(); ++i) {
1464 if (i != 0)
1465 out += ",";
1466 out += std::to_string(pair.first[i]);
1467 }
1468 out += "]:" + pair.second.str();
1469 first = false;
1470 }
1471 out += "}";
1472 return out;
1473 }
1474
1475 llvm::MDNode *toMD(llvm::LLVMContext &ctx) {
1476 llvm::SmallVector<llvm::Metadata *, 1> subMD;
1477 std::map<int, TypeTree> todo;
1479 for (auto &pair : mapping) {
1480 if (pair.first.size() == 0) {
1481 base = pair.second;
1482 continue;
1483 }
1484 auto next(pair.first);
1485 next.erase(next.begin());
1486 todo[pair.first[0]].mapping.insert(std::make_pair(next, pair.second));
1487 }
1488 subMD.push_back(llvm::MDString::get(ctx, base.str()));
1489 for (auto pair : todo) {
1490 // Offsets may be negative (e.g. -1 to denote any offset), so the
1491 // constant must be created as a signed value.
1492 subMD.push_back(llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
1493 llvm::IntegerType::get(ctx, 32), pair.first, /*IsSigned*/ true)));
1494 subMD.push_back(pair.second.toMD(ctx));
1495 }
1496 return llvm::MDNode::get(ctx, subMD);
1497 };
1498
1499 void insertFromMD(llvm::MDNode *md, const std::vector<int> &prev = {}) {
1500 ConcreteType base(
1501 llvm::cast<llvm::MDString>(md->getOperand(0))->getString(),
1502 md->getContext());
1503 if (base != BaseType::Unknown)
1504 mapping.insert(std::make_pair(prev, base));
1505 for (size_t i = 1; i < md->getNumOperands(); i += 2) {
1506 auto off = llvm::cast<llvm::ConstantInt>(
1507 llvm::cast<llvm::ConstantAsMetadata>(md->getOperand(i))
1508 ->getValue())
1509 ->getSExtValue();
1510 auto next(prev);
1511 next.push_back((int)off);
1512 insertFromMD(llvm::cast<llvm::MDNode>(md->getOperand(i + 1)), next);
1513 }
1514 }
1515
1516 static TypeTree fromMD(llvm::MDNode *md) {
1517 TypeTree ret;
1518 std::vector<int> off;
1519 ret.insertFromMD(md, off);
1520 return ret;
1521 }
1522};
1523
1524#endif
std::map< const std::vector< int >, const TypeResult > TypeTreeMapType
Definition TypeTree.h:68
llvm::cl::opt< unsigned > EnzymeMaxTypeDepth
static std::string to_string(const std::vector< int > x)
Helper function to print a vector of ints to a string.
Definition TypeTree.h:53
std::map< const std::vector< int >, ConcreteType > ConcreteTypeMapType
Definition TypeTree.h:67
llvm::cl::opt< bool > EnzymeTypeWarning
llvm::cl::opt< int > MaxTypeOffset
Maximum offset for type trees to keep.
std::shared_ptr< const TypeTree > TypeResult
Definition TypeTree.h:66
LLVMValueRef(* CustomErrorHandler)(const char *, LLVMValueRef, ErrorType, const void *, LLVMValueRef, LLVMBuilderRef)
Definition Utils.cpp:62
void EmitWarning(llvm::StringRef RemarkName, const llvm::DiagnosticLocation &Loc, const llvm::BasicBlock *BB, const Args &...args)
Definition Utils.h:252
@ TypeDepthExceeded
Concrete SubType of a given value.
bool binopIn(bool &Legal, const ConcreteType RHS, llvm::BinaryOperator::BinaryOps Op)
Set this to the logical binop of itself and RHS, using the Binop Op, returning true if this was chang...
std::string str() const
Convert the ConcreteType to a string.
bool checkedOrIn(const ConcreteType CT, bool PointerIntSame, bool &LegalOr)
Set this to the logical or of itself and CT, returning whether this value changed Setting PointerIntS...
bool orIn(const ConcreteType CT, bool PointerIntSame)
Set this to the logical or of itself and CT, returning whether this value changed Setting PointerIntS...
llvm::Type * isFloat() const
Return the floating point type, if this is a float.
Class representing the underlying types of values as sequences of offsets to a ConcreteType.
Definition TypeTree.h:72
bool orIn(const TypeTree &RHS, bool PointerIntSame)
Set this to the logical or of itself and RHS, returning whether this value changed Setting PointerInt...
Definition TypeTree.h:1328
TypeTree Only(int Off, llvm::Instruction *orig) const
Prepend an offset to all mappings.
Definition TypeTree.h:473
bool operator&=(const TypeTree &RHS)
Set this to the logical and of itself and RHS, returning whether this value changed If this and RHS a...
Definition TypeTree.h:1387
TypeTree Data0() const
Peel off the outermost index at offset 0.
Definition TypeTree.h:515
ConcreteType operator[](const std::vector< int > Seq) const
Lookup the underlying ConcreteType at a given offset sequence or Unknown if none exists.
Definition TypeTree.h:177
bool andIn(const TypeTree &RHS)
Set this to the logical and of itself and RHS, returning whether this value changed If this and RHS a...
Definition TypeTree.h:1366
const ConcreteTypeMapType & getMapping() const
Utility helper to lookup the mapping.
Definition TypeTree.h:173
static TypeTree parse(llvm::StringRef str, llvm::LLVMContext &ctx)
Definition TypeTree.h:86
bool checkedOrIn(const TypeTree &RHS, bool PointerIntSame, bool &LegalOr)
Set this to the logical or of itself and RHS, returning whether this value changed Setting PointerInt...
Definition TypeTree.h:1315
TypeTree ShiftIndices(const llvm::DataLayout &dl, const int offset, const int maxSize, size_t addOffset=0) const
Replace mappings in the range in [offset, offset+maxSize] with those in.
Definition TypeTree.h:912
TypeTree Lookup(size_t len, const llvm::DataLayout &dl) const
Select all submappings whose first index is in range [0, len) and remove the first index.
Definition TypeTree.h:595
void ReplaceIntWithAnything()
Replace all integer subtypes with anything.
Definition TypeTree.h:1146
llvm::MDNode * toMD(llvm::LLVMContext &ctx)
Definition TypeTree.h:1475
ConcreteType Inner0() const
Optimized version of Data0()[{}].
Definition TypeTree.h:550
bool operator|=(const TypeTree &RHS)
Set this to the logical or of itself and RHS, returning whether this value changed This assumes that ...
Definition TypeTree.h:1359
void remove(const std::vector< int > Seq)
Definition TypeTree.h:233
TypeTree ReplaceMinus() const
Replace -1 with 0.
Definition TypeTree.h:1131
bool operator==(const TypeTree &RHS) const
Chceck equality of two TypeTrees.
Definition TypeTree.h:1166
bool anyPointer(llvm::Value *val, const llvm::DataLayout &dl) const
Definition TypeTree.h:885
static TypeTree fromMD(llvm::MDNode *md)
Definition TypeTree.h:1516
bool isKnownPastPointer() const
Whether this TypeTree knows any non-pointer information.
Definition TypeTree.h:445
TypeTree Clear(size_t start, size_t end, size_t len) const
Remove any mappings in the range [start, end) or [len, inf) This function has special handling for -1...
Definition TypeTree.h:558
TypeTree KeepMinusOne(bool &legal) const
Keep only pointers (or anything's) to a repeated value (represented by -1)
Definition TypeTree.h:787
bool orIn(const std::vector< int > Seq, ConcreteType CT, bool PointerIntSame)
Set this to the logical or of itself and RHS, returning whether this value changed Setting PointerInt...
Definition TypeTree.h:1343
bool anyFloat(llvm::Value *val, const llvm::DataLayout &dl) const
Definition TypeTree.h:862
TypeTree JustInt() const
Select only the Integer ConcreteTypes.
Definition TypeTree.h:461
TypeTree JustAnything() const
Keep only mappings where the type is an Anything
Definition TypeTree.h:1155
bool checkedOrIn(const std::vector< int > &Seq, ConcreteType RHS, bool PointerIntSame, bool &LegalOr)
Definition TypeTree.h:1180
TypeTree()
Definition TypeTree.h:79
void CanonicalizeInPlace(size_t len, const llvm::DataLayout &dl)
Given that this tree represents something of at most size len, canonicalize this, creating -1's where...
Definition TypeTree.h:678
std::string str() const
Returns a string representation of this TypeTree.
Definition TypeTree.h:1455
bool isKnown() const
Whether this TypeTree contains any information.
Definition TypeTree.h:433
bool orIn(const std::vector< int > &Seq, ConcreteType RHS, bool PointerIntSame=false)
Definition TypeTree.h:1304
bool insert(const std::vector< int > Seq, ConcreteType CT, bool PointerIntSame=false)
Return if changed.
Definition TypeTree.h:236
void insertFromMD(llvm::MDNode *md, const std::vector< int > &prev={})
Definition TypeTree.h:1499
bool binopIn(bool &Legal, const TypeTree &RHS, llvm::BinaryOperator::BinaryOps Op)
Set this to the logical binop of itself and RHS, using the Binop Op, returning true if this was chang...
Definition TypeTree.h:1392
TypeTree(ConcreteType dat)
Definition TypeTree.h:80
bool operator<(const TypeTree &vd) const
How this TypeTree compares with another.
Definition TypeTree.h:430
TypeTree PurgeAnything() const
Keep only mappings where the type is not an Anything
Definition TypeTree.h:1113
llvm::Type * allFloat(llvm::Value *val, const llvm::DataLayout &dl, bool anythingIsFloat=false) const
Definition TypeTree.h:816
bool operator=(const TypeTree &RHS)
Set this to another TypeTree, returning if this was changed.
Definition TypeTree.h:1169
bool IsFullyDetermined() const
Definition TypeTree.h:221