@@ -96,20 +96,24 @@ impl ArgAttribute {
96
96
97
97
/// A compact representation of LLVM attributes (at least those relevant for this module)
98
98
/// that can be manipulated without interacting with LLVM's Attribute machinery.
99
- #[ derive( Copy , Clone , Debug , Default ) ]
99
+ #[ derive( Copy , Clone , Debug ) ]
100
100
pub struct ArgAttributes {
101
101
regular : ArgAttribute ,
102
- dereferenceable_bytes : u64 ,
102
+ pointee_size : Size ,
103
+ pointee_align : Option < Align >
103
104
}
104
105
105
106
impl ArgAttributes {
106
- pub fn set ( & mut self , attr : ArgAttribute ) -> & mut Self {
107
- self . regular = self . regular | attr;
108
- self
107
+ fn new ( ) -> Self {
108
+ ArgAttributes {
109
+ regular : ArgAttribute :: default ( ) ,
110
+ pointee_size : Size :: from_bytes ( 0 ) ,
111
+ pointee_align : None ,
112
+ }
109
113
}
110
114
111
- pub fn set_dereferenceable ( & mut self , size : Size ) -> & mut Self {
112
- self . dereferenceable_bytes = size . bytes ( ) ;
115
+ pub fn set ( & mut self , attr : ArgAttribute ) -> & mut Self {
116
+ self . regular = self . regular | attr ;
113
117
self
114
118
}
115
119
@@ -118,24 +122,52 @@ impl ArgAttributes {
118
122
}
119
123
120
124
pub fn apply_llfn ( & self , idx : AttributePlace , llfn : ValueRef ) {
125
+ let mut regular = self . regular ;
121
126
unsafe {
122
- self . regular . for_each_kind ( |attr| attr. apply_llfn ( idx, llfn) ) ;
123
- if self . dereferenceable_bytes != 0 {
124
- llvm:: LLVMRustAddDereferenceableAttr ( llfn,
125
- idx. as_uint ( ) ,
126
- self . dereferenceable_bytes ) ;
127
+ let deref = self . pointee_size . bytes ( ) ;
128
+ if deref != 0 {
129
+ if regular. contains ( ArgAttribute :: NonNull ) {
130
+ llvm:: LLVMRustAddDereferenceableAttr ( llfn,
131
+ idx. as_uint ( ) ,
132
+ deref) ;
133
+ } else {
134
+ llvm:: LLVMRustAddDereferenceableOrNullAttr ( llfn,
135
+ idx. as_uint ( ) ,
136
+ deref) ;
137
+ }
138
+ regular -= ArgAttribute :: NonNull ;
127
139
}
140
+ if let Some ( align) = self . pointee_align {
141
+ llvm:: LLVMRustAddAlignmentAttr ( llfn,
142
+ idx. as_uint ( ) ,
143
+ align. abi ( ) as u32 ) ;
144
+ }
145
+ regular. for_each_kind ( |attr| attr. apply_llfn ( idx, llfn) ) ;
128
146
}
129
147
}
130
148
131
149
pub fn apply_callsite ( & self , idx : AttributePlace , callsite : ValueRef ) {
150
+ let mut regular = self . regular ;
132
151
unsafe {
133
- self . regular . for_each_kind ( |attr| attr. apply_callsite ( idx, callsite) ) ;
134
- if self . dereferenceable_bytes != 0 {
135
- llvm:: LLVMRustAddDereferenceableCallSiteAttr ( callsite,
136
- idx. as_uint ( ) ,
137
- self . dereferenceable_bytes ) ;
152
+ let deref = self . pointee_size . bytes ( ) ;
153
+ if deref != 0 {
154
+ if regular. contains ( ArgAttribute :: NonNull ) {
155
+ llvm:: LLVMRustAddDereferenceableCallSiteAttr ( callsite,
156
+ idx. as_uint ( ) ,
157
+ deref) ;
158
+ } else {
159
+ llvm:: LLVMRustAddDereferenceableOrNullCallSiteAttr ( callsite,
160
+ idx. as_uint ( ) ,
161
+ deref) ;
162
+ }
163
+ regular -= ArgAttribute :: NonNull ;
164
+ }
165
+ if let Some ( align) = self . pointee_align {
166
+ llvm:: LLVMRustAddAlignmentCallSiteAttr ( callsite,
167
+ idx. as_uint ( ) ,
168
+ align. abi ( ) as u32 ) ;
138
169
}
170
+ regular. for_each_kind ( |attr| attr. apply_callsite ( idx, callsite) ) ;
139
171
}
140
172
}
141
173
}
@@ -439,12 +471,20 @@ pub struct ArgType<'tcx> {
439
471
440
472
impl < ' a , ' tcx > ArgType < ' tcx > {
441
473
fn new ( layout : TyLayout < ' tcx > ) -> ArgType < ' tcx > {
474
+ let mut attrs = ArgAttributes :: new ( ) ;
475
+
476
+ if let layout:: Abi :: Scalar ( ref scalar) = layout. abi {
477
+ if scalar. is_bool ( ) {
478
+ attrs. set ( ArgAttribute :: ZExt ) ;
479
+ }
480
+ }
481
+
442
482
ArgType {
443
483
kind : ArgKind :: Direct ,
444
484
layout,
445
485
cast : None ,
446
486
pad : None ,
447
- attrs : ArgAttributes :: default ( ) ,
487
+ attrs,
448
488
nested : vec ! [ ]
449
489
}
450
490
}
@@ -454,14 +494,16 @@ impl<'a, 'tcx> ArgType<'tcx> {
454
494
assert_eq ! ( self . kind, ArgKind :: Direct ) ;
455
495
456
496
// Wipe old attributes, likely not valid through indirection.
457
- self . attrs = ArgAttributes :: default ( ) ;
497
+ self . attrs = ArgAttributes :: new ( ) ;
458
498
459
499
// For non-immediate arguments the callee gets its own copy of
460
500
// the value on the stack, so there are no aliases. It's also
461
501
// program-invisible so can't possibly capture
462
502
self . attrs . set ( ArgAttribute :: NoAlias )
463
503
. set ( ArgAttribute :: NoCapture )
464
- . set_dereferenceable ( self . layout . size ) ;
504
+ . set ( ArgAttribute :: NonNull ) ;
505
+ self . attrs . pointee_size = self . layout . size ;
506
+ self . attrs . pointee_align = Some ( self . layout . align ) ;
465
507
466
508
self . kind = ArgKind :: Indirect ;
467
509
}
@@ -472,6 +514,22 @@ impl<'a, 'tcx> ArgType<'tcx> {
472
514
self . kind = ArgKind :: Ignore ;
473
515
}
474
516
517
+ fn safe_pointee ( & mut self , layout : TyLayout ) {
518
+ match self . layout . abi {
519
+ layout:: Abi :: Scalar ( layout:: Scalar {
520
+ value : layout:: Pointer ,
521
+ ref valid_range
522
+ } ) => {
523
+ if valid_range. start > 0 {
524
+ self . attrs . set ( ArgAttribute :: NonNull ) ;
525
+ }
526
+ self . attrs . pointee_size = layout. size ;
527
+ self . attrs . pointee_align = Some ( layout. align ) ;
528
+ }
529
+ _ => bug ! ( "ArgType::safe_pointee({:#?}): not a pointer" , self . layout)
530
+ }
531
+ }
532
+
475
533
pub fn extend_integer_width_to ( & mut self , bits : u64 ) {
476
534
// Only integers have signedness
477
535
if let layout:: Abi :: Scalar ( ref scalar) = self . layout . abi {
@@ -694,123 +752,115 @@ impl<'a, 'tcx> FnType<'tcx> {
694
752
_ => false
695
753
} ;
696
754
697
- let arg_of = |ty : Ty < ' tcx > , is_return : bool | {
698
- let mut arg = ArgType :: new ( ccx. layout_of ( ty) ) ;
699
- if let layout:: Abi :: Scalar ( ref scalar) = arg. layout . abi {
700
- if scalar. is_bool ( ) {
701
- arg. attrs . set ( ArgAttribute :: ZExt ) ;
702
- }
703
- }
704
- if arg. layout . is_zst ( ) {
705
- // For some forsaken reason, x86_64-pc-windows-gnu
706
- // doesn't ignore zero-sized struct arguments.
707
- // The same is true for s390x-unknown-linux-gnu.
708
- if is_return || rust_abi ||
709
- ( !win_x64_gnu && !linux_s390x) {
710
- arg. ignore ( ) ;
711
- }
712
- }
713
- arg
714
- } ;
715
-
716
- let ret_ty = sig. output ( ) ;
717
- let mut ret = arg_of ( ret_ty, true ) ;
718
-
719
- if !type_is_fat_ptr ( ccx, ret_ty) {
720
- // The `noalias` attribute on the return value is useful to a
721
- // function ptr caller.
722
- if ret_ty. is_box ( ) {
723
- // `Box` pointer return values never alias because ownership
724
- // is transferred
725
- ret. attrs . set ( ArgAttribute :: NoAlias ) ;
755
+ // Handle safe Rust thin and fat pointers.
756
+ let adjust_for_rust_type = |arg : & mut ArgType < ' tcx > , is_return : bool | {
757
+ // We only handle thin pointers here.
758
+ match arg. layout . abi {
759
+ layout:: Abi :: Scalar ( layout:: Scalar { value : layout:: Pointer , .. } ) => { }
760
+ _ => return
726
761
}
727
762
728
- // We can also mark the return value as `dereferenceable` in certain cases
729
- match ret_ty. sty {
730
- // These are not really pointers but pairs, (pointer, len)
731
- ty:: TyRef ( _, ty:: TypeAndMut { ty, .. } ) => {
732
- ret. attrs . set_dereferenceable ( ccx. size_of ( ty) ) ;
733
- }
734
- ty:: TyAdt ( def, _) if def. is_box ( ) => {
735
- ret. attrs . set_dereferenceable ( ccx. size_of ( ret_ty. boxed_ty ( ) ) ) ;
763
+ let mut ty = arg. layout . ty ;
764
+
765
+ // FIXME(eddyb) detect more nested cases than `Option<&T>` here.
766
+ match arg. layout . variants {
767
+ layout:: Variants :: NicheFilling { dataful_variant, .. } => {
768
+ let variant = arg. layout . for_variant ( ccx, dataful_variant) ;
769
+ for i in 0 ..variant. fields . count ( ) {
770
+ let field = variant. field ( ccx, i) ;
771
+ match field. abi {
772
+ layout:: Abi :: Scalar ( layout:: Scalar { value : layout:: Pointer , .. } ) => {
773
+ // We found the pointer field, use its type.
774
+ ty = field. ty ;
775
+ break ;
776
+ }
777
+ _ => { }
778
+ }
779
+ }
736
780
}
737
781
_ => { }
738
782
}
739
- }
740
783
741
- let mut args = Vec :: with_capacity ( inputs. len ( ) + extra_args. len ( ) ) ;
784
+ match ty. sty {
785
+ // `Box` pointer parameters never alias because ownership is transferred
786
+ ty:: TyAdt ( def, _) if def. is_box ( ) => {
787
+ arg. attrs . set ( ArgAttribute :: NoAlias ) ;
742
788
743
- // Handle safe Rust thin and fat pointers.
744
- let rust_ptr_attrs = |ty : Ty < ' tcx > , arg : & mut ArgType | match ty. sty {
745
- // `Box` pointer parameters never alias because ownership is transferred
746
- ty:: TyAdt ( def, _) if def. is_box ( ) => {
747
- arg. attrs . set ( ArgAttribute :: NoAlias ) ;
748
- Some ( ty. boxed_ty ( ) )
749
- }
789
+ arg. safe_pointee ( ccx. layout_of ( ty. boxed_ty ( ) ) ) ;
790
+ }
750
791
751
- ty:: TyRef ( _, mt) => {
752
- // `&mut` pointer parameters never alias other parameters, or mutable global data
753
- //
754
- // `&T` where `T` contains no `UnsafeCell<U>` is immutable, and can be marked as
755
- // both `readonly` and `noalias`, as LLVM's definition of `noalias` is based solely
756
- // on memory dependencies rather than pointer equality
757
- let is_freeze = ccx. shared ( ) . type_is_freeze ( mt. ty ) ;
758
-
759
- let no_alias_is_safe =
760
- if ccx. shared ( ) . tcx ( ) . sess . opts . debugging_opts . mutable_noalias ||
761
- ccx. shared ( ) . tcx ( ) . sess . panic_strategy ( ) == PanicStrategy :: Abort {
762
- // Mutable refrences or immutable shared references
763
- mt. mutbl == hir:: MutMutable || is_freeze
764
- } else {
765
- // Only immutable shared references
766
- mt. mutbl != hir:: MutMutable && is_freeze
767
- } ;
792
+ ty:: TyRef ( _, mt) => {
793
+ // `&mut` pointer parameters never alias other parameters,
794
+ // or mutable global data
795
+ //
796
+ // `&T` where `T` contains no `UnsafeCell<U>` is immutable,
797
+ // and can be marked as both `readonly` and `noalias`, as
798
+ // LLVM's definition of `noalias` is based solely on memory
799
+ // dependencies rather than pointer equality
800
+ let is_freeze = ccx. shared ( ) . type_is_freeze ( mt. ty ) ;
801
+
802
+ let no_alias_is_safe =
803
+ if ccx. shared ( ) . tcx ( ) . sess . opts . debugging_opts . mutable_noalias ||
804
+ ccx. shared ( ) . tcx ( ) . sess . panic_strategy ( ) == PanicStrategy :: Abort {
805
+ // Mutable refrences or immutable shared references
806
+ mt. mutbl == hir:: MutMutable || is_freeze
807
+ } else {
808
+ // Only immutable shared references
809
+ mt. mutbl != hir:: MutMutable && is_freeze
810
+ } ;
768
811
769
- if no_alias_is_safe {
770
- arg. attrs . set ( ArgAttribute :: NoAlias ) ;
771
- }
812
+ if no_alias_is_safe {
813
+ arg. attrs . set ( ArgAttribute :: NoAlias ) ;
814
+ }
772
815
773
- if mt. mutbl == hir:: MutImmutable && is_freeze {
774
- arg. attrs . set ( ArgAttribute :: ReadOnly ) ;
816
+ if mt. mutbl == hir:: MutImmutable && is_freeze && !is_return {
817
+ arg. attrs . set ( ArgAttribute :: ReadOnly ) ;
818
+ }
819
+
820
+ arg. safe_pointee ( ccx. layout_of ( mt. ty ) ) ;
775
821
}
822
+ _ => { }
823
+ }
776
824
777
- Some ( mt. ty )
825
+ // HACK(eddyb) LLVM inserts `llvm.assume` calls when inlining functions
826
+ // with align attributes, and those calls later block optimizations.
827
+ if !is_return {
828
+ arg. attrs . pointee_align = None ;
778
829
}
779
- _ => None
780
830
} ;
781
831
782
- for ty in inputs. iter ( ) . chain ( extra_args. iter ( ) ) {
783
- let mut arg = arg_of ( ty, false ) ;
784
-
785
- if type_is_fat_ptr ( ccx, ty) {
786
- let mut data = ArgType :: new ( arg. layout . field ( ccx, 0 ) ) ;
787
- let mut info = ArgType :: new ( arg. layout . field ( ccx, 1 ) ) ;
788
-
789
- if let Some ( inner) = rust_ptr_attrs ( ty, & mut data) {
790
- data. attrs . set ( ArgAttribute :: NonNull ) ;
791
- if ccx. tcx ( ) . struct_tail ( inner) . is_trait ( ) {
792
- // vtables can be safely marked non-null, readonly
793
- // and noalias.
794
- info. attrs . set ( ArgAttribute :: NonNull ) ;
795
- info. attrs . set ( ArgAttribute :: ReadOnly ) ;
796
- info. attrs . set ( ArgAttribute :: NoAlias ) ;
797
- }
798
- }
799
- // FIXME(eddyb) other ABIs don't have logic for nested.
800
- if rust_abi {
801
- arg. nested = vec ! [ data, info] ;
832
+ let arg_of = |ty : Ty < ' tcx > , is_return : bool | {
833
+ let mut arg = ArgType :: new ( ccx. layout_of ( ty) ) ;
834
+ if arg. layout . is_zst ( ) {
835
+ // For some forsaken reason, x86_64-pc-windows-gnu
836
+ // doesn't ignore zero-sized struct arguments.
837
+ // The same is true for s390x-unknown-linux-gnu.
838
+ if is_return || rust_abi ||
839
+ ( !win_x64_gnu && !linux_s390x) {
840
+ arg. ignore ( ) ;
802
841
}
842
+ }
843
+
844
+ // FIXME(eddyb) other ABIs don't have logic for nested.
845
+ if !is_return && type_is_fat_ptr ( ccx, arg. layout . ty ) && rust_abi {
846
+ arg. nested = vec ! [
847
+ ArgType :: new( arg. layout. field( ccx, 0 ) ) ,
848
+ ArgType :: new( arg. layout. field( ccx, 1 ) )
849
+ ] ;
850
+ adjust_for_rust_type ( & mut arg. nested [ 0 ] , false ) ;
851
+ adjust_for_rust_type ( & mut arg. nested [ 1 ] , false ) ;
803
852
} else {
804
- if let Some ( inner) = rust_ptr_attrs ( ty, & mut arg) {
805
- arg. attrs . set_dereferenceable ( ccx. size_of ( inner) ) ;
806
- }
853
+ adjust_for_rust_type ( & mut arg, is_return) ;
807
854
}
808
- args. push ( arg) ;
809
- }
855
+
856
+ arg
857
+ } ;
810
858
811
859
FnType {
812
- args,
813
- ret,
860
+ ret : arg_of ( sig. output ( ) , true ) ,
861
+ args : inputs. iter ( ) . chain ( extra_args. iter ( ) ) . map ( |ty| {
862
+ arg_of ( ty, false )
863
+ } ) . collect ( ) ,
814
864
variadic : sig. variadic ,
815
865
cconv,
816
866
}
0 commit comments