Auto merge of #29735 - Amanieu:asm_indirect_constraint, r=pnkfelix

This PR reverts #29543 and instead implements proper support for "=*m" and "+*m" indirect output operands. This provides a framework on top of which support for plain memory operands ("m", "=m" and "+m") can be implemented.

This also fixes the liveness analysis pass not handling read/write operands correctly.
This commit is contained in:
bors 2015-12-14 13:48:41 +00:00
commit 50a02b43ba
17 changed files with 132 additions and 57 deletions

View File

@ -368,8 +368,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
}), pred);
let post_outputs = self.exprs(outputs.map(|a| {
debug!("cfg::construct InlineAsm id:{} output:{:?}", expr.id, a);
let &(_, ref expr, _) = a;
&**expr
&*a.expr
}), post_inputs);
self.add_ast_node(expr.id, &[post_outputs])
}

View File

@ -458,9 +458,13 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
self.consume_expr(&**input);
}
for &(_, ref output, is_rw) in &ia.outputs {
self.mutate_expr(expr, &**output,
if is_rw { WriteAndRead } else { JustWrite });
for output in &ia.outputs {
if output.is_indirect {
self.consume_expr(&*output.expr);
} else {
self.mutate_expr(expr, &*output.expr,
if output.is_rw { WriteAndRead } else { JustWrite });
}
}
}

View File

