Skip to content

Commit ef018cb

Browse files
committed
[BuildLibCalls] Add noundef to standard I/O functions
This patch adds noundef to return value and arguments of standard I/O functions. With this patch, passing undef or poison to the functions becomes undefined behavior in LLVM IR. Since undef/poison is lowered from operations having UB in C/C++, passing undef to them was already UB in source. With this patch, the functions cannot return undef or poison anymore as well. According to C17 standard, ungetc/ungetwc/fgetpos/ftell can generate unspecified value; 3.19.3 says unspecified value is a valid value of the relevant type, and using unspecified value is unspecified behavior, which is not UB, so it cannot be undef (using undef is UB when e.g. it is used at branch condition). — The value of the file position indicator after a successful call to the ungetc function for a text stream, or the ungetwc function for any stream, until all pushed-back characters are read or discarded (7.21.7.10, 7.29.3.10). — The details of the value stored by the fgetpos function (7.21.9.1). — The details of the value returned by the ftell function for a text stream (7.21.9.4). In the long run, most of the functions listed in BuildLibCalls should have noundefs; to remove redundant diffs which will anyway disappear in the future, I added noundef to a few more non-I/O functions as well. Reviewed By: jdoerfert Differential Revision: https://reviews.llvm.org/D85345
1 parent 4b211b9 commit ef018cb

File tree

4 files changed

+144
-63
lines changed

4 files changed

+144
-63
lines changed

clang/test/CodeGen/PR3589-freestanding-libcalls.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm %s -o - | grep 'declare i32 @printf' | count 1
2-
// RUN: %clang_cc1 -triple i386-unknown-unknown -O2 -emit-llvm %s -o - | grep 'declare i32 @puts' | count 1
3-
// RUN: %clang_cc1 -triple i386-unknown-unknown -ffreestanding -O2 -emit-llvm %s -o - | not grep 'declare i32 @puts'
2+
// RUN: %clang_cc1 -triple i386-unknown-unknown -O2 -emit-llvm %s -o - | grep 'declare noundef i32 @puts' | count 1
3+
// RUN: %clang_cc1 -triple i386-unknown-unknown -ffreestanding -O2 -emit-llvm %s -o - | not grep 'declare noundef i32 @puts'
44

55
int printf(const char *, ...);
66

llvm/lib/Transforms/Utils/BuildLibCalls.cpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ STATISTIC(NumNoUnwind, "Number of functions inferred as nounwind");
3737
STATISTIC(NumNoCapture, "Number of arguments inferred as nocapture");
3838
STATISTIC(NumReadOnlyArg, "Number of arguments inferred as readonly");
3939
STATISTIC(NumNoAlias, "Number of function returns inferred as noalias");
40+
STATISTIC(NumNoUndef, "Number of function returns inferred as noundef returns");
4041
STATISTIC(NumNonNull, "Number of function returns inferred as nonnull returns");
4142
STATISTIC(NumReturnedArg, "Number of arguments inferred as returned");
4243

@@ -104,6 +105,24 @@ static bool setOnlyReadsMemory(Function &F, unsigned ArgNo) {
104105
return true;
105106
}
106107

108+
static bool setRetAndArgsNoUndef(Function &F) {
109+
bool Changed = false;
110+
if (!F.getReturnType()->isVoidTy() &&
111+
!F.hasAttribute(AttributeList::ReturnIndex, Attribute::NoUndef)) {
112+
F.addAttribute(AttributeList::ReturnIndex, Attribute::NoUndef);
113+
++NumNoUndef;
114+
Changed = true;
115+
}
116+
for (unsigned ArgNo = 0; ArgNo < F.arg_size(); ++ArgNo) {
117+
if (!F.hasParamAttribute(ArgNo, Attribute::NoUndef)) {
118+
F.addParamAttr(ArgNo, Attribute::NoUndef);
119+
++NumNoUndef;
120+
Changed = true;
121+
}
122+
}
123+
return Changed;
124+
}
125+
107126
static bool setRetNonNull(Function &F) {
108127
assert(F.getReturnType()->isPointerTy() &&
109128
"nonnull applies only to pointers");
@@ -227,6 +246,7 @@ bool llvm::inferLibFuncAttributes(Function &F, const TargetLibraryInfo &TLI) {
227246
Changed |= setOnlyReadsMemory(F, 1);
228247
return Changed;
229248
case LibFunc_scanf:
249+
Changed |= setRetAndArgsNoUndef(F);
230250
Changed |= setDoesNotThrow(F);
231251
Changed |= setDoesNotCapture(F, 0);
232252
Changed |= setOnlyReadsMemory(F, 0);
@@ -251,20 +271,23 @@ bool llvm::inferLibFuncAttributes(Function &F, const TargetLibraryInfo &TLI) {
251271
Changed |= setOnlyReadsMemory(F, 0);
252272
return Changed;
253273
case LibFunc_sscanf:
274+
Changed |= setRetAndArgsNoUndef(F);
254275
Changed |= setDoesNotThrow(F);
255276
Changed |= setDoesNotCapture(F, 0);
256277
Changed |= setDoesNotCapture(F, 1);
257278
Changed |= setOnlyReadsMemory(F, 0);
258279
Changed |= setOnlyReadsMemory(F, 1);
259280
return Changed;
260281
case LibFunc_sprintf:
282+
Changed |= setRetAndArgsNoUndef(F);
261283
Changed |= setDoesNotThrow(F);
262284
Changed |= setDoesNotCapture(F, 0);
263285
Changed |= setDoesNotAlias(F, 0);
264286
Changed |= setDoesNotCapture(F, 1);
265287
Changed |= setOnlyReadsMemory(F, 1);
266288
return Changed;
267289
case LibFunc_snprintf:
290+
Changed |= setRetAndArgsNoUndef(F);
268291
Changed |= setDoesNotThrow(F);
269292
Changed |= setDoesNotCapture(F, 0);
270293
Changed |= setDoesNotAlias(F, 0);
@@ -347,9 +370,11 @@ bool llvm::inferLibFuncAttributes(Function &F, const TargetLibraryInfo &TLI) {
347370
return Changed;
348371
case LibFunc_read:
349372
// May throw; "read" is a valid pthread cancellation point.
373+
Changed |= setRetAndArgsNoUndef(F);
350374
Changed |= setDoesNotCapture(F, 1);
351375
return Changed;
352376
case LibFunc_rewind:
377+
Changed |= setRetAndArgsNoUndef(F);
353378
Changed |= setDoesNotThrow(F);
354379
Changed |= setDoesNotCapture(F, 0);
355380
return Changed;
@@ -375,6 +400,7 @@ bool llvm::inferLibFuncAttributes(Function &F, const TargetLibraryInfo &TLI) {
375400
return Changed;
376401
case LibFunc_write:
377402
// May throw; "write" is a valid pthread cancellation point.
403+
Changed |= setRetAndArgsNoUndef(F);
378404
Changed |= setDoesNotCapture(F, 1);
379405
Changed |= setOnlyReadsMemory(F, 1);
380406
return Changed;
@@ -428,6 +454,7 @@ bool llvm::inferLibFuncAttributes(Function &F, const TargetLibraryInfo &TLI) {
428454
Changed |= setOnlyReadsMemory(F, 0);
429455
return Changed;
430456
case LibFunc_fopen:
457+
Changed |= setRetAndArgsNoUndef(F);
431458
Changed |= setDoesNotThrow(F);
432459
Changed |= setRetDoesNotAlias(F);
433460
Changed |= setDoesNotCapture(F, 0);
@@ -436,13 +463,21 @@ bool llvm::inferLibFuncAttributes(Function &F, const TargetLibraryInfo &TLI) {
436463
Changed |= setOnlyReadsMemory(F, 1);
437464
return Changed;
438465
case LibFunc_fdopen:
466+
Changed |= setRetAndArgsNoUndef(F);
439467
Changed |= setDoesNotThrow(F);
440468
Changed |= setRetDoesNotAlias(F);
441469
Changed |= setDoesNotCapture(F, 1);
442470
Changed |= setOnlyReadsMemory(F, 1);
443471
return Changed;
444472
case LibFunc_feof:
473+
Changed |= setRetAndArgsNoUndef(F);
474+
Changed |= setDoesNotThrow(F);
475+
Changed |= setDoesNotCapture(F, 0);
476+
return Changed;
445477
case LibFunc_free:
478+
Changed |= setDoesNotThrow(F);
479+
Changed |= setDoesNotCapture(F, 0);
480+
return Changed;
446481
case LibFunc_fseek:
447482
case LibFunc_ftell:
448483
case LibFunc_fgetc:
@@ -456,64 +491,88 @@ bool llvm::inferLibFuncAttributes(Function &F, const TargetLibraryInfo &TLI) {
456491
case LibFunc_flockfile:
457492
case LibFunc_funlockfile:
458493
case LibFunc_ftrylockfile:
494+
Changed |= setRetAndArgsNoUndef(F);
459495
Changed |= setDoesNotThrow(F);
460496
Changed |= setDoesNotCapture(F, 0);
461497
return Changed;
462498
case LibFunc_ferror:
499+
Changed |= setRetAndArgsNoUndef(F);
463500
Changed |= setDoesNotThrow(F);
464501
Changed |= setDoesNotCapture(F, 0);
465502
Changed |= setOnlyReadsMemory(F);
466503
return Changed;
467504
case LibFunc_fputc:
468505
case LibFunc_fputc_unlocked:
469506
case LibFunc_fstat:
507+
Changed |= setRetAndArgsNoUndef(F);
508+
Changed |= setDoesNotThrow(F);
509+
Changed |= setDoesNotCapture(F, 1);
510+
return Changed;
470511
case LibFunc_frexp:
471512
case LibFunc_frexpf:
472513
case LibFunc_frexpl:
514+
Changed |= setDoesNotThrow(F);
515+
Changed |= setDoesNotCapture(F, 1);
516+
return Changed;
473517
case LibFunc_fstatvfs:
518+
Changed |= setRetAndArgsNoUndef(F);
474519
Changed |= setDoesNotThrow(F);
475520
Changed |= setDoesNotCapture(F, 1);
476521
return Changed;
477522
case LibFunc_fgets:
478523
case LibFunc_fgets_unlocked:
524+
Changed |= setRetAndArgsNoUndef(F);
479525
Changed |= setDoesNotThrow(F);
480526
Changed |= setDoesNotCapture(F, 2);
481527
return Changed;
482528
case LibFunc_fread:
483529
case LibFunc_fread_unlocked:
530+
Changed |= setRetAndArgsNoUndef(F);
484531
Changed |= setDoesNotThrow(F);
485532
Changed |= setDoesNotCapture(F, 0);
486533
Changed |= setDoesNotCapture(F, 3);
487534
return Changed;
488535
case LibFunc_fwrite:
489536
case LibFunc_fwrite_unlocked:
537+
Changed |= setRetAndArgsNoUndef(F);
490538
Changed |= setDoesNotThrow(F);
491539
Changed |= setDoesNotCapture(F, 0);
492540
Changed |= setDoesNotCapture(F, 3);
493541
// FIXME: readonly #1?
494542
return Changed;
495543
case LibFunc_fputs:
496544
case LibFunc_fputs_unlocked:
545+
Changed |= setRetAndArgsNoUndef(F);
497546
Changed |= setDoesNotThrow(F);
498547
Changed |= setDoesNotCapture(F, 0);
499548
Changed |= setDoesNotCapture(F, 1);
500549
Changed |= setOnlyReadsMemory(F, 0);
501550
return Changed;
502551
case LibFunc_fscanf:
503552
case LibFunc_fprintf:
553+
Changed |= setRetAndArgsNoUndef(F);
504554
Changed |= setDoesNotThrow(F);
505555
Changed |= setDoesNotCapture(F, 0);
506556
Changed |= setDoesNotCapture(F, 1);
507557
Changed |= setOnlyReadsMemory(F, 1);
508558
return Changed;
509559
case LibFunc_fgetpos:
560+
Changed |= setRetAndArgsNoUndef(F);
510561
Changed |= setDoesNotThrow(F);
511562
Changed |= setDoesNotCapture(F, 0);
512563
Changed |= setDoesNotCapture(F, 1);
513564
return Changed;
514565
case LibFunc_getc:
566+
Changed |= setRetAndArgsNoUndef(F);
567+
Changed |= setDoesNotThrow(F);
568+
Changed |= setDoesNotCapture(F, 0);
569+
return Changed;
515570
case LibFunc_getlogin_r:
571+
Changed |= setDoesNotThrow(F);
572+
Changed |= setDoesNotCapture(F, 0);
573+
return Changed;
516574
case LibFunc_getc_unlocked:
575+
Changed |= setRetAndArgsNoUndef(F);
517576
Changed |= setDoesNotThrow(F);
518577
Changed |= setDoesNotCapture(F, 0);
519578
return Changed;
@@ -525,6 +584,7 @@ bool llvm::inferLibFuncAttributes(Function &F, const TargetLibraryInfo &TLI) {
525584
case LibFunc_gets:
526585
case LibFunc_getchar:
527586
case LibFunc_getchar_unlocked:
587+
Changed |= setRetAndArgsNoUndef(F);
528588
Changed |= setDoesNotThrow(F);
529589
return Changed;
530590
case LibFunc_getitimer:
@@ -537,6 +597,7 @@ bool llvm::inferLibFuncAttributes(Function &F, const TargetLibraryInfo &TLI) {
537597
Changed |= setOnlyReadsMemory(F, 0);
538598
return Changed;
539599
case LibFunc_ungetc:
600+
Changed |= setRetAndArgsNoUndef(F);
540601
Changed |= setDoesNotThrow(F);
541602
Changed |= setDoesNotCapture(F, 1);
542603
return Changed;
@@ -564,27 +625,32 @@ bool llvm::inferLibFuncAttributes(Function &F, const TargetLibraryInfo &TLI) {
564625
return Changed;
565626
case LibFunc_putc:
566627
case LibFunc_putc_unlocked:
628+
Changed |= setRetAndArgsNoUndef(F);
567629
Changed |= setDoesNotThrow(F);
568630
Changed |= setDoesNotCapture(F, 1);
569631
return Changed;
570632
case LibFunc_puts:
571633
case LibFunc_printf:
572634
case LibFunc_perror:
635+
Changed |= setRetAndArgsNoUndef(F);
573636
Changed |= setDoesNotThrow(F);
574637
Changed |= setDoesNotCapture(F, 0);
575638
Changed |= setOnlyReadsMemory(F, 0);
576639
return Changed;
577640
case LibFunc_pread:
578641
// May throw; "pread" is a valid pthread cancellation point.
642+
Changed |= setRetAndArgsNoUndef(F);
579643
Changed |= setDoesNotCapture(F, 1);
580644
return Changed;
581645
case LibFunc_pwrite:
582646
// May throw; "pwrite" is a valid pthread cancellation point.
647+
Changed |= setRetAndArgsNoUndef(F);
583648
Changed |= setDoesNotCapture(F, 1);
584649
Changed |= setOnlyReadsMemory(F, 1);
585650
return Changed;
586651
case LibFunc_putchar:
587652
case LibFunc_putchar_unlocked:
653+
Changed |= setRetAndArgsNoUndef(F);
588654
Changed |= setDoesNotThrow(F);
589655
return Changed;
590656
case LibFunc_popen:
@@ -600,18 +666,21 @@ bool llvm::inferLibFuncAttributes(Function &F, const TargetLibraryInfo &TLI) {
600666
Changed |= setDoesNotCapture(F, 0);
601667
return Changed;
602668
case LibFunc_vscanf:
669+
Changed |= setRetAndArgsNoUndef(F);
603670
Changed |= setDoesNotThrow(F);
604671
Changed |= setDoesNotCapture(F, 0);
605672
Changed |= setOnlyReadsMemory(F, 0);
606673
return Changed;
607674
case LibFunc_vsscanf:
675+
Changed |= setRetAndArgsNoUndef(F);
608676
Changed |= setDoesNotThrow(F);
609677
Changed |= setDoesNotCapture(F, 0);
610678
Changed |= setDoesNotCapture(F, 1);
611679
Changed |= setOnlyReadsMemory(F, 0);
612680
Changed |= setOnlyReadsMemory(F, 1);
613681
return Changed;
614682
case LibFunc_vfscanf:
683+
Changed |= setRetAndArgsNoUndef(F);
615684
Changed |= setDoesNotThrow(F);
616685
Changed |= setDoesNotCapture(F, 0);
617686
Changed |= setDoesNotCapture(F, 1);
@@ -622,25 +691,29 @@ bool llvm::inferLibFuncAttributes(Function &F, const TargetLibraryInfo &TLI) {
622691
Changed |= setRetDoesNotAlias(F);
623692
return Changed;
624693
case LibFunc_vprintf:
694+
Changed |= setRetAndArgsNoUndef(F);
625695
Changed |= setDoesNotThrow(F);
626696
Changed |= setDoesNotCapture(F, 0);
627697
Changed |= setOnlyReadsMemory(F, 0);
628698
return Changed;
629699
case LibFunc_vfprintf:
630700
case LibFunc_vsprintf:
701+
Changed |= setRetAndArgsNoUndef(F);
631702
Changed |= setDoesNotThrow(F);
632703
Changed |= setDoesNotCapture(F, 0);
633704
Changed |= setDoesNotCapture(F, 1);
634705
Changed |= setOnlyReadsMemory(F, 1);
635706
return Changed;
636707
case LibFunc_vsnprintf:
708+
Changed |= setRetAndArgsNoUndef(F);
637709
Changed |= setDoesNotThrow(F);
638710
Changed |= setDoesNotCapture(F, 0);
639711
Changed |= setDoesNotCapture(F, 2);
640712
Changed |= setOnlyReadsMemory(F, 2);
641713
return Changed;
642714
case LibFunc_open:
643715
// May throw; "open" is a valid pthread cancellation point.
716+
Changed |= setRetAndArgsNoUndef(F);
644717
Changed |= setDoesNotCapture(F, 0);
645718
Changed |= setOnlyReadsMemory(F, 0);
646719
return Changed;
@@ -693,14 +766,17 @@ bool llvm::inferLibFuncAttributes(Function &F, const TargetLibraryInfo &TLI) {
693766
Changed |= setOnlyReadsMemory(F, 1);
694767
return Changed;
695768
case LibFunc_under_IO_getc:
769+
Changed |= setRetAndArgsNoUndef(F);
696770
Changed |= setDoesNotThrow(F);
697771
Changed |= setDoesNotCapture(F, 0);
698772
return Changed;
699773
case LibFunc_under_IO_putc:
774+
Changed |= setRetAndArgsNoUndef(F);
700775
Changed |= setDoesNotThrow(F);
701776
Changed |= setDoesNotCapture(F, 1);
702777
return Changed;
703778
case LibFunc_dunder_isoc99_scanf:
779+
Changed |= setRetAndArgsNoUndef(F);
704780
Changed |= setDoesNotThrow(F);
705781
Changed |= setDoesNotCapture(F, 0);
706782
Changed |= setOnlyReadsMemory(F, 0);
@@ -714,13 +790,15 @@ bool llvm::inferLibFuncAttributes(Function &F, const TargetLibraryInfo &TLI) {
714790
Changed |= setOnlyReadsMemory(F, 0);
715791
return Changed;
716792
case LibFunc_dunder_isoc99_sscanf:
793+
Changed |= setRetAndArgsNoUndef(F);
717794
Changed |= setDoesNotThrow(F);
718795
Changed |= setDoesNotCapture(F, 0);
719796
Changed |= setDoesNotCapture(F, 1);
720797
Changed |= setOnlyReadsMemory(F, 0);
721798
Changed |= setOnlyReadsMemory(F, 1);
722799
return Changed;
723800
case LibFunc_fopen64:
801+
Changed |= setRetAndArgsNoUndef(F);
724802
Changed |= setDoesNotThrow(F);
725803
Changed |= setRetDoesNotAlias(F);
726804
Changed |= setDoesNotCapture(F, 0);
@@ -730,6 +808,7 @@ bool llvm::inferLibFuncAttributes(Function &F, const TargetLibraryInfo &TLI) {
730808
return Changed;
731809
case LibFunc_fseeko64:
732810
case LibFunc_ftello64:
811+
Changed |= setRetAndArgsNoUndef(F);
733812
Changed |= setDoesNotThrow(F);
734813
Changed |= setDoesNotCapture(F, 0);
735814
return Changed;
@@ -739,11 +818,13 @@ bool llvm::inferLibFuncAttributes(Function &F, const TargetLibraryInfo &TLI) {
739818
return Changed;
740819
case LibFunc_fstat64:
741820
case LibFunc_fstatvfs64:
821+
Changed |= setRetAndArgsNoUndef(F);
742822
Changed |= setDoesNotThrow(F);
743823
Changed |= setDoesNotCapture(F, 1);
744824
return Changed;
745825
case LibFunc_open64:
746826
// May throw; "open" is a valid pthread cancellation point.
827+
Changed |= setRetAndArgsNoUndef(F);
747828
Changed |= setDoesNotCapture(F, 0);
748829
Changed |= setOnlyReadsMemory(F, 0);
749830
return Changed;

llvm/test/CodeGen/X86/no-plt-libcalls.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ define void @printf_call() {
1717
}
1818

1919
; CHECK: Function Attrs: nofree nounwind nonlazybind
20-
; CHECK-NEXT: declare i32 @puts(i8* nocapture readonly)
20+
; CHECK-NEXT: declare noundef i32 @puts(i8* nocapture noundef readonly)
2121

2222
!llvm.module.flags = !{!0}
2323
!0 = !{i32 7, !"RtLibUseGOT", i32 1}

0 commit comments

Comments
 (0)