From cecffdc1d74718444b6654b120dcd57dcbad018f Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Mon, 11 May 2020 17:53:32 +0100 Subject: [PATCH] Fix const handling and add tests for const operands --- src/librustc_codegen_ssa/mir/block.rs | 219 +++++++++++++++----------- src/test/ui/asm/const.rs | 56 +++++++ 2 files changed, 184 insertions(+), 91 deletions(-) create mode 100644 src/test/ui/asm/const.rs diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index 923fcd326a28..b487ed8dea8b 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -13,6 +13,7 @@ use rustc_ast::ast; use rustc_hir::lang_items; use rustc_index::vec::Idx; use rustc_middle::mir; +use rustc_middle::mir::interpret::{AllocId, ConstValue, Pointer, Scalar}; use rustc_middle::mir::AssertKind; use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt}; use rustc_middle::ty::{self, Instance, Ty, TypeFoldable}; @@ -821,6 +822,123 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { cleanup, ); } + + fn codegen_asm_terminator( + &mut self, + helper: TerminatorCodegenHelper<'tcx>, + mut bx: Bx, + terminator: &mir::Terminator<'tcx>, + template: &[ast::InlineAsmTemplatePiece], + operands: &[mir::InlineAsmOperand<'tcx>], + options: ast::InlineAsmOptions, + destination: Option, + ) { + let span = terminator.source_info.span; + + let operands: Vec<_> = operands + .iter() + .map(|op| match *op { + mir::InlineAsmOperand::In { reg, ref value } => { + let value = self.codegen_operand(&mut bx, value); + InlineAsmOperandRef::In { reg, value } + } + mir::InlineAsmOperand::Out { reg, late, ref place } => { + let place = place.map(|place| self.codegen_place(&mut bx, place.as_ref())); + InlineAsmOperandRef::Out { reg, late, place } + } + mir::InlineAsmOperand::InOut { reg, late, ref in_value, ref out_place } => { + let in_value = self.codegen_operand(&mut bx, in_value); + let out_place = + out_place.map(|out_place| self.codegen_place(&mut bx, out_place.as_ref())); + InlineAsmOperandRef::InOut { reg, late, in_value, out_place } + } + mir::InlineAsmOperand::Const { ref value } => { + if let mir::Operand::Constant(constant) = value { + let const_value = self + .eval_mir_constant(constant) + .unwrap_or_else(|_| span_bug!(span, "asm const cannot be resolved")); + let ty = constant.literal.ty; + let size = bx.layout_of(ty).size; + let scalar = match const_value { + // Promoted constants are evaluated into a ByRef instead of a Scalar, + // but we want the scalar value here. + ConstValue::ByRef { alloc, offset } => { + let ptr = Pointer::new(AllocId(0), offset); + alloc + .read_scalar(&bx, ptr, size) + .and_then(|s| s.not_undef()) + .unwrap_or_else(|e| { + bx.tcx().sess.span_err( + span, + &format!("Could not evaluate asm const: {}", e), + ); + + // We are erroring out, just emit a dummy constant. + Scalar::from_u64(0) + }) + } + _ => span_bug!(span, "expected ByRef for promoted asm const"), + }; + let value = scalar.assert_bits(size); + let string = match ty.kind { + ty::Uint(_) => value.to_string(), + ty::Int(int_ty) => { + match int_ty.normalize(bx.tcx().sess.target.ptr_width) { + ast::IntTy::I8 => (value as i8).to_string(), + ast::IntTy::I16 => (value as i16).to_string(), + ast::IntTy::I32 => (value as i32).to_string(), + ast::IntTy::I64 => (value as i64).to_string(), + ast::IntTy::I128 => (value as i128).to_string(), + ast::IntTy::Isize => unreachable!(), + } + } + ty::Float(ast::FloatTy::F32) => { + f32::from_bits(value as u32).to_string() + } + ty::Float(ast::FloatTy::F64) => { + f64::from_bits(value as u64).to_string() + } + _ => span_bug!(span, "asm const has bad type {}", ty), + }; + InlineAsmOperandRef::Const { string } + } else { + span_bug!(span, "asm const is not a constant"); + } + } + mir::InlineAsmOperand::SymFn { ref value } => { + let literal = self.monomorphize(&value.literal); + if let ty::FnDef(def_id, substs) = literal.ty.kind { + let instance = ty::Instance::resolve( + bx.tcx(), + ty::ParamEnv::reveal_all(), + def_id, + substs, + ) + .unwrap() + .unwrap(); + InlineAsmOperandRef::SymFn { instance } + } else { + span_bug!(span, "invalid type for asm sym (fn)"); + } + } + mir::InlineAsmOperand::SymStatic { ref value } => { + if let Some(def_id) = value.check_static_ptr(bx.tcx()) { + InlineAsmOperandRef::SymStatic { def_id } + } else { + span_bug!(span, "invalid type for asm sym (static)"); + } + } + }) + .collect(); + + bx.codegen_inline_asm(template, &operands, options, span); + + if let Some(target) = destination { + helper.funclet_br(self, &mut bx, target); + } else { + bx.unreachable(); + } + } } impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { @@ -916,97 +1034,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bug!("borrowck false edges in codegen") } - mir::TerminatorKind::InlineAsm { template, ref operands, options, ref destination } => { - let span = terminator.source_info.span; - - let operands: Vec<_> = operands - .iter() - .map(|op| match *op { - mir::InlineAsmOperand::In { reg, ref value } => { - let value = self.codegen_operand(&mut bx, value); - InlineAsmOperandRef::In { reg, value } - } - mir::InlineAsmOperand::Out { reg, late, ref place } => { - let place = - place.map(|place| self.codegen_place(&mut bx, place.as_ref())); - InlineAsmOperandRef::Out { reg, late, place } - } - mir::InlineAsmOperand::InOut { reg, late, ref in_value, ref out_place } => { - let in_value = self.codegen_operand(&mut bx, in_value); - let out_place = out_place - .map(|out_place| self.codegen_place(&mut bx, out_place.as_ref())); - InlineAsmOperandRef::InOut { reg, late, in_value, out_place } - } - mir::InlineAsmOperand::Const { ref value } => { - if let mir::Operand::Constant(constant) = value { - let const_value = - self.eval_mir_constant(constant).unwrap_or_else(|_| { - span_bug!(span, "asm const cannot be resolved") - }); - let ty = constant.literal.ty; - let value = const_value - .try_to_bits_for_ty(bx.tcx(), ty::ParamEnv::reveal_all(), ty) - .unwrap_or_else(|| { - span_bug!(span, "asm const has non-scalar value") - }); - let string = match ty.kind { - ty::Uint(_) => value.to_string(), - ty::Int(int_ty) => { - match int_ty.normalize(bx.tcx().sess.target.ptr_width) { - ast::IntTy::I8 => (value as i8).to_string(), - ast::IntTy::I16 => (value as i16).to_string(), - ast::IntTy::I32 => (value as i32).to_string(), - ast::IntTy::I64 => (value as i64).to_string(), - ast::IntTy::I128 => (value as i128).to_string(), - ast::IntTy::Isize => unreachable!(), - } - } - ty::Float(ast::FloatTy::F32) => { - f32::from_bits(value as u32).to_string() - } - ty::Float(ast::FloatTy::F64) => { - f64::from_bits(value as u64).to_string() - } - _ => span_bug!(span, "asm const has bad type {}", ty), - }; - InlineAsmOperandRef::Const { string } - } else { - span_bug!(span, "asm const is not a constant"); - } - } - mir::InlineAsmOperand::SymFn { ref value } => { - let literal = self.monomorphize(&value.literal); - if let ty::FnDef(def_id, substs) = literal.ty.kind { - let instance = ty::Instance::resolve( - bx.tcx(), - ty::ParamEnv::reveal_all(), - def_id, - substs, - ) - .unwrap() - .unwrap(); - InlineAsmOperandRef::SymFn { instance } - } else { - span_bug!(span, "invalid type for asm sym (fn)"); - } - } - mir::InlineAsmOperand::SymStatic { ref value } => { - if let Some(def_id) = value.check_static_ptr(bx.tcx()) { - InlineAsmOperandRef::SymStatic { def_id } - } else { - span_bug!(span, "invalid type for asm sym (static)"); - } - } - }) - .collect(); - - bx.codegen_inline_asm(template, &operands, options, span); - - if let Some(target) = destination { - helper.funclet_br(self, &mut bx, *target); - } else { - bx.unreachable(); - } + mir::TerminatorKind::InlineAsm { template, ref operands, options, destination } => { + self.codegen_asm_terminator( + helper, + bx, + terminator, + template, + operands, + options, + destination, + ); } } } diff --git a/src/test/ui/asm/const.rs b/src/test/ui/asm/const.rs new file mode 100644 index 000000000000..e08da24f44a2 --- /dev/null +++ b/src/test/ui/asm/const.rs @@ -0,0 +1,56 @@ +// no-system-llvm +// only-x86_64 +// run-pass + +#![feature(asm)] + +use std::mem::size_of; + +trait Proj { + const C: usize; +} +impl Proj for i8 { + const C: usize = 8; +} +impl Proj for i16 { + const C: usize = 16; +} + +const fn constfn(x: usize) -> usize { + x +} + +fn generic() { + unsafe { + let a: usize; + asm!("mov {}, {}", out(reg) a, const size_of::()); + assert_eq!(a, size_of::()); + + let b: usize; + asm!("mov {}, {}", out(reg) b, const size_of::() + constfn(5)); + assert_eq!(b, size_of::() + 5); + + let c: usize; + asm!("mov {}, {}", out(reg) c, const T::C); + assert_eq!(c, T::C); + } +} + +fn main() { + unsafe { + let a: usize; + asm!("mov {}, {}", out(reg) a, const 5); + assert_eq!(a, 5); + + let b: usize; + asm!("mov {}, {}", out(reg) b, const constfn(5)); + assert_eq!(b, 5); + + let c: usize; + asm!("mov {}, {}", out(reg) c, const constfn(5) + constfn(5)); + assert_eq!(c, 10); + } + + generic::(); + generic::(); +}