Rollup merge of #131523 - nbdd0121:asm, r=compiler-errors
Fix asm goto with outputs and move it to a separate feature gate Tracking issue: #119364 This PR addresses 3 aspects of asm goto with outputs: * Codegen is fixed. My initial implementation has an oversight which cause the output to be only stored in fallthrough path, but not in label blocks. * Outputs can now be used with `options(noreturn)` if a label block is given. * All of this is moved to a new feature gate, because we likely want to stabilise `asm_goto` before asm goto with outputs. `@rustbot` labels: +A-inline-assembly +F-asm
This commit is contained in:
commit
c5230d1148
11 changed files with 178 additions and 50 deletions
|
@ -181,6 +181,8 @@ ast_lowering_underscore_expr_lhs_assign =
|
|||
.label = `_` not allowed here
|
||||
|
||||
ast_lowering_unstable_inline_assembly = inline assembly is not stable yet on this architecture
|
||||
ast_lowering_unstable_inline_assembly_label_operand_with_outputs =
|
||||
using both label and output operands for inline assembly is unstable
|
||||
ast_lowering_unstable_inline_assembly_label_operands =
|
||||
label operands for inline assembly are unstable
|
||||
ast_lowering_unstable_may_unwind = the `may_unwind` option is unstable
|
||||
|
|
|
@ -239,15 +239,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
}
|
||||
}
|
||||
InlineAsmOperand::Label { block } => {
|
||||
if !self.tcx.features().asm_goto() {
|
||||
feature_err(
|
||||
sess,
|
||||
sym::asm_goto,
|
||||
*op_sp,
|
||||
fluent::ast_lowering_unstable_inline_assembly_label_operands,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
hir::InlineAsmOperand::Label { block: self.lower_block(block, false) }
|
||||
}
|
||||
};
|
||||
|
@ -466,6 +457,41 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
}
|
||||
}
|
||||
|
||||
// Feature gate checking for asm goto.
|
||||
if let Some((_, op_sp)) =
|
||||
operands.iter().find(|(op, _)| matches!(op, hir::InlineAsmOperand::Label { .. }))
|
||||
{
|
||||
if !self.tcx.features().asm_goto() {
|
||||
feature_err(
|
||||
sess,
|
||||
sym::asm_goto,
|
||||
*op_sp,
|
||||
fluent::ast_lowering_unstable_inline_assembly_label_operands,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
|
||||
// In addition, check if an output operand is used.
|
||||
// This is gated behind an additional feature.
|
||||
let output_operand_used = operands.iter().any(|(op, _)| {
|
||||
matches!(
|
||||
op,
|
||||
hir::InlineAsmOperand::Out { expr: Some(_), .. }
|
||||
| hir::InlineAsmOperand::InOut { .. }
|
||||
| hir::InlineAsmOperand::SplitInOut { out_expr: Some(_), .. }
|
||||
)
|
||||
});
|
||||
if output_operand_used && !self.tcx.features().asm_goto_with_outputs() {
|
||||
feature_err(
|
||||
sess,
|
||||
sym::asm_goto_with_outputs,
|
||||
*op_sp,
|
||||
fluent::ast_lowering_unstable_inline_assembly_label_operand_with_outputs,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
|
||||
let operands = self.arena.alloc_from_iter(operands);
|
||||
let template = self.arena.alloc_from_iter(asm.template.iter().cloned());
|
||||
let template_strs = self.arena.alloc_from_iter(
|
||||
|
|
|
@ -300,7 +300,10 @@ pub fn parse_asm_args<'a>(
|
|||
if args.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output {
|
||||
dcx.emit_err(errors::AsmPureNoOutput { spans: args.options_spans.clone() });
|
||||
}
|
||||
if args.options.contains(ast::InlineAsmOptions::NORETURN) && !outputs_sp.is_empty() {
|
||||
if args.options.contains(ast::InlineAsmOptions::NORETURN)
|
||||
&& !outputs_sp.is_empty()
|
||||
&& labels_sp.is_empty()
|
||||
{
|
||||
let err = dcx.create_err(errors::AsmNoReturn { outputs_sp });
|
||||
// Bail out now since this is likely to confuse MIR
|
||||
return Err(err);
|
||||
|
|
|
@ -342,24 +342,32 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
|
|||
}
|
||||
attributes::apply_to_callsite(result, llvm::AttributePlace::Function, &{ attrs });
|
||||
|
||||
// Switch to the 'normal' basic block if we did an `invoke` instead of a `call`
|
||||
if let Some(dest) = dest {
|
||||
self.switch_to_block(dest);
|
||||
}
|
||||
// Write results to outputs. We need to do this for all possible control flow.
|
||||
//
|
||||
// Note that `dest` maybe populated with unreachable_block when asm goto with outputs
|
||||
// is used (because we need to codegen callbr which always needs a destination), so
|
||||
// here we use the NORETURN option to determine if `dest` should be used.
|
||||
for block in (if options.contains(InlineAsmOptions::NORETURN) { None } else { Some(dest) })
|
||||
.into_iter()
|
||||
.chain(labels.iter().copied().map(Some))
|
||||
{
|
||||
if let Some(block) = block {
|
||||
self.switch_to_block(block);
|
||||
}
|
||||
|
||||
// Write results to outputs
|
||||
for (idx, op) in operands.iter().enumerate() {
|
||||
if let InlineAsmOperandRef::Out { reg, place: Some(place), .. }
|
||||
| InlineAsmOperandRef::InOut { reg, out_place: Some(place), .. } = *op
|
||||
{
|
||||
let value = if output_types.len() == 1 {
|
||||
result
|
||||
} else {
|
||||
self.extract_value(result, op_idx[&idx] as u64)
|
||||
};
|
||||
let value =
|
||||
llvm_fixup_output(self, value, reg.reg_class(), &place.layout, instance);
|
||||
OperandValue::Immediate(value).store(self, place);
|
||||
for (idx, op) in operands.iter().enumerate() {
|
||||
if let InlineAsmOperandRef::Out { reg, place: Some(place), .. }
|
||||
| InlineAsmOperandRef::InOut { reg, out_place: Some(place), .. } = *op
|
||||
{
|
||||
let value = if output_types.len() == 1 {
|
||||
result
|
||||
} else {
|
||||
self.extract_value(result, op_idx[&idx] as u64)
|
||||
};
|
||||
let value =
|
||||
llvm_fixup_output(self, value, reg.reg_class(), &place.layout, instance);
|
||||
OperandValue::Immediate(value).store(self, place);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -378,6 +378,8 @@ declare_features! (
|
|||
(unstable, asm_experimental_arch, "1.58.0", Some(93335)),
|
||||
/// Allows using `label` operands in inline assembly.
|
||||
(unstable, asm_goto, "1.78.0", Some(119364)),
|
||||
/// Allows using `label` operands in inline assembly together with output operands.
|
||||
(unstable, asm_goto_with_outputs, "CURRENT_RUSTC_VERSION", Some(119364)),
|
||||
/// Allows the `may_unwind` option in inline assembly.
|
||||
(unstable, asm_unwind, "1.58.0", Some(93334)),
|
||||
/// Allows users to enforce equality of associated constants `TraitImpl<AssocConst=3>`.
|
||||
|
|
|
@ -417,6 +417,7 @@ symbols! {
|
|||
asm_const,
|
||||
asm_experimental_arch,
|
||||
asm_goto,
|
||||
asm_goto_with_outputs,
|
||||
asm_sym,
|
||||
asm_unwind,
|
||||
assert,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue