1
Fork 0

Auto merge of #122182 - matthiaskrgr:rollup-gzimi4c, r=matthiaskrgr

Rollup of 8 pull requests

Successful merges:

 - #118623 (Improve std::fs::read_to_string example)
 - #119365 (Add asm goto support to `asm!`)
 - #120608 (Docs for std::ptr::slice_from_raw_parts)
 - #121832 (Add new Tier-3 target: `loongarch64-unknown-linux-musl`)
 - #121938 (Fix quadratic behavior of repeated vectored writes)
 - #122099 (Add  `#[inline]` to `BTreeMap::new` constructor)
 - #122103 (Make TAITs and ATPITs capture late-bound lifetimes in scope)
 - #122143 (PassWrapper: update for llvm/llvm-project@a331937197)

Failed merges:

 - #122076 (Tweak the way we protect in-place function arguments in interpreters)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-03-08 09:34:05 +00:00
commit 1b2c53a15d
107 changed files with 1126 additions and 422 deletions

View file

@ -2302,6 +2302,9 @@ pub enum InlineAsmOperand {
Sym { Sym {
sym: InlineAsmSym, sym: InlineAsmSym,
}, },
Label {
block: P<Block>,
},
} }
impl InlineAsmOperand { impl InlineAsmOperand {
@ -2311,7 +2314,7 @@ impl InlineAsmOperand {
| Self::Out { reg, .. } | Self::Out { reg, .. }
| Self::InOut { reg, .. } | Self::InOut { reg, .. }
| Self::SplitInOut { reg, .. } => Some(reg), | Self::SplitInOut { reg, .. } => Some(reg),
Self::Const { .. } | Self::Sym { .. } => None, Self::Const { .. } | Self::Sym { .. } | Self::Label { .. } => None,
} }
} }
} }

View file

@ -1331,6 +1331,7 @@ pub fn noop_visit_inline_asm<T: MutVisitor>(asm: &mut InlineAsm, vis: &mut T) {
} }
InlineAsmOperand::Const { anon_const } => vis.visit_anon_const(anon_const), InlineAsmOperand::Const { anon_const } => vis.visit_anon_const(anon_const),
InlineAsmOperand::Sym { sym } => vis.visit_inline_asm_sym(sym), InlineAsmOperand::Sym { sym } => vis.visit_inline_asm_sym(sym),
InlineAsmOperand::Label { block } => vis.visit_block(block),
} }
} }
} }

View file

@ -823,6 +823,7 @@ pub fn walk_inline_asm<'a, V: Visitor<'a>>(visitor: &mut V, asm: &'a InlineAsm)
try_visit!(visitor.visit_anon_const(anon_const)) try_visit!(visitor.visit_anon_const(anon_const))
} }
InlineAsmOperand::Sym { sym } => try_visit!(visitor.visit_inline_asm_sym(sym)), InlineAsmOperand::Sym { sym } => try_visit!(visitor.visit_inline_asm_sym(sym)),
InlineAsmOperand::Label { block } => try_visit!(visitor.visit_block(block)),
} }
} }
V::Result::output() V::Result::output()

View file

@ -88,6 +88,9 @@ ast_lowering_invalid_abi_suggestion = did you mean
ast_lowering_invalid_asm_template_modifier_const = ast_lowering_invalid_asm_template_modifier_const =
asm template modifiers are not allowed for `const` arguments asm template modifiers are not allowed for `const` arguments
ast_lowering_invalid_asm_template_modifier_label =
asm template modifiers are not allowed for `label` arguments
ast_lowering_invalid_asm_template_modifier_reg_class = ast_lowering_invalid_asm_template_modifier_reg_class =
invalid asm template modifier for this register class invalid asm template modifier for this register class

View file

@ -3,9 +3,9 @@ use crate::{ImplTraitContext, ImplTraitPosition, ParamMode, ResolverAstLoweringE
use super::errors::{ use super::errors::{
AbiSpecifiedMultipleTimes, AttSyntaxOnlyX86, ClobberAbiNotSupported, AbiSpecifiedMultipleTimes, AttSyntaxOnlyX86, ClobberAbiNotSupported,
InlineAsmUnsupportedTarget, InvalidAbiClobberAbi, InvalidAsmTemplateModifierConst, InlineAsmUnsupportedTarget, InvalidAbiClobberAbi, InvalidAsmTemplateModifierConst,
InvalidAsmTemplateModifierRegClass, InvalidAsmTemplateModifierRegClassSub, InvalidAsmTemplateModifierLabel, InvalidAsmTemplateModifierRegClass,
InvalidAsmTemplateModifierSym, InvalidRegister, InvalidRegisterClass, RegisterClassOnlyClobber, InvalidAsmTemplateModifierRegClassSub, InvalidAsmTemplateModifierSym, InvalidRegister,
RegisterConflict, InvalidRegisterClass, RegisterClassOnlyClobber, RegisterConflict,
}; };
use super::LoweringContext; use super::LoweringContext;
@ -237,6 +237,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
} }
} }
} }
InlineAsmOperand::Label { block } => {
if !self.tcx.features().asm_goto {
feature_err(
sess,
sym::asm_goto,
*op_sp,
"label operands for inline assembly are unstable",
)
.emit();
}
hir::InlineAsmOperand::Label { block: self.lower_block(block, false) }
}
}; };
(op, self.lower_span(*op_sp)) (op, self.lower_span(*op_sp))
}) })
@ -296,6 +308,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
op_span: op_sp, op_span: op_sp,
}); });
} }
hir::InlineAsmOperand::Label { .. } => {
self.dcx().emit_err(InvalidAsmTemplateModifierLabel {
placeholder_span,
op_span: op_sp,
});
}
} }
} }
} }
@ -335,7 +353,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
hir::InlineAsmOperand::Const { .. } hir::InlineAsmOperand::Const { .. }
| hir::InlineAsmOperand::SymFn { .. } | hir::InlineAsmOperand::SymFn { .. }
| hir::InlineAsmOperand::SymStatic { .. } => { | hir::InlineAsmOperand::SymStatic { .. }
| hir::InlineAsmOperand::Label { .. } => {
unreachable!("{op:?} is not a register operand"); unreachable!("{op:?} is not a register operand");
} }
}; };

View file

@ -261,6 +261,16 @@ pub struct InvalidAsmTemplateModifierSym {
pub op_span: Span, pub op_span: Span,
} }
#[derive(Diagnostic, Clone, Copy)]
#[diag(ast_lowering_invalid_asm_template_modifier_label)]
pub struct InvalidAsmTemplateModifierLabel {
#[primary_span]
#[label(ast_lowering_template_modifier)]
pub placeholder_span: Span,
#[label(ast_lowering_argument)]
pub op_span: Span,
}
#[derive(Diagnostic, Clone, Copy)] #[derive(Diagnostic, Clone, Copy)]
#[diag(ast_lowering_register_class_only_clobber)] #[diag(ast_lowering_register_class_only_clobber)]
pub struct RegisterClassOnlyClobber { pub struct RegisterClassOnlyClobber {

View file

@ -275,7 +275,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
} }
Some(ty) => this.lower_ty( Some(ty) => this.lower_ty(
ty, ty,
ImplTraitContext::TypeAliasesOpaqueTy { in_assoc_ty: false }, ImplTraitContext::OpaqueTy {
origin: hir::OpaqueTyOrigin::TyAlias {
parent: this.local_def_id(id),
in_assoc_ty: false,
},
fn_kind: None,
},
), ),
}, },
); );
@ -936,7 +942,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
Some(ty) => { Some(ty) => {
let ty = this.lower_ty( let ty = this.lower_ty(
ty, ty,
ImplTraitContext::TypeAliasesOpaqueTy { in_assoc_ty: true }, ImplTraitContext::OpaqueTy {
origin: hir::OpaqueTyOrigin::TyAlias {
parent: this.local_def_id(i.id),
in_assoc_ty: true,
},
fn_kind: None,
},
); );
hir::ImplItemKind::Type(ty) hir::ImplItemKind::Type(ty)
} }

View file