@ -1166,12 +1166,19 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
hir::ExprInlineAsm(ref ia) => {
let succ = ia.outputs.iter().rev().fold(succ, |succ, &(_, ref expr, _)| {
// see comment on lvalues
// in propagate_through_lvalue_components()
let succ = self.write_lvalue(&**expr, succ, ACC_WRITE);
self.propagate_through_lvalue_components(&**expr, succ)
});
let succ = ia.outputs.iter().rev().fold(succ,
|succ, out| {
// see comment on lvalues
// in propagate_through_lvalue_components()
if out.is_indirect {
self.propagate_through_expr(&*out.expr, succ)
} else {
let acc = if out.is_rw { ACC_WRITE|ACC_READ } else { ACC_WRITE };
let succ = self.write_lvalue(&*out.expr, succ, acc);
self.propagate_through_lvalue_components(&*out.expr, succ)
}
}
);
// Inputs are executed first. Propagate last because of rev order
ia.inputs.iter().rev().fold(succ, |succ, &(_, ref expr)| {
self.propagate_through_expr(&**expr, succ)
@ -1416,9 +1423,11 @@ fn check_expr(this: &mut Liveness, expr: &Expr) {
}
// Output operands must be lvalues
for &(_, ref out, _) in &ia.outputs {
this.check_lvalue(&**out);
this.visit_expr(&**out);
for out in &ia.outputs {
if !out.is_indirect {
this.check_lvalue(&*out.expr);
}
this.visit_expr(&*out.expr);
}
intravisit::walk_expr(this, expr);

View File

@ -1117,7 +1117,14 @@ pub fn noop_fold_expr<T: Folder>(Expr { id, node, span, attrs }: Expr, folder: &
expn_id,
}) => ExprInlineAsm(InlineAsm {
inputs: inputs.move_map(|(c, input)| (c, folder.fold_expr(input))),
outputs: outputs.move_map(|(c, out, is_rw)| (c, folder.fold_expr(out), is_rw)),
outputs: outputs.move_map(|out| {
InlineAsmOutput {
constraint: out.constraint,
expr: folder.fold_expr(out.expr),
is_rw: out.is_rw,
is_indirect: out.is_indirect,
}
}),
asm: asm,
asm_str_style: asm_str_style,
clobbers: clobbers,

View File

@ -953,11 +953,19 @@ pub enum Ty_ {
TyInfer,
}
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct InlineAsmOutput {
pub constraint: InternedString,
pub expr: P<Expr>,
pub is_rw: bool,
pub is_indirect: bool,
}
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct InlineAsm {
pub asm: InternedString,
pub asm_str_style: StrStyle,
pub outputs: Vec<(InternedString, P<Expr>, bool)>,
pub outputs: Vec<InlineAsmOutput>,
pub inputs: Vec<(InternedString, P<Expr>)>,
pub clobbers: Vec<InternedString>,
pub volatile: bool,

View File

@ -803,8 +803,8 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
for &(_, ref input) in &ia.inputs {
visitor.visit_expr(&input)
}
for &(_, ref output, _) in &ia.outputs {
visitor.visit_expr(&output)
for output in &ia.outputs {
visitor.visit_expr(&output.expr)
}
}
}

View File

@ -1228,8 +1228,13 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
.map(|&(ref c, ref input)| (c.clone(), lower_expr(lctx, input)))
.collect(),
outputs: outputs.iter()
.map(|&(ref c, ref out, ref is_rw)| {
(c.clone(), lower_expr(lctx, out), *is_rw)
.map(|out| {
hir::InlineAsmOutput {
constraint: out.constraint.clone(),
expr: lower_expr(lctx, &out.expr),
is_rw: out.is_rw,
is_indirect: out.is_indirect,
}
})
.collect(),
asm: asm.clone(),

View File

@ -1502,15 +1502,15 @@ impl<'a> State<'a> {
try!(self.print_string(&a.asm, a.asm_str_style));
try!(self.word_space(":"));
try!(self.commasep(Inconsistent, &a.outputs, |s, &(ref co, ref o, is_rw)| {
match co.slice_shift_char() {
Some(('=', operand)) if is_rw => {
try!(self.commasep(Inconsistent, &a.outputs, |s, out| {
match out.constraint.slice_shift_char() {
Some(('=', operand)) if out.is_rw => {
try!(s.print_string(&format!("+{}", operand), ast::CookedStr))
}
_ => try!(s.print_string(&co, ast::CookedStr)),
_ => try!(s.print_string(&out.constraint, ast::CookedStr)),
}
try!(s.popen());
try!(s.print_expr(&**o));
try!(s.print_expr(&*out.expr));
try!(s.pclose());
Ok(())
}));

View File

@ -39,27 +39,39 @@ pub fn trans_inline_asm<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ia: &ast::InlineAsm)
let mut ext_constraints = Vec::new();
// Prepare the output operands
let outputs = ia.outputs.iter().enumerate().map(|(i, &(ref c, ref out, is_rw))| {
constraints.push((*c).clone());
let mut outputs = Vec::new();
let mut inputs = Vec::new();
for (i, out) in ia.outputs.iter().enumerate() {
constraints.push(out.constraint.clone());
let out_datum = unpack_datum!(bcx, expr::trans(bcx, &**out));
output_types.push(type_of::type_of(bcx.ccx(), out_datum.ty));
let val = out_datum.val;
if is_rw {
let out_datum = unpack_datum!(bcx, expr::trans(bcx, &*out.expr));
if out.is_indirect {
bcx = callee::trans_arg_datum(bcx,
expr_ty(bcx, &**out),
expr_ty(bcx, &*out.expr),
out_datum,
cleanup::CustomScope(temp_scope),
callee::DontAutorefArg,
&mut ext_inputs);
ext_constraints.push(i.to_string());
&mut inputs);
if out.is_rw {
ext_inputs.push(*inputs.last().unwrap());
ext_constraints.push(i.to_string());
}
} else {
output_types.push(type_of::type_of(bcx.ccx(), out_datum.ty));
outputs.push(out_datum.val);
if out.is_rw {
bcx = callee::trans_arg_datum(bcx,
expr_ty(bcx, &*out.expr),
out_datum,
cleanup::CustomScope(temp_scope),
callee::DontAutorefArg,
&mut ext_inputs);
ext_constraints.push(i.to_string());
}
}
val
}).collect::<Vec<_>>();
}
// Now the input operands
let mut inputs = Vec::new();
for &(ref c, ref input) in &ia.inputs {
constraints.push((*c).clone());

View File

@ -480,8 +480,8 @@ fn walk_expr(cx: &CrateContext,
walk_expr(cx, &**exp, scope_stack, scope_map);
}
for &(_, ref exp, _) in outputs {
walk_expr(cx, &**exp, scope_stack, scope_map);
for out in outputs {
walk_expr(cx, &*out.expr, scope_stack, scope_map);
}
}
}

View File

@ -3394,8 +3394,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
for &(_, ref input) in &ia.inputs {
check_expr(fcx, &**input);
}
for &(_, ref out, _) in &ia.outputs {
check_expr(fcx, &**out);
for out in &ia.outputs {
check_expr(fcx, &*out.expr);
}
fcx.write_nil(id);
}

View File

@ -1458,11 +1458,19 @@ pub enum AsmDialect {
Intel,
}
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct InlineAsmOutput {
pub constraint: InternedString,
pub expr: P<Expr>,
pub is_rw: bool,
pub is_indirect: bool,
}
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct InlineAsm {
pub asm: InternedString,
pub asm_str_style: StrStyle,
pub outputs: Vec<(InternedString, P<Expr>, bool)>,
pub outputs: Vec<InlineAsmOutput>,
pub inputs: Vec<(InternedString, P<Expr>)>,
pub clobbers: Vec<InternedString>,
pub volatile: bool,

View File

@ -125,7 +125,13 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
};
let is_rw = output.is_some();
outputs.push((output.unwrap_or(constraint), out, is_rw));
let is_indirect = constraint.contains("*");
outputs.push(ast::InlineAsmOutput {
constraint: output.unwrap_or(constraint),
expr: out,
is_rw: is_rw,
is_indirect: is_indirect,
});
}
}
Inputs => {
@ -139,9 +145,9 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
let (constraint, _str_style) = panictry!(p.parse_str());
if constraint.starts_with("=") && !constraint.contains("*") {
if constraint.starts_with("=") {
cx.span_err(p.last_span, "input operand constraint contains '='");
} else if constraint.starts_with("+") && !constraint.contains("*") {
} else if constraint.starts_with("+") {
cx.span_err(p.last_span, "input operand constraint contains '+'");
}

View File

@ -1303,8 +1303,13 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu
inputs: inputs.move_map(|(c, input)| {
(c, folder.fold_expr(input))
}),
outputs: outputs.move_map(|(c, out, is_rw)| {
(c, folder.fold_expr(out), is_rw)
outputs: outputs.move_map(|out| {
InlineAsmOutput {
constraint: out.constraint,
expr: folder.fold_expr(out.expr),
is_rw: out.is_rw,
is_indirect: out.is_indirect,
}
}),
asm: asm,
asm_str_style: asm_str_style,

View File

@ -2219,16 +2219,16 @@ impl<'a> State<'a> {
try!(self.word_space(":"));
try!(self.commasep(Inconsistent, &a.outputs,
|s, &(ref co, ref o, is_rw)| {
match co.slice_shift_char() {
Some(('=', operand)) if is_rw => {
|s, out| {
match out.constraint.slice_shift_char() {
Some(('=', operand)) if out.is_rw => {
try!(s.print_string(&format!("+{}", operand),
ast::CookedStr))
}
_ => try!(s.print_string(&co, ast::CookedStr))
_ => try!(s.print_string(&out.constraint, ast::CookedStr))
}
try!(s.popen());
try!(s.print_expr(&**o));
try!(s.print_expr(&*out.expr));
try!(s.pclose());
Ok(())
}));

View File

@ -786,8 +786,8 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
for &(_, ref input) in &ia.inputs {
visitor.visit_expr(&input)
}
for &(_, ref output, _) in &ia.outputs {
visitor.visit_expr(&output)
for output in &ia.outputs {
visitor.visit_expr(&output.expr)
}
}
}

View File

@ -22,17 +22,29 @@ fn read(ptr: &u32) -> u32 {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn write(ptr: &mut u32, val: u32) {
unsafe {
asm!("mov $1, $0" :: "=*m" (ptr), "r" (val));
asm!("mov $1, $0" : "=*m" (ptr) : "r" (val));
}
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn replace(ptr: &mut u32, val: u32) -> u32 {
let out: u32;
unsafe {
asm!("mov $0, $1; mov $2, $0" : "+*m" (ptr), "=&r" (out) : "r" (val));
}
out
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub fn main() {
let a = 1;
let mut b = 2;
assert_eq!(read(&a), 1);
let mut b = 2;
write(&mut b, 3);
assert_eq!(b, 3);
let mut c = 4;
assert_eq!(replace(&mut c, 5), 4);
assert_eq!(c, 5);
}
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]