Skip to content
Open
Show file tree
Hide file tree
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
8 changes: 7 additions & 1 deletion compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2841,6 +2841,9 @@ pub enum InlineAsmOperand {
Const {
anon_const: AnonConst,
},
Interpolate {
anon_const: AnonConst,
},
Sym {
sym: InlineAsmSym,
},
Expand All @@ -2856,7 +2859,10 @@ impl InlineAsmOperand {
| Self::Out { reg, .. }
| Self::InOut { reg, .. }
| Self::SplitInOut { reg, .. } => Some(reg),
Self::Const { .. } | Self::Sym { .. } | Self::Label { .. } => None,
Self::Const { .. }
| Self::Interpolate { .. }
| Self::Sym { .. }
| Self::Label { .. } => None,
}
}
}
Expand Down
20 changes: 16 additions & 4 deletions compiler/rustc_ast_lowering/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ use super::LoweringContext;
use super::errors::{
AbiSpecifiedMultipleTimes, AttSyntaxOnlyX86, ClobberAbiNotSupported,
InlineAsmUnsupportedTarget, InvalidAbiClobberAbi, InvalidAsmTemplateModifierConst,
InvalidAsmTemplateModifierLabel, InvalidAsmTemplateModifierRegClass,
InvalidAsmTemplateModifierRegClassSub, InvalidAsmTemplateModifierSym, InvalidRegister,
InvalidRegisterClass, RegisterClassOnlyClobber, RegisterClassOnlyClobberStable,
RegisterConflict,
InvalidAsmTemplateModifierInterpolate, InvalidAsmTemplateModifierLabel,
InvalidAsmTemplateModifierRegClass, InvalidAsmTemplateModifierRegClassSub,
InvalidAsmTemplateModifierSym, InvalidRegister, InvalidRegisterClass, RegisterClassOnlyClobber,
RegisterClassOnlyClobberStable, RegisterConflict,
};
use crate::{AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, ParamMode};