@ -265,13 +265,12 @@ enum ImplTraitContext {
/// Example: `fn foo() -> impl Debug`, where `impl Debug` is conceptually /// Example: `fn foo() -> impl Debug`, where `impl Debug` is conceptually
/// equivalent to a new opaque type like `type T = impl Debug; fn foo() -> T`. /// equivalent to a new opaque type like `type T = impl Debug; fn foo() -> T`.
/// ///
ReturnPositionOpaqueTy { OpaqueTy {
/// Origin: Either OpaqueTyOrigin::FnReturn or OpaqueTyOrigin::AsyncFn,
origin: hir::OpaqueTyOrigin, origin: hir::OpaqueTyOrigin,
fn_kind: FnDeclKind, /// Only used to change the lifetime capture rules, since
/// RPITIT captures all in scope, RPIT does not.
fn_kind: Option<FnDeclKind>,
}, },
/// Impl trait in type aliases.
TypeAliasesOpaqueTy { in_assoc_ty: bool },
/// `impl Trait` is unstably accepted in this position. /// `impl Trait` is unstably accepted in this position.
FeatureGated(ImplTraitPosition, Symbol), FeatureGated(ImplTraitPosition, Symbol),
/// `impl Trait` is not accepted in this position. /// `impl Trait` is not accepted in this position.
@ -1075,9 +1074,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// Disallow ATB in dyn types // Disallow ATB in dyn types
if self.is_in_dyn_type { if self.is_in_dyn_type {
let suggestion = match itctx { let suggestion = match itctx {
ImplTraitContext::ReturnPositionOpaqueTy { .. } ImplTraitContext::OpaqueTy { .. } | ImplTraitContext::Universal => {
| ImplTraitContext::TypeAliasesOpaqueTy { .. }
| ImplTraitContext::Universal => {
let bound_end_span = constraint let bound_end_span = constraint
.gen_args .gen_args
.as_ref() .as_ref()
@ -1417,22 +1414,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
TyKind::ImplTrait(def_node_id, bounds) => { TyKind::ImplTrait(def_node_id, bounds) => {
let span = t.span; let span = t.span;
match itctx { match itctx {
ImplTraitContext::ReturnPositionOpaqueTy { origin, fn_kind } => self ImplTraitContext::OpaqueTy { origin, fn_kind } => self.lower_opaque_impl_trait(
.lower_opaque_impl_trait(
span, span,
origin, origin,
*def_node_id, *def_node_id,
bounds, bounds,
Some(fn_kind), fn_kind,
itctx,
),
ImplTraitContext::TypeAliasesOpaqueTy { in_assoc_ty } => self
.lower_opaque_impl_trait(
span,
hir::OpaqueTyOrigin::TyAlias { in_assoc_ty },
*def_node_id,
bounds,
None,
itctx, itctx,
), ),
ImplTraitContext::Universal => { ImplTraitContext::Universal => {
@ -1553,9 +1540,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let captured_lifetimes_to_duplicate = match origin { let captured_lifetimes_to_duplicate = match origin {
hir::OpaqueTyOrigin::TyAlias { .. } => { hir::OpaqueTyOrigin::TyAlias { .. } => {
// in a TAIT like `type Foo<'a> = impl Foo<'a>`, we don't duplicate any // type alias impl trait and associated type position impl trait were
// lifetimes, since we don't have the issue that any are late-bound. // decided to capture all in-scope lifetimes, which we collect for
Vec::new() // all opaques during resolution.
self.resolver
.take_extra_lifetime_params(opaque_ty_node_id)
.into_iter()
.map(|(ident, id, _)| Lifetime { id, ident })
.collect()
} }
hir::OpaqueTyOrigin::FnReturn(..) => { hir::OpaqueTyOrigin::FnReturn(..) => {
if matches!( if matches!(
@ -1823,9 +1815,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
FnDeclKind::Fn FnDeclKind::Fn
| FnDeclKind::Inherent | FnDeclKind::Inherent
| FnDeclKind::Trait | FnDeclKind::Trait
| FnDeclKind::Impl => ImplTraitContext::ReturnPositionOpaqueTy { | FnDeclKind::Impl => ImplTraitContext::OpaqueTy {
origin: hir::OpaqueTyOrigin::FnReturn(self.local_def_id(fn_node_id)), origin: hir::OpaqueTyOrigin::FnReturn(self.local_def_id(fn_node_id)),
fn_kind: kind, fn_kind: Some(kind),
}, },
FnDeclKind::ExternFn => { FnDeclKind::ExternFn => {
ImplTraitContext::Disallowed(ImplTraitPosition::ExternFnReturn) ImplTraitContext::Disallowed(ImplTraitPosition::ExternFnReturn)
@ -1919,9 +1911,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
output, output,
coro, coro,
opaque_ty_span, opaque_ty_span,
ImplTraitContext::ReturnPositionOpaqueTy { ImplTraitContext::OpaqueTy {
origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id), origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
fn_kind, fn_kind: Some(fn_kind),
}, },
); );
arena_vec![this; bound] arena_vec![this; bound]

View file

@ -423,7 +423,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// fn f(_: impl Fn() -> impl Debug) -> impl Fn() -> impl Debug // fn f(_: impl Fn() -> impl Debug) -> impl Fn() -> impl Debug
// // disallowed --^^^^^^^^^^ allowed --^^^^^^^^^^ // // disallowed --^^^^^^^^^^ allowed --^^^^^^^^^^
// ``` // ```
FnRetTy::Ty(ty) if matches!(itctx, ImplTraitContext::ReturnPositionOpaqueTy { .. }) => { FnRetTy::Ty(ty) if matches!(itctx, ImplTraitContext::OpaqueTy { .. }) => {
if self.tcx.features().impl_trait_in_fn_trait_return { if self.tcx.features().impl_trait_in_fn_trait_return {
self.lower_ty(ty, itctx) self.lower_ty(ty, itctx)
} else { } else {

View file

@ -1452,6 +1452,10 @@ impl<'a> State<'a> {
s.print_path(&sym.path, true, 0); s.print_path(&sym.path, true, 0);
} }
} }
InlineAsmOperand::Label { block } => {
s.head("label");
s.print_block(block);
}
} }
} }
AsmArg::ClobberAbi(abi) => { AsmArg::ClobberAbi(abi) => {

View file

@ -723,7 +723,7 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro
operands, operands,
options: _, options: _,
line_spans: _, line_spans: _,
destination: _, targets: _,
unwind: _, unwind: _,
} => { } => {
for op in operands { for op in operands {
@ -749,7 +749,8 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro
} }
InlineAsmOperand::Const { value: _ } InlineAsmOperand::Const { value: _ }
| InlineAsmOperand::SymFn { value: _ } | InlineAsmOperand::SymFn { value: _ }
| InlineAsmOperand::SymStatic { def_id: _ } => {} | InlineAsmOperand::SymStatic { def_id: _ }
| InlineAsmOperand::Label { target_index: _ } => {}
} }
} }
} }

View file

@ -161,7 +161,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'cx, 'tcx> {
operands, operands,
options: _, options: _,
line_spans: _, line_spans: _,
destination: _, targets: _,
unwind: _, unwind: _,
} => { } => {
for op in operands { for op in operands {
@ -182,7 +182,8 @@ impl<'cx, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'cx, 'tcx> {
} }
InlineAsmOperand::Const { value: _ } InlineAsmOperand::Const { value: _ }
| InlineAsmOperand::SymFn { value: _ } | InlineAsmOperand::SymFn { value: _ }
| InlineAsmOperand::SymStatic { def_id: _ } => {} | InlineAsmOperand::SymStatic { def_id: _ }
| InlineAsmOperand::Label { target_index: _ } => {}
} }
} }
} }

View file

@ -367,14 +367,17 @@ fn check_opaque_type_parameter_valid(
span: Span, span: Span,
) -> Result<(), ErrorGuaranteed> { ) -> Result<(), ErrorGuaranteed> {
let opaque_ty_hir = tcx.hir().expect_item(opaque_type_key.def_id); let opaque_ty_hir = tcx.hir().expect_item(opaque_type_key.def_id);
let is_ty_alias = match opaque_ty_hir.expect_opaque_ty().origin { let (parent, is_ty_alias) = match opaque_ty_hir.expect_opaque_ty().origin {
OpaqueTyOrigin::TyAlias { .. } => true, OpaqueTyOrigin::TyAlias { parent, .. } => (parent, true),
OpaqueTyOrigin::AsyncFn(..) | OpaqueTyOrigin::FnReturn(..) => false, OpaqueTyOrigin::AsyncFn(parent) | OpaqueTyOrigin::FnReturn(parent) => (parent, false),
}; };
let opaque_generics = tcx.generics_of(opaque_type_key.def_id); let parent_generics = tcx.generics_of(parent);
let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default(); let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default();
for (i, arg) in opaque_type_key.args.iter().enumerate() {
// Only check the parent generics, which will ignore any of the
// duplicated lifetime args that come from reifying late-bounds.
for (i, arg) in opaque_type_key.args.iter().take(parent_generics.count()).enumerate() {
if let Err(guar) = arg.error_reported() { if let Err(guar) = arg.error_reported() {
return Err(guar); return Err(guar);
} }
@ -395,7 +398,7 @@ fn check_opaque_type_parameter_valid(
seen_params.entry(arg).or_default().push(i); seen_params.entry(arg).or_default().push(i);
} else { } else {
// Prevent `fn foo() -> Foo<u32>` from being defining. // Prevent `fn foo() -> Foo<u32>` from being defining.
let opaque_param = opaque_generics.param_at(i, tcx); let opaque_param = parent_generics.param_at(i, tcx);
let kind = opaque_param.kind.descr(); let kind = opaque_param.kind.descr();
return Err(tcx.dcx().emit_err(NonGenericOpaqueTypeParam { return Err(tcx.dcx().emit_err(NonGenericOpaqueTypeParam {
@ -409,10 +412,10 @@ fn check_opaque_type_parameter_valid(
for (_, indices) in seen_params { for (_, indices) in seen_params {
if indices.len() > 1 { if indices.len() > 1 {
let descr = opaque_generics.param_at(indices[0], tcx).kind.descr(); let descr = parent_generics.param_at(indices[0], tcx).kind.descr();
let spans: Vec<_> = indices let spans: Vec<_> = indices
.into_iter() .into_iter()
.map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id)) .map(|i| tcx.def_span(parent_generics.param_at(i, tcx).def_id))
.collect(); .collect();
#[allow(rustc::diagnostic_outside_of_impl)] #[allow(rustc::diagnostic_outside_of_impl)]
#[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::untranslatable_diagnostic)]

View file

@ -1770,8 +1770,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
self.assert_iscleanup(body, block_data, real_target, is_cleanup); self.assert_iscleanup(body, block_data, real_target, is_cleanup);
self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup); self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup);
} }
TerminatorKind::InlineAsm { destination, unwind, .. } => { TerminatorKind::InlineAsm { ref targets, unwind, .. } => {
if let Some(target) = destination { for &target in targets {
self.assert_iscleanup(body, block_data, target, is_cleanup); self.assert_iscleanup(body, block_data, target, is_cleanup);
} }
self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup); self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup);

View file

@ -19,6 +19,8 @@ builtin_macros_asm_expected_other = expected operand, {$is_global_asm ->
builtin_macros_asm_explicit_register_name = explicit register arguments cannot have names builtin_macros_asm_explicit_register_name = explicit register arguments cannot have names
builtin_macros_asm_mayunwind = asm labels are not allowed with the `may_unwind` option
builtin_macros_asm_modifier_invalid = asm template modifier must be a single character builtin_macros_asm_modifier_invalid = asm template modifier must be a single character
builtin_macros_asm_mutually_exclusive = the `{$opt1}` and `{$opt2}` options are mutually exclusive builtin_macros_asm_mutually_exclusive = the `{$opt1}` and `{$opt2}` options are mutually exclusive

View file

@ -164,6 +164,9 @@ pub fn parse_asm_args<'a>(
path: path.clone(), path: path.clone(),
}; };
ast::InlineAsmOperand::Sym { sym } ast::InlineAsmOperand::Sym { sym }
} else if !is_global_asm && p.eat_keyword(sym::label) {
let block = p.parse_block()?;
ast::InlineAsmOperand::Label { block }
} else if allow_templates { } else if allow_templates {
let template = p.parse_expr()?; let template = p.parse_expr()?;
// If it can't possibly expand to a string, provide diagnostics here to include other // If it can't possibly expand to a string, provide diagnostics here to include other
@ -240,6 +243,7 @@ pub fn parse_asm_args<'a>(
let mut have_real_output = false; let mut have_real_output = false;
let mut outputs_sp = vec![]; let mut outputs_sp = vec![];
let mut regclass_outputs = vec![]; let mut regclass_outputs = vec![];
let mut labels_sp = vec![];
for (op, op_sp) in &args.operands { for (op, op_sp) in &args.operands {
match op { match op {
ast::InlineAsmOperand::Out { reg, expr, .. } ast::InlineAsmOperand::Out { reg, expr, .. }
@ -257,6 +261,9 @@ pub fn parse_asm_args<'a>(
regclass_outputs.push(*op_sp); regclass_outputs.push(*op_sp);
} }
} }
ast::InlineAsmOperand::Label { .. } => {
labels_sp.push(*op_sp);
}
_ => {} _ => {}
} }
} }
@ -268,6 +275,9 @@ pub fn parse_asm_args<'a>(
// Bail out now since this is likely to confuse MIR // Bail out now since this is likely to confuse MIR
return Err(err); return Err(err);
} }
if args.options.contains(ast::InlineAsmOptions::MAY_UNWIND) && !labels_sp.is_empty() {
dcx.emit_err(errors::AsmMayUnwind { labels_sp });
}
if args.clobber_abis.len() > 0 { if args.clobber_abis.len() > 0 {
if is_global_asm { if is_global_asm {

View file

@ -766,6 +766,13 @@ pub(crate) struct AsmNoReturn {
pub(crate) outputs_sp: Vec<Span>, pub(crate) outputs_sp: Vec<Span>,
} }
#[derive(Diagnostic)]
#[diag(builtin_macros_asm_mayunwind)]
pub(crate) struct AsmMayUnwind {
#[primary_span]
pub(crate) labels_sp: Vec<Span>,
}
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(builtin_macros_global_asm_clobber_abi)] #[diag(builtin_macros_global_asm_clobber_abi)]
pub(crate) struct GlobalAsmClobberAbi { pub(crate) struct GlobalAsmClobberAbi {

View file

@ -445,7 +445,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
template, template,
operands, operands,
options, options,
destination, targets,
line_spans: _, line_spans: _,
unwind: _, unwind: _,
} => { } => {
@ -456,13 +456,25 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
); );
} }
let have_labels = if options.contains(InlineAsmOptions::NORETURN) {
!targets.is_empty()
} else {
targets.len() > 1
};
if have_labels {
fx.tcx.dcx().span_fatal(
source_info.span,
"cranelift doesn't support labels in inline assembly.",
);
}
crate::inline_asm::codegen_inline_asm_terminator( crate::inline_asm::codegen_inline_asm_terminator(
fx, fx,
source_info.span, source_info.span,
template, template,
operands, operands,
*options, *options,
*destination, targets.get(0).copied(),
); );
} }
TerminatorKind::UnwindTerminate(reason) => { TerminatorKind::UnwindTerminate(reason) => {

View file

@ -78,7 +78,8 @@ pub(crate) fn codegen_global_asm_item(tcx: TyCtxt<'_>, global_asm: &mut String,
InlineAsmOperand::In { .. } InlineAsmOperand::In { .. }
| InlineAsmOperand::Out { .. } | InlineAsmOperand::Out { .. }
| InlineAsmOperand::InOut { .. } | InlineAsmOperand::InOut { .. }
| InlineAsmOperand::SplitInOut { .. } => { | InlineAsmOperand::SplitInOut { .. }
| InlineAsmOperand::Label { .. } => {
span_bug!(op_sp, "invalid operand type for global_asm!") span_bug!(op_sp, "invalid operand type for global_asm!")
} }
} }

View file

@ -129,6 +129,9 @@ pub(crate) fn codegen_inline_asm_terminator<'tcx>(
let instance = Instance::mono(fx.tcx, def_id).polymorphize(fx.tcx); let instance = Instance::mono(fx.tcx, def_id).polymorphize(fx.tcx);
CInlineAsmOperand::Symbol { symbol: fx.tcx.symbol_name(instance).name.to_owned() } CInlineAsmOperand::Symbol { symbol: fx.tcx.symbol_name(instance).name.to_owned() }
} }
InlineAsmOperand::Label { .. } => {
span_bug!(span, "asm! label operands are not yet supported");
}
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();

View file

@ -107,7 +107,7 @@ enum ConstraintOrRegister {
impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, span: &[Span], instance: Instance<'_>, _dest_catch_funclet: Option<(Self::BasicBlock, Self::BasicBlock, Option<&Self::Funclet>)>) { fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, span: &[Span], instance: Instance<'_>, dest: Option<Self::BasicBlock>, _catch_funclet: Option<(Self::BasicBlock, Option<&Self::Funclet>)>) {
if options.contains(InlineAsmOptions::MAY_UNWIND) { if options.contains(InlineAsmOptions::MAY_UNWIND) {
self.sess().dcx() self.sess().dcx()
.create_err(UnwindingInlineAsm { span: span[0] }) .create_err(UnwindingInlineAsm { span: span[0] })
@ -126,6 +126,10 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
// added to `outputs.len()` // added to `outputs.len()`
let mut inputs = vec![]; let mut inputs = vec![];
// GCC index of a label equals its position in the array added to
// `outputs.len() + inputs.len()`.
let mut labels = vec![];
// Clobbers collected from `out("explicit register") _` and `inout("expl_reg") var => _` // Clobbers collected from `out("explicit register") _` and `inout("expl_reg") var => _`
let mut clobbers = vec![]; let mut clobbers = vec![];
@ -269,6 +273,10 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
// some targets to add a leading underscore (Mach-O). // some targets to add a leading underscore (Mach-O).
constants_len += self.tcx.symbol_name(Instance::mono(self.tcx, def_id)).name.len(); constants_len += self.tcx.symbol_name(Instance::mono(self.tcx, def_id)).name.len();
} }
InlineAsmOperandRef::Label { label } => {
labels.push(label);
}
} }
} }
@ -368,6 +376,10 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
InlineAsmOperandRef::Const { .. } => { InlineAsmOperandRef::Const { .. } => {
// processed in the previous pass // processed in the previous pass
} }
InlineAsmOperandRef::Label { .. } => {
// processed in the previous pass
}
} }
} }
@ -454,6 +466,14 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
InlineAsmOperandRef::Const { ref string } => { InlineAsmOperandRef::Const { ref string } => {
template_str.push_str(string); template_str.push_str(string);
} }
InlineAsmOperandRef::Label { label } => {
let label_gcc_index = labels.iter()
.position(|&l| l == label)
.expect("wrong rust index");
let gcc_index = label_gcc_index + outputs.len() + inputs.len();
push_to_template(Some('l'), gcc_index);
}
} }
} }
} }
@ -466,7 +486,12 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
// 4. Generate Extended Asm block // 4. Generate Extended Asm block
let block = self.llbb(); let block = self.llbb();
let extended_asm = block.add_extended_asm(None, &template_str); let extended_asm = if let Some(dest) = dest {
assert!(!labels.is_empty());
block.end_with_extended_asm_goto(None, &template_str, &labels, Some(dest))
} else {
block.add_extended_asm(None, &template_str)
};
for op in &outputs { for op in &outputs {
extended_asm.add_output_operand(None, &op.to_constraint(), op.tmp_var); extended_asm.add_output_operand(None, &op.to_constraint(), op.tmp_var);
@ -494,7 +519,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
if !options.contains(InlineAsmOptions::NOSTACK) { if !options.contains(InlineAsmOptions::NOSTACK) {
// TODO(@Commeownist): figure out how to align stack // TODO(@Commeownist): figure out how to align stack
} }
if options.contains(InlineAsmOptions::NORETURN) { if dest.is_none() && options.contains(InlineAsmOptions::NORETURN) {
let builtin_unreachable = self.context.get_builtin_function("__builtin_unreachable"); let builtin_unreachable = self.context.get_builtin_function("__builtin_unreachable");
let builtin_unreachable: RValue<'gcc> = unsafe { std::mem::transmute(builtin_unreachable) }; let builtin_unreachable: RValue<'gcc> = unsafe { std::mem::transmute(builtin_unreachable) };
self.call(self.type_void(), None, None, builtin_unreachable, &[], None); self.call(self.type_void(), None, None, builtin_unreachable, &[], None);

View file

@ -28,7 +28,8 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
options: InlineAsmOptions, options: InlineAsmOptions,
line_spans: &[Span], line_spans: &[Span],
instance: Instance<'_>, instance: Instance<'_>,
dest_catch_funclet: Option<(Self::BasicBlock, Self::BasicBlock, Option<&Self::Funclet>)>, dest: Option<Self::BasicBlock>,
catch_funclet: Option<(Self::BasicBlock, Option<&Self::Funclet>)>,
) { ) {
let asm_arch = self.tcx.sess.asm_arch.unwrap(); let asm_arch = self.tcx.sess.asm_arch.unwrap();
@ -165,6 +166,7 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
} }
// Build the template string // Build the template string
let mut labels = vec![];
let mut template_str = String::new(); let mut template_str = String::new();
for piece in template { for piece in template {
match *piece { match *piece {
@ -205,6 +207,11 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
// Only emit the raw symbol name // Only emit the raw symbol name
template_str.push_str(&format!("${{{}:c}}", op_idx[&operand_idx])); template_str.push_str(&format!("${{{}:c}}", op_idx[&operand_idx]));
} }
InlineAsmOperandRef::Label { label } => {
template_str.push_str(&format!("${{{}:l}}", constraints.len()));
constraints.push("!i".to_owned());
labels.push(label);
}
} }
} }
} }
@ -292,12 +299,14 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
&constraints.join(","), &constraints.join(","),
&inputs, &inputs,
output_type, output_type,
&labels,
volatile, volatile,
alignstack, alignstack,
dialect, dialect,
line_spans, line_spans,
options.contains(InlineAsmOptions::MAY_UNWIND), options.contains(InlineAsmOptions::MAY_UNWIND),
dest_catch_funclet, dest,
catch_funclet,
) )
.unwrap_or_else(|| span_bug!(line_spans[0], "LLVM asm constraint validation failed")); .unwrap_or_else(|| span_bug!(line_spans[0], "LLVM asm constraint validation failed"));
@ -317,7 +326,7 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
attributes::apply_to_callsite(result, llvm::AttributePlace::Function, &{ attrs }); attributes::apply_to_callsite(result, llvm::AttributePlace::Function, &{ attrs });
// Switch to the 'normal' basic block if we did an `invoke` instead of a `call` // Switch to the 'normal' basic block if we did an `invoke` instead of a `call`
if let Some((dest, _, _)) = dest_catch_funclet { if let Some(dest) = dest {
self.switch_to_block(dest); self.switch_to_block(dest);
} }
@ -415,16 +424,14 @@ pub(crate) fn inline_asm_call<'ll>(
cons: &str, cons: &str,
inputs: &[&'ll Value], inputs: &[&'ll Value],
output: &'ll llvm::Type, output: &'ll llvm::Type,
labels: &[&'ll llvm::BasicBlock],
volatile: bool, volatile: bool,
alignstack: bool, alignstack: bool,
dia: llvm::AsmDialect, dia: llvm::AsmDialect,
line_spans: &[Span], line_spans: &[Span],
unwind: bool, unwind: bool,
dest_catch_funclet: Option<( dest: Option<&'ll llvm::BasicBlock>,
&'ll llvm::BasicBlock, catch_funclet: Option<(&'ll llvm::BasicBlock, Option<&Funclet<'ll>>)>,
&'ll llvm::BasicBlock,
Option<&Funclet<'ll>>,
)>,
) -> Option<&'ll Value> { ) -> Option<&'ll Value> {
let volatile = if volatile { llvm::True } else { llvm::False }; let volatile = if volatile { llvm::True } else { llvm::False };
let alignstack = if alignstack { llvm::True } else { llvm::False }; let alignstack = if alignstack { llvm::True } else { llvm::False };
@ -457,8 +464,11 @@ pub(crate) fn inline_asm_call<'ll>(
can_throw, can_throw,
); );
let call = if let Some((dest, catch, funclet)) = dest_catch_funclet { let call = if !labels.is_empty() {
bx.invoke(fty, None, None, v, inputs, dest, catch, funclet) assert!(catch_funclet.is_none());
bx.callbr(fty, None, None, v, inputs, dest.unwrap(), labels, None)
} else if let Some((catch, funclet)) = catch_funclet {
bx.invoke(fty, None, None, v, inputs, dest.unwrap(), catch, funclet)
} else { } else {
bx.call(fty, None, None, v, inputs, None) bx.call(fty, None, None, v, inputs, None)
}; };

View file

