Skip to content

Commit 89d44c1

Browse files
committed
support #[target_feature] on #[naked] functions
1 parent 2b3cef8 commit 89d44c1

File tree

2 files changed

+379
-0
lines changed

2 files changed

+379
-0
lines changed

compiler/rustc_codegen_ssa/src/mir/naked_asm.rs

+224
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
use object::{Architecture, SubArchitecture};
12
use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind};
23
use rustc_attr_parsing::InstructionSetAttr;
34
use rustc_hir::def_id::DefId;
5+
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature};
46
use rustc_middle::mir::mono::{Linkage, MonoItem, MonoItemData, Visibility};
57
use rustc_middle::mir::{Body, InlineAsmOperand};
68
use 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+
107318
fn 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

Comments
 (0)