1+ use object:: { Architecture , SubArchitecture } ;
12use rustc_abi:: { BackendRepr , Float , Integer , Primitive , RegKind } ;
23use rustc_attr_parsing:: InstructionSetAttr ;
34use rustc_hir:: def_id:: DefId ;
5+ use rustc_middle:: middle:: codegen_fn_attrs:: { CodegenFnAttrs , TargetFeature } ;
46use rustc_middle:: mir:: mono:: { Linkage , MonoItem , MonoItemData , Visibility } ;
57use rustc_middle:: mir:: { Body , InlineAsmOperand } ;
68use rustc_middle:: ty:: layout:: { FnAbiOf , HasTyCtxt , HasTypingEnv , LayoutOf } ;
@@ -104,6 +106,215 @@ fn inline_to_global_operand<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
104106 }
105107}
106108
109+ // FIXME share code with `create_object_file`
110+ fn parse_architecture (
111+ sess : & rustc_session:: Session ,
112+ ) -> Option < ( Architecture , Option < SubArchitecture > ) > {
113+ let ( architecture, subarchitecture) = match & sess. target . arch [ ..] {
114+ "arm" => ( Architecture :: Arm , None ) ,
115+ "aarch64" => (
116+ if sess. target . pointer_width == 32 {
117+ Architecture :: Aarch64_Ilp32
118+ } else {
119+ Architecture :: Aarch64
120+ } ,
121+ None ,
122+ ) ,
123+ "x86" => ( Architecture :: I386 , None ) ,
124+ "s390x" => ( Architecture :: S390x , None ) ,
125+ "mips" | "mips32r6" => ( Architecture :: Mips , None ) ,
126+ "mips64" | "mips64r6" => ( Architecture :: Mips64 , None ) ,
127+ "x86_64" => (
128+ if sess. target . pointer_width == 32 {
129+ Architecture :: X86_64_X32
130+ } else {
131+ Architecture :: X86_64
132+ } ,
133+ None ,
134+ ) ,
135+ "powerpc" => ( Architecture :: PowerPc , None ) ,
136+ "powerpc64" => ( Architecture :: PowerPc64 , None ) ,
137+ "riscv32" => ( Architecture :: Riscv32 , None ) ,
138+ "riscv64" => ( Architecture :: Riscv64 , None ) ,
139+ "sparc" => {
140+ if sess. unstable_target_features . contains ( & sym:: v8plus) {
141+ // Target uses V8+, aka EM_SPARC32PLUS, aka 64-bit V9 but in 32-bit mode
142+ ( Architecture :: Sparc32Plus , None )
143+ } else {
144+ // Target uses V7 or V8, aka EM_SPARC
145+ ( Architecture :: Sparc , None )
146+ }
147+ }
148+ "sparc64" => ( Architecture :: Sparc64 , None ) ,
149+ "avr" => ( Architecture :: Avr , None ) ,
150+ "msp430" => ( Architecture :: Msp430 , None ) ,
151+ "hexagon" => ( Architecture :: Hexagon , None ) ,
152+ "bpf" => ( Architecture :: Bpf , None ) ,
153+ "loongarch64" => ( Architecture :: LoongArch64 , None ) ,
154+ "csky" => ( Architecture :: Csky , None ) ,
155+ "arm64ec" => ( Architecture :: Aarch64 , Some ( SubArchitecture :: Arm64EC ) ) ,
156+
157+ // added here
158+ "wasm32" => ( Architecture :: Wasm32 , None ) ,
159+ "wasm64" => ( Architecture :: Wasm64 , None ) ,
160+ "m68k" => ( Architecture :: M68k , None ) ,
161+
162+ // Unsupported architecture.
163+ _ => return None ,
164+ } ;
165+
166+ Some ( ( architecture, subarchitecture) )
167+ }
168+
169+ /// Enable the function's target features in the body of the function, then disable them again
170+ fn enable_disable_target_features < ' tcx > (
171+ tcx : TyCtxt < ' tcx > ,
172+ attrs : & CodegenFnAttrs ,
173+ ) -> Option < ( String , String ) > {
174+ use std:: fmt:: Write ;
175+
176+ let mut begin = String :: new ( ) ;
177+ let mut end = String :: new ( ) ;
178+
179+ let ( architecture, _subarchitecture) = parse_architecture ( tcx. sess ) ?;
180+ let features = attrs. target_features . iter ( ) . filter ( |attr| !attr. implied ) ;
181+
182+ match architecture {
183+ Architecture :: X86_64 | Architecture :: X86_64_X32 => { /* do nothing */ }
184+
185+ Architecture :: Aarch64 | Architecture :: Aarch64_Ilp32 | Architecture :: Arm => {
186+ // https://developer.arm.com/documentation/100067/0611/armclang-Integrated-Assembler/AArch32-Target-selection-directives?lang=en
187+
188+ for feature in features {
189+ writeln ! ( begin, ".arch_extension {}" , feature. name) . unwrap ( ) ;
190+
191+ writeln ! ( end, ".arch_extension no{}" , feature. name) . unwrap ( ) ;
192+ }
193+ }
194+ Architecture :: Riscv32 | Architecture :: Riscv64 => {
195+ // https://github.com/riscv-non-isa/riscv-asm-manual/blob/ad0de8c004e29c9a7ac33cfd054f4d4f9392f2fb/src/asm-manual.adoc#arch
196+
197+ for feature in features {
198+ writeln ! ( begin, ".option arch, +{}" , feature. name) . unwrap ( ) ;
199+
200+ writeln ! ( end, ".option arch, -{}" , feature. name) . unwrap ( ) ;
201+ }
202+ }
203+ Architecture :: Mips | Architecture :: Mips64 | Architecture :: Mips64_N32 => {
204+ // https://sourceware.org/binutils/docs/as/MIPS-ISA.html
205+ // https://sourceware.org/binutils/docs/as/MIPS-ASE-Instruction-Generation-Overrides.html
206+
207+ for feature in features {
208+ writeln ! ( begin, ".set {}" , feature. name) . unwrap ( ) ;
209+
210+ writeln ! ( end, ".set no{}" , feature. name) . unwrap ( ) ;
211+ }
212+ }
213+
214+ Architecture :: S390x => {
215+ // https://sourceware.org/binutils/docs/as/s390-Directives.html
216+
217+ // based on src/llvm-project/llvm/lib/Target/SystemZ/SystemZFeatures.td
218+ let isa_revision_for_feature = |feature : & TargetFeature | match feature. name . as_str ( ) {
219+ "backchain" => None , // does not define any instructions
220+ "deflate-conversion" => Some ( 13 ) ,
221+ "enhanced-sort" => Some ( 13 ) ,
222+ "guarded-storage" => Some ( 12 ) ,
223+ "high-word" => None , // technically 9, but LLVM supports only >= 10
224+ "nnp-assist" => Some ( 14 ) ,
225+ "transactional-execution" => Some ( 10 ) ,
226+ "vector" => Some ( 11 ) ,
227+ "vector-enhancements-1" => Some ( 12 ) ,
228+ "vector-enhancements-2" => Some ( 13 ) ,
229+ "vector-packed-decimal" => Some ( 12 ) ,
230+ "vector-packed-decimal-enhancement" => Some ( 13 ) ,
231+ "vector-packed-decimal-enhancement-2" => Some ( 14 ) ,
232+ _ => None ,
233+ } ;
234+
235+ if let Some ( minimum_isa) = features. filter_map ( isa_revision_for_feature) . max ( ) {
236+ writeln ! ( begin, ".machine arch{minimum_isa}" ) . unwrap ( ) ;
237+
238+ // NOTE: LLVM does not support `.machine push` and `.machine pop`, so we rely on these
239+ // target features only being applied to this ASM block (LLVM clears them for the next)
240+ //
241+ // https://github.com/llvm/llvm-project/blob/74306afe87b85cb9b5734044eb6c74b8290098b3/llvm/lib/Target/SystemZ/AsmParser/SystemZAsmParser.cpp#L1362
242+ }
243+ }
244+ Architecture :: PowerPc | Architecture :: PowerPc64 => {
245+ // https://www.ibm.com/docs/en/ssw_aix_71/assembler/assembler_pdf.pdf
246+
247+ // based on src/llvm-project/llvm/lib/Target/PowerPC/PPC.td
248+ let isa_revision_for_feature = |feature : & TargetFeature | match feature. name . as_str ( ) {
249+ "altivec" => Some ( 7 ) ,
250+ "partword-atomics" => Some ( 8 ) ,
251+ "power10-vector" => Some ( 10 ) ,
252+ "power8-altivec" => Some ( 8 ) ,
253+ "power8-crypto" => Some ( 8 ) ,
254+ "power8-vector" => Some ( 9 ) ,
255+ "power9-altivec" => Some ( 9 ) ,
256+ "power9-vector" => Some ( 9 ) ,
257+ "quadword-atomics" => Some ( 8 ) ,
258+ "vsx" => Some ( 7 ) ,
259+ _ => None ,
260+ } ;
261+
262+ if let Some ( minimum_isa) = features. filter_map ( isa_revision_for_feature) . max ( ) {
263+ writeln ! ( begin, ".machine push" ) . unwrap ( ) ;
264+
265+ // LLVM currently ignores the .machine directive, and allows all instructions regardless
266+ // of the machine. This may be fixed in the future.
267+ //
268+ // https://github.com/llvm/llvm-project/blob/74306afe87b85cb9b5734044eb6c74b8290098b3/llvm/lib/Target/PowerPC/AsmParser/PPCAsmParser.cpp#L1799
269+ writeln ! ( begin, ".machine pwr{minimum_isa}" ) . unwrap ( ) ;
270+
271+ writeln ! ( end, ".machine pop" ) . unwrap ( ) ;
272+ }
273+ }
274+
275+ Architecture :: M68k => {
276+ // https://sourceware.org/binutils/docs/as/M68K_002dDirectives.html#index-directives_002c-M680x0
277+
278+ // FIXME support m64k
279+ // return None;
280+ }
281+
282+ Architecture :: Wasm32 | Architecture :: Wasm64 => {
283+ // LLVM does not appear to accept any directive to enable target features
284+ //
285+ // https://github.com/llvm/llvm-project/blob/74306afe87b85cb9b5734044eb6c74b8290098b3/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp#L909
286+ return None ;
287+ }
288+
289+ Architecture :: LoongArch64 => {
290+ // LLVM does not appear to accept any directive to enable target features
291+ //
292+ // https://github.com/llvm/llvm-project/blob/74306afe87b85cb9b5734044eb6c74b8290098b3/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp#L1918
293+ }
294+
295+ // FIXME: support naked_asm! on more architectures
296+ Architecture :: Avr => return None ,
297+ Architecture :: Bpf => return None ,
298+ Architecture :: Csky => return None ,
299+ Architecture :: E2K32 => return None ,
300+ Architecture :: E2K64 => return None ,
301+ Architecture :: I386 => return None ,
302+ Architecture :: Hexagon => return None ,
303+ Architecture :: Msp430 => return None ,
304+ Architecture :: Sbf => return None ,
305+ Architecture :: Sharc => return None ,
306+ Architecture :: Sparc => return None ,
307+ Architecture :: Sparc32Plus => return None ,
308+ Architecture :: Sparc64 => return None ,
309+ Architecture :: Xtensa => return None ,
310+
311+ // the Architecture enum is non-exhaustive
312+ Architecture :: Unknown | _ => return None ,
313+ }
314+
315+ Some ( ( begin, end) )
316+ }
317+
107318fn prefix_and_suffix < ' tcx > (
108319 tcx : TyCtxt < ' tcx > ,
109320 instance : Instance < ' tcx > ,
@@ -186,6 +397,12 @@ fn prefix_and_suffix<'tcx>(
186397 Ok ( ( ) )
187398 } ;
188399
400+ let Some ( ( target_feature_begin, target_feature_end) ) =
401+ enable_disable_target_features ( tcx, attrs)
402+ else {
403+ panic ! ( "target features on naked functions are not supported for this architecture" ) ;
404+ } ;
405+
189406 let mut begin = String :: new ( ) ;
190407 let mut end = String :: new ( ) ;
191408 match asm_binary_format {
@@ -205,6 +422,8 @@ fn prefix_and_suffix<'tcx>(
205422 writeln ! ( begin, ".pushsection {section},\" ax\" , {progbits}" ) . unwrap ( ) ;
206423 writeln ! ( begin, ".balign {align}" ) . unwrap ( ) ;
207424 write_linkage ( & mut begin) . unwrap ( ) ;
425+ begin. push_str ( & target_feature_begin) ;
426+
208427 if let Visibility :: Hidden = item_data. visibility {
209428 writeln ! ( begin, ".hidden {asm_name}" ) . unwrap ( ) ;
210429 }
@@ -215,6 +434,7 @@ fn prefix_and_suffix<'tcx>(
215434 writeln ! ( begin, "{asm_name}:" ) . unwrap ( ) ;
216435
217436 writeln ! ( end) . unwrap ( ) ;
437+ end. push_str ( & target_feature_end) ;
218438 writeln ! ( end, ".size {asm_name}, . - {asm_name}" ) . unwrap ( ) ;
219439 writeln ! ( end, ".popsection" ) . unwrap ( ) ;
220440 if !arch_suffix. is_empty ( ) {
@@ -226,12 +446,14 @@ fn prefix_and_suffix<'tcx>(
226446 writeln ! ( begin, ".pushsection {},regular,pure_instructions" , section) . unwrap ( ) ;
227447 writeln ! ( begin, ".balign {align}" ) . unwrap ( ) ;
228448 write_linkage ( & mut begin) . unwrap ( ) ;
449+ begin. push_str ( & target_feature_begin) ;
229450 if let Visibility :: Hidden = item_data. visibility {
230451 writeln ! ( begin, ".private_extern {asm_name}" ) . unwrap ( ) ;
231452 }
232453 writeln ! ( begin, "{asm_name}:" ) . unwrap ( ) ;
233454
234455 writeln ! ( end) . unwrap ( ) ;
456+ end. push_str ( & target_feature_end) ;
235457 writeln ! ( end, ".popsection" ) . unwrap ( ) ;
236458 if !arch_suffix. is_empty ( ) {
237459 writeln ! ( end, "{}" , arch_suffix) . unwrap ( ) ;
@@ -242,13 +464,15 @@ fn prefix_and_suffix<'tcx>(
242464 writeln ! ( begin, ".pushsection {},\" xr\" " , section) . unwrap ( ) ;
243465 writeln ! ( begin, ".balign {align}" ) . unwrap ( ) ;
244466 write_linkage ( & mut begin) . unwrap ( ) ;
467+ begin. push_str ( & target_feature_begin) ;
245468 writeln ! ( begin, ".def {asm_name}" ) . unwrap ( ) ;
246469 writeln ! ( begin, ".scl 2" ) . unwrap ( ) ;
247470 writeln ! ( begin, ".type 32" ) . unwrap ( ) ;
248471 writeln ! ( begin, ".endef {asm_name}" ) . unwrap ( ) ;
249472 writeln ! ( begin, "{asm_name}:" ) . unwrap ( ) ;
250473
251474 writeln ! ( end) . unwrap ( ) ;
475+ end. push_str ( & target_feature_end) ;
252476 writeln ! ( end, ".popsection" ) . unwrap ( ) ;
253477 if !arch_suffix. is_empty ( ) {
254478 writeln ! ( end, "{}" , arch_suffix) . unwrap ( ) ;
0 commit comments