@ -1538,6 +1538,58 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
} }
} }
pub(crate) fn callbr(
&mut self,
llty: &'ll Type,
fn_attrs: Option<&CodegenFnAttrs>,
fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
llfn: &'ll Value,
args: &[&'ll Value],
default_dest: &'ll BasicBlock,
indirect_dest: &[&'ll BasicBlock],
funclet: Option<&Funclet<'ll>>,
) -> &'ll Value {
debug!("invoke {:?} with args ({:?})", llfn, args);
let args = self.check_call("callbr", llty, llfn, args);
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
let funclet_bundle = funclet_bundle.as_ref().map(|b| &*b.raw);
let mut bundles: SmallVec<[_; 2]> = SmallVec::new();
if let Some(funclet_bundle) = funclet_bundle {
bundles.push(funclet_bundle);
}
// Emit CFI pointer type membership test
self.cfi_type_test(fn_attrs, fn_abi, llfn);
// Emit KCFI operand bundle
let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn);
let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw);
if let Some(kcfi_bundle) = kcfi_bundle {
bundles.push(kcfi_bundle);
}
let callbr = unsafe {
llvm::LLVMRustBuildCallBr(
self.llbuilder,
llty,
llfn,
default_dest,
indirect_dest.as_ptr(),
indirect_dest.len() as c_uint,
args.as_ptr(),
args.len() as c_uint,
bundles.as_ptr(),
bundles.len() as c_uint,
UNNAMED,
)
};
if let Some(fn_abi) = fn_abi {
fn_abi.apply_attrs_callsite(self, callbr);
}
callbr
}
// Emits CFI pointer type membership tests. // Emits CFI pointer type membership tests.
fn cfi_type_test( fn cfi_type_test(
&mut self, &mut self,

View file

@ -448,12 +448,14 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> {
constraint, constraint,
inputs, inputs,
self.type_void(), self.type_void(),
&[],
true, true,
false, false,
llvm::AsmDialect::Att, llvm::AsmDialect::Att,
&[span], &[span],
false, false,
None, None,
None,
) )
.unwrap_or_else(|| bug!("failed to generate inline asm call for `black_box`")); .unwrap_or_else(|| bug!("failed to generate inline asm call for `black_box`"));

View file

@ -1616,6 +1616,20 @@ extern "C" {
Name: *const c_char, Name: *const c_char,
) -> &'a Value; ) -> &'a Value;
pub fn LLVMRustBuildCallBr<'a>(
B: &Builder<'a>,
Ty: &'a Type,
Fn: &'a Value,
DefaultDest: &'a BasicBlock,
IndirectDests: *const &'a BasicBlock,
NumIndirectDests: c_uint,
Args: *const &'a Value,
NumArgs: c_uint,
OpBundles: *const &OperandBundleDef<'a>,
NumOpBundles: c_uint,
Name: *const c_char,
) -> &'a Value;
pub fn LLVMRustSetFastMath(Instr: &Value); pub fn LLVMRustSetFastMath(Instr: &Value);
pub fn LLVMRustSetAlgebraicMath(Instr: &Value); pub fn LLVMRustSetAlgebraicMath(Instr: &Value);
pub fn LLVMRustSetAllowReassoc(Instr: &Value); pub fn LLVMRustSetAllowReassoc(Instr: &Value);

View file

@ -264,7 +264,8 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
mir::UnwindAction::Unreachable => None, mir::UnwindAction::Unreachable => None,
}; };
if let Some(cleanup) = unwind_target { if operands.iter().any(|x| matches!(x, InlineAsmOperandRef::Label { .. })) {
assert!(unwind_target.is_none());
let ret_llbb = if let Some(target) = destination { let ret_llbb = if let Some(target) = destination {
fx.llbb(target) fx.llbb(target)
} else { } else {
@ -277,11 +278,29 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
options, options,
line_spans, line_spans,
instance, instance,
Some((ret_llbb, cleanup, self.funclet(fx))), Some(ret_llbb),
None,
);
MergingSucc::False
} else if let Some(cleanup) = unwind_target {
let ret_llbb = if let Some(target) = destination {
fx.llbb(target)
} else {
fx.unreachable_block()
};
bx.codegen_inline_asm(
template,
operands,
options,
line_spans,
instance,
Some(ret_llbb),
Some((cleanup, self.funclet(fx))),
); );
MergingSucc::False MergingSucc::False
} else { } else {
bx.codegen_inline_asm(template, operands, options, line_spans, instance, None); bx.codegen_inline_asm(template, operands, options, line_spans, instance, None, None);
if let Some(target) = destination { if let Some(target) = destination {
self.funclet_br(fx, bx, target, mergeable_succ) self.funclet_br(fx, bx, target, mergeable_succ)
@ -1100,7 +1119,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
operands: &[mir::InlineAsmOperand<'tcx>], operands: &[mir::InlineAsmOperand<'tcx>],
options: ast::InlineAsmOptions, options: ast::InlineAsmOptions,
line_spans: &[Span], line_spans: &[Span],
destination: Option<mir::BasicBlock>, targets: &[mir::BasicBlock],
unwind: mir::UnwindAction, unwind: mir::UnwindAction,
instance: Instance<'_>, instance: Instance<'_>,
mergeable_succ: bool, mergeable_succ: bool,
@ -1152,6 +1171,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
mir::InlineAsmOperand::SymStatic { def_id } => { mir::InlineAsmOperand::SymStatic { def_id } => {
InlineAsmOperandRef::SymStatic { def_id } InlineAsmOperandRef::SymStatic { def_id }
} }
mir::InlineAsmOperand::Label { target_index } => {
InlineAsmOperandRef::Label { label: self.llbb(targets[target_index]) }
}
}) })
.collect(); .collect();
@ -1162,7 +1184,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
&operands, &operands,
options, options,
line_spans, line_spans,
destination, if options.contains(InlineAsmOptions::NORETURN) {
None
} else {
targets.get(0).copied()
},
unwind, unwind,
instance, instance,
mergeable_succ, mergeable_succ,
@ -1318,7 +1344,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
ref operands, ref operands,
options, options,
line_spans, line_spans,
destination, ref targets,
unwind, unwind,
} => self.codegen_asm_terminator( } => self.codegen_asm_terminator(
helper, helper,
@ -1328,7 +1354,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
operands, operands,
options, options,
line_spans, line_spans,
destination, targets,
unwind, unwind,
self.instance, self.instance,
mergeable_succ(), mergeable_succ(),

View file

@ -76,7 +76,8 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> {
hir::InlineAsmOperand::In { .. } hir::InlineAsmOperand::In { .. }
| hir::InlineAsmOperand::Out { .. } | hir::InlineAsmOperand::Out { .. }
| hir::InlineAsmOperand::InOut { .. } | hir::InlineAsmOperand::InOut { .. }
| hir::InlineAsmOperand::SplitInOut { .. } => { | hir::InlineAsmOperand::SplitInOut { .. }
| hir::InlineAsmOperand::Label { .. } => {
span_bug!(*op_sp, "invalid operand type for global_asm!") span_bug!(*op_sp, "invalid operand type for global_asm!")
} }
}) })

View file

@ -33,6 +33,9 @@ pub enum InlineAsmOperandRef<'tcx, B: BackendTypes + ?Sized> {
SymStatic { SymStatic {
def_id: DefId, def_id: DefId,
}, },
Label {
label: B::BasicBlock,
},
} }
#[derive(Debug)] #[derive(Debug)]
@ -51,7 +54,8 @@ pub trait AsmBuilderMethods<'tcx>: BackendTypes {
options: InlineAsmOptions, options: InlineAsmOptions,
line_spans: &[Span], line_spans: &[Span],
instance: Instance<'_>, instance: Instance<'_>,
dest_catch_funclet: Option<(Self::BasicBlock, Self::BasicBlock, Option<&Self::Funclet>)>, dest: Option<Self::BasicBlock>,
catch_funclet: Option<(Self::BasicBlock, Option<&Self::Funclet>)>,
); );
} }

View file

@ -232,9 +232,6 @@ const_eval_non_const_fn_call =
const_eval_non_const_impl = const_eval_non_const_impl =
impl defined here, but it is not `const` impl defined here, but it is not `const`
const_eval_noreturn_asm_returned =
returned from noreturn inline assembly
const_eval_not_enough_caller_args = const_eval_not_enough_caller_args =
calling a function with fewer arguments than it requires calling a function with fewer arguments than it requires

View file

@ -374,11 +374,17 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
kind: Option<MemoryKind<Self::MemoryKind>>, kind: Option<MemoryKind<Self::MemoryKind>>,
) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>>>; ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>>>;
/// Evaluate the inline assembly.
///
/// This should take care of jumping to the next block (one of `targets`) when asm goto
/// is triggered, `targets[0]` when the assembly falls through, or diverge in case of
/// `InlineAsmOptions::NORETURN` being set.
fn eval_inline_asm( fn eval_inline_asm(
_ecx: &mut InterpCx<'mir, 'tcx, Self>, _ecx: &mut InterpCx<'mir, 'tcx, Self>,
_template: &'tcx [InlineAsmTemplatePiece], _template: &'tcx [InlineAsmTemplatePiece],
_operands: &[mir::InlineAsmOperand<'tcx>], _operands: &[mir::InlineAsmOperand<'tcx>],
_options: InlineAsmOptions, _options: InlineAsmOptions,
_targets: &[mir::BasicBlock],
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
throw_unsup_format!("inline assembly is not supported") throw_unsup_format!("inline assembly is not supported")
} }

View file

@ -1,6 +1,5 @@
use std::borrow::Cow; use std::borrow::Cow;
use rustc_ast::ast::InlineAsmOptions;
use rustc_middle::{ use rustc_middle::{
mir, mir,
ty::{ ty::{
@ -224,15 +223,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
terminator.kind terminator.kind
), ),
InlineAsm { template, ref operands, options, destination, .. } => { InlineAsm { template, ref operands, options, ref targets, .. } => {
M::eval_inline_asm(self, template, operands, options)?; M::eval_inline_asm(self, template, operands, options, targets)?;
if options.contains(InlineAsmOptions::NORETURN) {
throw_ub_custom!(fluent::const_eval_noreturn_asm_returned);
}
self.go_to_block(
destination
.expect("InlineAsm terminators without noreturn must have a destination"),
)
} }
} }

View file

@ -471,9 +471,9 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
self.check_edge(location, *real_target, EdgeKind::Normal); self.check_edge(location, *real_target, EdgeKind::Normal);
self.check_unwind_edge(location, *unwind); self.check_unwind_edge(location, *unwind);
} }
TerminatorKind::InlineAsm { destination, unwind, .. } => { TerminatorKind::InlineAsm { targets, unwind, .. } => {
if let Some(destination) = destination { for &target in targets {
self.check_edge(location, *destination, EdgeKind::Normal); self.check_edge(location, target, EdgeKind::Normal);
} }
self.check_unwind_edge(location, *unwind); self.check_unwind_edge(location, *unwind);
} }

View file

@ -345,6 +345,8 @@ declare_features! (
(unstable, asm_const, "1.58.0", Some(93332)), (unstable, asm_const, "1.58.0", Some(93332)),
/// Enables experimental inline assembly support for additional architectures. /// Enables experimental inline assembly support for additional architectures.
(unstable, asm_experimental_arch, "1.58.0", Some(93335)), (unstable, asm_experimental_arch, "1.58.0", Some(93335)),
/// Allows using `label` operands in inline assembly.
(unstable, asm_goto, "CURRENT_RUSTC_VERSION", Some(119364)),
/// Allows the `may_unwind` option in inline assembly. /// Allows the `may_unwind` option in inline assembly.
(unstable, asm_unwind, "1.58.0", Some(93334)), (unstable, asm_unwind, "1.58.0", Some(93334)),
/// Allows users to enforce equality of associated constants `TraitImpl<AssocConst=3>`. /// Allows users to enforce equality of associated constants `TraitImpl<AssocConst=3>`.

View file

@ -2562,6 +2562,8 @@ pub enum OpaqueTyOrigin {
AsyncFn(LocalDefId), AsyncFn(LocalDefId),
/// type aliases: `type Foo = impl Trait;` /// type aliases: `type Foo = impl Trait;`
TyAlias { TyAlias {
/// The type alias or associated type parent of the TAIT/ATPIT
parent: LocalDefId,
/// associated types in impl blocks for traits. /// associated types in impl blocks for traits.
in_assoc_ty: bool, in_assoc_ty: bool,
}, },
@ -2650,6 +2652,9 @@ pub enum InlineAsmOperand<'hir> {
path: QPath<'hir>, path: QPath<'hir>,
def_id: DefId, def_id: DefId,
}, },
Label {
block: &'hir Block<'hir>,
},
} }
impl<'hir> InlineAsmOperand<'hir> { impl<'hir> InlineAsmOperand<'hir> {
@ -2659,7 +2664,10 @@ impl<'hir> InlineAsmOperand<'hir> {
| Self::Out { reg, .. } | Self::Out { reg, .. }
| Self::InOut { reg, .. } | Self::InOut { reg, .. }
| Self::SplitInOut { reg, .. } => Some(reg), | Self::SplitInOut { reg, .. } => Some(reg),
Self::Const { .. } | Self::SymFn { .. } | Self::SymStatic { .. } => None, Self::Const { .. }
| Self::SymFn { .. }
| Self::SymStatic { .. }
| Self::Label { .. } => None,
} }
} }
@ -2680,6 +2688,12 @@ pub struct InlineAsm<'hir> {
pub line_spans: &'hir [Span], pub line_spans: &'hir [Span],
} }
impl InlineAsm<'_> {
pub fn contains_label(&self) -> bool {
self.operands.iter().any(|x| matches!(x.0, InlineAsmOperand::Label { .. }))
}
}
/// Represents a parameter in a function header. /// Represents a parameter in a function header.
#[derive(Debug, Clone, Copy, HashStable_Generic)] #[derive(Debug, Clone, Copy, HashStable_Generic)]
pub struct Param<'hir> { pub struct Param<'hir> {

View file

@ -1289,6 +1289,7 @@ pub fn walk_inline_asm<'v, V: Visitor<'v>>(
InlineAsmOperand::SymStatic { path, .. } => { InlineAsmOperand::SymStatic { path, .. } => {
try_visit!(visitor.visit_qpath(path, id, *op_sp)); try_visit!(visitor.visit_qpath(path, id, *op_sp));
} }
InlineAsmOperand::Label { block } => try_visit!(visitor.visit_block(block)),
} }
} }
V::Result::output() V::Result::output()

View file

@ -339,8 +339,9 @@ fn check_opaque_meets_bounds<'tcx>(
origin: &hir::OpaqueTyOrigin, origin: &hir::OpaqueTyOrigin,
) -> Result<(), ErrorGuaranteed> { ) -> Result<(), ErrorGuaranteed> {
let defining_use_anchor = match *origin { let defining_use_anchor = match *origin {
hir::OpaqueTyOrigin::FnReturn(did) | hir::OpaqueTyOrigin::AsyncFn(did) => did, hir::OpaqueTyOrigin::FnReturn(did)
hir::OpaqueTyOrigin::TyAlias { .. } => tcx.impl_trait_parent(def_id), | hir::OpaqueTyOrigin::AsyncFn(did)
| hir::OpaqueTyOrigin::TyAlias { parent: did, .. } => did,
}; };
let param_env = tcx.param_env(defining_use_anchor); let param_env = tcx.param_env(defining_use_anchor);
@ -351,14 +352,14 @@ fn check_opaque_meets_bounds<'tcx>(
let ocx = ObligationCtxt::new(&infcx); let ocx = ObligationCtxt::new(&infcx);
let args = match *origin { let args = match *origin {
hir::OpaqueTyOrigin::FnReturn(parent) | hir::OpaqueTyOrigin::AsyncFn(parent) => { hir::OpaqueTyOrigin::FnReturn(parent)
GenericArgs::identity_for_item(tcx, parent).extend_to( | hir::OpaqueTyOrigin::AsyncFn(parent)
tcx, | hir::OpaqueTyOrigin::TyAlias { parent, .. } => GenericArgs::identity_for_item(
def_id.to_def_id(), tcx, parent,
|param, _| tcx.map_rpit_lifetime_to_fn_lifetime(param.def_id.expect_local()).into(),
) )
} .extend_to(tcx, def_id.to_def_id(), |param, _| {
hir::OpaqueTyOrigin::TyAlias { .. } => GenericArgs::identity_for_item(tcx, def_id), tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()).into()
}),
}; };
let opaque_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); let opaque_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args);

View file

@ -470,6 +470,8 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
} }
}; };
} }
// No special checking is needed for labels.
hir::InlineAsmOperand::Label { .. } => {}
} }
} }
} }

View file

