forked from checkedc/checkedc-clang
-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathProgramInfo.cpp
More file actions
1164 lines (1044 loc) · 40.7 KB
/
ProgramInfo.cpp
File metadata and controls
1164 lines (1044 loc) · 40.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//=--ProgramInfo.cpp----------------------------------------------*- C++-*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Implementation of ProgramInfo methods.
//===----------------------------------------------------------------------===//
#include "clang/3C/ProgramInfo.h"
#include "clang/3C/3CGlobalOptions.h"
#include "clang/3C/ConstraintsGraph.h"
#include "clang/3C/MappingVisitor.h"
#include "clang/3C/Utils.h"
#include "llvm/Support/JSON.h"
#include <sstream>
using namespace clang;
ProgramInfo::ProgramInfo() : Persisted(true) {
ExternalFunctionFVCons.clear();
StaticFunctionFVCons.clear();
}
void dumpExtFuncMap(const ProgramInfo::ExternalFunctionMapType &EMap,
raw_ostream &O) {
for (const auto &DefM : EMap) {
O << "Func Name:" << DefM.first << " => [ ";
DefM.second->print(O);
O << " ]\n";
}
}
void dumpStaticFuncMap(const ProgramInfo::StaticFunctionMapType &EMap,
raw_ostream &O) {
for (const auto &DefM : EMap) {
O << "File Name:" << DefM.first << " => ";
for (const auto &Tmp : DefM.second) {
O << " Func Name:" << Tmp.first << " => [ \n";
Tmp.second->print(O);
O << " ]\n";
}
O << "\n";
}
}
void dumpExtFuncMapJson(const ProgramInfo::ExternalFunctionMapType &EMap,
raw_ostream &O) {
bool AddComma = false;
for (const auto &DefM : EMap) {
if (AddComma) {
O << ",\n";
}
O << "{\"FuncName\":\"" << DefM.first << "\", \"Constraints\":[";
DefM.second->dumpJson(O);
O << "]}";
AddComma = true;
}
}
void dumpStaticFuncMapJson(const ProgramInfo::StaticFunctionMapType &EMap,
raw_ostream &O) {
bool AddComma = false;
for (const auto &DefM : EMap) {
if (AddComma) {
O << ",\n";
}
// The `FuncName` and `FileName` field names are backwards: this is actually
// the file name, hence the need to defend against special characters.
O << "{\"FuncName\":" << llvm::json::Value(DefM.first)
<< ", \"Constraints\":[";
bool AddComma1 = false;
for (const auto &J : DefM.second) {
if (AddComma1) {
O << ",";
}
O << "{\"FileName\":\"" << J.first << "\", \"FVConstraints\":[";
J.second->dumpJson(O);
O << "]}\n";
AddComma1 = true;
}
O << "]}";
AddComma = true;
}
}
void ProgramInfo::print(raw_ostream &O) const {
CS.print(O);
O << "\n";
O << "Constraint Variables\n";
for (const auto &I : Variables) {
PersistentSourceLoc L = I.first;
L.print(O);
O << "=>[ ";
I.second->print(O);
O << " ]\n";
}
O << "External Function Definitions\n";
dumpExtFuncMap(ExternalFunctionFVCons, O);
O << "Static Function Definitions\n";
dumpStaticFuncMap(StaticFunctionFVCons, O);
}
void ProgramInfo::dumpJson(llvm::raw_ostream &O) const {
O << "{\"Setup\":";
CS.dumpJson(O);
// Dump the constraint variables.
O << ", \"ConstraintVariables\":[";
bool AddComma = false;
for (const auto &I : Variables) {
if (AddComma) {
O << ",\n";
}
PersistentSourceLoc L = I.first;
O << "{\"line\":";
O << llvm::json::Value(L.toString());
O << ",\"Variables\":[";
I.second->dumpJson(O);
O << "]}";
AddComma = true;
}
O << "]";
O << ", \"ExternalFunctionDefinitions\":[";
dumpExtFuncMapJson(ExternalFunctionFVCons, O);
O << "], \"StaticFunctionDefinitions\":[";
dumpStaticFuncMapJson(StaticFunctionFVCons, O);
O << "]}";
}
// Given a ConstraintVariable V, retrieve all of the unique
// constraint variables used by V. If V is just a
// PointerVariableConstraint, then this is just the contents
// of 'vars'. If it either has a function pointer, or V is
// a function, then recurses on the return and parameter
// constraints.
static void getVarsFromConstraint(ConstraintVariable *V, CAtoms &R,
std::set<ConstraintVariable *> &Visited) {
if (Visited.find(V) == Visited.end()) {
Visited.insert(V);
if (auto *PVC = dyn_cast_or_null<PVConstraint>(V)) {
R.insert(R.begin(), PVC->getCvars().begin(), PVC->getCvars().end());
if (FVConstraint *FVC = PVC->getFV())
getVarsFromConstraint(FVC, R, Visited);
} else if (auto *FVC = dyn_cast_or_null<FVConstraint>(V)) {
getVarsFromConstraint(FVC->getInternalReturn(), R, Visited);
for (unsigned I = 0; I < FVC->numParams(); I++)
getVarsFromConstraint(FVC->getInternalParam(I), R, Visited);
}
}
}
// Print aggregate stats
void ProgramInfo::printAggregateStats(const std::set<std::string> &F,
llvm::raw_ostream &O) {
std::vector<Atom *> AllAtoms;
CVarSet Visited;
CAtoms FoundVars;
unsigned int TotP, TotNt, TotA, TotWi;
TotP = TotNt = TotA = TotWi = 0;
CVarSet ArrPtrs, NtArrPtrs;
ConstraintVariable *Tmp = nullptr;
for (auto &I : Variables) {
ConstraintVariable *C = I.second;
std::string FileName = I.first.getFileName();
if (F.count(FileName) ||
FileName.find(_3COpts.BaseDir) != std::string::npos) {
if (C->isForValidDecl()) {
FoundVars.clear();
getVarsFromConstraint(C, FoundVars, Visited);
std::copy(FoundVars.begin(), FoundVars.end(),
std::back_inserter(AllAtoms));
Tmp = C;
if (FVConstraint *FV = dyn_cast<FVConstraint>(C)) {
Tmp = FV->getInternalReturn();
}
// If this is a var atom?
if (Tmp->hasNtArr(CS.getVariables(), 0)) {
NtArrPtrs.insert(Tmp);
} else if (Tmp->hasArr(CS.getVariables(), 0)) {
ArrPtrs.insert(Tmp);
}
}
}
}
for (const auto &N : AllAtoms) {
ConstAtom *CA = CS.getAssignment(N);
switch (CA->getKind()) {
case Atom::A_Arr:
TotA += 1;
break;
case Atom::A_NTArr:
TotNt += 1;
break;
case Atom::A_Ptr:
TotP += 1;
break;
case Atom::A_Wild:
TotWi += 1;
break;
case Atom::A_Var:
case Atom::A_Const:
llvm_unreachable("bad constant in environment map");
}
}
O << "{\"AggregateStats\":[";
O << "{\""
<< "TotalStats"
<< "\":{";
O << "\"constraints\":" << AllAtoms.size() << ",";
O << "\"ptr\":" << TotP << ",";
O << "\"ntarr\":" << TotNt << ",";
O << "\"arr\":" << TotA << ",";
O << "\"wild\":" << TotWi;
O << "}},";
O << "{\"ArrBoundsStats\":";
ArrBInfo.printStats(O, ArrPtrs, true);
O << "},";
O << "{\"NtArrBoundsStats\":";
ArrBInfo.printStats(O, NtArrPtrs, true);
O << "},";
O << "{\"PerformanceStats\":";
PerfS.printPerformanceStats(O, true);
O << "}";
O << "]}";
}
// Print out statistics of constraint variables on a per-file basis.
void ProgramInfo::printStats(const std::set<std::string> &F, raw_ostream &O,
bool OnlySummary, bool JsonFormat) {
if (!OnlySummary && !JsonFormat) {
O << "Enable itype propagation:" << _3COpts.EnablePropThruIType << "\n";
O << "Sound handling of var args functions:" << _3COpts.HandleVARARGS
<< "\n";
}
std::map<std::string, std::tuple<int, int, int, int, int>> FilesToVars;
CVarSet InSrcCVars, Visited;
unsigned int TotC, TotP, TotNt, TotA, TotWi;
TotC = TotP = TotNt = TotA = TotWi = 0;
// First, build the map and perform the aggregation.
for (auto &I : Variables) {
std::string FileName = I.first.getFileName();
if (F.count(FileName) ||
FileName.find(_3COpts.BaseDir) != std::string::npos) {
int VarC = 0;
int PC = 0;
int NtaC = 0;
int AC = 0;
int WC = 0;
auto J = FilesToVars.find(FileName);
if (J != FilesToVars.end())
std::tie(VarC, PC, NtaC, AC, WC) = J->second;
ConstraintVariable *C = I.second;
if (C->isForValidDecl()) {
InSrcCVars.insert(C);
CAtoms FoundVars;
getVarsFromConstraint(C, FoundVars, Visited);
VarC += FoundVars.size();
for (const auto &N : FoundVars) {
ConstAtom *CA = CS.getAssignment(N);
switch (CA->getKind()) {
case Atom::A_Arr:
AC += 1;
break;
case Atom::A_NTArr:
NtaC += 1;
break;
case Atom::A_Ptr:
PC += 1;
break;
case Atom::A_Wild:
WC += 1;
break;
case Atom::A_Var:
case Atom::A_Const:
llvm_unreachable("bad constant in environment map");
}
}
}
FilesToVars[FileName] =
std::tuple<int, int, int, int, int>(VarC, PC, NtaC, AC, WC);
}
}
// Then, dump the map to output.
// if not only summary then dump everything.
if (JsonFormat) {
O << "{\"Stats\":{";
O << "\"ConstraintStats\":{";
}
if (!OnlySummary) {
if (JsonFormat) {
O << "\"Individual\":[";
} else {
O << "file|#constraints|#ptr|#ntarr|#arr|#wild\n";
}
}
bool AddComma = false;
for (const auto &I : FilesToVars) {
int V, P, Nt, A, W;
std::tie(V, P, Nt, A, W) = I.second;
TotC += V;
TotP += P;
TotNt += Nt;
TotA += A;
TotWi += W;
if (!OnlySummary) {
if (JsonFormat) {
if (AddComma) {
O << ",\n";
}
O << "{" << llvm::json::Value(I.first) << ":{";
O << "\"constraints\":" << V << ",";
O << "\"ptr\":" << P << ",";
O << "\"ntarr\":" << Nt << ",";
O << "\"arr\":" << A << ",";
O << "\"wild\":" << W;
O << "}}";
AddComma = true;
} else {
O << I.first << "|" << V << "|" << P << "|" << Nt << "|" << A << "|"
<< W;
O << "\n";
}
}
}
if (!OnlySummary && JsonFormat) {
O << "],";
}
if (!JsonFormat) {
O << "Summary\nTotalConstraints|TotalPtrs|TotalNTArr|TotalArr|TotalWild\n";
O << TotC << "|" << TotP << "|" << TotNt << "|" << TotA << "|" << TotWi
<< "\n";
} else {
O << "\"Summary\":{";
O << "\"TotalConstraints\":" << TotC << ",";
O << "\"TotalPtrs\":" << TotP << ",";
O << "\"TotalNTArr\":" << TotNt << ",";
O << "\"TotalArr\":" << TotA << ",";
O << "\"TotalWild\":" << TotWi;
O << "}},\n";
}
if (_3COpts.AllTypes) {
if (JsonFormat) {
O << "\"BoundsStats\":";
}
ArrBInfo.printStats(O, InSrcCVars, JsonFormat);
if (JsonFormat)
O << ",";
}
if (JsonFormat) {
O << "\"PerformanceStats\":";
}
PerfS.printPerformanceStats(O, JsonFormat);
if (JsonFormat) {
O << "}}";
}
}
bool ProgramInfo::link() {
// For every global symbol in all the global symbols that we have found
// go through and apply rules for whether they are functions or variables.
if (_3COpts.Verbose)
llvm::errs() << "Linking!\n";
// Equate the constraints for all global variables.
// This is needed for variables that are defined as extern.
for (const auto &V : GlobalVariableSymbols) {
const std::set<PVConstraint *> &C = V.second;
if (C.size() > 1) {
std::set<PVConstraint *>::iterator I = C.begin();
std::set<PVConstraint *>::iterator J = C.begin();
++J;
if (_3COpts.Verbose)
llvm::errs() << "Global variables:" << V.first << "\n";
while (J != C.end()) {
constrainConsVarGeq(*I, *J, CS, nullptr, Same_to_Same, true, this);
++I;
++J;
}
}
}
for (const auto &V : ExternGVars) {
// if a definition for this global variable has not been seen,
// constrain everything about it
if (!V.second) {
std::string VarName = V.first;
std::string Rsn =
"External global variable " + VarName + " has no definition";
const std::set<PVConstraint *> &C = GlobalVariableSymbols[VarName];
for (const auto &Var : C) {
// TODO: Is there an easy way to get a PSL to attach to the constraint?
Var->constrainToWild(CS, Rsn);
}
}
}
// For every global function that is an unresolved external, constrain
// its parameter types to be wild. Unless it has a bounds-safe annotation.
for (const auto &U : ExternalFunctionFVCons)
linkFunction(U.second);
// Repeat for static functions.
//
// Static functions that don't have a body will always cause a linking
// error during compilation. They may still be useful as code is developed,
// so we treat them as if they are external, and constrain parameters
// to wild as appropriate.
for (const auto &U : StaticFunctionFVCons)
for (const auto &V : U.second)
linkFunction(V.second);
return true;
}
void ProgramInfo::linkFunction(FunctionVariableConstraint *FV) {
// If there was a checked type on a variable in the input program, it
// should stay that way. Otherwise, we shouldn't be adding a checked type
// to an undefined function.
std::string Rsn = (FV->hasBody() ? "" : "Unchecked pointer in parameter or "
"return of undefined function " +
FV->getName());
// Handle the cases where itype parameters should not be treated as their
// unchecked type.
// TODO: Ditto re getting a PSL (in the case in which Rsn is non-empty and
// it is actually used).
FV->equateWithItype(*this, Rsn, nullptr);
// Used to apply constraints to parameters and returns for function without a
// body. In the default configuration, the function is fully constrained so
// that parameters and returns are considered unchecked. When 3C is run with
// --infer-types-for-undefs, only internal variables are constrained, allowing
// external variables to solve to checked types meaning the parameter will be
// rewritten to an itype.
auto LinkComponent = [this, Rsn](const FVComponentVariable *FVC) {
FVC->getInternal()->constrainToWild(CS, Rsn);
if (!_3COpts.InferTypesForUndefs &&
!FVC->getExternal()->srcHasItype() && !FVC->getExternal()->isGeneric())
FVC->getExternal()->constrainToWild(CS, Rsn);
};
if (!FV->hasBody()) {
LinkComponent(FV->getCombineReturn());
for (unsigned I = 0; I < FV->numParams(); I++)
LinkComponent(FV->getCombineParam(I));
}
}
// Populate Variables, VarDeclToStatement, RVariables, and DepthMap with
// AST data structures that correspond do the data stored in PDMap and
// ReversePDMap.
void ProgramInfo::enterCompilationUnit(ASTContext &Context) {
assert(Persisted);
// Get a set of all of the PersistentSourceLoc's we need to fill in.
std::set<PersistentSourceLoc> P;
//for (auto I : PersistentVariables)
// P.insert(I.first);
// Resolve the PersistentSourceLoc to one of Decl,Stmt,Type.
MappingVisitor V(P, Context);
TranslationUnitDecl *TUD = Context.getTranslationUnitDecl();
for (const auto &D : TUD->decls())
V.TraverseDecl(D);
Persisted = false;
return;
}
// Remove any references we maintain to AST data structure pointers.
// After this, the Variables, VarDeclToStatement, RVariables, and DepthMap
// should all be empty.
void ProgramInfo::exitCompilationUnit() {
assert(!Persisted);
Persisted = true;
return;
}
FunctionVariableConstraint *
ProgramInfo::insertNewFVConstraint(FunctionDecl *FD, FVConstraint *NewC,
ASTContext *C) {
std::string FuncName = FD->getNameAsString();
// Choose a storage location
// assume a global function, but change to a static if not
ExternalFunctionMapType *Map = &ExternalFunctionFVCons;
if (!FD->isGlobal()) {
// if the filename has not yet been seen, just insert and we're done
auto Psl = PersistentSourceLoc::mkPSL(FD, *C);
std::string FileName = Psl.getFileName();
if (StaticFunctionFVCons.find(FileName) == StaticFunctionFVCons.end()) {
StaticFunctionFVCons[FileName][FuncName] = NewC;
return NewC;
}
// store in static map
Map = &StaticFunctionFVCons[FileName];
}
// if the function has not yet been seen, just insert and we're done
if (Map->find(FuncName) == Map->end()) {
(*Map)[FuncName] = NewC;
return NewC;
}
// Resolve conflicts
auto *OldC = (*Map)[FuncName];
std::string ReasonFailed = "";
int OldCount = OldC->numParams();
int NewCount = NewC->numParams();
// merge short parameter lists into long ones
// Choose number of params, but favor definitions if available
if ((OldCount < NewCount) ||
(OldCount == NewCount && !OldC->hasBody() && NewC->hasBody())) {
NewC->mergeDeclaration(OldC, *this, ReasonFailed);
(*Map)[FuncName] = NewC;
} else {
OldC->mergeDeclaration(NewC, *this, ReasonFailed);
}
// If successful, we're done and can skip error reporting
if (ReasonFailed == "")
return (*Map)[FuncName];
// Error reporting
reportCustomDiagnostic(C->getDiagnostics(),
DiagnosticsEngine::Fatal,
"merging failed for %q0 due to %1",
FD->getLocation())
<< FD << ReasonFailed;
// A failed merge will provide poor data, but the diagnostic error report
// will cause the program to terminate after the variable adder step.
return (*Map)[FuncName];
}
// For each pointer type in the declaration of D, add a variable to the
// constraint system for that pointer type.
void ProgramInfo::addVariable(clang::DeclaratorDecl *D,
clang::ASTContext *AstContext) {
assert(!Persisted);
PersistentSourceLoc PLoc = PersistentSourceLoc::mkPSL(D, *AstContext);
assert(PLoc.valid());
// We only add a PVConstraint if Variables[PLoc] does not exist.
// Functions are exempt from this check because they need to be added to the
// Extern/Static function map even if they are inside a macro expansion.
if (Variables.find(PLoc) != Variables.end() && !isa<FunctionDecl>(D)) {
// Two variables can have the same source locations when they are
// declared inside the same macro expansion. The first instance of the
// source location will have been constrained to WILD, so it's safe to bail
// without doing anymore work.
if (!Rewriter::isRewritable(D->getLocation())) {
// If we're not in a macro, we should make the constraint variable WILD
// anyways. This happens if the name of the variable is a macro defined
// differently is different parts of the program.
std::string Rsn = "Duplicate source location. Possibly part of a macro.";
Variables[PLoc]->constrainToWild(CS, Rsn, &PLoc);
}
return;
}
ConstraintVariable *NewCV = nullptr;
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
// Function Decls have FVConstraints.
std::string FuncName = FD->getNameAsString();
FVConstraint *F = new FVConstraint(D, *this, *AstContext);
F->setValidDecl();
// Handling of PSL collision for functions is different since we need to
// consider the static and extern function maps.
if (Variables.find(PLoc) != Variables.end()) {
// Try to find a previous definition based on function name
if (!getFuncConstraint(FD, AstContext)) {
// No function with the same name exists. It's concerning that
// something already exists at this source location, but we add the
// function to the function map anyways. The function map indexes by
// function name, so there's no collision.
insertNewFVConstraint(FD, F, AstContext);
constrainWildIfMacro(F, FD->getLocation());
} else {
// A function with the same name exists in the same source location.
// This happens when a function is defined in a header file which is
// included in multiple translation units. getFuncConstraint returned
// non-null, so we know that the definition has been processed already,
// and there is no more work to do.
}
return;
}
// Store the FVConstraint in the global and Variables maps. It may be
// merged with others if this is a redeclaration, and the merged version
// is returned.
F = insertNewFVConstraint(FD, F, AstContext);
NewCV = F;
auto RetTy = FD->getReturnType();
unifyIfTypedef(RetTy, *AstContext, F->getExternalReturn(), Wild_to_Safe);
unifyIfTypedef(RetTy, *AstContext, F->getInternalReturn(), Safe_to_Wild);
ensureNtCorrect(RetTy, *AstContext, F->getExternalReturn());
ensureNtCorrect(RetTy, *AstContext, F->getInternalReturn());
// Add mappings from the parameters PLoc to the constraint variables for
// the parameters.
for (unsigned I = 0; I < FD->getNumParams(); I++) {
ParmVarDecl *PVD = FD->getParamDecl(I);
QualType ParamTy = PVD->getType();
PVConstraint *PVInternal = F->getInternalParam(I);
PVConstraint *PVExternal = F->getExternalParam(I);
unifyIfTypedef(ParamTy, *AstContext, PVExternal, Wild_to_Safe);
unifyIfTypedef(ParamTy, *AstContext, PVInternal, Safe_to_Wild);
ensureNtCorrect(ParamTy, *AstContext, PVInternal);
ensureNtCorrect(ParamTy, *AstContext, PVExternal);
PVInternal->setValidDecl();
PersistentSourceLoc PSL = PersistentSourceLoc::mkPSL(PVD, *AstContext);
// Constraint variable is stored on the parent function, so we need to
// constrain to WILD even if we don't end up storing this in the map.
constrainWildIfMacro(PVExternal, PVD->getLocation());
// If this is "main", constrain its argv parameter to a nested arr
if (_3COpts.AllTypes && FuncName == "main" && FD->isGlobal() && I == 1) {
PVInternal->constrainOuterTo(CS, CS.getArr());
PVInternal->constrainIdxTo(CS, CS.getNTArr(), 1);
}
// It is possible to have a param decl in a macro when the function is
// not.
if (Variables.find(PSL) != Variables.end())
continue;
Variables[PSL] = PVInternal;
}
} else if (VarDecl *VD = dyn_cast<VarDecl>(D)) {
assert(!isa<ParmVarDecl>(VD));
QualType QT = VD->getTypeSourceInfo()->getTypeLoc().getType();
if (QT->isPointerType() || QT->isArrayType()) {
PVConstraint *P = new PVConstraint(D, *this, *AstContext);
P->setValidDecl();
NewCV = P;
std::string VarName(VD->getName());
unifyIfTypedef(QT, *AstContext, P);
ensureNtCorrect(VD->getType(), *AstContext, P);
if (VD->hasGlobalStorage()) {
// If we see a definition for this global variable, indicate so in
// ExternGVars.
if (VD->hasDefinition() || VD->hasDefinition(*AstContext)) {
ExternGVars[VarName] = true;
}
// If we don't, check that we haven't seen one before before setting to
// false.
else if (!ExternGVars[VarName]) {
ExternGVars[VarName] = false;
}
GlobalVariableSymbols[VarName].insert(P);
}
}
} else if (FieldDecl *FlD = dyn_cast<FieldDecl>(D)) {
QualType QT = FlD->getTypeSourceInfo()->getTypeLoc().getType();
if (QT->isPointerType() || QT->isArrayType()) {
PVConstraint *P = new PVConstraint(D, *this, *AstContext);
unifyIfTypedef(QT, *AstContext, P);
NewCV = P;
NewCV->setValidDecl();
}
} else
llvm_unreachable("unknown decl type");
assert("We shouldn't be adding a null CV to Variables map." && NewCV);
if (!canWrite(PLoc.getFileName())) {
NewCV->equateWithItype(*this, UNWRITABLE_REASON, &PLoc);
NewCV->constrainToWild(CS, UNWRITABLE_REASON, &PLoc);
}
constrainWildIfMacro(NewCV, D->getLocation());
Variables[PLoc] = NewCV;
}
void ProgramInfo::ensureNtCorrect(const QualType &QT, const ASTContext &C,
PointerVariableConstraint *PV) {
if (_3COpts.AllTypes && !canBeNtArray(QT)) {
PV->constrainOuterTo(CS, CS.getArr(), true, true);
}
}
void ProgramInfo::unifyIfTypedef(const QualType &QT, ASTContext &Context,
PVConstraint *P, ConsAction CA) {
if (const auto *TDT = dyn_cast<TypedefType>(QT.getTypePtr())) {
auto *TDecl = TDT->getDecl();
auto PSL = PersistentSourceLoc::mkPSL(TDecl, Context);
auto O = lookupTypedef(PSL);
if (O.hasValue()) {
auto *Bounds = &O.getValue();
P->setTypedef(Bounds, TDecl->getNameAsString());
constrainConsVarGeq(P, Bounds, CS, &PSL, CA, false, this);
}
}
}
ProgramInfo::IDAndTranslationUnit ProgramInfo::getExprKey(Expr *E,
ASTContext *C) const {
return std::make_pair(getStmtIdWorkaround(E, *C),
TranslationUnitIdxMap.at(C));
}
bool ProgramInfo::hasPersistentConstraints(Expr *E, ASTContext *C) const {
return ExprConstraintVars.find(getExprKey(E, C)) != ExprConstraintVars.end();
}
const CVarSet &ProgramInfo::getPersistentConstraintsSet(clang::Expr *E,
ASTContext *C) const {
return getPersistentConstraints(E, C).first;
}
void ProgramInfo::storePersistentConstraints(clang::Expr *E,
const CVarSet &Vars,
ASTContext *C) {
BKeySet EmptySet;
EmptySet.clear();
storePersistentConstraints(E, std::make_pair(Vars, EmptySet), C);
}
// Get the pair of set of constraint variables and set of bounds key
// for an expression that will persist between the constraint generation
// and rewriting pass. If the expression already has a set of persistent
// constraints, this set is returned. Otherwise, the set provided in the
// arguments is stored persistent and returned. This is required for
// correct cast insertion.
const CSetBkeyPair &ProgramInfo::getPersistentConstraints(Expr *E,
ASTContext *C) const {
assert(hasPersistentConstraints(E, C) &&
"Persistent constraints not present.");
return ExprConstraintVars.at(getExprKey(E, C));
}
void ProgramInfo::storePersistentConstraints(Expr *E, const CSetBkeyPair &Vars,
ASTContext *C) {
assert(!hasPersistentConstraints(E, C) &&
"Persistent constraints already present.");
auto PSL = PersistentSourceLoc::mkPSL(E, *C);
if (PSL.valid() && !canWrite(PSL.getFileName()))
for (ConstraintVariable *CVar : Vars.first)
CVar->constrainToWild(CS, UNWRITABLE_REASON, &PSL);
IDAndTranslationUnit Key = getExprKey(E, C);
ExprConstraintVars[Key] = Vars;
ExprLocations[Key] = PSL;
}
void ProgramInfo::removePersistentConstraints(Expr *E, ASTContext *C) {
assert(hasPersistentConstraints(E, C) &&
"Persistent constraints not present.");
IDAndTranslationUnit Key = getExprKey(E, C);
// Save VarAtom locations so they can be used to assign source locations to
// root causes.
for (auto *CV : ExprConstraintVars[Key].first)
if (auto *PVC = dyn_cast<PointerVariableConstraint>(CV))
for (Atom *A : PVC->getCvars())
if (auto *VA = dyn_cast<VarAtom>(A))
DeletedAtomLocations[VA->getLoc()] = ExprLocations[Key];
ExprConstraintVars.erase(Key);
ExprLocations.erase(Key);
}
// The Rewriter won't let us re-write things that are in macros. So, we
// should check to see if what we just added was defined within a macro.
// If it was, we should constrain it to top. This is sad. Hopefully,
// someday, the Rewriter will become less lame and let us re-write stuff
// in macros.
void ProgramInfo::constrainWildIfMacro(ConstraintVariable *CV,
SourceLocation Location,
PersistentSourceLoc *PSL) {
std::string Rsn = "Pointer in Macro declaration.";
if (!Rewriter::isRewritable(Location))
CV->constrainToWild(CS, Rsn, PSL);
}
//std::string ProgramInfo::getUniqueDeclKey(Decl *D, ASTContext *C) {
// auto Psl = PersistentSourceLoc::mkPSL(D, *C);
// std::string FileName = Psl.getFileName() + ":" +
// std::to_string(Psl.getLineNo());
// std::string Dname = D->getDeclKindName();
// if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
// Dname = FD->getNameAsString();
// }
// std::string DeclKey = FileName + ":" + Dname;
// return DeclKey;
//}
//
//std::string ProgramInfo::getUniqueFuncKey(FunctionDecl *D,
// ASTContext *C) {
// // Get unique key for a function: which is function name,
// // file and line number.
// if (FunctionDecl *FuncDef = getDefinition(D)) {
// D = FuncDef;
// }
// return getUniqueDeclKey(D, C);
//}
FVConstraint *ProgramInfo::getFuncConstraint(FunctionDecl *D,
ASTContext *C) const {
std::string FuncName = D->getNameAsString();
if (D->isGlobal()) {
// Is this a global (externally visible) function?
return getExtFuncDefnConstraint(FuncName);
}
// Static function.
auto Psl = PersistentSourceLoc::mkPSL(D, *C);
std::string FileName = Psl.getFileName();
return getStaticFuncConstraint(FuncName, FileName);
}
FVConstraint *ProgramInfo::getFuncFVConstraint(FunctionDecl *FD,
ASTContext *C) {
std::string FuncName = FD->getNameAsString();
FVConstraint *FunFVar = nullptr;
if (FD->isGlobal()) {
FunFVar = getExtFuncDefnConstraint(FuncName);
// FIXME: We are being asked to access a function never declared; best
// action?
if (FunFVar == nullptr) {
// make one
FVConstraint *F = new FVConstraint(FD, *this, *C);
assert(!F->hasBody());
assert("FunFVar can only be null if FuncName is not in the map!" &&
ExternalFunctionFVCons.find(FuncName) ==
ExternalFunctionFVCons.end());
ExternalFunctionFVCons[FuncName] = F;
FunFVar = ExternalFunctionFVCons[FuncName];
}
} else {
auto Psl = PersistentSourceLoc::mkPSL(FD, *C);
std::string FileName = Psl.getFileName();
FunFVar = getStaticFuncConstraint(FuncName, FileName);
}
return FunFVar;
}
// Given a decl, return the variables for the constraints of the Decl.
// Returns null if a constraint variable could not be found for the decl.
CVarOption ProgramInfo::getVariable(clang::Decl *D, clang::ASTContext *C) {
assert(!Persisted);
if (ParmVarDecl *PD = dyn_cast<ParmVarDecl>(D)) {
DeclContext *DC = PD->getParentFunctionOrMethod();
// This can fail for extern definitions
if (!DC)
return CVarOption();
FunctionDecl *FD = dyn_cast<FunctionDecl>(DC);
// Get the parameter index with in the function.
unsigned int PIdx = getParameterIndex(PD, FD);
// Get corresponding FVConstraint vars.
FVConstraint *FunFVar = getFuncFVConstraint(FD, C);
assert(FunFVar != nullptr && "Unable to find function constraints.");
return CVarOption(*FunFVar->getInternalParam(PIdx));
}
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
FVConstraint *FunFVar = getFuncFVConstraint(FD, C);
if (FunFVar == nullptr) {
llvm::errs() << "No fun constraints for " << FD->getName() << "?!\n";
}
return CVarOption(*FunFVar);
}
/* neither function nor function parameter */
auto I = Variables.find(PersistentSourceLoc::mkPSL(D, *C));
if (I != Variables.end())
return CVarOption(*I->second);
return CVarOption();
}
FVConstraint *
ProgramInfo::getExtFuncDefnConstraint(std::string FuncName) const {
if (ExternalFunctionFVCons.find(FuncName) != ExternalFunctionFVCons.end()) {
return ExternalFunctionFVCons.at(FuncName);
}
return nullptr;
}
FVConstraint *ProgramInfo::getStaticFuncConstraint(std::string FuncName,
std::string FileName) const {
if (StaticFunctionFVCons.find(FileName) != StaticFunctionFVCons.end() &&
StaticFunctionFVCons.at(FileName).find(FuncName) !=
StaticFunctionFVCons.at(FileName).end()) {
return StaticFunctionFVCons.at(FileName).at(FuncName);
}
return nullptr;
}
// From the given constraint graph, this method computes the interim constraint
// state that contains constraint vars which are directly assigned WILD and
// other constraint vars that have been determined to be WILD because they
// depend on other constraint vars that are directly assigned WILD.
bool ProgramInfo::computeInterimConstraintState(
const std::set<std::string> &FilePaths) {
// Get all the valid vars of interest i.e., all the Vars that are present
// in one of the files being compiled.
CAtoms ValidVarsVec;
std::set<Atom *> AllValidVars;
CVarSet Visited;
CAtoms Tmp;
for (const auto &I : Variables) {
std::string FileName = I.first.getFileName();
ConstraintVariable *C = I.second;
if (C->isForValidDecl()) {
Tmp.clear();
getVarsFromConstraint(C, Tmp, Visited);
AllValidVars.insert(Tmp.begin(), Tmp.end());
if (canWrite(FileName))
ValidVarsVec.insert(ValidVarsVec.begin(), Tmp.begin(), Tmp.end());
}
}
// Make that into set, for efficiency.
std::set<Atom *> ValidVarsS;
ValidVarsS.insert(ValidVarsVec.begin(), ValidVarsVec.end());
auto GetLocOrZero = [](const Atom *Val) {
if (const auto *VA = dyn_cast<VarAtom>(Val))
return VA->getLoc();
return (ConstraintKey)0;
};
CVars ValidVarsKey;
std::transform(ValidVarsS.begin(), ValidVarsS.end(),
std::inserter(ValidVarsKey, ValidVarsKey.end()), GetLocOrZero);
CVars AllValidVarsKey;
std::transform(AllValidVars.begin(), AllValidVars.end(),
std::inserter(AllValidVarsKey, AllValidVarsKey.end()),
GetLocOrZero);
CState.clear();
std::set<Atom *> DirectWildVarAtoms;
CS.getChkCG().getSuccessors(CS.getWild(), DirectWildVarAtoms);
CVars TmpCGrp;
CVars OnlyIndirect;
for (auto *A : DirectWildVarAtoms) {
auto *VA = dyn_cast<VarAtom>(A);
if (VA == nullptr)
continue;
TmpCGrp.clear();
OnlyIndirect.clear();
auto BFSVisitor = [&](Atom *SearchAtom) {
auto *SearchVA = dyn_cast<VarAtom>(SearchAtom);
if (SearchVA && AllValidVars.find(SearchVA) != AllValidVars.end()) {
CState.RCMap[SearchVA->getLoc()].insert(VA->getLoc());
if (ValidVarsKey.find(SearchVA->getLoc()) != ValidVarsKey.end())
TmpCGrp.insert(SearchVA->getLoc());
if (DirectWildVarAtoms.find(SearchVA) == DirectWildVarAtoms.end()) {
OnlyIndirect.insert(SearchVA->getLoc());
}
}
};
CS.getChkCG().visitBreadthFirst(VA, BFSVisitor);
CState.TotalNonDirectWildAtoms.insert(OnlyIndirect.begin(),
OnlyIndirect.end());
// Should we consider only pointers which with in the source files or
// external pointers that affected pointers within the source files.
CState.AllWildAtoms.insert(VA->getLoc());
CVars &CGrp = CState.SrcWMap[VA->getLoc()];
CGrp.insert(TmpCGrp.begin(), TmpCGrp.end());
}
findIntersection(CState.AllWildAtoms, ValidVarsKey, CState.InSrcWildAtoms);
findIntersection(CState.TotalNonDirectWildAtoms, ValidVarsKey,
CState.InSrcNonDirectWildAtoms);
// The ConstraintVariable for a variable normally appears in Variables for the
// definition, but it may also be reused directly in ExprConstraintVars for a
// reference to that variable. We want to give priority to the PSL of the
// definition, not the reference. We currently achieve this by processing