Fix const handling and add tests for const operands

This commit is contained in:
Amanieu d'Antras 2020-05-11 17:53:32 +01:00
parent 6f8be8cc8c
commit cecffdc1d7
2 changed files with 184 additions and 91 deletions

View File

@ -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<mir::BasicBlock>,
) {
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,
);
}
}
}

56
src/test/ui/asm/const.rs Normal file
View File

@ -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<T: Proj>() {
unsafe {
let a: usize;
asm!("mov {}, {}", out(reg) a, const size_of::<T>());
assert_eq!(a, size_of::<T>());
let b: usize;
asm!("mov {}, {}", out(reg) b, const size_of::<T>() + constfn(5));
assert_eq!(b, size_of::<T>() + 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::<i8>();
generic::<i16>();
}