@ -339,7 +339,7 @@ fn compute_bidirectional_outlives_predicates<'tcx>(
predicates: &mut Vec<(ty::Clause<'tcx>, Span)>, predicates: &mut Vec<(ty::Clause<'tcx>, Span)>,
) { ) {
for param in opaque_own_params { for param in opaque_own_params {
let orig_lifetime = tcx.map_rpit_lifetime_to_fn_lifetime(param.def_id.expect_local()); let orig_lifetime = tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local());
if let ty::ReEarlyParam(..) = *orig_lifetime { if let ty::ReEarlyParam(..) = *orig_lifetime {
let dup_lifetime = ty::Region::new_early_param( let dup_lifetime = ty::Region::new_early_param(
tcx, tcx,

View file

@ -514,38 +514,11 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
// These sorts of items have no lifetime parameters at all. // These sorts of items have no lifetime parameters at all.
intravisit::walk_item(self, item); intravisit::walk_item(self, item);
} }
hir::ItemKind::OpaqueTy(hir::OpaqueTy {
origin: hir::OpaqueTyOrigin::TyAlias { .. },
..
}) => {
// Opaque types are visited when we visit the
// `TyKind::OpaqueDef`, so that they have the lifetimes from
// their parent opaque_ty in scope.
//
// The core idea here is that since OpaqueTys are generated with the impl Trait as
// their owner, we can keep going until we find the Item that owns that. We then
// conservatively add all resolved lifetimes. Otherwise we run into problems in
// cases like `type Foo<'a> = impl Bar<As = impl Baz + 'a>`.
let parent_item = self.tcx.hir().get_parent_item(item.hir_id());
let resolved_lifetimes: &ResolveBoundVars =
self.tcx.resolve_bound_vars(parent_item);
// We need to add *all* deps, since opaque tys may want them from *us*
for (&owner, defs) in resolved_lifetimes.defs.iter() {
defs.iter().for_each(|(&local_id, region)| {
self.map.defs.insert(hir::HirId { owner, local_id }, *region);
});
}
for (&owner, late_bound_vars) in resolved_lifetimes.late_bound_vars.iter() {
late_bound_vars.iter().for_each(|(&local_id, late_bound_vars)| {
self.record_late_bound_vars(
hir::HirId { owner, local_id },
late_bound_vars.clone(),
);
});
}
}
hir::ItemKind::OpaqueTy(&hir::OpaqueTy { hir::ItemKind::OpaqueTy(&hir::OpaqueTy {
origin: hir::OpaqueTyOrigin::FnReturn(parent) | hir::OpaqueTyOrigin::AsyncFn(parent), origin:
hir::OpaqueTyOrigin::FnReturn(parent)
| hir::OpaqueTyOrigin::AsyncFn(parent)
| hir::OpaqueTyOrigin::TyAlias { parent, .. },
generics, generics,
.. ..
}) => { }) => {
@ -683,26 +656,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
// the opaque_ty generics // the opaque_ty generics
let opaque_ty = self.tcx.hir().item(item_id); let opaque_ty = self.tcx.hir().item(item_id);
match &opaque_ty.kind { match &opaque_ty.kind {
hir::ItemKind::OpaqueTy(hir::OpaqueTy { hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin: _, .. }) => {}
origin: hir::OpaqueTyOrigin::TyAlias { .. },
..
}) => {
intravisit::walk_ty(self, ty);
// Elided lifetimes and late-bound lifetimes (from the parent)
// are not allowed in non-return position impl Trait
let scope = Scope::LateBoundary {
s: &Scope::TraitRefBoundary { s: self.scope },
what: "type alias impl trait",
};
self.with(scope, |this| intravisit::walk_item(this, opaque_ty));
return;
}
hir::ItemKind::OpaqueTy(hir::OpaqueTy {
origin: hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..),
..
}) => {}
i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i), i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i),
}; };

View file

@ -553,11 +553,11 @@ pub(super) fn type_of_opaque(
Ok(ty::EarlyBinder::bind(match tcx.hir_node_by_def_id(def_id) { Ok(ty::EarlyBinder::bind(match tcx.hir_node_by_def_id(def_id) {
Node::Item(item) => match item.kind { Node::Item(item) => match item.kind {
ItemKind::OpaqueTy(OpaqueTy { ItemKind::OpaqueTy(OpaqueTy {
origin: hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false }, origin: hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false, .. },
.. ..
}) => opaque::find_opaque_ty_constraints_for_tait(tcx, def_id), }) => opaque::find_opaque_ty_constraints_for_tait(tcx, def_id),
ItemKind::OpaqueTy(OpaqueTy { ItemKind::OpaqueTy(OpaqueTy {
origin: hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: true }, origin: hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: true, .. },
.. ..
}) => opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type(tcx, def_id), }) => opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type(tcx, def_id),
// Opaque types desugared from `impl Trait`. // Opaque types desugared from `impl Trait`.

View file

@ -125,15 +125,7 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc
// By default, RPIT are invariant wrt type and const generics, but they are bivariant wrt // By default, RPIT are invariant wrt type and const generics, but they are bivariant wrt
// lifetime generics. // lifetime generics.
let variances = std::iter::repeat(ty::Invariant).take(generics.count()); let mut variances = vec![ty::Invariant; generics.count()];
let mut variances: Vec<_> = match tcx.opaque_type_origin(item_def_id) {
rustc_hir::OpaqueTyOrigin::FnReturn(_) | rustc_hir::OpaqueTyOrigin::AsyncFn(_) => {
variances.collect()
}
// But TAIT are invariant for all generics
rustc_hir::OpaqueTyOrigin::TyAlias { .. } => return tcx.arena.alloc_from_iter(variances),
};
// Mark all lifetimes from parent generics as unused (Bivariant). // Mark all lifetimes from parent generics as unused (Bivariant).
// This will be overridden later if required. // This will be overridden later if required.

View file

@ -1265,6 +1265,10 @@ impl<'a> State<'a> {
s.space(); s.space();
s.print_qpath(path, true); s.print_qpath(path, true);
} }
hir::InlineAsmOperand::Label { block } => {
s.head("label");
s.print_block(block);
}
}, },
AsmArg::Options(opts) => { AsmArg::Options(opts) => {
s.word("options"); s.word("options");

View file

@ -3232,6 +3232,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
fn check_expr_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>) -> Ty<'tcx> { fn check_expr_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>) -> Ty<'tcx> {
let mut diverge = asm.options.contains(ast::InlineAsmOptions::NORETURN);
for (op, _op_sp) in asm.operands { for (op, _op_sp) in asm.operands {
match op { match op {
hir::InlineAsmOperand::In { expr, .. } => { hir::InlineAsmOperand::In { expr, .. } => {
@ -3253,13 +3255,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// be well-formed. // be well-formed.
hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymFn { .. } => {} hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymFn { .. } => {}
hir::InlineAsmOperand::SymStatic { .. } => {} hir::InlineAsmOperand::SymStatic { .. } => {}
hir::InlineAsmOperand::Label { block } => {
let previous_diverges = self.diverges.get();
// The label blocks should have unit return value or diverge.
let ty =
self.check_block_with_expected(block, ExpectHasType(self.tcx.types.unit));
if !ty.is_never() {
self.demand_suptype(block.span, self.tcx.types.unit, ty);
diverge = false;
}
// We need this to avoid false unreachable warning when a label diverges.
self.diverges.set(previous_diverges);
} }
} }
if asm.options.contains(ast::InlineAsmOptions::NORETURN) {
self.tcx.types.never
} else {
Ty::new_unit(self.tcx)
} }
if diverge { self.tcx.types.never } else { self.tcx.types.unit }
} }
fn check_offset_of( fn check_offset_of(

View file

@ -293,6 +293,9 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
| hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::Const { .. }
| hir::InlineAsmOperand::SymFn { .. } | hir::InlineAsmOperand::SymFn { .. }
| hir::InlineAsmOperand::SymStatic { .. } => {} | hir::InlineAsmOperand::SymStatic { .. } => {}
hir::InlineAsmOperand::Label { block } => {
self.walk_block(block);
}
} }
} }
} }

View file

@ -391,7 +391,7 @@ impl<'tcx> InferCtxt<'tcx> {
// Anonymous `impl Trait` // Anonymous `impl Trait`
hir::OpaqueTyOrigin::FnReturn(parent) => parent == parent_def_id, hir::OpaqueTyOrigin::FnReturn(parent) => parent == parent_def_id,
// Named `type Foo = impl Bar;` // Named `type Foo = impl Bar;`
hir::OpaqueTyOrigin::TyAlias { in_assoc_ty } => { hir::OpaqueTyOrigin::TyAlias { in_assoc_ty, .. } => {
if in_assoc_ty { if in_assoc_ty {
self.tcx.opaque_types_defined_by(parent_def_id).contains(&def_id) self.tcx.opaque_types_defined_by(parent_def_id).contains(&def_id)
} else { } else {

View file

@ -451,14 +451,30 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
Options.ObjectFilenameForDebug = OutputObjFile; Options.ObjectFilenameForDebug = OutputObjFile;
} }
if (!strcmp("zlib", DebugInfoCompression) && llvm::compression::zlib::isAvailable()) { if (!strcmp("zlib", DebugInfoCompression) && llvm::compression::zlib::isAvailable()) {
#if LLVM_VERSION_GE(19, 0)
Options.MCOptions.CompressDebugSections = DebugCompressionType::Zlib;
#else
Options.CompressDebugSections = DebugCompressionType::Zlib; Options.CompressDebugSections = DebugCompressionType::Zlib;
#endif
} else if (!strcmp("zstd", DebugInfoCompression) && llvm::compression::zstd::isAvailable()) { } else if (!strcmp("zstd", DebugInfoCompression) && llvm::compression::zstd::isAvailable()) {
#if LLVM_VERSION_GE(19, 0)
Options.MCOptions.CompressDebugSections = DebugCompressionType::Zstd;
#else
Options.CompressDebugSections = DebugCompressionType::Zstd; Options.CompressDebugSections = DebugCompressionType::Zstd;
#endif
} else if (!strcmp("none", DebugInfoCompression)) { } else if (!strcmp("none", DebugInfoCompression)) {
#if LLVM_VERSION_GE(19, 0)
Options.MCOptions.CompressDebugSections = DebugCompressionType::None;
#else
Options.CompressDebugSections = DebugCompressionType::None; Options.CompressDebugSections = DebugCompressionType::None;
#endif
} }
#if LLVM_VERSION_GE(19, 0)
Options.MCOptions.X86RelaxRelocations = RelaxELFRelocations;
#else
Options.RelaxELFRelocations = RelaxELFRelocations; Options.RelaxELFRelocations = RelaxELFRelocations;
#endif
Options.UseInitArray = UseInitArray; Options.UseInitArray = UseInitArray;
#if LLVM_VERSION_LT(17, 0) #if LLVM_VERSION_LT(17, 0)

View file

@ -1549,6 +1549,31 @@ LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
Name)); Name));
} }
extern "C" LLVMValueRef
LLVMRustBuildCallBr(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
LLVMBasicBlockRef DefaultDest,
LLVMBasicBlockRef *IndirectDests, unsigned NumIndirectDests,
LLVMValueRef *Args, unsigned NumArgs,
OperandBundleDef **OpBundles, unsigned NumOpBundles,
const char *Name) {
Value *Callee = unwrap(Fn);
FunctionType *FTy = unwrap<FunctionType>(Ty);
// FIXME: Is there a way around this?
std::vector<BasicBlock*> IndirectDestsUnwrapped;
IndirectDestsUnwrapped.reserve(NumIndirectDests);
for (unsigned i = 0; i < NumIndirectDests; ++i) {
IndirectDestsUnwrapped.push_back(unwrap(IndirectDests[i]));
}
return wrap(unwrap(B)->CreateCallBr(
FTy, Callee, unwrap(DefaultDest),
ArrayRef<BasicBlock*>(IndirectDestsUnwrapped),
ArrayRef<Value*>(unwrap(Args), NumArgs),
ArrayRef<OperandBundleDef>(*OpBundles, NumOpBundles),
Name));
}
extern "C" void LLVMRustPositionBuilderAtStart(LLVMBuilderRef B, extern "C" void LLVMRustPositionBuilderAtStart(LLVMBuilderRef B,
LLVMBasicBlockRef BB) { LLVMBasicBlockRef BB) {
auto Point = unwrap(BB)->getFirstInsertionPt(); auto Point = unwrap(BB)->getFirstInsertionPt();

View file

@ -1716,13 +1716,13 @@ mod size_asserts {
use super::*; use super::*;
use rustc_data_structures::static_assert_size; use rustc_data_structures::static_assert_size;
// tidy-alphabetical-start // tidy-alphabetical-start
static_assert_size!(BasicBlockData<'_>, 136); static_assert_size!(BasicBlockData<'_>, 144);
static_assert_size!(LocalDecl<'_>, 40); static_assert_size!(LocalDecl<'_>, 40);
static_assert_size!(SourceScopeData<'_>, 72); static_assert_size!(SourceScopeData<'_>, 72);
static_assert_size!(Statement<'_>, 32); static_assert_size!(Statement<'_>, 32);
static_assert_size!(StatementKind<'_>, 16); static_assert_size!(StatementKind<'_>, 16);
static_assert_size!(Terminator<'_>, 104); static_assert_size!(Terminator<'_>, 112);
static_assert_size!(TerminatorKind<'_>, 88); static_assert_size!(TerminatorKind<'_>, 96);
static_assert_size!(VarDebugInfo<'_>, 88); static_assert_size!(VarDebugInfo<'_>, 88);
// tidy-alphabetical-end // tidy-alphabetical-end
} }

View file

@ -7,7 +7,7 @@ use std::path::{Path, PathBuf};
use crate::mir::interpret::ConstAllocation; use crate::mir::interpret::ConstAllocation;
use super::graphviz::write_mir_fn_graphviz; use super::graphviz::write_mir_fn_graphviz;
use rustc_ast::InlineAsmTemplatePiece; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_middle::mir::interpret::{ use rustc_middle::mir::interpret::{
alloc_range, read_target_uint, AllocBytes, AllocId, Allocation, GlobalAlloc, Pointer, alloc_range, read_target_uint, AllocBytes, AllocId, Allocation, GlobalAlloc, Pointer,
Provenance, Provenance,
@ -830,6 +830,9 @@ impl<'tcx> TerminatorKind<'tcx> {
InlineAsmOperand::SymStatic { def_id } => { InlineAsmOperand::SymStatic { def_id } => {
write!(fmt, "sym_static {def_id:?}")?; write!(fmt, "sym_static {def_id:?}")?;
} }
InlineAsmOperand::Label { target_index } => {
write!(fmt, "label {target_index}")?;
}
} }
} }
write!(fmt, ", options({options:?}))") write!(fmt, ", options({options:?}))")
@ -868,16 +871,19 @@ impl<'tcx> TerminatorKind<'tcx> {
vec!["real".into(), "unwind".into()] vec!["real".into(), "unwind".into()]
} }
FalseUnwind { unwind: _, .. } => vec!["real".into()], FalseUnwind { unwind: _, .. } => vec!["real".into()],
InlineAsm { destination: Some(_), unwind: UnwindAction::Cleanup(_), .. } => { InlineAsm { options, ref targets, unwind, .. } => {
vec!["return".into(), "unwind".into()] let mut vec = Vec::with_capacity(targets.len() + 1);
if !options.contains(InlineAsmOptions::NORETURN) {
vec.push("return".into());
} }
InlineAsm { destination: Some(_), unwind: _, .. } => { vec.resize(targets.len(), "label".into());
vec!["return".into()]
if let UnwindAction::Cleanup(_) = unwind {
vec.push("unwind".into());
} }
InlineAsm { destination: None, unwind: UnwindAction::Cleanup(_), .. } => {
vec!["unwind".into()] vec
} }
InlineAsm { destination: None, unwind: _, .. } => vec![],
} }
} }
} }

View file

@ -793,9 +793,10 @@ pub enum TerminatorKind<'tcx> {
/// used to map assembler errors back to the line in the source code. /// used to map assembler errors back to the line in the source code.
line_spans: &'tcx [Span], line_spans: &'tcx [Span],
/// Destination block after the inline assembly returns, unless it is /// Valid targets for the inline assembly.
/// diverging (InlineAsmOptions::NORETURN). /// The first element is the fallthrough destination, unless
destination: Option<BasicBlock>, /// InlineAsmOptions::NORETURN is set.
targets: Vec<BasicBlock>,
/// Action to be taken if the inline assembly unwinds. This is present /// Action to be taken if the inline assembly unwinds. This is present
/// if and only if InlineAsmOptions::MAY_UNWIND is set. /// if and only if InlineAsmOptions::MAY_UNWIND is set.
@ -918,6 +919,10 @@ pub enum InlineAsmOperand<'tcx> {
SymStatic { SymStatic {
def_id: DefId, def_id: DefId,
}, },
Label {
/// This represents the index into the `targets` array in `TerminatorKind::InlineAsm`.
target_index: usize,
},
} }
/// Type for MIR `Assert` terminator error messages. /// Type for MIR `Assert` terminator error messages.

View file

@ -347,8 +347,7 @@ pub struct Terminator<'tcx> {
} }
pub type Successors<'a> = impl DoubleEndedIterator<Item = BasicBlock> + 'a; pub type Successors<'a> = impl DoubleEndedIterator<Item = BasicBlock> + 'a;
pub type SuccessorsMut<'a> = pub type SuccessorsMut<'a> = impl DoubleEndedIterator<Item = &'a mut BasicBlock> + 'a;
iter::Chain<std::option::IntoIter<&'a mut BasicBlock>, slice::IterMut<'a, BasicBlock>>;
impl<'tcx> Terminator<'tcx> { impl<'tcx> Terminator<'tcx> {
#[inline] #[inline]
@ -382,40 +381,36 @@ impl<'tcx> TerminatorKind<'tcx> {
pub fn successors(&self) -> Successors<'_> { pub fn successors(&self) -> Successors<'_> {
use self::TerminatorKind::*; use self::TerminatorKind::*;
match *self { match *self {
Call { target: Some(t), unwind: UnwindAction::Cleanup(ref u), .. } Call { target: Some(ref t), unwind: UnwindAction::Cleanup(u), .. }
| Yield { resume: t, drop: Some(ref u), .. } | Yield { resume: ref t, drop: Some(u), .. }
| Drop { target: t, unwind: UnwindAction::Cleanup(ref u), .. } | Drop { target: ref t, unwind: UnwindAction::Cleanup(u), .. }
| Assert { target: t, unwind: UnwindAction::Cleanup(ref u), .. } | Assert { target: ref t, unwind: UnwindAction::Cleanup(u), .. }
| FalseUnwind { real_target: t, unwind: UnwindAction::Cleanup(ref u) } | FalseUnwind { real_target: ref t, unwind: UnwindAction::Cleanup(u) } => {
| InlineAsm { destination: Some(t), unwind: UnwindAction::Cleanup(ref u), .. } => { slice::from_ref(t).into_iter().copied().chain(Some(u))
Some(t).into_iter().chain(slice::from_ref(u).into_iter().copied())
} }
Goto { target: t } Goto { target: ref t }
| Call { target: None, unwind: UnwindAction::Cleanup(t), .. } | Call { target: None, unwind: UnwindAction::Cleanup(ref t), .. }
| Call { target: Some(t), unwind: _, .. } | Call { target: Some(ref t), unwind: _, .. }
| Yield { resume: t, drop: None, .. } | Yield { resume: ref t, drop: None, .. }
| Drop { target: t, unwind: _, .. } | Drop { target: ref t, unwind: _, .. }
| Assert { target: t, unwind: _, .. } | Assert { target: ref t, unwind: _, .. }
| FalseUnwind { real_target: t, unwind: _ } | FalseUnwind { real_target: ref t, unwind: _ } => {
| InlineAsm { destination: None, unwind: UnwindAction::Cleanup(t), .. } slice::from_ref(t).into_iter().copied().chain(None)
| InlineAsm { destination: Some(t), unwind: _, .. } => {
Some(t).into_iter().chain((&[]).into_iter().copied())
} }
UnwindResume UnwindResume
| UnwindTerminate(_) | UnwindTerminate(_)
| CoroutineDrop | CoroutineDrop
| Return | Return
| Unreachable | Unreachable
| Call { target: None, unwind: _, .. } | Call { target: None, unwind: _, .. } => (&[]).into_iter().copied().chain(None),
| InlineAsm { destination: None, unwind: _, .. } => { InlineAsm { ref targets, unwind: UnwindAction::Cleanup(u), .. } => {
None.into_iter().chain((&[]).into_iter().copied()) targets.iter().copied().chain(Some(u))
} }
SwitchInt { ref targets, .. } => { InlineAsm { ref targets, unwind: _, .. } => targets.iter().copied().chain(None),
None.into_iter().chain(targets.targets.iter().copied()) SwitchInt { ref targets, .. } => targets.targets.iter().copied().chain(None),
FalseEdge { ref real_target, imaginary_target } => {
slice::from_ref(real_target).into_iter().copied().chain(Some(imaginary_target))
} }
FalseEdge { real_target, ref imaginary_target } => Some(real_target)
.into_iter()
.chain(slice::from_ref(imaginary_target).into_iter().copied()),
} }
} }
@ -427,33 +422,31 @@ impl<'tcx> TerminatorKind<'tcx> {
| Yield { resume: ref mut t, drop: Some(ref mut u), .. } | Yield { resume: ref mut t, drop: Some(ref mut u), .. }
| Drop { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. } | Drop { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. }
| Assert { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. } | Assert { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. }
| FalseUnwind { real_target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u) } | FalseUnwind { real_target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u) } => {
| InlineAsm { slice::from_mut(t).into_iter().chain(Some(u))
destination: Some(ref mut t), }
unwind: UnwindAction::Cleanup(ref mut u),
..
} => Some(t).into_iter().chain(slice::from_mut(u)),
Goto { target: ref mut t } Goto { target: ref mut t }
| Call { target: None, unwind: UnwindAction::Cleanup(ref mut t), .. } | Call { target: None, unwind: UnwindAction::Cleanup(ref mut t), .. }
| Call { target: Some(ref mut t), unwind: _, .. } | Call { target: Some(ref mut t), unwind: _, .. }
| Yield { resume: ref mut t, drop: None, .. } | Yield { resume: ref mut t, drop: None, .. }
| Drop { target: ref mut t, unwind: _, .. } | Drop { target: ref mut t, unwind: _, .. }
| Assert { target: ref mut t, unwind: _, .. } | Assert { target: ref mut t, unwind: _, .. }
| FalseUnwind { real_target: ref mut t, unwind: _ } | FalseUnwind { real_target: ref mut t, unwind: _ } => {
| InlineAsm { destination: None, unwind: UnwindAction::Cleanup(ref mut t), .. } slice::from_mut(t).into_iter().chain(None)
| InlineAsm { destination: Some(ref mut t), unwind: _, .. } => {
Some(t).into_iter().chain(&mut [])
} }
UnwindResume UnwindResume
| UnwindTerminate(_) | UnwindTerminate(_)
| CoroutineDrop | CoroutineDrop
| Return | Return
| Unreachable | Unreachable
| Call { target: None, unwind: _, .. } | Call { target: None, unwind: _, .. } => (&mut []).into_iter().chain(None),
| InlineAsm { destination: None, unwind: _, .. } => None.into_iter().chain(&mut []), InlineAsm { ref mut targets, unwind: UnwindAction::Cleanup(ref mut u), .. } => {
SwitchInt { ref mut targets, .. } => None.into_iter().chain(&mut targets.targets), targets.iter_mut().chain(Some(u))
}
InlineAsm { ref mut targets, unwind: _, .. } => targets.iter_mut().chain(None),
SwitchInt { ref mut targets, .. } => targets.targets.iter_mut().chain(None),
FalseEdge { ref mut real_target, ref mut imaginary_target } => { FalseEdge { ref mut real_target, ref mut imaginary_target } => {
Some(real_target).into_iter().chain(slice::from_mut(imaginary_target)) slice::from_mut(real_target).into_iter().chain(Some(imaginary_target))
} }
} }
} }
@ -525,7 +518,7 @@ pub enum TerminatorEdges<'mir, 'tcx> {
Double(BasicBlock, BasicBlock), Double(BasicBlock, BasicBlock),
/// Special action for `Yield`, `Call` and `InlineAsm` terminators. /// Special action for `Yield`, `Call` and `InlineAsm` terminators.
AssignOnReturn { AssignOnReturn {
return_: Option<BasicBlock>, return_: &'mir [BasicBlock],
/// The cleanup block, if it exists. /// The cleanup block, if it exists.
cleanup: Option<BasicBlock>, cleanup: Option<BasicBlock>,
place: CallReturnPlaces<'mir, 'tcx>, place: CallReturnPlaces<'mir, 'tcx>,
@ -589,31 +582,37 @@ impl<'tcx> TerminatorKind<'tcx> {
TerminatorEdges::Double(real_target, imaginary_target) TerminatorEdges::Double(real_target, imaginary_target)
} }
Yield { resume: target, drop, resume_arg, value: _ } => { Yield { resume: ref target, drop, resume_arg, value: _ } => {
TerminatorEdges::AssignOnReturn { TerminatorEdges::AssignOnReturn {
return_: Some(target), return_: slice::from_ref(target),
cleanup: drop, cleanup: drop,
place: CallReturnPlaces::Yield(resume_arg), place: CallReturnPlaces::Yield(resume_arg),
} }
} }
Call { unwind, destination, target, func: _, args: _, fn_span: _, call_source: _ } => { Call {
TerminatorEdges::AssignOnReturn { unwind,
return_: target, destination,
ref target,
func: _,
args: _,
fn_span: _,
call_source: _,
} => TerminatorEdges::AssignOnReturn {
return_: target.as_ref().map(slice::from_ref).unwrap_or_default(),
cleanup: unwind.cleanup_block(), cleanup: unwind.cleanup_block(),
place: CallReturnPlaces::Call(destination), place: CallReturnPlaces::Call(destination),
} },
}
InlineAsm { InlineAsm {
template: _, template: _,
ref operands, ref operands,
options: _, options: _,
line_spans: _, line_spans: _,
destination, ref targets,
unwind, unwind,
} => TerminatorEdges::AssignOnReturn { } => TerminatorEdges::AssignOnReturn {
return_: destination, return_: targets,
cleanup: unwind.cleanup_block(), cleanup: unwind.cleanup_block(),
place: CallReturnPlaces::InlineAsm(operands), place: CallReturnPlaces::InlineAsm(operands),
}, },

View file

@ -565,7 +565,7 @@ macro_rules! make_mir_visitor {
operands, operands,
options: _, options: _,
line_spans: _, line_spans: _,
destination: _, targets: _,
unwind: _, unwind: _,
} => { } => {
for op in operands { for op in operands {
@ -595,7 +595,8 @@ macro_rules! make_mir_visitor {
self.visit_constant(value, location); self.visit_constant(value, location);
} }
InlineAsmOperand::Out { place: None, .. } InlineAsmOperand::Out { place: None, .. }
| InlineAsmOperand::SymStatic { def_id: _ } => {} | InlineAsmOperand::SymStatic { def_id: _ }
| InlineAsmOperand::Label { target_index: _ } => {}
} }
} }
} }

View file

@ -565,6 +565,9 @@ pub enum InlineAsmOperand<'tcx> {
SymStatic { SymStatic {
def_id: DefId, def_id: DefId,
}, },
Label {
block: BlockId,
},
} }
#[derive(Copy, Clone, Debug, PartialEq, HashStable)] #[derive(Copy, Clone, Debug, PartialEq, HashStable)]

View file

@ -162,6 +162,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
| Const { value: _, span: _ } | Const { value: _, span: _ }
| SymFn { value: _, span: _ } | SymFn { value: _, span: _ }
| SymStatic { def_id: _ } => {} | SymStatic { def_id: _ } => {}
Label { block } => visitor.visit_block(&visitor.thir()[*block]),
} }
} }
} }

View file