Expand Down Expand Up @@ -199,6 +199,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
InlineAsmOperand::Const { anon_const } => hir::InlineAsmOperand::Const {
anon_const: self.lower_const_block(anon_const),
},
InlineAsmOperand::Interpolate { anon_const } => {
hir::InlineAsmOperand::Interpolate {
anon_const: self.lower_const_block(anon_const),
}
}
InlineAsmOperand::Sym { sym } => {
let static_def_id = self
.get_partial_res(sym.id)
Expand Down Expand Up @@ -285,6 +290,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
op_span: op_sp,
});
}
hir::InlineAsmOperand::Interpolate { .. } => {
self.dcx().emit_err(InvalidAsmTemplateModifierInterpolate {
placeholder_span,
op_span: op_sp,
});
}
hir::InlineAsmOperand::SymFn { .. }
| hir::InlineAsmOperand::SymStatic { .. } => {
self.dcx().emit_err(InvalidAsmTemplateModifierSym {
Expand Down Expand Up @@ -354,6 +365,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
| hir::InlineAsmOperand::SplitInOut { .. } => (true, true),

hir::InlineAsmOperand::Const { .. }
| hir::InlineAsmOperand::Interpolate { .. }
| hir::InlineAsmOperand::SymFn { .. }
| hir::InlineAsmOperand::SymStatic { .. }
| hir::InlineAsmOperand::Label { .. } => {
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_ast_lowering/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,16 @@ pub(crate) struct InvalidAsmTemplateModifierConst {
pub op_span: Span,
}

#[derive(Diagnostic)]
#[diag("asm template modifiers are not allowed for `interpolate` arguments")]
pub(crate) struct InvalidAsmTemplateModifierInterpolate {
#[primary_span]
#[label("template modifier")]
pub placeholder_span: Span,
#[label("argument")]
pub op_span: Span,
}

#[derive(Diagnostic)]
#[diag("asm template modifiers are not allowed for `sym` arguments")]
pub(crate) struct InvalidAsmTemplateModifierSym {
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1665,6 +1665,7 @@ impl<'a> State<'a> {
matches!(reg, InlineAsmRegOrRegClass::Reg(_))
}
InlineAsmOperand::Const { .. }
| InlineAsmOperand::Interpolate { .. }
| InlineAsmOperand::Sym { .. }
| InlineAsmOperand::Label { .. } => false,
}
Expand Down Expand Up @@ -1805,6 +1806,11 @@ impl<'a> State<'a> {
s.space();
s.print_expr(&anon_const.value, FixupContext::default());
}
InlineAsmOperand::Interpolate { anon_const } => {
s.word("interpolate");
s.space();
s.print_expr(&anon_const.value, FixupContext::default());
}
InlineAsmOperand::Sym { sym } => {
s.word("sym");
s.space();
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,7 @@ impl<'a, 'tcx> ResultsVisitor<'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a,
}
}
InlineAsmOperand::Const { value: _ }
| InlineAsmOperand::Interpolate { value: _ }
| InlineAsmOperand::SymFn { value: _ }
| InlineAsmOperand::SymStatic { def_id: _ }
| InlineAsmOperand::Label { target_index: _ } => {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> {
}
}
InlineAsmOperand::Const { value: _ }
| InlineAsmOperand::Interpolate { value: _ }
| InlineAsmOperand::SymFn { value: _ }
| InlineAsmOperand::SymStatic { def_id: _ }
| InlineAsmOperand::Label { target_index: _ } => {}
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_cranelift/src/global_asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ fn codegen_global_asm_inner<'tcx>(
GlobalAsmOperandRef::Const { ref string } => {
global_asm.push_str(string);
}
GlobalAsmOperandRef::Interpolate { string } => {
global_asm.push_str(string);
}
GlobalAsmOperandRef::SymFn { instance } => {
if cfg!(not(feature = "inline_asm_sym")) {
tcx.dcx().span_err(
Expand Down
20 changes: 19 additions & 1 deletion compiler/rustc_codegen_cranelift/src/inline_asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ pub(crate) enum CInlineAsmOperand<'tcx> {
Const {
value: String,
},
Interpolate {
value: &'tcx str,
},
Symbol {
symbol: String,
},
Expand Down Expand Up @@ -104,6 +107,16 @@ pub(crate) fn codegen_inline_asm_terminator<'tcx>(
);
CInlineAsmOperand::Const { value }
}
InlineAsmOperand::Interpolate { ref value } => {
let (const_value, ty) = crate::constant::eval_mir_constant(fx, value);
let value = rustc_codegen_ssa::common::asm_interpolate_to_str(
fx.tcx,
span,
const_value,
fx.layout_of(ty),
);
CInlineAsmOperand::Interpolate { value }
}
InlineAsmOperand::SymFn { ref value } => {
if cfg!(not(feature = "inline_asm_sym")) {
fx.tcx
Expand Down Expand Up @@ -227,7 +240,9 @@ pub(crate) fn codegen_inline_asm_inner<'tcx>(
outputs.push((asm_gen.stack_slots_output[i].unwrap(), *out_place));
}
}
CInlineAsmOperand::Const { value: _ } | CInlineAsmOperand::Symbol { symbol: _ } => {}
CInlineAsmOperand::Const { value: _ }
| CInlineAsmOperand::Interpolate { value: _ }
| CInlineAsmOperand::Symbol { symbol: _ } => {}
}
}

Expand Down Expand Up @@ -585,6 +600,9 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> {
CInlineAsmOperand::Const { ref value } => {
generated_asm.push_str(value);
}
CInlineAsmOperand::Interpolate { value } => {
generated_asm.push_str(value);
}
CInlineAsmOperand::Symbol { ref symbol } => {
if binary_format == BinaryFormat::Macho {
generated_asm.push('_');
Expand Down
19 changes: 19 additions & 0 deletions compiler/rustc_codegen_gcc/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,10 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
constants_len += string.len() + att_dialect as usize;
}

InlineAsmOperandRef::Interpolate { string } => {
constants_len += string.len() + att_dialect as usize;
}

InlineAsmOperandRef::SymFn { instance } => {
// FIXME(@Amanieu): Additional mangling is needed on
// some targets to add a leading underscore (Mach-O)
Expand Down Expand Up @@ -422,6 +426,10 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
// processed in the previous pass
}

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

InlineAsmOperandRef::Label { .. } => {
// processed in the previous pass
}
Expand Down Expand Up @@ -515,6 +523,10 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
template_str.push_str(string);
}

InlineAsmOperandRef::Interpolate { string } => {
template_str.push_str(string);

@Amanieu Amanieu Jun 3, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same here, this needs the same escaping as the main template string.

View changes since the review

}

InlineAsmOperandRef::Label { label } => {
let label_gcc_index =
labels.iter().position(|&l| l == label).expect("wrong rust index");
Expand Down Expand Up @@ -921,6 +933,13 @@ impl<'gcc, 'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
template_str.push_str(string);
}

GlobalAsmOperandRef::Interpolate { string } => {
// Interpolate 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);
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_codegen_llvm/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
// Const operands get injected directly into the template
template_str.push_str(string);
}
InlineAsmOperandRef::Interpolate { string } => {
// Interpolate operands get injected directly into the template
template_str.push_str(string);
}
Comment on lines +211 to +214

@traviscross traviscross May 31, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What's the intended behavior of this?

fn zero(mut x: u64) -> u64 {
    unsafe {
        asm!("mov {0}, {1}", interpolate "$0", inout(reg) x,
             options(att_syntax));
    }
    x
}

View changes since the review

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This absolutely needs to have $ escaped, just like a normal template string piece.

InlineAsmOperandRef::SymFn { .. }
| InlineAsmOperandRef::SymStatic { .. } => {
// Only emit the raw symbol name
Expand Down Expand Up @@ -409,6 +413,12 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
// here unlike normal inline assembly.
template_str.push_str(string);
}
GlobalAsmOperandRef::Interpolate { string } => {
// Interpolate 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);
Expand Down
25 changes: 25 additions & 0 deletions compiler/rustc_codegen_ssa/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,31 @@ where
}
}
}
rustc_hir::InlineAsmOperand::Interpolate { ref anon_const } => {
match cx.tcx().const_eval_poly(anon_const.def_id.to_def_id()) {
Ok(const_value) => {
let ty =
cx.tcx().typeck_body(anon_const.body).node_type(anon_const.hir_id);
let string = common::asm_interpolate_to_str(
cx.tcx(),
*op_sp,
const_value,
cx.layout_of(ty),
);
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::Interpolate { string: "" }
}
Err(ErrorHandled::TooGeneric(_)) => {
span_bug!(*op_sp, "asm interpolate cannot be resolved; too generic")
}
}
}
rustc_hir::InlineAsmOperand::SymFn { expr } => {
let ty = cx.tcx().typeck(item_id.owner_id).expr_ty(expr);
let instance = match ty.kind() {
Expand Down
59 changes: 59 additions & 0 deletions compiler/rustc_codegen_ssa/src/common.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#![allow(non_camel_case_types)]

use rustc_abi::{BackendRepr, Primitive, Scalar, Size};
use rustc_hir::LangItem;
use rustc_hir::attrs::PeImportNameType;
use rustc_middle::mir::interpret::{self, alloc_range};
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{self, Instance, TyCtxt};
use rustc_middle::{bug, mir, span_bug};
Expand Down Expand Up @@ -170,6 +172,63 @@ pub fn asm_const_to_str<'tcx>(
}
}

pub fn asm_interpolate_to_str<'tcx>(

@Amanieu Amanieu Jun 3, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not too familiar with the consteval code, but I wonder if some ugly edge cases could be triggered by passing in a const-transmute of something that isn't a slice (e.g. transmute a tuple of a dangling pointer + a random length to a &str). We shouldn't ICE in these cases, and instead return a proper error.

View changes since the review

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

consteval will check for validity invariant violations, so that should be the case, but safety invariant such as UTF-8 is not checked.

tcx: TyCtxt<'tcx>,
sp: Span,
const_value: mir::ConstValue,
ty_and_layout: TyAndLayout<'tcx>,
) -> &'tcx str {
let (alloc_id, range) = match const_value {
mir::ConstValue::Slice { alloc_id, meta } => {
(alloc_id, alloc_range(Size::ZERO, Size::from_bytes(meta)))
}
Comment on lines +182 to +184

@daxpedda daxpedda May 18, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I was unable to actually trigger this.
If this really can't happen we should remove it.

View changes since the review

mir::ConstValue::Indirect { alloc_id, offset } => {
let alloc = tcx.global_alloc(alloc_id).unwrap_memory().inner();

let BackendRepr::ScalarPair(
a @ Scalar::Initialized { value: Primitive::Pointer(_), .. },
b @ Scalar::Initialized { value: Primitive::Int(..), .. },
) = ty_and_layout.backend_repr
else {
span_bug!(
sp,
"expected slice ABI in Indirect for promoted asm interpolate, but got {:#?}",
ty_and_layout.backend_repr
)
};

let (a_size, b_size) = (a.size(&tcx), b.size(&tcx));
let b_offset = (offset + a_size).align_to(b.align(&tcx).abi);

let ptr = alloc.read_scalar(&tcx, alloc_range(offset, a_size), true);
let size = alloc.read_scalar(&tcx, alloc_range(b_offset, b_size), false);
let Ok(interpret::Scalar::Ptr(ptr, _)) = ptr else {
span_bug!(
sp,
"expected Ptr in ScalarPair for promoted asm interpolate, but got {ptr:#?}",
)
};
let Ok(interpret::Scalar::Int(size)) = size else {
span_bug!(
sp,
"expected Int in ScalarPair for promoted asm interpolate, but got {size:#?}",
)
};
Comment on lines +205 to +216

@daxpedda daxpedda May 18, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure if this is overkill. Happy to just unwrap() + unreachable!() if it actually makes sense. I assume at least the interpret::Scalar variant could be behind an unreachable!() because we check the initial abi::Scalar above.

View changes since the review


(ptr.provenance.alloc_id(), alloc_range(Size::ZERO, Size::from_bytes(size)))
}
_ => span_bug!(
sp,
"expected Slice or Indirect for promoted asm interpolate, but got {:#?}",
const_value
),
};

let alloc = tcx.global_alloc(alloc_id).unwrap_memory().inner();
let bytes = alloc.get_bytes_strip_provenance(&tcx, range).unwrap();
unsafe { str::from_utf8_unchecked(bytes) }

@nbdd0121 nbdd0121 Jun 3, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is unsound as bytes can be invalid UTF-8

View changes since the review

}

pub fn is_mingw_gnu_toolchain(target: &Target) -> bool {
target.os == Os::Windows && target.env == Env::Gnu && target.cfg_abi == CfgAbi::Unspecified
}
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_codegen_ssa/src/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1438,6 +1438,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
);
InlineAsmOperandRef::Const { string }
}
mir::InlineAsmOperand::Interpolate { ref value } => {
let const_value = self.eval_mir_constant(value);
let string = common::asm_interpolate_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_);
if let ty::FnDef(def_id, args) = *const_.ty().kind() {
Expand Down
Loading
Loading