Skip to content

Support using const pointers in asm const operand #138618

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions compiler/rustc_codegen_cranelift/src/global_asm.rs
Original file line number Diff line number Diff line change
@@ -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);
}
@@ -104,7 +105,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 } => {
@@ -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",
);
}
}
}
}
154 changes: 123 additions & 31 deletions compiler/rustc_codegen_gcc/src/asm.rs
Original file line number Diff line number Diff line change
@@ -2,16 +2,16 @@

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;
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::*;

@@ -296,10 +296,18 @@ 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;
}

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)
@@ -411,6 +419,10 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
});
}

InlineAsmOperandRef::Interpolate { .. } => {
// processed in the previous pass
}

InlineAsmOperandRef::Const { .. } => {
// processed in the previous pass
}
@@ -488,6 +500,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)
@@ -504,7 +525,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);
}

@@ -837,13 +858,110 @@ 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();

// Default to Intel syntax on x86
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.prov_and_relative_offset();
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,
mir::interpret::GlobalAlloc::TypeId { .. } => {
// This is not an actual allocation, just return the offset.
return format!("{}", offset.bytes());
}
};

// 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_uncached(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::<Vec<_>>();

// Build the template string
let mut template_str = ".pushsection .text\n".to_owned();
if att_dialect {
@@ -867,33 +985,7 @@ impl<'gcc, 'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
}
}
InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span: _ } => {
match operands[operand_idx] {
GlobalAsmOperandRef::Const { 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]);
}
}
}
154 changes: 121 additions & 33 deletions compiler/rustc_codegen_llvm/src/asm.rs
Original file line number Diff line number Diff line change
@@ -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;
@@ -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,7 +210,10 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
template_str.push_str(&format!("${{{}}}", op_idx[&operand_idx]));
}
}
InlineAsmOperandRef::Const { ref string } => {
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);
}
@@ -381,9 +389,119 @@ 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();

// 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.prov_and_relative_offset();
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,
mir::interpret::GlobalAlloc::TypeId { .. } => {
// This is not an actual allocation, just return the offset.
return format!("{}", offset.bytes());
}
};
let alloc = alloc.inner();