@ -1254,7 +1254,7 @@ impl<'tcx> TyCtxt<'tcx> {
if self.def_kind(scope) == DefKind::OpaqueTy { if self.def_kind(scope) == DefKind::OpaqueTy {
// Lifetime params of opaque types are synthetic and thus irrelevant to // Lifetime params of opaque types are synthetic and thus irrelevant to
// diagnostics. Map them back to their origin! // diagnostics. Map them back to their origin!
region = self.map_rpit_lifetime_to_fn_lifetime(def_id); region = self.map_opaque_lifetime_to_parent_lifetime(def_id);
continue; continue;
} }
break (scope, ty::BrNamed(def_id.into(), self.item_name(def_id.into()))); break (scope, ty::BrNamed(def_id.into(), self.item_name(def_id.into())));
@ -2219,31 +2219,31 @@ impl<'tcx> TyCtxt<'tcx> {
) )
} }
/// Given the def-id of an early-bound lifetime on an RPIT corresponding to /// Given the def-id of an early-bound lifetime on an opaque corresponding to
/// a duplicated captured lifetime, map it back to the early- or late-bound /// a duplicated captured lifetime, map it back to the early- or late-bound
/// lifetime of the function from which it originally as captured. If it is /// lifetime of the function from which it originally as captured. If it is
/// a late-bound lifetime, this will represent the liberated (`ReLateParam`) lifetime /// a late-bound lifetime, this will represent the liberated (`ReLateParam`) lifetime
/// of the signature. /// of the signature.
// FIXME(RPITIT): if we ever synthesize new lifetimes for RPITITs and not just // FIXME(RPITIT): if we ever synthesize new lifetimes for RPITITs and not just
// re-use the generics of the opaque, this function will need to be tweaked slightly. // re-use the generics of the opaque, this function will need to be tweaked slightly.
pub fn map_rpit_lifetime_to_fn_lifetime( pub fn map_opaque_lifetime_to_parent_lifetime(
self, self,
mut rpit_lifetime_param_def_id: LocalDefId, mut opaque_lifetime_param_def_id: LocalDefId,
) -> ty::Region<'tcx> { ) -> ty::Region<'tcx> {
debug_assert!( debug_assert!(
matches!(self.def_kind(rpit_lifetime_param_def_id), DefKind::LifetimeParam), matches!(self.def_kind(opaque_lifetime_param_def_id), DefKind::LifetimeParam),
"{rpit_lifetime_param_def_id:?} is a {}", "{opaque_lifetime_param_def_id:?} is a {}",
self.def_descr(rpit_lifetime_param_def_id.to_def_id()) self.def_descr(opaque_lifetime_param_def_id.to_def_id())
); );
loop { loop {
let parent = self.local_parent(rpit_lifetime_param_def_id); let parent = self.local_parent(opaque_lifetime_param_def_id);
let hir::OpaqueTy { lifetime_mapping, .. } = let hir::OpaqueTy { lifetime_mapping, .. } =
self.hir_node_by_def_id(parent).expect_item().expect_opaque_ty(); self.hir_node_by_def_id(parent).expect_item().expect_opaque_ty();
let Some((lifetime, _)) = lifetime_mapping let Some((lifetime, _)) = lifetime_mapping
.iter() .iter()
.find(|(_, duplicated_param)| *duplicated_param == rpit_lifetime_param_def_id) .find(|(_, duplicated_param)| *duplicated_param == opaque_lifetime_param_def_id)
else { else {
bug!("duplicated lifetime param should be present"); bug!("duplicated lifetime param should be present");
}; };
@ -2256,7 +2256,7 @@ impl<'tcx> TyCtxt<'tcx> {
// of the opaque we mapped from. Continue mapping. // of the opaque we mapped from. Continue mapping.
if matches!(self.def_kind(new_parent), DefKind::OpaqueTy) { if matches!(self.def_kind(new_parent), DefKind::OpaqueTy) {
debug_assert_eq!(self.parent(parent.to_def_id()), new_parent); debug_assert_eq!(self.parent(parent.to_def_id()), new_parent);
rpit_lifetime_param_def_id = ebv.expect_local(); opaque_lifetime_param_def_id = ebv.expect_local();
continue; continue;
} }

View file

@ -1940,18 +1940,6 @@ impl<'tcx> TyCtxt<'tcx> {
matches!(self.trait_of_item(def_id), Some(trait_id) if self.has_attr(trait_id, sym::const_trait)) matches!(self.trait_of_item(def_id), Some(trait_id) if self.has_attr(trait_id, sym::const_trait))
} }
/// Returns the `DefId` of the item within which the `impl Trait` is declared.
/// For type-alias-impl-trait this is the `type` alias.
/// For impl-trait-in-assoc-type this is the assoc type.
/// For return-position-impl-trait this is the function.
pub fn impl_trait_parent(self, mut def_id: LocalDefId) -> LocalDefId {
// Find the surrounding item (type alias or assoc type)
while let DefKind::OpaqueTy = self.def_kind(def_id) {
def_id = self.local_parent(def_id);
}
def_id
}
pub fn impl_method_has_trait_impl_trait_tys(self, def_id: DefId) -> bool { pub fn impl_method_has_trait_impl_trait_tys(self, def_id: DefId) -> bool {
if self.def_kind(def_id) != DefKind::AssocFn { if self.def_kind(def_id) != DefKind::AssocFn {
return false; return false;

View file

@ -383,6 +383,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
line_spans, line_spans,
}) => { }) => {
use rustc_middle::{mir, thir}; use rustc_middle::{mir, thir};
let destination_block = this.cfg.start_new_block();
let mut targets = if options.contains(InlineAsmOptions::NORETURN) {
vec![]
} else {
vec![destination_block]
};
let operands = operands let operands = operands
.into_iter() .into_iter()
.map(|op| match *op { .map(|op| match *op {
@ -438,14 +446,28 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
thir::InlineAsmOperand::SymStatic { def_id } => { thir::InlineAsmOperand::SymStatic { def_id } => {
mir::InlineAsmOperand::SymStatic { def_id } mir::InlineAsmOperand::SymStatic { def_id }
} }
thir::InlineAsmOperand::Label { block } => {
let target = this.cfg.start_new_block();
let target_index = targets.len();
targets.push(target);
let tmp = this.get_unit_temp();
let target = unpack!(this.ast_block(tmp, target, block, source_info));
this.cfg.terminate(
target,
source_info,
TerminatorKind::Goto { target: destination_block },
);
mir::InlineAsmOperand::Label { target_index }
}
}) })
.collect(); .collect();
if !options.contains(InlineAsmOptions::NORETURN) { if !expr.ty.is_never() {
this.cfg.push_assign_unit(block, source_info, destination, this.tcx); this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
} }
let destination_block = this.cfg.start_new_block();
this.cfg.terminate( this.cfg.terminate(
block, block,
source_info, source_info,
@ -454,11 +476,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
operands, operands,
options, options,
line_spans, line_spans,
destination: if options.contains(InlineAsmOptions::NORETURN) { targets,
None
} else {
Some(destination_block)
},
unwind: if options.contains(InlineAsmOptions::MAY_UNWIND) { unwind: if options.contains(InlineAsmOptions::MAY_UNWIND) {
UnwindAction::Continue UnwindAction::Continue
} else { } else {

View file

@ -199,9 +199,10 @@ impl<'mir, 'tcx, C: TerminatorClassifier<'tcx>> TriColorVisitor<BasicBlocks<'tcx
| TerminatorKind::Unreachable | TerminatorKind::Unreachable
| TerminatorKind::Yield { .. } => ControlFlow::Break(NonRecursive), | TerminatorKind::Yield { .. } => ControlFlow::Break(NonRecursive),
// A diverging InlineAsm is treated as non-recursing // A InlineAsm without targets (diverging and contains no labels)
TerminatorKind::InlineAsm { destination, .. } => { // is treated as non-recursing.
if destination.is_some() { TerminatorKind::InlineAsm { ref targets, .. } => {
if !targets.is_empty() {
ControlFlow::Continue(()) ControlFlow::Continue(())
} else { } else {
ControlFlow::Break(NonRecursive) ControlFlow::Break(NonRecursive)

View file

@ -656,6 +656,9 @@ impl<'tcx> Cx<'tcx> {
hir::InlineAsmOperand::SymStatic { path: _, def_id } => { hir::InlineAsmOperand::SymStatic { path: _, def_id } => {
InlineAsmOperand::SymStatic { def_id } InlineAsmOperand::SymStatic { def_id }
} }
hir::InlineAsmOperand::Label { block } => {
InlineAsmOperand::Label { block: self.mirror_block(block) }
}
}) })
.collect(), .collect(),
options: asm.options, options: asm.options,

View file

@ -889,6 +889,12 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
print_indented!(self, format!("def_id: {:?}", def_id), depth_lvl + 1); print_indented!(self, format!("def_id: {:?}", def_id), depth_lvl + 1);
print_indented!(self, "}", depth_lvl + 1); print_indented!(self, "}", depth_lvl + 1);
} }
InlineAsmOperand::Label { block } => {
print_indented!(self, "InlineAsmOperand::Block {", depth_lvl);
print_indented!(self, "block:", depth_lvl + 1);
self.print_block(*block, depth_lvl + 2);
print_indented!(self, "}", depth_lvl + 1);
}
} }
} }
} }

View file

@ -242,9 +242,9 @@ impl Direction for Backward {
propagate(pred, &tmp); propagate(pred, &tmp);
} }
mir::TerminatorKind::InlineAsm { mir::TerminatorKind::InlineAsm { ref targets, ref operands, .. }
destination: Some(dest), ref operands, .. if targets.contains(&bb) =>
} if dest == bb => { {
let mut tmp = exit_state.clone(); let mut tmp = exit_state.clone();
analysis.apply_call_return_effect( analysis.apply_call_return_effect(
&mut tmp, &mut tmp,
@ -491,9 +491,12 @@ impl Direction for Forward {
if let Some(cleanup) = cleanup { if let Some(cleanup) = cleanup {
propagate(cleanup, exit_state); propagate(cleanup, exit_state);
} }
if let Some(return_) = return_ {
if !return_.is_empty() {
analysis.apply_call_return_effect(exit_state, bb, place); analysis.apply_call_return_effect(exit_state, bb, place);
propagate(return_, exit_state); for &target in return_ {
propagate(target, exit_state);
}
} }
} }
TerminatorEdges::SwitchInt { targets, discr } => { TerminatorEdges::SwitchInt { targets, discr } => {

View file

@ -299,7 +299,9 @@ where
})?; })?;
} }
mir::TerminatorKind::InlineAsm { destination: Some(_), ref operands, .. } => { mir::TerminatorKind::InlineAsm { ref targets, ref operands, .. }
if !targets.is_empty() =>
{
self.write_row(w, "", "(on successful return)", |this, w, fmt| { self.write_row(w, "", "(on successful return)", |this, w, fmt| {
let state_on_unwind = this.results.get().clone(); let state_on_unwind = this.results.get().clone();
this.results.apply_custom_effect(|analysis, state| { this.results.apply_custom_effect(|analysis, state| {

View file

@ -271,7 +271,8 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
InlineAsmOperand::In { .. } InlineAsmOperand::In { .. }
| InlineAsmOperand::Const { .. } | InlineAsmOperand::Const { .. }
| InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymFn { .. }
| InlineAsmOperand::SymStatic { .. } => {} | InlineAsmOperand::SymStatic { .. }
| InlineAsmOperand::Label { .. } => {}
} }
} }
} }

View file

@ -491,7 +491,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> {
ref operands, ref operands,
options: _, options: _,
line_spans: _, line_spans: _,
destination: _, targets: _,
unwind: _, unwind: _,
} => { } => {
for op in operands { for op in operands {
@ -515,7 +515,8 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> {
} }
InlineAsmOperand::Const { value: _ } InlineAsmOperand::Const { value: _ }
| InlineAsmOperand::SymFn { value: _ } | InlineAsmOperand::SymFn { value: _ }
| InlineAsmOperand::SymStatic { def_id: _ } => {} | InlineAsmOperand::SymStatic { def_id: _ }
| InlineAsmOperand::Label { target_index: _ } => {}
} }
} }
} }

View file

@ -349,12 +349,20 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera
| FalseUnwind { real_target: target, .. } | FalseUnwind { real_target: target, .. }
| Goto { target } => CoverageSuccessors::Chainable(target), | Goto { target } => CoverageSuccessors::Chainable(target),
// These terminators can normally be chained, except when they have no // A call terminator can normally be chained, except when they have no
// successor because they are known to diverge. // successor because they are known to diverge.
Call { target: maybe_target, .. } | InlineAsm { destination: maybe_target, .. } => { Call { target: maybe_target, .. } => match maybe_target {
match maybe_target {
Some(target) => CoverageSuccessors::Chainable(target), Some(target) => CoverageSuccessors::Chainable(target),
None => CoverageSuccessors::NotChainable(&[]), None => CoverageSuccessors::NotChainable(&[]),
},
// An inline asm terminator can normally be chained, except when it diverges or uses asm
// goto.
InlineAsm { ref targets, .. } => {
if targets.len() == 1 {
CoverageSuccessors::Chainable(targets[0])
} else {
CoverageSuccessors::NotChainable(targets)
} }
} }

View file

@ -88,7 +88,6 @@ impl<'tcx> MockBlocks<'tcx> {
| TerminatorKind::FalseEdge { real_target: ref mut target, .. } | TerminatorKind::FalseEdge { real_target: ref mut target, .. }
| TerminatorKind::FalseUnwind { real_target: ref mut target, .. } | TerminatorKind::FalseUnwind { real_target: ref mut target, .. }
| TerminatorKind::Goto { ref mut target } | TerminatorKind::Goto { ref mut target }
| TerminatorKind::InlineAsm { destination: Some(ref mut target), .. }
| TerminatorKind::Yield { resume: ref mut target, .. } => *target = to_block, | TerminatorKind::Yield { resume: ref mut target, .. } => *target = to_block,
ref invalid => bug!("Invalid from_block: {:?}", invalid), ref invalid => bug!("Invalid from_block: {:?}", invalid),
} }
@ -185,10 +184,12 @@ fn debug_basic_blocks(mir_body: &Body<'_>) -> String {
| TerminatorKind::FalseEdge { real_target: target, .. } | TerminatorKind::FalseEdge { real_target: target, .. }
| TerminatorKind::FalseUnwind { real_target: target, .. } | TerminatorKind::FalseUnwind { real_target: target, .. }
| TerminatorKind::Goto { target } | TerminatorKind::Goto { target }
| TerminatorKind::InlineAsm { destination: Some(target), .. }
| TerminatorKind::Yield { resume: target, .. } => { | TerminatorKind::Yield { resume: target, .. } => {
format!("{}{:?}:{} -> {:?}", sp, bb, kind.name(), target) format!("{}{:?}:{} -> {:?}", sp, bb, kind.name(), target)
} }
TerminatorKind::InlineAsm { targets, .. } => {
format!("{}{:?}:{} -> {:?}", sp, bb, kind.name(), targets)
}
TerminatorKind::SwitchInt { targets, .. } => { TerminatorKind::SwitchInt { targets, .. } => {
format!("{}{:?}:{} -> {:?}", sp, bb, kind.name(), targets) format!("{}{:?}:{} -> {:?}", sp, bb, kind.name(), targets)
} }

View file

@ -648,7 +648,8 @@ impl WriteInfo {
} }
InlineAsmOperand::Const { .. } InlineAsmOperand::Const { .. }
| InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymFn { .. }
| InlineAsmOperand::SymStatic { .. } => (), | InlineAsmOperand::SymStatic { .. }
| InlineAsmOperand::Label { .. } => {}
} }
} }
} }

View file

@ -1036,8 +1036,8 @@ impl<'tcx> MutVisitor<'tcx> for Integrator<'_, 'tcx> {
{ {
bug!("False unwinds should have been removed before inlining") bug!("False unwinds should have been removed before inlining")
} }
TerminatorKind::InlineAsm { ref mut destination, ref mut unwind, .. } => { TerminatorKind::InlineAsm { ref mut targets, ref mut unwind, .. } => {
if let Some(ref mut tgt) = *destination { for tgt in targets.iter_mut() {
*tgt = self.map_block(*tgt); *tgt = self.map_block(*tgt);
} }
*unwind = self.map_unwind(*unwind); *unwind = self.map_unwind(*unwind);

View file

@ -446,7 +446,8 @@ fn collect_items_rec<'tcx>(
hir::InlineAsmOperand::In { .. } hir::InlineAsmOperand::In { .. }
| hir::InlineAsmOperand::Out { .. } | hir::InlineAsmOperand::Out { .. }
| hir::InlineAsmOperand::InOut { .. } | hir::InlineAsmOperand::InOut { .. }
| hir::InlineAsmOperand::SplitInOut { .. } => { | hir::InlineAsmOperand::SplitInOut { .. }
| hir::InlineAsmOperand::Label { .. } => {
span_bug!(*op_sp, "invalid operand type for global_asm!") span_bug!(*op_sp, "invalid operand type for global_asm!")
} }
} }

View file

@ -448,7 +448,7 @@ impl<'a> Parser<'a> {
} }
/// Parses a block. No inner attributes are allowed. /// Parses a block. No inner attributes are allowed.
pub(super) fn parse_block(&mut self) -> PResult<'a, P<Block>> { pub fn parse_block(&mut self) -> PResult<'a, P<Block>> {
let (attrs, block) = self.parse_inner_attrs_and_block()?; let (attrs, block) = self.parse_inner_attrs_and_block()?;
if let [.., last] = &*attrs { if let [.., last] = &*attrs {
self.error_on_forbidden_inner_attr( self.error_on_forbidden_inner_attr(

View file

@ -86,7 +86,6 @@ use crate::errors;
use self::LiveNodeKind::*; use self::LiveNodeKind::*;
use self::VarKind::*; use self::VarKind::*;
use rustc_ast::InlineAsmOptions;
use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::fx::FxIndexMap;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::*; use rustc_hir::def::*;
@ -416,6 +415,12 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span, expr.hir_id)); self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span, expr.hir_id));
} }
// Inline assembly may contain labels.
hir::ExprKind::InlineAsm(asm) if asm.contains_label() => {
self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span, expr.hir_id));
intravisit::walk_expr(self, expr);
}
// otherwise, live nodes are not required: // otherwise, live nodes are not required:
hir::ExprKind::Index(..) hir::ExprKind::Index(..)
| hir::ExprKind::Field(..) | hir::ExprKind::Field(..)
@ -1045,20 +1050,53 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
| hir::ExprKind::Repeat(ref e, _) => self.propagate_through_expr(e, succ), | hir::ExprKind::Repeat(ref e, _) => self.propagate_through_expr(e, succ),
hir::ExprKind::InlineAsm(asm) => { hir::ExprKind::InlineAsm(asm) => {
// Handle non-returning asm //
let mut succ = if asm.options.contains(InlineAsmOptions::NORETURN) { // (inputs)
self.exit_ln // |
} else { // v
succ // (outputs)
}; // / \
// | |
// v v
// (labels)(fallthrough)
// | |
// v v
// ( succ / exit_ln )
// Do a first pass for writing outputs only // Handle non-returning asm
let mut succ =
if self.typeck_results.expr_ty(expr).is_never() { self.exit_ln } else { succ };
// Do a first pass for labels only
if asm.contains_label() {
let ln = self.live_node(expr.hir_id, expr.span);
self.init_from_succ(ln, succ);
for (op, _op_sp) in asm.operands.iter().rev() {
match op {
hir::InlineAsmOperand::Label { block } => {
let label_ln = self.propagate_through_block(block, succ);
self.merge_from_succ(ln, label_ln);
}
hir::InlineAsmOperand::In { .. }
| hir::InlineAsmOperand::Out { .. }
| hir::InlineAsmOperand::InOut { .. }
| hir::InlineAsmOperand::SplitInOut { .. }
| hir::InlineAsmOperand::Const { .. }
| hir::InlineAsmOperand::SymFn { .. }
| hir::InlineAsmOperand::SymStatic { .. } => {}
}
}
succ = ln;
}
// Do a second pass for writing outputs only
for (op, _op_sp) in asm.operands.iter().rev() { for (op, _op_sp) in asm.operands.iter().rev() {
match op { match op {
hir::InlineAsmOperand::In { .. } hir::InlineAsmOperand::In { .. }
| hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::Const { .. }
| hir::InlineAsmOperand::SymFn { .. } | hir::InlineAsmOperand::SymFn { .. }
| hir::InlineAsmOperand::SymStatic { .. } => {} | hir::InlineAsmOperand::SymStatic { .. }
| hir::InlineAsmOperand::Label { .. } => {}
hir::InlineAsmOperand::Out { expr, .. } => { hir::InlineAsmOperand::Out { expr, .. } => {
if let Some(expr) = expr { if let Some(expr) = expr {
succ = self.write_place(expr, succ, ACC_WRITE); succ = self.write_place(expr, succ, ACC_WRITE);
@ -1075,7 +1113,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
} }
} }
// Then do a second pass for inputs // Then do a third pass for inputs
for (op, _op_sp) in asm.operands.iter().rev() { for (op, _op_sp) in asm.operands.iter().rev() {
match op { match op {
hir::InlineAsmOperand::In { expr, .. } => { hir::InlineAsmOperand::In { expr, .. } => {
@ -1097,7 +1135,8 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
} }
hir::InlineAsmOperand::Const { .. } hir::InlineAsmOperand::Const { .. }
| hir::InlineAsmOperand::SymFn { .. } | hir::InlineAsmOperand::SymFn { .. }
| hir::InlineAsmOperand::SymStatic { .. } => {} | hir::InlineAsmOperand::SymStatic { .. }
| hir::InlineAsmOperand::Label { .. } => {}
} }
} }
succ succ

View file

@ -237,7 +237,8 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
InlineAsmOperand::In { .. } InlineAsmOperand::In { .. }
| InlineAsmOperand::Out { .. } | InlineAsmOperand::Out { .. }
| InlineAsmOperand::InOut { .. } | InlineAsmOperand::InOut { .. }
| InlineAsmOperand::SplitInOut { .. } => Some(op_sp), | InlineAsmOperand::SplitInOut { .. }
| InlineAsmOperand::Label { .. } => Some(op_sp),
}) })
.collect(); .collect();
if !unsupported_operands.is_empty() { if !unsupported_operands.is_empty() {

View file

@ -1242,6 +1242,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
self.resolve_anon_const(anon_const, AnonConstKind::InlineConst); self.resolve_anon_const(anon_const, AnonConstKind::InlineConst);
} }
InlineAsmOperand::Sym { sym } => self.visit_inline_asm_sym(sym), InlineAsmOperand::Sym { sym } => self.visit_inline_asm_sym(sym),
InlineAsmOperand::Label { block } => self.visit_block(block),
} }
} }
} }

View file

@ -559,7 +559,8 @@ impl<'tcx> Stable<'tcx> for mir::InlineAsmOperand<'tcx> {
} }
InlineAsmOperand::Const { .. } InlineAsmOperand::Const { .. }
| InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymFn { .. }
| InlineAsmOperand::SymStatic { .. } => (None, None), | InlineAsmOperand::SymStatic { .. }
| InlineAsmOperand::Label { .. } => (None, None),
}; };
stable_mir::mir::InlineAsmOperand { in_value, out_place, raw_rpr: format!("{self:?}") } stable_mir::mir::InlineAsmOperand { in_value, out_place, raw_rpr: format!("{self:?}") }
@ -632,14 +633,15 @@ impl<'tcx> Stable<'tcx> for mir::TerminatorKind<'tcx> {
operands, operands,
options, options,
line_spans, line_spans,
destination, targets,
unwind, unwind,
} => TerminatorKind::InlineAsm { } => TerminatorKind::InlineAsm {
template: format!("{template:?}"), template: format!("{template:?}"),
operands: operands.iter().map(|operand| operand.stable(tables)).collect(), operands: operands.iter().map(|operand| operand.stable(tables)).collect(),
options: format!("{options:?}"), options: format!("{options:?}"),
line_spans: format!("{line_spans:?}"), line_spans: format!("{line_spans:?}"),
destination: destination.map(|d| d.as_usize()), // FIXME: Figure out how to do labels in SMIR
destination: targets.first().map(|d| d.as_usize()),
unwind: unwind.stable(tables), unwind: unwind.stable(tables),
}, },
mir::TerminatorKind::Yield { .. } mir::TerminatorKind::Yield { .. }

