From b390503d49152ba030c00b6a98fb0dd63e217827 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Thu, 17 Oct 2024 04:58:36 +0100 Subject: [PATCH 1/6] Rename `*AsmOperandRef::Const` to `Interpolate` This is used for string interpolation currently, so rename it as so. The name `Const` will be used to denote a general CTFE constant that can either be integer or pointer. --- compiler/rustc_codegen_cranelift/src/global_asm.rs | 2 +- compiler/rustc_codegen_gcc/src/asm.rs | 8 ++++---- compiler/rustc_codegen_llvm/src/asm.rs | 4 ++-- compiler/rustc_codegen_ssa/src/base.rs | 4 ++-- compiler/rustc_codegen_ssa/src/mir/block.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/naked_asm.rs | 2 +- compiler/rustc_codegen_ssa/src/traits/asm.rs | 4 ++-- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/global_asm.rs b/compiler/rustc_codegen_cranelift/src/global_asm.rs index 203b443269fa7..3cf402f20dc09 100644 --- a/compiler/rustc_codegen_cranelift/src/global_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/global_asm.rs @@ -104,7 +104,7 @@ fn codegen_global_asm_inner<'tcx>( InlineAsmTemplatePiece::String(ref s) => global_asm.push_str(s), InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span } => { match operands[operand_idx] { - GlobalAsmOperandRef::Const { ref string } => { + GlobalAsmOperandRef::Interpolate { ref string } => { global_asm.push_str(string); } GlobalAsmOperandRef::SymFn { instance } => { diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index c35337ae7ce0c..ab1bbe9335efc 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -294,7 +294,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { } } - InlineAsmOperandRef::Const { ref string } => { + InlineAsmOperandRef::Interpolate { ref string } => { constants_len += string.len() + att_dialect as usize; } @@ -409,7 +409,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { }); } - InlineAsmOperandRef::Const { .. } => { + InlineAsmOperandRef::Interpolate { .. } => { // processed in the previous pass } @@ -502,7 +502,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { template_str.push_str(name); } - InlineAsmOperandRef::Const { ref string } => { + InlineAsmOperandRef::Interpolate { ref string } => { template_str.push_str(string); } @@ -865,7 +865,7 @@ impl<'gcc, 'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { } InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span: _ } => { match operands[operand_idx] { - GlobalAsmOperandRef::Const { ref string } => { + GlobalAsmOperandRef::Interpolate { ref string } => { // Const operands get injected directly into the // template. Note that we don't need to escape % // here unlike normal inline assembly. diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 9e3893d5314ae..ef33ef3eb0cab 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -205,7 +205,7 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { template_str.push_str(&format!("${{{}}}", op_idx[&operand_idx])); } } - InlineAsmOperandRef::Const { ref string } => { + InlineAsmOperandRef::Interpolate { ref string } => { // Const operands get injected directly into the template template_str.push_str(string); } @@ -398,7 +398,7 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { InlineAsmTemplatePiece::String(ref s) => template_str.push_str(s), InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span: _ } => { match operands[operand_idx] { - GlobalAsmOperandRef::Const { ref string } => { + GlobalAsmOperandRef::Interpolate { ref string } => { // Const operands get injected directly into the // template. Note that we don't need to escape $ // here unlike normal inline assembly. diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 93cbd4cbb7cc9..64324584a62f7 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -440,14 +440,14 @@ where const_value, cx.layout_of(ty), ); - GlobalAsmOperandRef::Const { string } + GlobalAsmOperandRef::Interpolate { string } } Err(ErrorHandled::Reported { .. }) => { // An error has already been reported and // compilation is guaranteed to fail if execution // hits this path. So an empty string instead of // a stringified constant value will suffice. - GlobalAsmOperandRef::Const { string: String::new() } + GlobalAsmOperandRef::Interpolate { string: String::new() } } Err(ErrorHandled::TooGeneric(_)) => { span_bug!(*op_sp, "asm const cannot be resolved; too generic") diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 950f19a6f0f4e..791391627ff57 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1237,7 +1237,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { const_value, bx.layout_of(value.ty()), ); - InlineAsmOperandRef::Const { string } + InlineAsmOperandRef::Interpolate { string } } mir::InlineAsmOperand::SymFn { ref value } => { let const_ = self.monomorphize(value.const_); diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index d2a687359e0bc..2f667fa8b2f37 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -85,7 +85,7 @@ fn inline_to_global_operand<'a, 'tcx, Cx: LayoutOf<'tcx, LayoutOfResult = TyAndL cx.layout_of(mono_type), ); - GlobalAsmOperandRef::Const { string } + GlobalAsmOperandRef::Interpolate { string } } InlineAsmOperand::SymFn { value } => { let mono_type = instance.instantiate_mir_and_normalize_erasing_regions( diff --git a/compiler/rustc_codegen_ssa/src/traits/asm.rs b/compiler/rustc_codegen_ssa/src/traits/asm.rs index cc7a6a3f19e9e..5af433cd95f65 100644 --- a/compiler/rustc_codegen_ssa/src/traits/asm.rs +++ b/compiler/rustc_codegen_ssa/src/traits/asm.rs @@ -25,7 +25,7 @@ pub enum InlineAsmOperandRef<'tcx, B: BackendTypes + ?Sized> { in_value: OperandRef<'tcx, B::Value>, out_place: Option>, }, - Const { + Interpolate { string: String, }, SymFn { @@ -41,7 +41,7 @@ pub enum InlineAsmOperandRef<'tcx, B: BackendTypes + ?Sized> { #[derive(Debug)] pub enum GlobalAsmOperandRef<'tcx> { - Const { string: String }, + Interpolate { string: String }, SymFn { instance: Instance<'tcx> }, SymStatic { def_id: DefId }, } From 602565828e7b89be79ac3be84f4025ad8fb2fc7c Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Thu, 17 Oct 2024 08:31:35 +0100 Subject: [PATCH 2/6] Add `InlineAsmOperandRef::Const` This is intended for supporting passing arbitrary CTFE const into inline assembly. --- compiler/rustc_codegen_gcc/src/asm.rs | 21 ++++++++++++++++++++ compiler/rustc_codegen_llvm/src/asm.rs | 8 ++++++++ compiler/rustc_codegen_ssa/src/traits/asm.rs | 3 +++ 3 files changed, 32 insertions(+) diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index ab1bbe9335efc..b0ea7d54eca08 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -298,6 +298,14 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { constants_len += string.len() + att_dialect as usize; } + InlineAsmOperandRef::Const { value } => { + inputs.push(AsmInOperand { + constraint: Cow::Borrowed("i"), + rust_idx, + val: value.immediate(), + }); + } + InlineAsmOperandRef::SymFn { instance } => { // TODO(@Amanieu): Additional mangling is needed on // some targets to add a leading underscore (Mach-O) @@ -413,6 +421,10 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { // processed in the previous pass } + InlineAsmOperandRef::Const { .. } => { + // processed in the previous pass + } + InlineAsmOperandRef::Label { .. } => { // processed in the previous pass } @@ -486,6 +498,15 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { push_to_template(modifier, gcc_index); } + InlineAsmOperandRef::Const { .. } => { + let in_gcc_index = inputs + .iter() + .position(|op| operand_idx == op.rust_idx) + .expect("wrong rust index"); + let gcc_index = in_gcc_index + outputs.len(); + push_to_template(None, gcc_index); + } + InlineAsmOperandRef::SymFn { instance } => { // TODO(@Amanieu): Additional mangling is needed on // some targets to add a leading underscore (Mach-O) diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index ef33ef3eb0cab..3dbc721f7eeba 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -158,6 +158,11 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { constraints.push(format!("{}", op_idx[&idx])); } } + InlineAsmOperandRef::Const { value } => { + inputs.push(value.immediate()); + op_idx.insert(idx, constraints.len()); + constraints.push("i".to_string()); + } InlineAsmOperandRef::SymFn { instance } => { inputs.push(self.cx.get_fn(instance)); op_idx.insert(idx, constraints.len()); @@ -205,6 +210,9 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { template_str.push_str(&format!("${{{}}}", op_idx[&operand_idx])); } } + InlineAsmOperandRef::Const { .. } => { + template_str.push_str(&format!("${{{}:c}}", op_idx[&operand_idx])); + } InlineAsmOperandRef::Interpolate { ref string } => { // Const operands get injected directly into the template template_str.push_str(string); diff --git a/compiler/rustc_codegen_ssa/src/traits/asm.rs b/compiler/rustc_codegen_ssa/src/traits/asm.rs index 5af433cd95f65..67f18d25a8ee4 100644 --- a/compiler/rustc_codegen_ssa/src/traits/asm.rs +++ b/compiler/rustc_codegen_ssa/src/traits/asm.rs @@ -28,6 +28,9 @@ pub enum InlineAsmOperandRef<'tcx, B: BackendTypes + ?Sized> { Interpolate { string: String, }, + Const { + value: OperandRef<'tcx, B::Value>, + }, SymFn { instance: Instance<'tcx>, }, From 4f2768a1c7d84acb4f0d29403f572620467c1609 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Thu, 24 Oct 2024 06:22:18 +0100 Subject: [PATCH 3/6] Implement asm_const_ptr for inline asm --- compiler/rustc_codegen_ssa/src/mir/block.rs | 21 ++++++---- compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_hir_typeck/messages.ftl | 4 ++ compiler/rustc_hir_typeck/src/errors.rs | 7 ++++ compiler/rustc_hir_typeck/src/inline_asm.rs | 35 ++++++++++++++++- compiler/rustc_span/src/symbol.rs | 1 + tests/ui/asm/const-refs-to-static.rs | 9 +++-- tests/ui/asm/const-refs-to-static.stderr | 22 ----------- tests/ui/asm/invalid-const-operand.rs | 6 +-- tests/ui/asm/invalid-const-operand.stderr | 38 +++++-------------- .../feature-gate-asm_const_ptr.rs | 10 +++++ .../feature-gate-asm_const_ptr.stderr | 13 +++++++ 12 files changed, 100 insertions(+), 68 deletions(-) delete mode 100644 tests/ui/asm/const-refs-to-static.stderr create mode 100644 tests/ui/feature-gates/feature-gate-asm_const_ptr.rs create mode 100644 tests/ui/feature-gates/feature-gate-asm_const_ptr.stderr diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 791391627ff57..c6e9fb5a482ae 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1230,14 +1230,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { InlineAsmOperandRef::InOut { reg, late, in_value, out_place } } mir::InlineAsmOperand::Const { ref value } => { - let const_value = self.eval_mir_constant(value); - let string = common::asm_const_to_str( - bx.tcx(), - span, - const_value, - bx.layout_of(value.ty()), - ); - InlineAsmOperandRef::Interpolate { string } + if value.ty().is_any_ptr() { + let value = self.eval_mir_constant_to_operand(bx, value); + InlineAsmOperandRef::Const { value } + } else { + let const_value = self.eval_mir_constant(value); + let string = common::asm_const_to_str( + bx.tcx(), + span, + const_value, + bx.layout_of(value.ty()), + ); + InlineAsmOperandRef::Interpolate { string } + } } mir::InlineAsmOperand::SymFn { ref value } => { let const_ = self.monomorphize(value.const_); diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index a5f89b7a076ae..46489c70ed247 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -372,6 +372,8 @@ declare_features! ( (unstable, arbitrary_self_types, "1.23.0", Some(44874)), /// Allows inherent and trait methods with arbitrary self types that are raw pointers. (unstable, arbitrary_self_types_pointers, "1.83.0", Some(44874)), + /// Allows using `const` operands with pointer in inline assembly. + (unstable, asm_const_ptr, "CURRENT_RUSTC_VERSION", Some(128464)), /// Enables experimental inline assembly support for additional architectures. (unstable, asm_experimental_arch, "1.58.0", Some(93335)), /// Enables experimental register support in inline assembly. diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 23309102c4da5..3680d86821917 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -11,6 +11,10 @@ hir_typeck_arg_mismatch_indeterminate = argument type mismatch was detected, but .note = we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new hir_typeck_as_deref_suggestion = consider using `as_deref` here + +hir_typeck_asm_const_ptr_unstable = + using pointers in asm `const` operand is experimental + hir_typeck_base_expression_double_dot = base expression required after `..` hir_typeck_base_expression_double_dot_add_expr = add a base expression here hir_typeck_base_expression_double_dot_enable_default_field_values = diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 732795535087e..9f72e05390e62 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -14,6 +14,13 @@ use rustc_span::{Ident, Span, Symbol}; use crate::fluent_generated as fluent; +#[derive(Diagnostic)] +#[diag(hir_typeck_asm_const_ptr_unstable)] +pub(crate) struct AsmConstPtrUnstable { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(hir_typeck_base_expression_double_dot, code = E0797)] pub(crate) struct BaseExpressionDoubleDot { diff --git a/compiler/rustc_hir_typeck/src/inline_asm.rs b/compiler/rustc_hir_typeck/src/inline_asm.rs index 6399f0a78ae2f..53b2a202dcada 100644 --- a/compiler/rustc_hir_typeck/src/inline_asm.rs +++ b/compiler/rustc_hir_typeck/src/inline_asm.rs @@ -14,7 +14,7 @@ use rustc_target::asm::{ use rustc_trait_selection::infer::InferCtxtExt; use crate::FnCtxt; -use crate::errors::RegisterTypeUnstable; +use crate::errors::{AsmConstPtrUnstable, RegisterTypeUnstable}; pub(crate) struct InlineAsmCtxt<'a, 'tcx> { target_features: &'tcx FxIndexSet, @@ -511,7 +511,36 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { match ty.kind() { ty::Error(_) => {} _ if ty.is_integral() => {} + ty::FnPtr(..) => { + if !self.tcx().features().asm_const_ptr() { + self.tcx() + .sess + .create_feature_err( + AsmConstPtrUnstable { span: op_sp }, + sym::asm_const_ptr, + ) + .emit(); + } + } + ty::RawPtr(pointee, _) | ty::Ref(_, pointee, _) + if self.is_thin_ptr_ty(op_sp, *pointee) => + { + if !self.tcx().features().asm_const_ptr() { + self.tcx() + .sess + .create_feature_err( + AsmConstPtrUnstable { span: op_sp }, + sym::asm_const_ptr, + ) + .emit(); + } + } _ => { + let const_possible_ty = if !self.tcx().features().asm_const_ptr() { + "integer" + } else { + "integer or thin pointer" + }; self.fcx .dcx() .struct_span_err(op_sp, "invalid type for `const` operand") @@ -519,7 +548,9 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { self.tcx().def_span(anon_const.def_id), format!("is {} `{}`", ty.kind().article(), ty), ) - .with_help("`const` operands must be of an integer type") + .with_help(format!( + "`const` operands must be of an {const_possible_ty} type" + )) .emit(); } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3a95447308a90..1d49fafcaaa68 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -474,6 +474,7 @@ symbols! { as_str, asm, asm_const, + asm_const_ptr, asm_experimental_arch, asm_experimental_reg, asm_goto, diff --git a/tests/ui/asm/const-refs-to-static.rs b/tests/ui/asm/const-refs-to-static.rs index ce2c5b3246ec8..8058d70550aba 100644 --- a/tests/ui/asm/const-refs-to-static.rs +++ b/tests/ui/asm/const-refs-to-static.rs @@ -1,19 +1,20 @@ //@ needs-asm-support //@ ignore-nvptx64 //@ ignore-spirv +//@ build-pass + +#![feature(asm_const_ptr)] use std::arch::{asm, global_asm}; use std::ptr::addr_of; static FOO: u8 = 42; -global_asm!("{}", const addr_of!(FOO)); -//~^ ERROR invalid type for `const` operand +global_asm!("/* {} */", const addr_of!(FOO)); #[no_mangle] fn inline() { - unsafe { asm!("{}", const addr_of!(FOO)) }; - //~^ ERROR invalid type for `const` operand + unsafe { asm!("/* {} */", const addr_of!(FOO)) }; } fn main() {} diff --git a/tests/ui/asm/const-refs-to-static.stderr b/tests/ui/asm/const-refs-to-static.stderr deleted file mode 100644 index 10e1ca5bd6068..0000000000000 --- a/tests/ui/asm/const-refs-to-static.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: invalid type for `const` operand - --> $DIR/const-refs-to-static.rs:10:19 - | -LL | global_asm!("{}", const addr_of!(FOO)); - | ^^^^^^------------- - | | - | is a `*const u8` - | - = help: `const` operands must be of an integer type - -error: invalid type for `const` operand - --> $DIR/const-refs-to-static.rs:15:25 - | -LL | unsafe { asm!("{}", const addr_of!(FOO)) }; - | ^^^^^^------------- - | | - | is a `*const u8` - | - = help: `const` operands must be of an integer type - -error: aborting due to 2 previous errors - diff --git a/tests/ui/asm/invalid-const-operand.rs b/tests/ui/asm/invalid-const-operand.rs index bbf4001752a4b..e8f437196ec1c 100644 --- a/tests/ui/asm/invalid-const-operand.rs +++ b/tests/ui/asm/invalid-const-operand.rs @@ -2,6 +2,8 @@ //@ ignore-nvptx64 //@ ignore-spirv +#![feature(asm_const_ptr)] + use std::arch::{asm, global_asm}; // Const operands must be integers and must be constants. @@ -12,7 +14,6 @@ global_asm!("{}", const 0i128); global_asm!("{}", const 0f32); //~^ ERROR invalid type for `const` operand global_asm!("{}", const 0 as *mut u8); -//~^ ERROR invalid type for `const` operand fn test1() { unsafe { @@ -24,8 +25,7 @@ fn test1() { asm!("{}", const 0f32); //~^ ERROR invalid type for `const` operand asm!("{}", const 0 as *mut u8); - //~^ ERROR invalid type for `const` operand - asm!("{}", const &0); + asm!("{}", const b"Foo".as_slice()); //~^ ERROR invalid type for `const` operand } } diff --git a/tests/ui/asm/invalid-const-operand.stderr b/tests/ui/asm/invalid-const-operand.stderr index 01aa843c6fb19..d09571a49221a 100644 --- a/tests/ui/asm/invalid-const-operand.stderr +++ b/tests/ui/asm/invalid-const-operand.stderr @@ -35,55 +35,35 @@ LL + const x: /* Type */ = 0; | error: invalid type for `const` operand - --> $DIR/invalid-const-operand.rs:12:19 + --> $DIR/invalid-const-operand.rs:14:19 | LL | global_asm!("{}", const 0f32); | ^^^^^^---- | | | is an `f32` | - = help: `const` operands must be of an integer type - -error: invalid type for `const` operand - --> $DIR/invalid-const-operand.rs:14:19 - | -LL | global_asm!("{}", const 0 as *mut u8); - | ^^^^^^------------ - | | - | is a `*mut u8` - | - = help: `const` operands must be of an integer type + = help: `const` operands must be of an integer or thin pointer type error: invalid type for `const` operand - --> $DIR/invalid-const-operand.rs:24:20 + --> $DIR/invalid-const-operand.rs:25:20 | LL | asm!("{}", const 0f32); | ^^^^^^---- | | | is an `f32` | - = help: `const` operands must be of an integer type - -error: invalid type for `const` operand - --> $DIR/invalid-const-operand.rs:26:20 - | -LL | asm!("{}", const 0 as *mut u8); - | ^^^^^^------------ - | | - | is a `*mut u8` - | - = help: `const` operands must be of an integer type + = help: `const` operands must be of an integer or thin pointer type error: invalid type for `const` operand --> $DIR/invalid-const-operand.rs:28:20 | -LL | asm!("{}", const &0); - | ^^^^^^-- +LL | asm!("{}", const b"Foo".as_slice()); + | ^^^^^^----------------- | | - | is a `&i32` + | is a `&[u8]` | - = help: `const` operands must be of an integer type + = help: `const` operands must be of an integer or thin pointer type -error: aborting due to 8 previous errors +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0435`. diff --git a/tests/ui/feature-gates/feature-gate-asm_const_ptr.rs b/tests/ui/feature-gates/feature-gate-asm_const_ptr.rs new file mode 100644 index 0000000000000..cc124ae39a399 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-asm_const_ptr.rs @@ -0,0 +1,10 @@ +//@ only-x86_64 + +use std::arch::asm; + +fn main() { + unsafe { + asm!("/* {} */", const &0); + //~^ ERROR using pointers in asm `const` operand is experimental + } +} diff --git a/tests/ui/feature-gates/feature-gate-asm_const_ptr.stderr b/tests/ui/feature-gates/feature-gate-asm_const_ptr.stderr new file mode 100644 index 0000000000000..8cc21dd853b49 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-asm_const_ptr.stderr @@ -0,0 +1,13 @@ +error[E0658]: using pointers in asm `const` operand is experimental + --> $DIR/feature-gate-asm_const_ptr.rs:7:26 + | +LL | asm!("/* {} */", const &0); + | ^^^^^^^^ + | + = note: see issue #128464 for more information + = help: add `#![feature(asm_const_ptr)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. From ca092d0eb415a8a5528cd69bb97361921f0ddedb Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 12 Mar 2025 21:22:03 +0000 Subject: [PATCH 4/6] Give global_asm mangled names `global_asm!` themselves don't need symbol names; they currently only have a symbol name during mono collecion to identify them to partitioning algorithm. However it will have shims generated under it which will need unique symbol names. The name themselves ultimately doesn't matter, so they're generated like other shim instances. --- compiler/rustc_middle/src/mir/mono.rs | 2 +- compiler/rustc_symbol_mangling/src/legacy.rs | 5 +++++ compiler/rustc_symbol_mangling/src/v0.rs | 11 ++++++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 7243f87ee6380..d3051e0fa9066 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -120,7 +120,7 @@ impl<'tcx> MonoItem<'tcx> { MonoItem::Fn(instance) => tcx.symbol_name(instance), MonoItem::Static(def_id) => tcx.symbol_name(Instance::mono(tcx, def_id)), MonoItem::GlobalAsm(item_id) => { - SymbolName::new(tcx, &format!("global_asm_{:?}", item_id.owner_id)) + tcx.symbol_name(Instance::mono(tcx, item_id.owner_id.to_def_id())) } } } diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index db102abda7fa3..5f1cd889bf566 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -36,6 +36,11 @@ pub(super) fn mangle<'tcx>( debug!(?instance_ty); break; } + DefPathData::GlobalAsm => { + // `global_asm!` doesn't have a type. + instance_ty = tcx.types.unit; + break; + } _ => { // if we're making a symbol for something, there ought // to be a value or type-def or something in there diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 4a99ce09b39ab..4ac0dd0527c4e 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -873,6 +873,16 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { // are effectively living in their parent modules. DefPathData::ForeignMod => return print_prefix(self), + // Global asm are handled similar to shims. + DefPathData::GlobalAsm => { + return self.path_append_ns( + print_prefix, + 'S', + disambiguated_data.disambiguator as u64, + "global_asm", + ); + } + // Uppercase categories are more stable than lowercase ones. DefPathData::TypeNs(_) => 't', DefPathData::ValueNs(_) => 'v', @@ -886,7 +896,6 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { // These should never show up as `path_append` arguments. DefPathData::CrateRoot | DefPathData::Use - | DefPathData::GlobalAsm | DefPathData::Impl | DefPathData::MacroNs(_) | DefPathData::LifetimeNs(_) From 825f97e8bb812775384adf484914a97338e35cca Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 12 Mar 2025 22:48:32 +0000 Subject: [PATCH 5/6] Collect constants within `global_asm!` in collector This is currently a no-op, but will be useful when const in `global_asm!` can be pointers. --- compiler/rustc_monomorphize/src/collector.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index c6a81e60b2b52..2f8f1e609420b 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -486,10 +486,19 @@ fn collect_items_rec<'tcx>( if let hir::ItemKind::GlobalAsm { asm, .. } = item.kind { for (op, op_sp) in asm.operands { match *op { - hir::InlineAsmOperand::Const { .. } => { - // Only constants which resolve to a plain integer - // are supported. Therefore the value should not - // depend on any other items. + hir::InlineAsmOperand::Const { anon_const } => { + match tcx.const_eval_poly(anon_const.def_id.to_def_id()) { + Ok(val) => { + collect_const_value(tcx, val, &mut used_items); + } + Err(ErrorHandled::TooGeneric(..)) => { + span_bug!(*op_sp, "asm const cannot be resolved; too generic") + } + Err(err @ ErrorHandled::Reported(..)) => { + err.emit_note(tcx); + continue; + } + } } hir::InlineAsmOperand::SymFn { expr } => { let fn_ty = tcx.typeck(item_id.owner_id).expr_ty(expr); From 424000451f3f065957e3a23b4c0c05dc7a199eaa Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 14 Mar 2025 16:36:59 +0000 Subject: [PATCH 6/6] Implement asm_const_ptr for global_asm and naked_asm --- .../rustc_codegen_cranelift/src/global_asm.rs | 9 +- compiler/rustc_codegen_gcc/src/asm.rs | 125 ++++++++++++---- compiler/rustc_codegen_llvm/src/asm.rs | 140 ++++++++++++++---- compiler/rustc_codegen_ssa/src/base.rs | 40 +++-- compiler/rustc_codegen_ssa/src/common.rs | 2 +- .../rustc_codegen_ssa/src/mir/naked_asm.rs | 32 ++-- compiler/rustc_codegen_ssa/src/traits/asm.rs | 4 + compiler/rustc_monomorphize/src/collector.rs | 3 +- tests/assembly/asm/global_asm.rs | 5 + tests/assembly/asm/x86-types.rs | 14 +- tests/ui/asm/invalid-const-operand.rs | 7 +- tests/ui/asm/invalid-const-operand.stderr | 20 ++- .../feature-gate-asm_const_ptr.rs | 14 +- .../feature-gate-asm_const_ptr.stderr | 24 ++- 14 files changed, 344 insertions(+), 95 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/global_asm.rs b/compiler/rustc_codegen_cranelift/src/global_asm.rs index 3cf402f20dc09..ec9d00f92e5d5 100644 --- a/compiler/rustc_codegen_cranelift/src/global_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/global_asm.rs @@ -8,10 +8,10 @@ use std::sync::Arc; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_codegen_ssa::traits::{AsmCodegenMethods, GlobalAsmOperandRef}; -use rustc_middle::ty::TyCtxt; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, HasTypingEnv, LayoutError, LayoutOfHelpers, }; +use rustc_middle::ty::{Instance, TyCtxt}; use rustc_session::config::{OutputFilenames, OutputType}; use rustc_target::asm::InlineAsmArch; @@ -29,6 +29,7 @@ impl<'tcx> AsmCodegenMethods<'tcx> for GlobalAsmContext<'_, 'tcx> { operands: &[GlobalAsmOperandRef<'tcx>], options: InlineAsmOptions, _line_spans: &[Span], + _instance: Instance<'_>, ) { codegen_global_asm_inner(self.tcx, self.global_asm, template, operands, options); } @@ -132,6 +133,12 @@ fn codegen_global_asm_inner<'tcx>( let symbol = tcx.symbol_name(instance); global_asm.push_str(symbol.name); } + GlobalAsmOperandRef::ConstPointer { value: _ } => { + tcx.dcx().span_err( + span, + "asm! and global_asm! const pointer operands are not yet supported", + ); + } } } } diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index b0ea7d54eca08..b0ed88882a0ad 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use gccjit::{LValue, RValue, ToRValue, Type}; +use gccjit::{GlobalKind, LValue, RValue, ToRValue, Type}; use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_codegen_ssa::mir::operand::OperandValue; use rustc_codegen_ssa::mir::place::PlaceRef; @@ -8,8 +8,8 @@ use rustc_codegen_ssa::traits::{ AsmBuilderMethods, AsmCodegenMethods, BaseTypeCodegenMethods, BuilderMethods, GlobalAsmOperandRef, InlineAsmOperandRef, }; -use rustc_middle::bug; use rustc_middle::ty::Instance; +use rustc_middle::{bug, mir}; use rustc_span::Span; use rustc_target::asm::*; @@ -855,6 +855,7 @@ impl<'gcc, 'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { operands: &[GlobalAsmOperandRef<'tcx>], options: InlineAsmOptions, _line_spans: &[Span], + instance: Instance<'tcx>, ) { let asm_arch = self.tcx.sess.asm_arch.unwrap(); @@ -862,6 +863,98 @@ impl<'gcc, 'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { let att_dialect = matches!(asm_arch, InlineAsmArch::X86 | InlineAsmArch::X86_64) && options.contains(InlineAsmOptions::ATT_SYNTAX); + // Convert all operands to string interpolations + let converted_operands = operands + .iter() + .enumerate() + .map(|(operand_idx, operand)| { + match *operand { + GlobalAsmOperandRef::Interpolate { ref string } => { + // Const operands get injected directly into the + // template. Note that we don't need to escape $ + // here unlike normal inline assembly. + string.to_owned() + } + GlobalAsmOperandRef::ConstPointer { value } => { + let (prov, offset) = value.into_parts(); + let global_alloc = self.tcx.global_alloc(prov.alloc_id()); + let symbol = 'sym: { + let alloc = match global_alloc { + mir::interpret::GlobalAlloc::Function { instance } => { + let function = get_fn(self, instance); + self.add_used_function(function); + // TODO(@Amanieu): Additional mangling is needed on + // some targets to add a leading underscore (Mach-O) + // or byte count suffixes (x86 Windows). + break 'sym self.tcx.symbol_name(instance).name.to_owned(); + } + mir::interpret::GlobalAlloc::VTable(ty, dyn_ty) => self + .tcx + .global_alloc(self.tcx.vtable_allocation(( + ty, + dyn_ty.principal().map(|principal| { + self.tcx + .instantiate_bound_regions_with_erased(principal) + }), + ))) + .unwrap_memory(), + mir::interpret::GlobalAlloc::Static(def_id) => { + // TODO(antoyo): set the global variable as used. + // TODO(@Amanieu): Additional mangling is needed on + // some targets to add a leading underscore (Mach-O). + let instance = Instance::mono(self.tcx, def_id); + break 'sym self.tcx.symbol_name(instance).name.to_owned(); + } + mir::interpret::GlobalAlloc::Memory(alloc) => alloc, + }; + + // For ZSTs directly codegen an aligned pointer. + if alloc.inner().len() == 0 { + assert_eq!(offset.bytes(), 0); + return format!("{}", alloc.inner().align.bytes()); + } + + let asm_name = self.tcx.symbol_name(instance); + let sym_name = format!("{asm_name}.{operand_idx}"); + + let init = crate::consts::const_alloc_to_gcc(self, alloc); + let alloc = alloc.inner(); + let typ = self.val_ty(init).get_aligned(alloc.align.bytes()); + + let global = self.declare_global_with_linkage( + &sym_name, + typ, + GlobalKind::Exported, + ); + global.global_set_initializer_rvalue(init); + // TODO(nbdd0121): set unnamed address. + // TODO(nbdd0121): set the global variable as used. + + sym_name + }; + + let offset = offset.bytes(); + if offset != 0 { format!("{symbol}+{offset}") } else { symbol } + } + GlobalAsmOperandRef::SymFn { instance } => { + let function = get_fn(self, instance); + self.add_used_function(function); + // TODO(@Amanieu): Additional mangling is needed on + // some targets to add a leading underscore (Mach-O) + // or byte count suffixes (x86 Windows). + self.tcx.symbol_name(instance).name.to_owned() + } + GlobalAsmOperandRef::SymStatic { def_id } => { + // TODO(antoyo): set the global variable as used. + // TODO(@Amanieu): Additional mangling is needed on + // some targets to add a leading underscore (Mach-O). + let instance = Instance::mono(self.tcx, def_id); + self.tcx.symbol_name(instance).name.to_owned() + } + } + }) + .collect::>(); + // Build the template string let mut template_str = ".pushsection .text\n".to_owned(); if att_dialect { @@ -885,33 +978,7 @@ impl<'gcc, 'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { } } InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span: _ } => { - match operands[operand_idx] { - GlobalAsmOperandRef::Interpolate { ref string } => { - // Const operands get injected directly into the - // template. Note that we don't need to escape % - // here unlike normal inline assembly. - template_str.push_str(string); - } - - GlobalAsmOperandRef::SymFn { instance } => { - let function = get_fn(self, instance); - self.add_used_function(function); - // TODO(@Amanieu): Additional mangling is needed on - // some targets to add a leading underscore (Mach-O) - // or byte count suffixes (x86 Windows). - let name = self.tcx.symbol_name(instance).name; - template_str.push_str(name); - } - - GlobalAsmOperandRef::SymStatic { def_id } => { - // TODO(antoyo): set the global variable as used. - // TODO(@Amanieu): Additional mangling is needed on - // some targets to add a leading underscore (Mach-O). - let instance = Instance::mono(self.tcx, def_id); - let name = self.tcx.symbol_name(instance).name; - template_str.push_str(name); - } - } + template_str.push_str(&converted_operands[operand_idx]); } } } diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 3dbc721f7eeba..09b1f5b2d8844 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -7,7 +7,7 @@ use rustc_codegen_ssa::traits::*; use rustc_data_structures::fx::FxHashMap; use rustc_middle::ty::Instance; use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::{bug, span_bug}; +use rustc_middle::{bug, mir, span_bug}; use rustc_span::{Pos, Span, Symbol, sym}; use rustc_target::asm::*; use smallvec::SmallVec; @@ -389,6 +389,7 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { operands: &[GlobalAsmOperandRef<'tcx>], options: InlineAsmOptions, _line_spans: &[Span], + instance: Instance<'tcx>, ) { let asm_arch = self.tcx.sess.asm_arch.unwrap(); @@ -396,6 +397,111 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { let intel_syntax = matches!(asm_arch, InlineAsmArch::X86 | InlineAsmArch::X86_64) && !options.contains(InlineAsmOptions::ATT_SYNTAX); + // Convert all operands to string interpolations + let converted_operands = operands + .iter() + .enumerate() + .map(|(operand_idx, operand)| { + match *operand { + GlobalAsmOperandRef::Interpolate { ref string } => { + // Const operands get injected directly into the + // template. Note that we don't need to escape $ + // here unlike normal inline assembly. + string.to_owned() + } + GlobalAsmOperandRef::ConstPointer { value } => { + let (prov, offset) = value.into_parts(); + let global_alloc = self.tcx.global_alloc(prov.alloc_id()); + let llval = 'llval: { + let alloc = match global_alloc { + mir::interpret::GlobalAlloc::Function { instance } => { + break 'llval self.get_fn(instance); + } + mir::interpret::GlobalAlloc::VTable(ty, dyn_ty) => self + .tcx + .global_alloc(self.tcx.vtable_allocation(( + ty, + dyn_ty.principal().map(|principal| { + self.tcx + .instantiate_bound_regions_with_erased(principal) + }), + ))) + .unwrap_memory(), + mir::interpret::GlobalAlloc::Static(def_id) => { + break 'llval self + .renamed_statics + .borrow() + .get(&def_id) + .copied() + .unwrap_or_else(|| self.get_static(def_id)); + } + mir::interpret::GlobalAlloc::Memory(alloc) => alloc, + }; + + // For ZSTs directly codegen an aligned pointer. + if alloc.inner().len() == 0 { + assert_eq!(offset.bytes(), 0); + return format!("{}", alloc.inner().align.bytes()); + } + + let asm_name = self.tcx.symbol_name(instance); + let sym_name = format!("{asm_name}.{operand_idx}"); + + let init = crate::consts::const_alloc_to_llvm( + self, alloc, /*static*/ false, + ); + let alloc = alloc.inner(); + let g = self.static_addr_of_mut(init, alloc.align, None); + if alloc.mutability.is_not() { + // NB: we can't use `static_addr_of_impl` here to avoid sharing + // the global, as we need to set name and linkage. + unsafe { llvm::LLVMSetGlobalConstant(g, llvm::True) }; + } + + llvm::set_value_name(g, sym_name.as_bytes()); + + // `static_addr_of_mut` gives us a private global which can't be + // used by global asm. Update it to a hidden internal global instead. + llvm::set_linkage(g, llvm::Linkage::InternalLinkage); + llvm::set_visibility(g, llvm::Visibility::Hidden); + g + }; + self.add_compiler_used_global(llval); + let symbol = llvm::build_string(|s| unsafe { + llvm::LLVMRustGetMangledName(llval, s); + }) + .expect("symbol is not valid UTF-8"); + + let offset = offset.bytes(); + if offset != 0 { format!("{symbol}+{offset}") } else { symbol } + } + GlobalAsmOperandRef::SymFn { instance } => { + let llval = self.get_fn(instance); + self.add_compiler_used_global(llval); + let symbol = llvm::build_string(|s| unsafe { + llvm::LLVMRustGetMangledName(llval, s); + }) + .expect("symbol is not valid UTF-8"); + symbol + } + GlobalAsmOperandRef::SymStatic { def_id } => { + let llval = self + .renamed_statics + .borrow() + .get(&def_id) + .copied() + .unwrap_or_else(|| self.get_static(def_id)); + self.add_compiler_used_global(llval); + let symbol = llvm::build_string(|s| unsafe { + llvm::LLVMRustGetMangledName(llval, s); + }) + .expect("symbol is not valid UTF-8"); + symbol + } + } + }) + .collect::>(); + // Build the template string let mut template_str = String::new(); if intel_syntax { @@ -405,37 +511,7 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { match *piece { InlineAsmTemplatePiece::String(ref s) => template_str.push_str(s), InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span: _ } => { - match operands[operand_idx] { - GlobalAsmOperandRef::Interpolate { ref string } => { - // Const operands get injected directly into the - // template. Note that we don't need to escape $ - // here unlike normal inline assembly. - template_str.push_str(string); - } - GlobalAsmOperandRef::SymFn { instance } => { - let llval = self.get_fn(instance); - self.add_compiler_used_global(llval); - let symbol = llvm::build_string(|s| unsafe { - llvm::LLVMRustGetMangledName(llval, s); - }) - .expect("symbol is not valid UTF-8"); - template_str.push_str(&symbol); - } - GlobalAsmOperandRef::SymStatic { def_id } => { - let llval = self - .renamed_statics - .borrow() - .get(&def_id) - .copied() - .unwrap_or_else(|| self.get_static(def_id)); - self.add_compiler_used_global(llval); - let symbol = llvm::build_string(|s| unsafe { - llvm::LLVMRustGetMangledName(llval, s); - }) - .expect("symbol is not valid UTF-8"); - template_str.push_str(&symbol); - } - } + template_str.push_str(&converted_operands[operand_idx]) } } } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 64324584a62f7..731f513cdc129 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -20,9 +20,9 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType}; use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_middle::middle::{exported_symbols, lang_items}; -use rustc_middle::mir::BinOp; -use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::mir::interpret::{ErrorHandled, Scalar}; use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem, MonoItemPartitions}; +use rustc_middle::mir::{BinOp, ConstValue}; use rustc_middle::query::Providers; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; @@ -434,13 +434,27 @@ where Ok(const_value) => { let ty = cx.tcx().typeck_body(anon_const.body).node_type(anon_const.hir_id); - let string = common::asm_const_to_str( - cx.tcx(), - *op_sp, - const_value, - cx.layout_of(ty), - ); - GlobalAsmOperandRef::Interpolate { string } + let ConstValue::Scalar(scalar) = const_value else { + span_bug!( + *op_sp, + "expected Scalar for promoted asm const, but got {:#?}", + const_value + ) + }; + match scalar { + Scalar::Int(_) => { + let string = common::asm_const_to_str( + cx.tcx(), + *op_sp, + const_value, + cx.layout_of(ty), + ); + GlobalAsmOperandRef::Interpolate { string } + } + Scalar::Ptr(value, _) => { + GlobalAsmOperandRef::ConstPointer { value } + } + } } Err(ErrorHandled::Reported { .. }) => { // An error has already been reported and @@ -482,7 +496,13 @@ where }) .collect(); - cx.codegen_global_asm(asm.template, &operands, asm.options, asm.line_spans); + cx.codegen_global_asm( + asm.template, + &operands, + asm.options, + asm.line_spans, + Instance::mono(cx.tcx(), item_id.owner_id.to_def_id()), + ); } else { span_bug!(item.span, "Mismatch between hir::Item type and MonoItem type") } diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index 6d0c9d8d06645..aff8ffb1b700f 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -165,7 +165,7 @@ pub fn asm_const_to_str<'tcx>( }; let value = scalar.assert_scalar_int().to_bits(ty_and_layout.size); match ty_and_layout.ty.kind() { - ty::Uint(_) => value.to_string(), + ty::Uint(_) | ty::RawPtr(..) | ty::Ref(..) => value.to_string(), ty::Int(int_ty) => match int_ty.normalize(tcx.sess.target.pointer_width) { ty::IntTy::I8 => (value as i8).to_string(), ty::IntTy::I16 => (value as i16).to_string(), diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 2f667fa8b2f37..2e9eac3cb0b98 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -1,8 +1,9 @@ use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind}; use rustc_attr_parsing::InstructionSetAttr; use rustc_hir::def_id::DefId; +use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::mono::{Linkage, MonoItemData, Visibility}; -use rustc_middle::mir::{InlineAsmOperand, START_BLOCK}; +use rustc_middle::mir::{ConstValue, InlineAsmOperand, START_BLOCK}; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout}; use rustc_middle::ty::{Instance, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, span_bug, ty}; @@ -53,7 +54,7 @@ pub fn codegen_naked_asm< template_vec.extend(template.iter().cloned()); template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(end.into())); - cx.codegen_global_asm(&template_vec, &operands, options, line_spans); + cx.codegen_global_asm(&template_vec, &operands, options, line_spans, instance); } fn inline_to_global_operand<'a, 'tcx, Cx: LayoutOf<'tcx, LayoutOfResult = TyAndLayout<'tcx>>>( @@ -78,14 +79,25 @@ fn inline_to_global_operand<'a, 'tcx, Cx: LayoutOf<'tcx, LayoutOfResult = TyAndL ty::EarlyBinder::bind(value.ty()), ); - let string = common::asm_const_to_str( - cx.tcx(), - value.span, - const_value, - cx.layout_of(mono_type), - ); - - GlobalAsmOperandRef::Interpolate { string } + let ConstValue::Scalar(scalar) = const_value else { + span_bug!( + value.span, + "expected Scalar for promoted asm const, but got {:#?}", + const_value + ) + }; + match scalar { + Scalar::Int(_) => { + let string = common::asm_const_to_str( + cx.tcx(), + value.span, + const_value, + cx.layout_of(mono_type), + ); + GlobalAsmOperandRef::Interpolate { string } + } + Scalar::Ptr(value, _) => GlobalAsmOperandRef::ConstPointer { value }, + } } InlineAsmOperand::SymFn { value } => { let mono_type = instance.instantiate_mir_and_normalize_erasing_regions( diff --git a/compiler/rustc_codegen_ssa/src/traits/asm.rs b/compiler/rustc_codegen_ssa/src/traits/asm.rs index 67f18d25a8ee4..fdba58645a491 100644 --- a/compiler/rustc_codegen_ssa/src/traits/asm.rs +++ b/compiler/rustc_codegen_ssa/src/traits/asm.rs @@ -1,5 +1,6 @@ use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_hir::def_id::DefId; +use rustc_middle::mir; use rustc_middle::ty::Instance; use rustc_span::Span; use rustc_target::asm::InlineAsmRegOrRegClass; @@ -45,6 +46,7 @@ pub enum InlineAsmOperandRef<'tcx, B: BackendTypes + ?Sized> { #[derive(Debug)] pub enum GlobalAsmOperandRef<'tcx> { Interpolate { string: String }, + ConstPointer { value: mir::interpret::Pointer }, SymFn { instance: Instance<'tcx> }, SymStatic { def_id: DefId }, } @@ -64,12 +66,14 @@ pub trait AsmBuilderMethods<'tcx>: BackendTypes { } pub trait AsmCodegenMethods<'tcx> { + /// Code generate a global or naked assembly. fn codegen_global_asm( &mut self, template: &[InlineAsmTemplatePiece], operands: &[GlobalAsmOperandRef<'tcx>], options: InlineAsmOptions, line_spans: &[Span], + instance: Instance<'tcx>, ); /// The mangled name of this instance diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 2f8f1e609420b..1cd91e0e52821 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -494,8 +494,7 @@ fn collect_items_rec<'tcx>( Err(ErrorHandled::TooGeneric(..)) => { span_bug!(*op_sp, "asm const cannot be resolved; too generic") } - Err(err @ ErrorHandled::Reported(..)) => { - err.emit_note(tcx); + Err(ErrorHandled::Reported(..)) => { continue; } } diff --git a/tests/assembly/asm/global_asm.rs b/tests/assembly/asm/global_asm.rs index 8a4bf98c7450b..deb8d72f076b5 100644 --- a/tests/assembly/asm/global_asm.rs +++ b/tests/assembly/asm/global_asm.rs @@ -5,6 +5,7 @@ //@ compile-flags: -C symbol-mangling-version=v0 #![crate_type = "rlib"] +#![feature(asm_const_ptr)] use std::arch::global_asm; @@ -26,6 +27,10 @@ global_asm!("call {}", sym my_func); global_asm!("lea rax, [rip + {}]", sym MY_STATIC); // CHECK: call _RNvC[[CRATE_IDENT:[a-zA-Z0-9]{12}]]_10global_asm6foobar global_asm!("call {}", sym foobar); +// CHECK: lea rax, [rip + _RNSC[[CRATE_IDENT]]_10global_asms4_10global_asm.0] +global_asm!("lea rax, [rip + {}]", const &1); +// CHECK: lea rax, [rip + _RNSC[[CRATE_IDENT]]_10global_asms5_10global_asm.0+4] +global_asm!("lea rax, [rip + {}]", const &[1; 2][1]); // CHECK: _RNvC[[CRATE_IDENT]]_10global_asm6foobar: fn foobar() { loop {} diff --git a/tests/assembly/asm/x86-types.rs b/tests/assembly/asm/x86-types.rs index 6120ed0d53275..921dfc69b646a 100644 --- a/tests/assembly/asm/x86-types.rs +++ b/tests/assembly/asm/x86-types.rs @@ -9,7 +9,7 @@ //@ compile-flags: -C target-feature=+avx512bw //@ compile-flags: -Zmerge-functions=disabled -#![feature(no_core, repr_simd, f16, f128)] +#![feature(no_core, repr_simd, f16, f128, asm_const_ptr)] #![crate_type = "rlib"] #![no_core] #![allow(asm_sub_register, non_camel_case_types)] @@ -92,6 +92,18 @@ pub unsafe fn sym_fn() { asm!("call {}", sym extern_func); } +// NOTE: this only works for x64, as this test is compiled with PIC, +// and on x86 PIC symbol can't be constant. +// x86_64-LABEL: const_ptr: +// x86_64: #APP +// x86_64: mov al, byte ptr [{{.*}}anon{{.*}}] +// x86_64: #NO_APP +#[cfg(x86_64)] +#[no_mangle] +pub unsafe fn const_ptr() { + asm!("mov al, byte ptr [{}]", const &1); +} + // CHECK-LABEL: sym_static: // CHECK: #APP // CHECK: mov al, byte ptr [extern_static] diff --git a/tests/ui/asm/invalid-const-operand.rs b/tests/ui/asm/invalid-const-operand.rs index e8f437196ec1c..218b49ecb8e1e 100644 --- a/tests/ui/asm/invalid-const-operand.rs +++ b/tests/ui/asm/invalid-const-operand.rs @@ -17,7 +17,7 @@ global_asm!("{}", const 0 as *mut u8); fn test1() { unsafe { - // Const operands must be integers and must be constants. + // Const operands must be integers or thin pointers asm!("{}", const 0); asm!("{}", const 0i32); @@ -25,8 +25,13 @@ fn test1() { asm!("{}", const 0f32); //~^ ERROR invalid type for `const` operand asm!("{}", const 0 as *mut u8); + asm!("{}", const &0); asm!("{}", const b"Foo".as_slice()); //~^ ERROR invalid type for `const` operand + + asm!("{}", const test1 as fn()); + asm!("{}", const test1); + //~^ ERROR invalid type for `const` operand } } diff --git a/tests/ui/asm/invalid-const-operand.stderr b/tests/ui/asm/invalid-const-operand.stderr index d09571a49221a..c6b492788b0dc 100644 --- a/tests/ui/asm/invalid-const-operand.stderr +++ b/tests/ui/asm/invalid-const-operand.stderr @@ -1,5 +1,5 @@ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/invalid-const-operand.rs:44:26 + --> $DIR/invalid-const-operand.rs:49:26 | LL | asm!("{}", const x); | ^ non-constant value @@ -11,7 +11,7 @@ LL + const x: /* Type */ = 0; | error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/invalid-const-operand.rs:47:36 + --> $DIR/invalid-const-operand.rs:52:36 | LL | asm!("{}", const const_foo(x)); | ^ non-constant value @@ -23,7 +23,7 @@ LL + const x: /* Type */ = 0; | error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/invalid-const-operand.rs:50:36 + --> $DIR/invalid-const-operand.rs:55:36 | LL | asm!("{}", const const_bar(x)); | ^ non-constant value @@ -55,7 +55,7 @@ LL | asm!("{}", const 0f32); = help: `const` operands must be of an integer or thin pointer type error: invalid type for `const` operand - --> $DIR/invalid-const-operand.rs:28:20 + --> $DIR/invalid-const-operand.rs:29:20 | LL | asm!("{}", const b"Foo".as_slice()); | ^^^^^^----------------- @@ -64,6 +64,16 @@ LL | asm!("{}", const b"Foo".as_slice()); | = help: `const` operands must be of an integer or thin pointer type -error: aborting due to 6 previous errors +error: invalid type for `const` operand + --> $DIR/invalid-const-operand.rs:33:20 + | +LL | asm!("{}", const test1); + | ^^^^^^----- + | | + | is a `fn() {test1}` + | + = help: `const` operands must be of an integer or thin pointer type + +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0435`. diff --git a/tests/ui/feature-gates/feature-gate-asm_const_ptr.rs b/tests/ui/feature-gates/feature-gate-asm_const_ptr.rs index cc124ae39a399..cdcb5995a0f08 100644 --- a/tests/ui/feature-gates/feature-gate-asm_const_ptr.rs +++ b/tests/ui/feature-gates/feature-gate-asm_const_ptr.rs @@ -1,8 +1,20 @@ //@ only-x86_64 -use std::arch::asm; +use std::arch::{asm, global_asm, naked_asm}; + +global_asm!("/* {} */", const &0); +//~^ ERROR using pointers in asm `const` operand is experimental + +#[unsafe(naked)] +extern "C" fn naked() { + unsafe { + naked_asm!("ret /* {} */", const &0); + //~^ ERROR using pointers in asm `const` operand is experimental + } +} fn main() { + naked(); unsafe { asm!("/* {} */", const &0); //~^ ERROR using pointers in asm `const` operand is experimental diff --git a/tests/ui/feature-gates/feature-gate-asm_const_ptr.stderr b/tests/ui/feature-gates/feature-gate-asm_const_ptr.stderr index 8cc21dd853b49..a804d8fe44be5 100644 --- a/tests/ui/feature-gates/feature-gate-asm_const_ptr.stderr +++ b/tests/ui/feature-gates/feature-gate-asm_const_ptr.stderr @@ -1,5 +1,25 @@ error[E0658]: using pointers in asm `const` operand is experimental - --> $DIR/feature-gate-asm_const_ptr.rs:7:26 + --> $DIR/feature-gate-asm_const_ptr.rs:5:25 + | +LL | global_asm!("/* {} */", const &0); + | ^^^^^^^^ + | + = note: see issue #128464 for more information + = help: add `#![feature(asm_const_ptr)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: using pointers in asm `const` operand is experimental + --> $DIR/feature-gate-asm_const_ptr.rs:11:36 + | +LL | naked_asm!("ret /* {} */", const &0); + | ^^^^^^^^ + | + = note: see issue #128464 for more information + = help: add `#![feature(asm_const_ptr)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: using pointers in asm `const` operand is experimental + --> $DIR/feature-gate-asm_const_ptr.rs:19:26 | LL | asm!("/* {} */", const &0); | ^^^^^^^^ @@ -8,6 +28,6 @@ LL | asm!("/* {} */", const &0); = help: add `#![feature(asm_const_ptr)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 1 previous error +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0658`.