// For ZSTs directly codegen an aligned pointer.
if alloc.len() == 0 {
assert_eq!(offset.bytes(), 0);
return format!("{}", alloc.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 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::<Vec<_>>();

// Build the template string
let mut template_str = String::new();

@@ -401,37 +519,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::Const { 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])
}
}
}
42 changes: 31 additions & 11 deletions compiler/rustc_codegen_ssa/src/base.rs
Original file line number Diff line number Diff line change
@@ -19,9 +19,9 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType};
use rustc_middle::middle::exported_symbols::{self, SymbolExportKind};
use rustc_middle::middle::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};
@@ -409,20 +409,34 @@ 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::Const { 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
// 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")
@@ -457,7 +471,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")
}
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/common.rs
Original file line number Diff line number Diff line change
@@ -156,7 +156,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(),
21 changes: 13 additions & 8 deletions compiler/rustc_codegen_ssa/src/mir/block.rs
Original file line number Diff line number Diff line change
@@ -1186,14 +1186,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::Const { 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_);
34 changes: 23 additions & 11 deletions compiler/rustc_codegen_ssa/src/mir/naked_asm.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind};
use rustc_attr_data_structures::InstructionSetAttr;
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, ty};
use rustc_middle::{bug, span_bug, ty};
use rustc_span::sym;
use rustc_target::callconv::{ArgAbi, FnAbi, PassMode};
use rustc_target::spec::BinaryFormat;
@@ -52,7 +53,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>>>(
@@ -77,14 +78,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::Const { 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(
17 changes: 15 additions & 2 deletions compiler/rustc_codegen_ssa/src/traits/asm.rs
Original file line number Diff line number Diff line change
@@ -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;
@@ -25,9 +26,18 @@ pub enum InlineAsmOperandRef<'tcx, B: BackendTypes + ?Sized> {
in_value: OperandRef<'tcx, B::Value>,
out_place: Option<PlaceRef<'tcx, B::Value>>,
},
Const {
/// Interpolate a string directly into the inline assembly.
///
/// This is distinct from `Const`, which can reference a const pointer or reference (and thus is
/// a const in Rust/linker sense but not a literal value).
///
/// We currently use this for constant integers. They could technically use `Const` as well.
Interpolate {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add more doc comment explaining the significance of the difference between Interpolate and Const for backends. It begs the question for why we ever turn const operands into strings, for example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some explaination. Technically we should be able to use Const for integers, although I am a bit conservative here and don't want to change how existing things work.

This might also be useful to have in the future if we decided to add interpolate "some CTFE string to be interpolated" new type of operand.

string: String,
},
Const {
value: OperandRef<'tcx, B::Value>,
},
SymFn {
instance: Instance<'tcx>,
},
@@ -41,7 +51,8 @@ pub enum InlineAsmOperandRef<'tcx, B: BackendTypes + ?Sized> {

#[derive(Debug)]
pub enum GlobalAsmOperandRef<'tcx> {
Const { string: String },
Interpolate { string: String },
ConstPointer { value: mir::interpret::Pointer },
SymFn { instance: Instance<'tcx> },
SymStatic { def_id: DefId },
}
@@ -61,12 +72,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
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
@@ -377,6 +377,8 @@ declare_features! (
(unstable, arbitrary_self_types_pointers, "1.83.0", Some(44874)),
/// Allows #[cfg(...)] on inline assembly templates and operands.
(unstable, asm_cfg, "1.89.0", Some(140364)),
/// 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.
4 changes: 4 additions & 0 deletions compiler/rustc_hir_typeck/messages.ftl
Original file line number Diff line number Diff line change
@@ -15,6 +15,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 =
7 changes: 7 additions & 0 deletions compiler/rustc_hir_typeck/src/errors.rs
Original file line number Diff line number Diff line change
@@ -18,6 +18,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 {
35 changes: 33 additions & 2 deletions compiler/rustc_hir_typeck/src/inline_asm.rs
Original file line number Diff line number Diff line change
@@ -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<Symbol>,
@@ -511,15 +511,46 @@ 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")
.with_span_label(
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();
}
}
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/mir/mono.rs
Original file line number Diff line number Diff line change
@@ -121,7 +121,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()))
}
}
}
16 changes: 12 additions & 4 deletions compiler/rustc_monomorphize/src/collector.rs
Original file line number Diff line number Diff line change
@@ -486,10 +486,18 @@ 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(ErrorHandled::Reported(..)) => {
continue;
}
}
}
hir::InlineAsmOperand::SymFn { expr } => {
let fn_ty = tcx.typeck(item_id.owner_id).expr_ty(expr);
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
@@ -468,6 +468,7 @@ symbols! {
asm,
asm_cfg,
asm_const,
asm_const_ptr,
asm_experimental_arch,
asm_experimental_reg,
asm_goto,
5 changes: 5 additions & 0 deletions compiler/rustc_symbol_mangling/src/legacy.rs
Original file line number Diff line number Diff line change
@@ -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
11 changes: 10 additions & 1 deletion compiler/rustc_symbol_mangling/src/v0.rs
Original file line number Diff line number Diff line change
@@ -867,6 +867,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 => {
Copy link
Member

@compiler-errors compiler-errors Jul 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just give this a prefix? Then we'll encode it with that prefix + disambiguator + empty name. That is to say, it feels a bit obtuse to encode it like this, not that it's necessarily wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is that existing demangler should be able to demangle this without any changes. The symbol generated for global_asms should not be very meaningful, so similar to shims.

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',
@@ -880,7 +890,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(_)
5 changes: 5 additions & 0 deletions tests/assembly/asm/global_asm.rs
Original file line number Diff line number Diff line change
@@ -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 {}
14 changes: 13 additions & 1 deletion tests/assembly/asm/x86-types.rs
Original file line number Diff line number Diff line change
@@ -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{{.*}}]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// x86_64: mov al, byte ptr [{{.*}}anon{{.*}}]

What was the expansion you observed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mov al, byte ptr [.Lanon.2e8c0013b01edcd3779e8174c2338a00.0]

// 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]
9 changes: 5 additions & 4 deletions tests/ui/asm/const-refs-to-static.rs
Original file line number Diff line number Diff line change
@@ -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() {}
22 changes: 0 additions & 22 deletions tests/ui/asm/const-refs-to-static.stderr

This file was deleted.

11 changes: 8 additions & 3 deletions tests/ui/asm/invalid-const-operand.rs
Original file line number Diff line number Diff line change
@@ -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,20 +14,23 @@ 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 {
// Const operands must be integers and must be constants.
// Const operands must be integers or thin pointers

asm!("{}", const 0);
asm!("{}", const 0i32);
asm!("{}", const 0i128);
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

asm!("{}", const test1 as fn());
asm!("{}", const test1);
//~^ ERROR invalid type for `const` operand
}
}
46 changes: 18 additions & 28 deletions tests/ui/asm/invalid-const-operand.stderr
Original file line number Diff line number Diff line change
@@ -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
@@ -35,55 +35,45 @@ 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
= help: `const` operands must be of an integer or thin pointer type

error: invalid type for `const` operand
--> $DIR/invalid-const-operand.rs:26:20
--> $DIR/invalid-const-operand.rs:29:20
|
LL | asm!("{}", const 0 as *mut u8);
| ^^^^^^------------
LL | asm!("{}", const b"Foo".as_slice());
| ^^^^^^-----------------
| |
| is a `*mut u8`
| 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: invalid type for `const` operand
--> $DIR/invalid-const-operand.rs:28:20
--> $DIR/invalid-const-operand.rs:33:20
|
LL | asm!("{}", const &0);
| ^^^^^^--
LL | asm!("{}", const test1);
| ^^^^^^-----
| |
| is a `&i32`
| is a `fn() {test1}`
|
= 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 7 previous errors

For more information about this error, try `rustc --explain E0435`.
22 changes: 22 additions & 0 deletions tests/ui/feature-gates/feature-gate-asm_const_ptr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//@ only-x86_64

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
}
}
33 changes: 33 additions & 0 deletions tests/ui/feature-gates/feature-gate-asm_const_ptr.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
error[E0658]: using pointers in asm `const` operand is experimental
--> $DIR/feature-gate-asm_const_ptr.rs:5:25
|
LL | global_asm!("/* {} */", const &0);
| ^^^^^^^^
|
= note: see issue #128464 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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);
| ^^^^^^^^
|
= note: see issue #128464 <https://github.com/rust-lang/rust/issues/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 3 previous errors

For more information about this error, try `rustc --explain E0658`.