View file

@ -399,6 +399,7 @@ symbols! {
asm, asm,
asm_const, asm_const,
asm_experimental_arch, asm_experimental_arch,
asm_goto,
asm_sym, asm_sym,
asm_unwind, asm_unwind,
assert, assert,

View file

@ -1404,6 +1404,7 @@ supported_targets! {
("i686-unknown-linux-gnu", i686_unknown_linux_gnu), ("i686-unknown-linux-gnu", i686_unknown_linux_gnu),
("i586-unknown-linux-gnu", i586_unknown_linux_gnu), ("i586-unknown-linux-gnu", i586_unknown_linux_gnu),
("loongarch64-unknown-linux-gnu", loongarch64_unknown_linux_gnu), ("loongarch64-unknown-linux-gnu", loongarch64_unknown_linux_gnu),
("loongarch64-unknown-linux-musl", loongarch64_unknown_linux_musl),
("m68k-unknown-linux-gnu", m68k_unknown_linux_gnu), ("m68k-unknown-linux-gnu", m68k_unknown_linux_gnu),
("csky-unknown-linux-gnuabiv2", csky_unknown_linux_gnuabiv2), ("csky-unknown-linux-gnuabiv2", csky_unknown_linux_gnuabiv2),
("csky-unknown-linux-gnuabiv2hf", csky_unknown_linux_gnuabiv2hf), ("csky-unknown-linux-gnuabiv2hf", csky_unknown_linux_gnuabiv2hf),

View file

@ -0,0 +1,19 @@
use crate::spec::{base, Target, TargetOptions};
pub fn target() -> Target {
Target {
llvm_target: "loongarch64-unknown-linux-musl".into(),
description: None,
pointer_width: 64,
data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(),
arch: "loongarch64".into(),
options: TargetOptions {
cpu: "generic".into(),
features: "+f,+d".into(),
llvm_abiname: "lp64d".into(),
max_atomic_width: Some(64),
crt_static_default: false,
..base::linux_musl::opts()
},
}
}

View file

@ -71,7 +71,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<'
// the end of the list corresponding to the opaque's generics. // the end of the list corresponding to the opaque's generics.
for param in &generics.params[tcx.generics_of(fn_def_id).params.len()..] { for param in &generics.params[tcx.generics_of(fn_def_id).params.len()..] {
let orig_lt = let orig_lt =
tcx.map_rpit_lifetime_to_fn_lifetime(param.def_id.expect_local()); tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local());
if matches!(*orig_lt, ty::ReLateParam(..)) { if matches!(*orig_lt, ty::ReLateParam(..)) {
mapping.insert( mapping.insert(
orig_lt, orig_lt,

View file

@ -141,7 +141,7 @@ impl<'tcx> OpaqueTypeCollector<'tcx> {
trace!(?origin); trace!(?origin);
match origin { match origin {
rustc_hir::OpaqueTyOrigin::FnReturn(_) | rustc_hir::OpaqueTyOrigin::AsyncFn(_) => {} rustc_hir::OpaqueTyOrigin::FnReturn(_) | rustc_hir::OpaqueTyOrigin::AsyncFn(_) => {}
rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty } => { rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty, .. } => {
if !in_assoc_ty { if !in_assoc_ty {
if !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) { if !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) {
return; return;

View file

@ -624,6 +624,7 @@ impl<K, V> BTreeMap<K, V> {
/// ``` /// ```
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_btree_new", since = "1.66.0")] #[rustc_const_stable(feature = "const_btree_new", since = "1.66.0")]
#[inline]
#[must_use] #[must_use]
pub const fn new() -> BTreeMap<K, V> { pub const fn new() -> BTreeMap<K, V> {
BTreeMap { root: None, length: 0, alloc: ManuallyDrop::new(Global), _marker: PhantomData } BTreeMap { root: None, length: 0, alloc: ManuallyDrop::new(Global), _marker: PhantomData }

View file

@ -796,6 +796,17 @@ pub const fn from_mut<T: ?Sized>(r: &mut T) -> *mut T {
/// let slice = ptr::slice_from_raw_parts(raw_pointer, 3); /// let slice = ptr::slice_from_raw_parts(raw_pointer, 3);
/// assert_eq!(unsafe { &*slice }[2], 7); /// assert_eq!(unsafe { &*slice }[2], 7);
/// ``` /// ```
///
/// You must ensure that the pointer is valid and not null before dereferencing
/// the raw slice. A slice reference must never have a null pointer, even if it's empty.
///
/// ```rust,should_panic
/// use std::ptr;
/// let danger: *const [u8] = ptr::slice_from_raw_parts(ptr::null(), 0);
/// unsafe {
/// danger.as_ref().expect("references must not be null");
/// }
/// ```
#[inline] #[inline]
#[stable(feature = "slice_from_raw_parts", since = "1.42.0")] #[stable(feature = "slice_from_raw_parts", since = "1.42.0")]
#[rustc_const_stable(feature = "const_slice_from_raw_parts", since = "1.64.0")] #[rustc_const_stable(feature = "const_slice_from_raw_parts", since = "1.64.0")]
@ -805,11 +816,13 @@ pub const fn slice_from_raw_parts<T>(data: *const T, len: usize) -> *const [T] {
from_raw_parts(data.cast(), len) from_raw_parts(data.cast(), len)
} }
/// Forms a raw mutable slice from a pointer and a length.
///
/// The `len` argument is the number of **elements**, not the number of bytes.
///
/// Performs the same functionality as [`slice_from_raw_parts`], except that a /// Performs the same functionality as [`slice_from_raw_parts`], except that a
/// raw mutable slice is returned, as opposed to a raw immutable slice. /// raw mutable slice is returned, as opposed to a raw immutable slice.
/// ///
/// See the documentation of [`slice_from_raw_parts`] for more details.
///
/// This function is safe, but actually using the return value is unsafe. /// This function is safe, but actually using the return value is unsafe.
/// See the documentation of [`slice::from_raw_parts_mut`] for slice safety requirements. /// See the documentation of [`slice::from_raw_parts_mut`] for slice safety requirements.
/// ///
@ -830,6 +843,17 @@ pub const fn slice_from_raw_parts<T>(data: *const T, len: usize) -> *const [T] {
/// ///
/// assert_eq!(unsafe { &*slice }[2], 99); /// assert_eq!(unsafe { &*slice }[2], 99);
/// ``` /// ```
///
/// You must ensure that the pointer is valid and not null before dereferencing
/// the raw slice. A slice reference must never have a null pointer, even if it's empty.
///
/// ```rust,should_panic
/// use std::ptr;
/// let danger: *mut [u8] = ptr::slice_from_raw_parts_mut(ptr::null_mut(), 0);
/// unsafe {
/// danger.as_mut().expect("references must not be null");
/// }
/// ```
#[inline] #[inline]
#[stable(feature = "slice_from_raw_parts", since = "1.42.0")] #[stable(feature = "slice_from_raw_parts", since = "1.42.0")]
#[rustc_const_unstable(feature = "const_slice_from_raw_parts_mut", issue = "67456")] #[rustc_const_unstable(feature = "const_slice_from_raw_parts_mut", issue = "67456")]

View file

@ -248,10 +248,10 @@ pub struct DirBuilder {
/// ///
/// ```no_run /// ```no_run
/// use std::fs; /// use std::fs;
/// use std::net::SocketAddr;
/// ///
/// fn main() -> Result<(), Box<dyn std::error::Error + 'static>> { /// fn main() -> Result<(), Box<dyn std::error::Error + 'static>> {
/// let foo: SocketAddr = String::from_utf8_lossy(&fs::read("address.txt")?).parse()?; /// let data: Vec<u8> = fs::read("image.jpg")?;
/// assert_eq!(data[0..3], [0xFF, 0xD8, 0xFF]);
/// Ok(()) /// Ok(())
/// } /// }
/// ``` /// ```
@ -290,11 +290,11 @@ pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
/// ///
/// ```no_run /// ```no_run
/// use std::fs; /// use std::fs;
/// use std::net::SocketAddr;
/// use std::error::Error; /// use std::error::Error;
/// ///
/// fn main() -> Result<(), Box<dyn Error>> { /// fn main() -> Result<(), Box<dyn Error>> {
/// let foo: SocketAddr = fs::read_to_string("address.txt")?.parse()?; /// let message: String = fs::read_to_string("message.txt")?;
/// println!("{}", message);
/// Ok(()) /// Ok(())
/// } /// }
/// ``` /// ```

View file

@ -554,10 +554,12 @@ impl<W: ?Sized + Write> Write for BufWriter<W> {
// same underlying buffer, as otherwise the buffers wouldn't fit in memory). If the // same underlying buffer, as otherwise the buffers wouldn't fit in memory). If the
// computation overflows, then surely the input cannot fit in our buffer, so we forward // computation overflows, then surely the input cannot fit in our buffer, so we forward
// to the inner writer's `write_vectored` method to let it handle it appropriately. // to the inner writer's `write_vectored` method to let it handle it appropriately.
let saturated_total_len = let mut saturated_total_len: usize = 0;
bufs.iter().fold(0usize, |acc, b| acc.saturating_add(b.len()));
if saturated_total_len > self.spare_capacity() { for buf in bufs {
saturated_total_len = saturated_total_len.saturating_add(buf.len());
if saturated_total_len > self.spare_capacity() && !self.buf.is_empty() {
// Flush if the total length of the input exceeds our buffer's spare capacity. // Flush if the total length of the input exceeds our buffer's spare capacity.
// If we would have overflowed, this condition also holds, and we need to flush. // If we would have overflowed, this condition also holds, and we need to flush.
self.flush_buf()?; self.flush_buf()?;
@ -570,8 +572,10 @@ impl<W: ?Sized + Write> Write for BufWriter<W> {
self.panicked = true; self.panicked = true;
let r = self.get_mut().write_vectored(bufs); let r = self.get_mut().write_vectored(bufs);
self.panicked = false; self.panicked = false;
r return r;
} else { }
}
// `saturated_total_len < self.buf.capacity()` implies that we did not saturate. // `saturated_total_len < self.buf.capacity()` implies that we did not saturate.
// SAFETY: We checked whether or not the spare capacity was large enough above. If // SAFETY: We checked whether or not the spare capacity was large enough above. If
@ -582,7 +586,6 @@ impl<W: ?Sized + Write> Write for BufWriter<W> {
}; };
Ok(saturated_total_len) Ok(saturated_total_len)
}
} else { } else {
let mut iter = bufs.iter(); let mut iter = bufs.iter();
let mut total_written = if let Some(buf) = iter.by_ref().find(|&buf| !buf.is_empty()) { let mut total_written = if let Some(buf) = iter.by_ref().find(|&buf| !buf.is_empty()) {

View file

@ -175,6 +175,10 @@ impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> {
} }
// Find the buffer containing the last newline // Find the buffer containing the last newline
// FIXME: This is overly slow if there are very many bufs and none contain
// newlines. e.g. writev() on Linux only writes up to 1024 slices, so
// scanning the rest is wasted effort. This makes write_all_vectored()
// quadratic.
let last_newline_buf_idx = bufs let last_newline_buf_idx = bufs
.iter() .iter()
.enumerate() .enumerate()
@ -215,10 +219,15 @@ impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> {
// Don't try to reconstruct the exact amount written; just bail // Don't try to reconstruct the exact amount written; just bail
// in the event of a partial write // in the event of a partial write
let lines_len = lines.iter().map(|buf| buf.len()).sum(); let mut lines_len: usize = 0;
for buf in lines {
// With overlapping/duplicate slices the total length may in theory
// exceed usize::MAX
lines_len = lines_len.saturating_add(buf.len());
if flushed < lines_len { if flushed < lines_len {
return Ok(flushed); return Ok(flushed);
} }
}
// Now that the write has succeeded, buffer the rest (or as much of the // Now that the write has succeeded, buffer the rest (or as much of the
// rest as possible) // rest as possible)

View file

@ -128,8 +128,8 @@ impl Write for StdoutRaw {
} }
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
let total = bufs.iter().map(|b| b.len()).sum(); let total = || bufs.iter().map(|b| b.len()).sum();
handle_ebadf(self.0.write_vectored(bufs), total) handle_ebadf_lazy(self.0.write_vectored(bufs), total)
} }
#[inline] #[inline]
@ -160,8 +160,8 @@ impl Write for StderrRaw {
} }
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
let total = bufs.iter().map(|b| b.len()).sum(); let total = || bufs.iter().map(|b| b.len()).sum();
handle_ebadf(self.0.write_vectored(bufs), total) handle_ebadf_lazy(self.0.write_vectored(bufs), total)
} }
#[inline] #[inline]
@ -193,6 +193,13 @@ fn handle_ebadf<T>(r: io::Result<T>, default: T) -> io::Result<T> {
} }
} }
fn handle_ebadf_lazy<T>(r: io::Result<T>, default: impl FnOnce() -> T) -> io::Result<T> {
match r {
Err(ref e) if stdio::is_ebadf(e) => Ok(default()),
r => r,
}
}
/// A handle to the standard input stream of a process. /// A handle to the standard input stream of a process.
/// ///
/// Each handle is a shared reference to a global buffer of input data to this /// Each handle is a shared reference to a global buffer of input data to this

View file

@ -165,6 +165,7 @@ target | std | notes
`i686-unknown-freebsd` | ✓ | 32-bit FreeBSD [^x86_32-floats-return-ABI] `i686-unknown-freebsd` | ✓ | 32-bit FreeBSD [^x86_32-floats-return-ABI]
`i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 [^x86_32-floats-return-ABI] `i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 [^x86_32-floats-return-ABI]
[`i686-unknown-uefi`](platform-support/unknown-uefi.md) | * | 32-bit UEFI [`i686-unknown-uefi`](platform-support/unknown-uefi.md) | * | 32-bit UEFI
[`loongarch64-unknown-linux-musl`](platform-support/loongarch-linux.md) | ? | | LoongArch64 Linux (LP64D ABI) with musl 1.2.3
[`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | | LoongArch64 Bare-metal (LP64D ABI) [`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | | LoongArch64 Bare-metal (LP64D ABI)
[`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | | LoongArch64 Bare-metal (LP64S ABI) [`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | | LoongArch64 Bare-metal (LP64S ABI)
[`nvptx64-nvidia-cuda`](platform-support/nvptx64-nvidia-cuda.md) | * | --emit=asm generates PTX code that [runs on NVIDIA GPUs] [`nvptx64-nvidia-cuda`](platform-support/nvptx64-nvidia-cuda.md) | * | --emit=asm generates PTX code that [runs on NVIDIA GPUs]

View file

@ -0,0 +1,30 @@
# `asm_goto`
The tracking issue for this feature is: [#119364]
[#119364]: https://github.com/rust-lang/rust/issues/119364
------------------------
This feature adds a `label <block>` operand type to `asm!`.
Example:
```rust,ignore (partial-example, x86-only)
unsafe {
asm!(
"jmp {}",
label {
println!("Jumped from asm!");
}
);
}
```
The block must have unit type or diverge.
When `label <block>` is used together with `noreturn` option, it means that the
assembly will not fallthrough. It's allowed to jump to a label within the
assembly. In this case, the entire `asm!` expression will have an unit type as
opposed to diverging, if not all label blocks diverge. The `asm!` expression
still diverges if `noreturn` option is used and all label blocks diverge.

View file

@ -255,6 +255,9 @@ fn never_loop_expr<'tcx>(
InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymStatic { .. } => { InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymStatic { .. } => {
NeverLoopResult::Normal NeverLoopResult::Normal
}, },
InlineAsmOperand::Label { block } => {
never_loop_block(cx, block, local_labels, main_loop_id)
}
})), })),
ExprKind::OffsetOf(_, _) ExprKind::OffsetOf(_, _)
| ExprKind::Yield(_, _) | ExprKind::Yield(_, _)

View file

@ -833,6 +833,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_body(anon_const.body); self.hash_body(anon_const.body);
}, },
InlineAsmOperand::SymStatic { path, def_id: _ } => self.hash_qpath(path), InlineAsmOperand::SymStatic { path, def_id: _ } => self.hash_qpath(path),
InlineAsmOperand::Label { block } => self.hash_block(block),
} }
} }
}, },

View file

@ -234,6 +234,9 @@
//@ revisions: loongarch64_unknown_linux_gnu //@ revisions: loongarch64_unknown_linux_gnu
//@ [loongarch64_unknown_linux_gnu] compile-flags: --target loongarch64-unknown-linux-gnu //@ [loongarch64_unknown_linux_gnu] compile-flags: --target loongarch64-unknown-linux-gnu
//@ [loongarch64_unknown_linux_gnu] needs-llvm-components: loongarch //@ [loongarch64_unknown_linux_gnu] needs-llvm-components: loongarch
//@ revisions: loongarch64_unknown_linux_musl
//@ [loongarch64_unknown_linux_musl] compile-flags: --target loongarch64-unknown-linux-musl
//@ [loongarch64_unknown_linux_musl] needs-llvm-components: loongarch
//@ revisions: loongarch64_unknown_none //@ revisions: loongarch64_unknown_none
//@ [loongarch64_unknown_none] compile-flags: --target loongarch64-unknown-none //@ [loongarch64_unknown_none] compile-flags: --target loongarch64-unknown-none
//@ [loongarch64_unknown_none] needs-llvm-components: loongarch //@ [loongarch64_unknown_none] needs-llvm-components: loongarch

51
tests/codegen/asm-goto.rs Normal file
View file

@ -0,0 +1,51 @@
//@ compile-flags: -O
//@ only-x86_64
#![crate_type = "rlib"]
#![feature(asm_goto)]
use std::arch::asm;
#[no_mangle]
pub extern "C" fn panicky() {}
struct Foo;
impl Drop for Foo {
fn drop(&mut self) {
println!();
}
}
// CHECK-LABEL: @asm_goto
#[no_mangle]
pub unsafe fn asm_goto() {
// CHECK: callbr void asm sideeffect alignstack inteldialect "
// CHECK-NEXT: to label %[[FALLTHROUGHBB:[a-b0-9]+]] [label %[[JUMPBB:[a-b0-9]+]]]
asm!("jmp {}", label {});
}
// CHECK-LABEL: @asm_goto_with_outputs
#[no_mangle]
pub unsafe fn asm_goto_with_outputs() -> u64 {
let out: u64;
// CHECK: [[RES:%[0-9]+]] = callbr i64 asm sideeffect alignstack inteldialect "
// CHECK-NEXT: to label %[[FALLTHROUGHBB:[a-b0-9]+]] [label %[[JUMPBB:[a-b0-9]+]]]
asm!("{} /* {} */", out(reg) out, label { return 1; });
// CHECK: [[JUMPBB]]:
// CHECK-NEXT: [[RET:%.+]] = phi i64 [ [[RES]], %[[FALLTHROUGHBB]] ], [ 1, %start ]
// CHECK-NEXT: ret i64 [[RET]]
out
}
// CHECK-LABEL: @asm_goto_noreturn
#[no_mangle]
pub unsafe fn asm_goto_noreturn() -> u64 {
let out: u64;
// CHECK: callbr void asm sideeffect alignstack inteldialect "
// CHECK-NEXT: to label %unreachable [label %[[JUMPBB:[a-b0-9]+]]]
asm!("jmp {}", label { return 1; }, options(noreturn));
// CHECK: [[JUMPBB]]:
// CHECK-NEXT: ret i64 1
out
}

View file

@ -130,17 +130,17 @@ LL | asm!("{1}", in("x0") foo, const bar);
| | | |
| explicit register argument | explicit register argument
error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `""` error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""`
--> $DIR/parse-error.rs:66:29 --> $DIR/parse-error.rs:66:29
| |
LL | asm!("", options(), ""); LL | asm!("", options(), "");
| ^^ expected one of 9 possible tokens | ^^ expected one of 10 possible tokens
error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `"{}"` error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `"{}"`
--> $DIR/parse-error.rs:68:33 --> $DIR/parse-error.rs:68:33
| |
LL | asm!("{}", in(reg) foo, "{}", out(reg) foo); LL | asm!("{}", in(reg) foo, "{}", out(reg) foo);
| ^^^^ expected one of 9 possible tokens | ^^^^ expected one of 10 possible tokens
error: asm template must be a string literal error: asm template must be a string literal
--> $DIR/parse-error.rs:70:14 --> $DIR/parse-error.rs:70:14

View file

@ -142,3 +142,5 @@ global_asm!(format!("{{{}}}", 0), const FOO);
//~^ ERROR asm template must be a string literal //~^ ERROR asm template must be a string literal
global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
//~^ ERROR asm template must be a string literal //~^ ERROR asm template must be a string literal
global_asm!("{}", label {});
//~^ ERROR expected operand, options, or additional template string

View file

@ -176,17 +176,17 @@ LL | asm!("{a}", a = const foo, a = const bar);
| |
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"`
error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `""` error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""`
--> $DIR/parse-error.rs:82:29 --> $DIR/parse-error.rs:82:29
| |
LL | asm!("", options(), ""); LL | asm!("", options(), "");
| ^^ expected one of 9 possible tokens | ^^ expected one of 10 possible tokens
error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `"{}"` error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `"{}"`
--> $DIR/parse-error.rs:84:33 --> $DIR/parse-error.rs:84:33
| |
LL | asm!("{}", in(reg) foo, "{}", out(reg) foo); LL | asm!("{}", in(reg) foo, "{}", out(reg) foo);
| ^^^^ expected one of 9 possible tokens | ^^^^ expected one of 10 possible tokens
error: asm template must be a string literal error: asm template must be a string literal
--> $DIR/parse-error.rs:86:14 --> $DIR/parse-error.rs:86:14
@ -362,6 +362,12 @@ LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
| |
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
error: expected operand, options, or additional template string
--> $DIR/parse-error.rs:145:19
|
LL | global_asm!("{}", label {});
| ^^^^^^^^ expected operand, options, or additional template string
error[E0435]: attempt to use a non-constant value in a constant error[E0435]: attempt to use a non-constant value in a constant
--> $DIR/parse-error.rs:39:37 --> $DIR/parse-error.rs:39:37
| |
@ -407,6 +413,6 @@ LL | let mut bar = 0;
LL | asm!("{a}", a = const foo, a = const bar); LL | asm!("{a}", a = const foo, a = const bar);
| ^^^ non-constant value | ^^^ non-constant value
error: aborting due to 63 previous errors error: aborting due to 64 previous errors
For more information about this error, try `rustc --explain E0435`. For more information about this error, try `rustc --explain E0435`.

View file

@ -1,5 +1,7 @@
//@ only-x86_64 //@ only-x86_64
#![feature(asm_unwind, asm_goto)]
use std::arch::{asm, global_asm}; use std::arch::{asm, global_asm};
fn main() { fn main() {
@ -14,6 +16,8 @@ fn main() {
//~^ ERROR asm with the `pure` option must have at least one output //~^ ERROR asm with the `pure` option must have at least one output
asm!("{}", out(reg) foo, options(noreturn)); asm!("{}", out(reg) foo, options(noreturn));
//~^ ERROR asm outputs are not allowed with the `noreturn` option //~^ ERROR asm outputs are not allowed with the `noreturn` option
asm!("{}", label {}, options(may_unwind));
//~^ ERROR asm labels are not allowed with the `may_unwind` option
} }
unsafe { unsafe {

View file

@ -1,35 +1,41 @@
error: the `nomem` and `readonly` options are mutually exclusive error: the `nomem` and `readonly` options are mutually exclusive
--> $DIR/bad-options.rs:8:18 --> $DIR/bad-options.rs:10:18
| |
LL | asm!("", options(nomem, readonly)); LL | asm!("", options(nomem, readonly));
| ^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^
error: the `pure` and `noreturn` options are mutually exclusive error: the `pure` and `noreturn` options are mutually exclusive
--> $DIR/bad-options.rs:10:18 --> $DIR/bad-options.rs:12:18
| |
LL | asm!("", options(pure, nomem, noreturn)); LL | asm!("", options(pure, nomem, noreturn));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: asm with the `pure` option must have at least one output error: asm with the `pure` option must have at least one output
--> $DIR/bad-options.rs:10:18 --> $DIR/bad-options.rs:12:18
| |
LL | asm!("", options(pure, nomem, noreturn)); LL | asm!("", options(pure, nomem, noreturn));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: asm with the `pure` option must have at least one output error: asm with the `pure` option must have at least one output
--> $DIR/bad-options.rs:13:33 --> $DIR/bad-options.rs:15:33
| |
LL | asm!("{}", in(reg) foo, options(pure, nomem)); LL | asm!("{}", in(reg) foo, options(pure, nomem));
| ^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^
error: asm outputs are not allowed with the `noreturn` option error: asm outputs are not allowed with the `noreturn` option
--> $DIR/bad-options.rs:15:20 --> $DIR/bad-options.rs:17:20
| |
LL | asm!("{}", out(reg) foo, options(noreturn)); LL | asm!("{}", out(reg) foo, options(noreturn));
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
error: asm labels are not allowed with the `may_unwind` option
--> $DIR/bad-options.rs:19:20
|
LL | asm!("{}", label {}, options(may_unwind));
| ^^^^^^^^
error: asm with `clobber_abi` must specify explicit registers for outputs error: asm with `clobber_abi` must specify explicit registers for outputs
--> $DIR/bad-options.rs:22:20 --> $DIR/bad-options.rs:26:20
| |
LL | asm!("{}", out(reg) foo, clobber_abi("C")); LL | asm!("{}", out(reg) foo, clobber_abi("C"));
| ^^^^^^^^^^^^ ---------------- clobber_abi | ^^^^^^^^^^^^ ---------------- clobber_abi
@ -37,7 +43,7 @@ LL | asm!("{}", out(reg) foo, clobber_abi("C"));
| generic outputs | generic outputs
error: asm with `clobber_abi` must specify explicit registers for outputs error: asm with `clobber_abi` must specify explicit registers for outputs
--> $DIR/bad-options.rs:24:20 --> $DIR/bad-options.rs:28:20
| |
LL | asm!("{}", out(reg) foo, clobber_abi("C"), clobber_abi("C")); LL | asm!("{}", out(reg) foo, clobber_abi("C"), clobber_abi("C"));
| ^^^^^^^^^^^^ ---------------- ---------------- clobber_abi | ^^^^^^^^^^^^ ---------------- ---------------- clobber_abi
@ -46,43 +52,43 @@ LL | asm!("{}", out(reg) foo, clobber_abi("C"), clobber_abi("C"));
| generic outputs | generic outputs
error: expected one of `)`, `att_syntax`, or `raw`, found `nomem` error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
--> $DIR/bad-options.rs:31:25 --> $DIR/bad-options.rs:35:25
| |
LL | global_asm!("", options(nomem)); LL | global_asm!("", options(nomem));
| ^^^^^ expected one of `)`, `att_syntax`, or `raw` | ^^^^^ expected one of `)`, `att_syntax`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `readonly` error: expected one of `)`, `att_syntax`, or `raw`, found `readonly`
--> $DIR/bad-options.rs:33:25 --> $DIR/bad-options.rs:37:25
| |
LL | global_asm!("", options(readonly)); LL | global_asm!("", options(readonly));
| ^^^^^^^^ expected one of `)`, `att_syntax`, or `raw` | ^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `noreturn` error: expected one of `)`, `att_syntax`, or `raw`, found `noreturn`
--> $DIR/bad-options.rs:35:25 --> $DIR/bad-options.rs:39:25
| |
LL | global_asm!("", options(noreturn)); LL | global_asm!("", options(noreturn));
| ^^^^^^^^ expected one of `)`, `att_syntax`, or `raw` | ^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `pure` error: expected one of `)`, `att_syntax`, or `raw`, found `pure`
--> $DIR/bad-options.rs:37:25 --> $DIR/bad-options.rs:41:25
| |
LL | global_asm!("", options(pure)); LL | global_asm!("", options(pure));
| ^^^^ expected one of `)`, `att_syntax`, or `raw` | ^^^^ expected one of `)`, `att_syntax`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `nostack` error: expected one of `)`, `att_syntax`, or `raw`, found `nostack`
--> $DIR/bad-options.rs:39:25 --> $DIR/bad-options.rs:43:25
| |
LL | global_asm!("", options(nostack)); LL | global_asm!("", options(nostack));
| ^^^^^^^ expected one of `)`, `att_syntax`, or `raw` | ^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `preserves_flags` error: expected one of `)`, `att_syntax`, or `raw`, found `preserves_flags`
--> $DIR/bad-options.rs:41:25 --> $DIR/bad-options.rs:45:25
| |
LL | global_asm!("", options(preserves_flags)); LL | global_asm!("", options(preserves_flags));
| ^^^^^^^^^^^^^^^ expected one of `)`, `att_syntax`, or `raw` | ^^^^^^^^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
error: invalid ABI for `clobber_abi` error: invalid ABI for `clobber_abi`
--> $DIR/bad-options.rs:20:18 --> $DIR/bad-options.rs:24:18
| |
LL | asm!("", clobber_abi("foo")); LL | asm!("", clobber_abi("foo"));
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
@ -90,12 +96,12 @@ LL | asm!("", clobber_abi("foo"));
= note: the following ABIs are supported on this target: `C`, `system`, `efiapi`, `win64`, `sysv64` = note: the following ABIs are supported on this target: `C`, `system`, `efiapi`, `win64`, `sysv64`
error: `C` ABI specified multiple times error: `C` ABI specified multiple times
--> $DIR/bad-options.rs:24:52 --> $DIR/bad-options.rs:28:52
| |
LL | asm!("{}", out(reg) foo, clobber_abi("C"), clobber_abi("C")); LL | asm!("{}", out(reg) foo, clobber_abi("C"), clobber_abi("C"));
| ---------------- ^^^^^^^^^^^^^^^^ | ---------------- ^^^^^^^^^^^^^^^^
| | | |
| previously specified here | previously specified here
error: aborting due to 15 previous errors error: aborting due to 16 previous errors

View file

@ -0,0 +1,23 @@
warning: unreachable statement
--> $DIR/goto.rs:99:9
|
LL | / asm!(
LL | | "jmp {}",
LL | | label {
LL | | return;
LL | | },
LL | | options(noreturn)
LL | | );
| |_________- any code following this expression is unreachable
LL | unreachable!();
| ^^^^^^^^^^^^^^ unreachable statement
|
note: the lint level is defined here
--> $DIR/goto.rs:89:8
|
LL | #[warn(unreachable_code)]
| ^^^^^^^^^^^^^^^^
= note: this warning originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info)
warning: 1 warning emitted

111
tests/ui/asm/x86_64/goto.rs Normal file
View file

@ -0,0 +1,111 @@
//@ only-x86_64
//@ run-pass
//@ needs-asm-support
//@ revisions: mirunsafeck thirunsafeck
//@ [thirunsafeck]compile-flags: -Z thir-unsafeck
#![deny(unreachable_code)]
#![feature(asm_goto)]
use std::arch::asm;
fn goto_fallthough() {
unsafe {
asm!(
"/* {} */",
label {
unreachable!();
}
)
}
}
fn goto_jump() {
unsafe {
let mut value = false;
asm!(
"jmp {}",
label {
value = true;
}
);
assert!(value);
}
}
// asm goto with outputs cause miscompilation in LLVM. UB can be triggered
// when outputs are used inside the label block when optimisation is enabled.
// See: https://github.com/llvm/llvm-project/issues/74483
/*
fn goto_out_fallthrough() {
unsafe {
let mut out: usize;
asm!(
"lea {}, [{} + 1]",
"/* {} */",
out(reg) out,
in(reg) 0x12345678usize,
label {
unreachable!();
}
);
assert_eq!(out, 0x12345679);
}
}
fn goto_out_jump() {
unsafe {
let mut value = false;
let mut out: usize;
asm!(
"lea {}, [{} + 1]",
"jmp {}",
out(reg) out,
in(reg) 0x12345678usize,
label {
value = true;
assert_eq!(out, 0x12345679);
}
);
assert!(value);
}
}
*/
fn goto_noreturn() {
unsafe {
let a;
asm!(
"jmp {}",
label {
a = 1;
},
options(noreturn)
);
assert_eq!(a, 1);
}
}
#[warn(unreachable_code)]
fn goto_noreturn_diverge() {
unsafe {
asm!(
"jmp {}",
label {
return;
},
options(noreturn)
);
unreachable!();
//~^ WARN unreachable statement
}
}
fn main() {
goto_fallthough();
goto_jump();
// goto_out_fallthrough();
// goto_out_jump();
goto_noreturn();
goto_noreturn_diverge();
}

View file

@ -0,0 +1,23 @@
warning: unreachable statement
--> $DIR/goto.rs:99:9
|
LL | / asm!(
LL | | "jmp {}",
LL | | label {
LL | | return;
LL | | },
LL | | options(noreturn)
LL | | );
| |_________- any code following this expression is unreachable
LL | unreachable!();
| ^^^^^^^^^^^^^^ unreachable statement
|
note: the lint level is defined here
--> $DIR/goto.rs:89:8
|
LL | #[warn(unreachable_code)]
| ^^^^^^^^^^^^^^^^
= note: this warning originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info)
warning: 1 warning emitted

View file

@ -0,0 +1,10 @@
//@ only-x86_64
use std::arch::asm;
fn main() {
unsafe {
asm!("jmp {}", label {});
//~^ ERROR label operands for inline assembly are unstable
}
}

Some files were not shown because too many files have changed in this diff Show more